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: