You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@skywalking.apache.org by wu...@apache.org on 2026/04/15 03:48:22 UTC
(skywalking) branch master updated: PromQL: support querying Zipkin metadata and TraceQL support more tags and variables querying in Grafana (#13819)
This is an automated email from the ASF dual-hosted git repository.
wusheng pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/skywalking.git
The following commit(s) were added to refs/heads/master by this push:
new 9ea3bab32f PromQL: support querying Zipkin metadata and TraceQL support more tags and variables querying in Grafana (#13819)
9ea3bab32f is described below
commit 9ea3bab32f8344ece2949f640a2baf52d7c6d465
Author: Wan Kai <wa...@foxmail.com>
AuthorDate: Wed Apr 15 11:48:16 2026 +0800
PromQL: support querying Zipkin metadata and TraceQL support more tags and variables querying in Grafana (#13819)
---
docs/en/changes/changes.md | 2 +
docs/en/setup/backend/ui-grafana.md | 13 ++-
.../server-query-plugin/promql-plugin/pom.xml | 5 +
.../oap/query/promql/entity/LabelName.java | 5 +-
.../oap/query/promql/handler/PromQLApiHandler.java | 122 +++++++++++++++++++++
.../oap/query/promql/rt/PromQLMatchVisitor.java | 12 +-
.../handler/SkyWalkingTraceQLApiHandler.java | 72 ++++++------
.../query/traceql/handler/TraceQLApiHandler.java | 4 +
.../traceql/handler/ZipkinTraceQLApiHandler.java | 31 ++++--
.../oap/query/traceql/rt/TraceQLQueryParams.java | 12 +-
.../oap/query/traceql/rt/TraceQLQueryVisitor.java | 7 +-
.../query/tempo/parser/TraceQLQueryParserTest.java | 30 +++++
12 files changed, 254 insertions(+), 61 deletions(-)
diff --git a/docs/en/changes/changes.md b/docs/en/changes/changes.md
index 28f09e090f..375ef49e9d 100644
--- a/docs/en/changes/changes.md
+++ b/docs/en/changes/changes.md
@@ -21,6 +21,8 @@
* Fix duplicate calls in `EndpointTopologyBuilder` — calls were not deduplicated unlike `ServiceTopologyBuilder`, causing duplicate entries when storage returns multiple records for the same relation.
* Use `containsOnce` and `noDuplicates` for topology dependency e2e expected files to enforce no-duplicate verification.
* Bump infra-e2e to `ef073ad` to include `noDuplicates` pipe function support.
+* PromQL: support querying Zipkin metadata (service name, remote service name, span name).
+* TraceQL: support more tags and variables in Grafana for querying.
#### UI
diff --git a/docs/en/setup/backend/ui-grafana.md b/docs/en/setup/backend/ui-grafana.md
index 19173b5d64..1f52506c9e 100644
--- a/docs/en/setup/backend/ui-grafana.md
+++ b/docs/en/setup/backend/ui-grafana.md
@@ -139,20 +139,27 @@ The following steps are an example of configuring a `Trace` dashboard for SkyWal
1. Go to the `Explore` page, select the `Tempo` data source named `SkyWalkingTraceQL` (the name you configured). Add it to the dashboard and save it as `SkyWalking Trace`.
<img src="https://skywalking.apache.org/screenshots/10.4.0/traceql/grafana-tempo-skywalking-explore.png"/>
2. Configure variables for the dashboard:
-<img src="https://skywalking.apache.org/screenshots/10.4.0/traceql/grafana-tempo-skywalking-variables.png"/>
+***Notice:*** The variables (Service/Instance/Endpoint) querying leverage the Prometheus data source `SkyWalkingPromQL(DS_SW_Prometheus)`.
+<img src="https://skywalking.apache.org/screenshots/10.5.0/traceql/grafana-tempo-skywalking-variables.png"/>
+3. If you want the Service/Instance/Endpoint variables to support the `All` option,
+enable `Allow custom values` and set `Custom all value` to `*` so it matches the backend wildcard.
+<img src="https://skywalking.apache.org/screenshots/10.5.0/traceql/grafana-tempo-skywalking-variables-all.png"/>
#### Add Trace Panel
The following steps show how to add a trace panel for SkyWalking native trace.
1. Choose `Table` chart or edit your saved panel.
-2. Set `Query type` to `Search` and set the query condition `Service Name` to variable `$Service`.
+2. Set `Query type` to `Search` and set the query condition to variables, such as `Service Name` to `$Service`.
3. Set other query conditions if needed.
4. Test query and save the panel.
-<img src="https://skywalking.apache.org/screenshots/10.4.0/traceql/grafana-tempo-skywalking-panel.png"/>
+<img src="https://skywalking.apache.org/screenshots/10.5.0/traceql/grafana-tempo-skywalking-panel.png"/>
5. If you click the trace ID on the trace panel, it will jump to the explore page and show the trace details.
<img src="https://skywalking.apache.org/screenshots/10.4.0/traceql/grafana-tempo-skywalking-trace-detail.png"/>
### Zipkin Trace
Same as the SkyWalking native trace, but use the Zipkin Tempo data source you configured.
+The following are the variables and trace panel configuration screenshots for Zipkin trace:
+<img src="https://skywalking.apache.org/screenshots/10.5.0/traceql/grafana-tempo-zipkin-variables.png"/>
+<img src="https://skywalking.apache.org/screenshots/10.5.0/traceql/grafana-tempo-zipkin-panel.png"/>
## Preview on demo.skywalking.a.o
SkyWalking community provides a preview site for services of `General` and `Service Mesh` layers from the demo environment.
diff --git a/oap-server/server-query-plugin/promql-plugin/pom.xml b/oap-server/server-query-plugin/promql-plugin/pom.xml
index 8428116b04..090546dc98 100644
--- a/oap-server/server-query-plugin/promql-plugin/pom.xml
+++ b/oap-server/server-query-plugin/promql-plugin/pom.xml
@@ -39,6 +39,11 @@
<artifactId>query-graphql-plugin</artifactId>
<version>${project.version}</version>
</dependency>
+ <dependency>
+ <groupId>org.apache.skywalking</groupId>
+ <artifactId>zipkin-query-plugin</artifactId>
+ <version>${project.version}</version>
+ </dependency>
<dependency>
<groupId>org.antlr</groupId>
<artifactId>antlr4-runtime</artifactId>
diff --git a/oap-server/server-query-plugin/promql-plugin/src/main/java/org/apache/skywalking/oap/query/promql/entity/LabelName.java b/oap-server/server-query-plugin/promql-plugin/src/main/java/org/apache/skywalking/oap/query/promql/entity/LabelName.java
index a891dec704..630dee356c 100644
--- a/oap-server/server-query-plugin/promql-plugin/src/main/java/org/apache/skywalking/oap/query/promql/entity/LabelName.java
+++ b/oap-server/server-query-plugin/promql-plugin/src/main/java/org/apache/skywalking/oap/query/promql/entity/LabelName.java
@@ -51,7 +51,10 @@ public enum LabelName {
//for service_instance_relation_traffic
DEST_SERVICE_INSTANCE("dest_service_instance"),
//for endpoint_relation
- DEST_ENDPOINT("dest_endpoint");
+ DEST_ENDPOINT("dest_endpoint"),
+ //for zipkin
+ REMOTE_SERVICE("remote_service"),
+ SPAN_NAME("span_name");
final String label;
diff --git a/oap-server/server-query-plugin/promql-plugin/src/main/java/org/apache/skywalking/oap/query/promql/handler/PromQLApiHandler.java b/oap-server/server-query-plugin/promql-plugin/src/main/java/org/apache/skywalking/oap/query/promql/handler/PromQLApiHandler.java
index 4fa5a286a5..8fae3dbc39 100644
--- a/oap-server/server-query-plugin/promql-plugin/src/main/java/org/apache/skywalking/oap/query/promql/handler/PromQLApiHandler.java
+++ b/oap-server/server-query-plugin/promql-plugin/src/main/java/org/apache/skywalking/oap/query/promql/handler/PromQLApiHandler.java
@@ -78,6 +78,7 @@ import org.apache.skywalking.oap.query.promql.rt.result.MetricsRangeResult;
import org.apache.skywalking.oap.query.promql.rt.PromQLExprQueryVisitor;
import org.apache.skywalking.oap.query.promql.rt.result.ParseResult;
import org.apache.skywalking.oap.query.promql.rt.result.ScalarResult;
+import org.apache.skywalking.oap.query.zipkin.ZipkinQueryService;
import org.apache.skywalking.oap.server.core.CoreModule;
import org.apache.skywalking.oap.server.core.analysis.IDManager;
import org.apache.skywalking.oap.server.core.analysis.Layer;
@@ -101,6 +102,9 @@ import org.apache.skywalking.oap.server.core.query.type.Service;
import org.apache.skywalking.oap.server.core.query.type.ServiceInstance;
import org.apache.skywalking.oap.server.core.storage.annotation.Column;
import org.apache.skywalking.oap.server.core.storage.annotation.ValueColumnMetadata;
+import org.apache.skywalking.oap.server.core.zipkin.ZipkinServiceRelationTraffic;
+import org.apache.skywalking.oap.server.core.zipkin.ZipkinServiceSpanTraffic;
+import org.apache.skywalking.oap.server.core.zipkin.ZipkinServiceTraffic;
import org.apache.skywalking.oap.server.library.module.ModuleManager;
import org.apache.skywalking.oap.server.library.util.StringUtil;
import org.apache.skywalking.promql.rt.grammar.PromQLLexer;
@@ -113,6 +117,7 @@ public class PromQLApiHandler {
private final MetadataQueryV2 metadataQuery;
private final ModuleManager moduleManager;
private final PromQLConfig config;
+ private final ZipkinQueryService zipkinQueryService;
private MetricsMetadataQueryService metricsMetadataQueryService;
private MetricsQueryService metricsQueryService;
private AggregationQueryService aggregationQueryService;
@@ -135,6 +140,7 @@ public class PromQLApiHandler {
public PromQLApiHandler(ModuleManager moduleManager, PromQLConfig config) {
this.metadataQuery = new MetadataQueryV2(moduleManager);
this.moduleManager = moduleManager;
+ this.zipkinQueryService = new ZipkinQueryService(moduleManager);
this.config = config;
}
@@ -334,6 +340,23 @@ public class PromQLApiHandler {
endpoint -> {
response.getData().add(endpoint.getName());
});
+ } else if (Objects.equals(metricName, ZipkinServiceTraffic.INDEX_NAME)) {
+ queryZipkinServiceTraffic(parseResult, limitNum).forEach(service -> {
+ response.getData().add(service);
+ });
+ } else if (Objects.equals(metricName, ZipkinServiceRelationTraffic.INDEX_NAME)) {
+ // When the service name contains special characters, such as dot,
+ // Grafana variables query will escape it when pass to API, we need to remove the escapes before query.
+ // For example, service name "order.service" will be escaped to "order\\.service", we need to remove the escape character "\" before query.
+ String serviceName = removeEscapes(parseResult.getLabelMap().get(LabelName.SERVICE.getLabel()));
+ queryZipkinServiceRelationTraffic(parseResult, serviceName, limitNum).forEach(relation -> {
+ response.getData().add(relation);
+ });
+ } else if (Objects.equals(metricName, ZipkinServiceSpanTraffic.INDEX_NAME)) {
+ String serviceName = removeEscapes(parseResult.getLabelMap().get(LabelName.SERVICE.getLabel()));
+ queryZipkinServiceSpanTraffic(parseResult, serviceName, limitNum).forEach(span -> {
+ response.getData().add(span);
+ });
}
} catch (IllegalExpressionException e) {
response.setStatus(ResultStatus.ERROR);
@@ -395,6 +418,28 @@ public class PromQLApiHandler {
endpoint -> {
response.getData().add(buildMetricInfoFromTraffic(metricName, endpoint));
});
+ } else if (Objects.equals(metricName, ZipkinServiceTraffic.INDEX_NAME)) {
+ queryZipkinServiceTraffic(parseResult, limitNum).forEach(name -> {
+ MetricInfo metricInfo = new MetricInfo(metricName);
+ metricInfo.getLabels().add(new LabelValuePair(LabelName.SERVICE.getLabel(), name));
+ response.getData().add(metricInfo);
+ });
+ } else if (Objects.equals(metricName, ZipkinServiceRelationTraffic.INDEX_NAME)) {
+ String serviceName = removeEscapes(parseResult.getLabelMap().get(LabelName.SERVICE.getLabel()));
+ queryZipkinServiceRelationTraffic(parseResult, serviceName, limitNum).forEach(name -> {
+ MetricInfo metricInfo = new MetricInfo(metricName);
+ metricInfo.getLabels().add(new LabelValuePair(LabelName.SERVICE.getLabel(), serviceName));
+ metricInfo.getLabels().add(new LabelValuePair(LabelName.REMOTE_SERVICE.getLabel(), name));
+ response.getData().add(metricInfo);
+ });
+ } else if (Objects.equals(metricName, ZipkinServiceSpanTraffic.INDEX_NAME)) {
+ String serviceName = removeEscapes(parseResult.getLabelMap().get(LabelName.SERVICE.getLabel()));
+ queryZipkinServiceSpanTraffic(parseResult, serviceName, limitNum).forEach(name -> {
+ MetricInfo metricInfo = new MetricInfo(metricName);
+ metricInfo.getLabels().add(new LabelValuePair(LabelName.SERVICE.getLabel(), serviceName));
+ metricInfo.getLabels().add(new LabelValuePair(LabelName.SPAN_NAME.getLabel(), name));
+ response.getData().add(metricInfo);
+ });
}
} catch (IllegalExpressionException e) {
response.setStatus(ResultStatus.ERROR);
@@ -869,6 +914,83 @@ public class PromQLApiHandler {
return result;
}
+ private List<String> queryZipkinServiceTraffic(MatcherSetResult parseResult, int limitNum) throws IOException {
+ List<String> result = new ArrayList<>();
+ final List<String> serviceNames = zipkinQueryService.getServiceNames();
+ MatcherSetResult.NameMatcher matcher = parseResult.getNameMatcher();
+ if (matcher != null) {
+ String serviceName = matcher.getMatchString();
+ if (matcher.getMatchOp() == PromQLParser.EQ) {
+ serviceNames.stream()
+ .filter(s -> s.equals(serviceName)).findFirst().ifPresent(result::add);
+ } else if (matcher.getMatchOp() == PromQLParser.NEQ) {
+ serviceNames.stream()
+ .filter(s -> !s.equals(serviceName)).limit(limitNum).forEach(result::add);
+ } else if (matcher.getMatchOp() == PromQLParser.RM) {
+ serviceNames.stream()
+ .filter(s -> s.matches(serviceName)).limit(limitNum).forEach(result::add);
+ } else if (matcher.getMatchOp() == PromQLParser.NRM) {
+ serviceNames.stream()
+ .filter(s -> !s.matches(serviceName)).limit(limitNum).forEach(result::add);
+ }
+ } else {
+ serviceNames.stream().limit(limitNum).forEach(result::add);
+ }
+ return result;
+ }
+
+ private List<String> queryZipkinServiceRelationTraffic(MatcherSetResult parseResult, String serviceName, int limitNum) throws IOException, IllegalExpressionException {
+ List<String> result = new ArrayList<>();
+ if (StringUtil.isBlank(serviceName)) {
+ throw new IllegalExpressionException("label {service} should not be empty.");
+ }
+ final List<String> remoteServices = zipkinQueryService.getRemoteServiceNames(serviceName);
+ MatcherSetResult.NameMatcher matcher = parseResult.getNameMatcher();
+ if (matcher != null) {
+ String remoteServiceName = matcher.getMatchString();
+ if (matcher.getMatchOp() == PromQLParser.EQ) {
+ remoteServices.stream().filter(s -> s.equals(remoteServiceName)).findFirst().ifPresent(result::add);
+ } else if (matcher.getMatchOp() == PromQLParser.NEQ) {
+ remoteServices.stream().filter(s -> !s.equals(remoteServiceName)).limit(limitNum).forEach(result::add);
+ } else if (matcher.getMatchOp() == PromQLParser.RM) {
+ remoteServices.stream().filter(s -> s.matches(remoteServiceName)).limit(limitNum).forEach(result::add);
+ } else if (matcher.getMatchOp() == PromQLParser.NRM) {
+ remoteServices.stream().filter(s -> !s.matches(remoteServiceName)).limit(limitNum).forEach(result::add);
+ }
+ } else {
+ remoteServices.stream().limit(limitNum).forEach(result::add);
+ }
+ return result;
+ }
+
+ private List<String> queryZipkinServiceSpanTraffic(MatcherSetResult parseResult, String serviceName, int limitNum) throws IOException, IllegalExpressionException {
+ List<String> result = new ArrayList<>();
+ if (StringUtil.isBlank(serviceName)) {
+ throw new IllegalExpressionException("label {service} should not be empty.");
+ }
+ final List<String> spanNames = zipkinQueryService.getSpanNames(serviceName);
+ MatcherSetResult.NameMatcher matcher = parseResult.getNameMatcher();
+ if (matcher != null) {
+ String spanName = matcher.getMatchString();
+ if (matcher.getMatchOp() == PromQLParser.EQ) {
+ spanNames.stream().filter(s -> s.equals(spanName)).findFirst().ifPresent(result::add);
+ } else if (matcher.getMatchOp() == PromQLParser.NEQ) {
+ spanNames.stream().filter(s -> !s.equals(spanName)).limit(limitNum).forEach(result::add);
+ } else if (matcher.getMatchOp() == PromQLParser.RM) {
+ spanNames.stream().filter(s -> s.matches(spanName)).limit(limitNum).forEach(result::add);
+ } else if (matcher.getMatchOp() == PromQLParser.NRM) {
+ spanNames.stream().filter(s -> !s.matches(spanName)).limit(limitNum).forEach(result::add);
+ }
+ } else {
+ spanNames.stream().limit(limitNum).forEach(result::add);
+ }
+ return result;
+ }
+
+ public static String removeEscapes(String input) {
+ return input == null ? null : input.replace("\\", "");
+ }
+
public enum QueryType {
INSTANT,
RANGE,
diff --git a/oap-server/server-query-plugin/promql-plugin/src/main/java/org/apache/skywalking/oap/query/promql/rt/PromQLMatchVisitor.java b/oap-server/server-query-plugin/promql-plugin/src/main/java/org/apache/skywalking/oap/query/promql/rt/PromQLMatchVisitor.java
index 447038d4a5..1a695382ff 100644
--- a/oap-server/server-query-plugin/promql-plugin/src/main/java/org/apache/skywalking/oap/query/promql/rt/PromQLMatchVisitor.java
+++ b/oap-server/server-query-plugin/promql-plugin/src/main/java/org/apache/skywalking/oap/query/promql/rt/PromQLMatchVisitor.java
@@ -25,6 +25,9 @@ import org.apache.skywalking.oap.query.promql.rt.result.ParseResultType;
import org.apache.skywalking.oap.server.core.analysis.manual.endpoint.EndpointTraffic;
import org.apache.skywalking.oap.server.core.analysis.manual.instance.InstanceTraffic;
import org.apache.skywalking.oap.server.core.analysis.manual.service.ServiceTraffic;
+import org.apache.skywalking.oap.server.core.zipkin.ZipkinServiceRelationTraffic;
+import org.apache.skywalking.oap.server.core.zipkin.ZipkinServiceSpanTraffic;
+import org.apache.skywalking.oap.server.core.zipkin.ZipkinServiceTraffic;
import org.apache.skywalking.promql.rt.grammar.PromQLParser;
import org.apache.skywalking.promql.rt.grammar.PromQLParserBaseVisitor;
@@ -46,7 +49,14 @@ public class PromQLMatchVisitor extends PromQLParserBaseVisitor<MatcherSetResult
|| ((metricName.equals(InstanceTraffic.INDEX_NAME)) && LabelName.SERVICE_INSTANCE.getLabel()
.equals(labelName))
|| ((metricName.equals(EndpointTraffic.INDEX_NAME)) && LabelName.ENDPOINT.getLabel()
- .equals(labelName))) {
+ .equals(labelName))
+ || ((metricName.equals(ZipkinServiceTraffic.INDEX_NAME)) && LabelName.SERVICE.getLabel()
+ .equals(labelName))
+ || ((metricName.equals(ZipkinServiceRelationTraffic.INDEX_NAME)) && LabelName.REMOTE_SERVICE.getLabel()
+ .equals(labelName))
+ || ((metricName.equals(ZipkinServiceSpanTraffic.INDEX_NAME)) && LabelName.SPAN_NAME.getLabel()
+ .equals(
+ labelName))) {
result.setNameMatcher(
new MatcherSetResult.NameMatcher(
metricName, labelValueTrim, labelCtx.matchOp().getStart().getType()));
diff --git a/oap-server/server-query-plugin/traceql-plugin/src/main/java/org/apache/skywalking/oap/query/traceql/handler/SkyWalkingTraceQLApiHandler.java b/oap-server/server-query-plugin/traceql-plugin/src/main/java/org/apache/skywalking/oap/query/traceql/handler/SkyWalkingTraceQLApiHandler.java
index 2f0e054157..2e472b7bd0 100644
--- a/oap-server/server-query-plugin/traceql-plugin/src/main/java/org/apache/skywalking/oap/query/traceql/handler/SkyWalkingTraceQLApiHandler.java
+++ b/oap-server/server-query-plugin/traceql-plugin/src/main/java/org/apache/skywalking/oap/query/traceql/handler/SkyWalkingTraceQLApiHandler.java
@@ -161,18 +161,23 @@ public class SkyWalkingTraceQLApiHandler extends TraceQLApiHandler {
condition.setQueryDuration(duration);
// Set service ID if service name is provided
- if (StringUtil.isNotBlank(queryParams.getServiceName())) {
+ if (StringUtil.isNotBlank(queryParams.getServiceName())
+ && !queryParams.getServiceName().equals(ALL)) {
String serviceId = IDManager.ServiceID.buildId(queryParams.getServiceName(), true);
condition.setServiceId(serviceId);
}
- if (StringUtil.isNotBlank(queryParams.getServiceInstance()) && StringUtil.isNotBlank(condition.getServiceId())) {
+ if (StringUtil.isNotBlank(queryParams.getServiceInstance())
+ && StringUtil.isNotBlank(condition.getServiceId())
+ && !queryParams.getServiceInstance().equals(ALL)) {
String instanceId = IDManager.ServiceInstanceID.buildId(condition.getServiceId(), queryParams.getServiceInstance());
condition.setServiceInstanceId(instanceId);
}
// Set endpoint ID if span name is provided
- if (StringUtil.isNotBlank(queryParams.getSpanName()) && StringUtil.isNotBlank(condition.getServiceId())) {
+ if (StringUtil.isNotBlank(queryParams.getSpanName())
+ && StringUtil.isNotBlank(condition.getServiceId())
+ && !queryParams.getSpanName().equals(ALL)) {
// Use IDManager to build endpoint ID
condition.setEndpointId(
IDManager.EndpointID.buildId(condition.getServiceId(), queryParams.getSpanName())
@@ -330,7 +335,7 @@ public class SkyWalkingTraceQLApiHandler extends TraceQLApiHandler {
}
// Handle 'name' tag with TraceQL query filter
- if (tagName.equals(NAME)) {
+ if (tagName.equals(NAME) || tagName.equals(RESOURCE_INSTANCE)) {
if (query.isPresent() && !query.get().isEmpty()) {
TraceQLParseResult parseResult = TraceQLQueryParser.extractParams(query.get());
if (parseResult.hasError()) {
@@ -338,43 +343,30 @@ public class SkyWalkingTraceQLApiHandler extends TraceQLApiHandler {
}
TraceQLQueryParams traceQLParams = parseResult.getParams();
TagValuesResponse response = new TagValuesResponse();
- if (StringUtil.isNotBlank(traceQLParams.getServiceName())) {
+ if (StringUtil.isNotBlank(traceQLParams.getServiceName()) && !traceQLParams.getServiceName().equals(ALL)) {
String serviceId = IDManager.ServiceID.buildId(traceQLParams.getServiceName(), true);
- List<Endpoint> endpoints = metadataQueryService.findEndpoint(
- "",
- serviceId,
- limit.orElse(100),
- duration
- );
-
- for (Endpoint endpoint : endpoints) {
- response.getTagValues().add(new TagValuesResponse.TagValue(TYPE_STRING, endpoint.getName()));
- }
- }
- return successResponse(response);
- } else {
- // Return empty list if no query provided, to avoid error as Grafana queries this every time when user enters the query page.
- return successResponse(new TagValuesResponse());
- }
- }
- if (tagName.equals(RESOURCE_INSTANCE)) {
- if (query.isPresent() && !query.get().isEmpty()) {
- TraceQLParseResult parseResult = TraceQLQueryParser.extractParams(query.get());
- if (parseResult.hasError()) {
- return badRequestResponse(parseResult.getErrorInfo());
- }
- TraceQLQueryParams traceQLParams = parseResult.getParams();
- TagValuesResponse response = new TagValuesResponse();
- if (StringUtil.isNotBlank(traceQLParams.getServiceName())) {
- String serviceId = IDManager.ServiceID.buildId(traceQLParams.getServiceName(), true);
- List<ServiceInstance> instances = metadataQueryService.listInstances(
- duration,
- serviceId
-
- );
-
- for (ServiceInstance instance : instances) {
- response.getTagValues().add(new TagValuesResponse.TagValue(TYPE_STRING, instance.getName()));
+ if (tagName.equals(NAME)) {
+ List<Endpoint> endpoints = metadataQueryService.findEndpoint(
+ "",
+ serviceId,
+ limit.orElse(100),
+ duration
+ );
+
+ for (Endpoint endpoint : endpoints) {
+ response.getTagValues()
+ .add(new TagValuesResponse.TagValue(TYPE_STRING, endpoint.getName()));
+ }
+ } else if (tagName.equals(RESOURCE_INSTANCE)) {
+ List<ServiceInstance> instances = metadataQueryService.listInstances(
+ duration,
+ serviceId
+
+ );
+
+ for (ServiceInstance instance : instances) {
+ response.getTagValues().add(new TagValuesResponse.TagValue(TYPE_STRING, instance.getName()));
+ }
}
}
return successResponse(response);
diff --git a/oap-server/server-query-plugin/traceql-plugin/src/main/java/org/apache/skywalking/oap/query/traceql/handler/TraceQLApiHandler.java b/oap-server/server-query-plugin/traceql-plugin/src/main/java/org/apache/skywalking/oap/query/traceql/handler/TraceQLApiHandler.java
index 09e10b1ec3..9ddd2f3447 100644
--- a/oap-server/server-query-plugin/traceql-plugin/src/main/java/org/apache/skywalking/oap/query/traceql/handler/TraceQLApiHandler.java
+++ b/oap-server/server-query-plugin/traceql-plugin/src/main/java/org/apache/skywalking/oap/query/traceql/handler/TraceQLApiHandler.java
@@ -61,9 +61,11 @@ public abstract class TraceQLApiHandler {
// Resource-scoped tag names
public static final String RESOURCE_SERVICE_NAME = "resource.service.name";
public static final String RESOURCE_SERVICE = "resource.service";
+ public static final String RESOURCE_REMOTE_SERVICE = "resource.remote.service";
public static final String RESOURCE_INSTANCE = "resource.instance";
public static final String SERVICE_NAME = "service.name";
public static final String SERVICE = "service";
+ public static final String REMOTE_SERVICE = "remote.service";
public static final String INSTANCE = "instance";
// Span-scoped tag prefix and names
@@ -76,6 +78,8 @@ public abstract class TraceQLApiHandler {
public static final String OK = "ok";
public static final String TYPE_STRING = "string";
+ public static final String ALL = "*";
+
@Get
@Path("/ready")
public HttpResponse ready() throws IOException {
diff --git a/oap-server/server-query-plugin/traceql-plugin/src/main/java/org/apache/skywalking/oap/query/traceql/handler/ZipkinTraceQLApiHandler.java b/oap-server/server-query-plugin/traceql-plugin/src/main/java/org/apache/skywalking/oap/query/traceql/handler/ZipkinTraceQLApiHandler.java
index e2e8fe18a5..5868d8112d 100644
--- a/oap-server/server-query-plugin/traceql-plugin/src/main/java/org/apache/skywalking/oap/query/traceql/handler/ZipkinTraceQLApiHandler.java
+++ b/oap-server/server-query-plugin/traceql-plugin/src/main/java/org/apache/skywalking/oap/query/traceql/handler/ZipkinTraceQLApiHandler.java
@@ -153,10 +153,16 @@ public class ZipkinTraceQLApiHandler extends TraceQLApiHandler {
TraceQLQueryParams traceQLParams = parseResult.getParams();
// Apply TraceQL parameters
- if (StringUtil.isNotBlank(traceQLParams.getServiceName())) {
+ if (StringUtil.isNotBlank(traceQLParams.getServiceName())
+ && !traceQLParams.getServiceName().equals(ALL)) {
queryRequestBuilder.serviceName(traceQLParams.getServiceName());
}
- if (StringUtil.isNotBlank(traceQLParams.getSpanName())) {
+ if (StringUtil.isNotBlank(traceQLParams.getRemoteServiceName())
+ && !traceQLParams.getRemoteServiceName().equals(ALL)) {
+ queryRequestBuilder.remoteServiceName(traceQLParams.getRemoteServiceName());
+ }
+ if (StringUtil.isNotBlank(traceQLParams.getSpanName())
+ && !traceQLParams.getSpanName().equals(ALL)) {
queryRequestBuilder.spanName(traceQLParams.getSpanName());
}
@@ -243,6 +249,7 @@ public class ZipkinTraceQLApiHandler extends TraceQLApiHandler {
duration
);
resourceScope.getTags().add(SERVICE);
+ resourceScope.getTags().add(REMOTE_SERVICE);
response.getScopes().add(resourceScope);
spanScope.getTags().addAll(tagKeys);
response.getScopes().add(spanScope);
@@ -288,7 +295,7 @@ public class ZipkinTraceQLApiHandler extends TraceQLApiHandler {
}
return successResponse(response);
}
- if (tagName.equals(NAME)) {
+ if (tagName.equals(NAME) || tagName.equals(RESOURCE_REMOTE_SERVICE)) {
if (query.isPresent() && !query.get().isEmpty()) {
TraceQLParseResult parseResult = TraceQLQueryParser.extractParams(query.get());
if (parseResult.hasError()) {
@@ -296,11 +303,19 @@ public class ZipkinTraceQLApiHandler extends TraceQLApiHandler {
}
TraceQLQueryParams traceQLParams = parseResult.getParams();
TagValuesResponse serviceNameRsp = new TagValuesResponse();
- if (StringUtil.isNotBlank(traceQLParams.getServiceName())) {
- List<String> spanNames = zipkinQueryService.getSpanNames(traceQLParams.getServiceName());
- for (String spanName : spanNames) {
- TagValuesResponse.TagValue tagValue = new TagValuesResponse.TagValue(TYPE_STRING, spanName);
- serviceNameRsp.getTagValues().add(tagValue);
+ if (StringUtil.isNotBlank(traceQLParams.getServiceName()) && !traceQLParams.getServiceName().equals(ALL)) {
+ if (tagName.equals(NAME)) {
+ List<String> spanNames = zipkinQueryService.getSpanNames(traceQLParams.getServiceName());
+ for (String spanName : spanNames) {
+ TagValuesResponse.TagValue tagValue = new TagValuesResponse.TagValue(TYPE_STRING, spanName);
+ serviceNameRsp.getTagValues().add(tagValue);
+ }
+ } else if (tagName.equals(RESOURCE_REMOTE_SERVICE)) {
+ List<String> remoteServiceNames = zipkinQueryService.getRemoteServiceNames(traceQLParams.getServiceName());
+ for (String rs : remoteServiceNames) {
+ TagValuesResponse.TagValue tagValue = new TagValuesResponse.TagValue(TYPE_STRING, rs);
+ serviceNameRsp.getTagValues().add(tagValue);
+ }
}
}
return successResponse(serviceNameRsp);
diff --git a/oap-server/server-query-plugin/traceql-plugin/src/main/java/org/apache/skywalking/oap/query/traceql/rt/TraceQLQueryParams.java b/oap-server/server-query-plugin/traceql-plugin/src/main/java/org/apache/skywalking/oap/query/traceql/rt/TraceQLQueryParams.java
index 325724105e..f1b5d385ae 100644
--- a/oap-server/server-query-plugin/traceql-plugin/src/main/java/org/apache/skywalking/oap/query/traceql/rt/TraceQLQueryParams.java
+++ b/oap-server/server-query-plugin/traceql-plugin/src/main/java/org/apache/skywalking/oap/query/traceql/rt/TraceQLQueryParams.java
@@ -33,7 +33,12 @@ public class TraceQLQueryParams {
private String serviceName;
/**
- * Service instance name filter
+ * remote service name filter, only for Zipkin traces
+ */
+ private String remoteServiceName;
+
+ /**
+ * Service instance name filter, only for SkyWalking native traces
*/
private String serviceInstance;
@@ -62,11 +67,6 @@ public class TraceQLQueryParams {
*/
private String httpStatusCode;
- /**
- * Span kind filter
- */
- private String kind;
-
/**
* Status filter
*/
diff --git a/oap-server/server-query-plugin/traceql-plugin/src/main/java/org/apache/skywalking/oap/query/traceql/rt/TraceQLQueryVisitor.java b/oap-server/server-query-plugin/traceql-plugin/src/main/java/org/apache/skywalking/oap/query/traceql/rt/TraceQLQueryVisitor.java
index 1882e9ec15..f86ad18ddf 100644
--- a/oap-server/server-query-plugin/traceql-plugin/src/main/java/org/apache/skywalking/oap/query/traceql/rt/TraceQLQueryVisitor.java
+++ b/oap-server/server-query-plugin/traceql-plugin/src/main/java/org/apache/skywalking/oap/query/traceql/rt/TraceQLQueryVisitor.java
@@ -52,6 +52,11 @@ public class TraceQLQueryVisitor extends TraceQLParserBaseVisitor<TraceQLParseRe
params.setServiceName(value);
}
break;
+ case "resource.remote.service":
+ if ("=".equals(operator)) {
+ params.setRemoteServiceName(value);
+ }
+ break;
case "resource.instance":
if ("=".equals(operator)) {
params.setServiceInstance(value);
@@ -105,8 +110,6 @@ public class TraceQLQueryVisitor extends TraceQLParserBaseVisitor<TraceQLParseRe
}
} else if ("status".equals(field)) {
params.setStatus(value);
- } else if ("kind".equals(field)) {
- params.setKind(value);
}
return visitChildren(ctx);
diff --git a/oap-server/server-query-plugin/traceql-plugin/src/test/java/org/apache/skywalking/oap/query/tempo/parser/TraceQLQueryParserTest.java b/oap-server/server-query-plugin/traceql-plugin/src/test/java/org/apache/skywalking/oap/query/tempo/parser/TraceQLQueryParserTest.java
index 091a654e3e..5a561ccdef 100644
--- a/oap-server/server-query-plugin/traceql-plugin/src/test/java/org/apache/skywalking/oap/query/tempo/parser/TraceQLQueryParserTest.java
+++ b/oap-server/server-query-plugin/traceql-plugin/src/test/java/org/apache/skywalking/oap/query/tempo/parser/TraceQLQueryParserTest.java
@@ -52,6 +52,26 @@ public class TraceQLQueryParserTest {
assertEquals("backend", params.getServiceName());
}
+ @Test
+ public void testScopeRemoteService() {
+ String query = "{resource.remote.service=\"backend\"}";
+ TraceQLParseResult result = TraceQLQueryParser.extractParams(query);
+ assertFalse(result.hasError(), "Parse should succeed");
+ TraceQLQueryParams params = result.getParams();
+ assertNotNull(params);
+ assertEquals("backend", params.getRemoteServiceName());
+ }
+
+ @Test
+ public void testScopeServiceInstance() {
+ String query = "{resource.instance=\"backend_instance\"}";
+ TraceQLParseResult result = TraceQLQueryParser.extractParams(query);
+ assertFalse(result.hasError(), "Parse should succeed");
+ TraceQLQueryParams params = result.getParams();
+ assertNotNull(params);
+ assertEquals("backend_instance", params.getServiceInstance());
+ }
+
@Test
public void testDurationFilter() {
String query = "{duration > 100ms}";
@@ -106,6 +126,16 @@ public class TraceQLQueryParserTest {
assertEquals("HTTP GET", params.getSpanName());
}
+ @Test
+ public void testSpanName() {
+ String query = "{span.name=\"HTTP GET\"}";
+ TraceQLParseResult result = TraceQLQueryParser.extractParams(query);
+ assertFalse(result.hasError(), "Parse should succeed");
+ TraceQLQueryParams params = result.getParams();
+ assertNotNull(params);
+ assertEquals("HTTP GET", params.getSpanName());
+ }
+
@Test
public void testComplexQueryWithAllFields() {
// Test the exact query from user: