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 2024/06/18 08:58:10 UTC

(skywalking) branch master updated: Support trace MQE query for debugging. (#12342)

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 0b7272d813 Support trace MQE query for debugging. (#12342)
0b7272d813 is described below

commit 0b7272d813885ac6952211c80620f80bd33b47f2
Author: Wan Kai <wa...@foxmail.com>
AuthorDate: Tue Jun 18 16:58:03 2024 +0800

    Support trace MQE query for debugging. (#12342)
---
 docs/en/api/query-protocol.md                      |   6 +-
 docs/en/changes/changes.md                         |   1 +
 docs/en/debugging/query-tracing.md                 | 343 +++++++++++++++
 docs/menu.yml                                      |   2 +
 .../apache/skywalking/mqe/rt/MQEVisitorBase.java   | 469 ++++++++++++---------
 .../skywalking/mqe/rt/type/ExpressionResult.java   |   2 +
 .../oap/server/core/alarm/provider/AlarmRule.java  |  40 +-
 .../server/core/alarm/provider/RunningRule.java    |  82 ++--
 .../server/core/query/AggregationQueryService.java |  22 +-
 .../oap/server/core/query/MetricsQueryService.java |  54 ++-
 .../oap/server/core/query/RecordQueryService.java  |  22 +-
 .../oap/server/core/query/input/Duration.java      |   2 +
 .../oap/server/core/query/input/Entity.java        |   2 +
 .../server/core/query/input/MetricsCondition.java  |   2 +
 .../server/core/query/input/RecordCondition.java   |   2 +
 .../oap/server/core/query/input/TopNCondition.java |   2 +
 .../oap/server/core/query/type/KeyValue.java       |   2 +
 .../DebuggingSpan.java}                            |  38 +-
 .../core/query/type/debugging/DebuggingTrace.java  |  50 +++
 .../type/debugging/DebuggingTraceContext.java      |  68 +++
 .../debugging-query-plugin/pom.xml                 |   7 +-
 .../oap/query/debug/DebuggingHTTPHandler.java      |  89 ++++
 .../oap/query/debug/DebuggingSpanRsp.java}         |  33 +-
 .../oap/query/debug/DebuggingTraceRsp.java}        |  28 +-
 .../oap/query/debug/mqe/DebuggingMQERsp.java}      |  30 +-
 .../oap/query/graphql/mqe/rt/MQEVisitor.java       | 133 +++---
 .../graphql/resolver/MetricsExpressionQuery.java   |  74 ++--
 .../src/main/resources/query-protocol              |   2 +-
 .../banyandb/BanyanDBAggregationQueryDAO.java      | 124 +++++-
 .../plugin/banyandb/BanyanDBRecordsQueryDAO.java   |  66 ++-
 .../storage/plugin/banyandb/MetadataRegistry.java  |   3 +
 .../banyandb/measure/BanyanDBMetricsQueryDAO.java  |  86 +++-
 .../elasticsearch/query/AggregationQueryEsDAO.java |  50 ++-
 .../elasticsearch/query/MetricsQueryEsDAO.java     |  74 +++-
 .../elasticsearch/query/RecordsQueryEsDAO.java     |  47 ++-
 35 files changed, 1617 insertions(+), 440 deletions(-)

diff --git a/docs/en/api/query-protocol.md b/docs/en/api/query-protocol.md
index af84589b5e..40622b967b 100644
--- a/docs/en/api/query-protocol.md
+++ b/docs/en/api/query-protocol.md
@@ -90,9 +90,12 @@ extend type Query {
     # Get the list of all available metrics in the current OAP server.
     # Param, regex, could be used to filter the metrics by name.
     listMetrics(regex: String): [MetricDefinition!]!
-    execExpression(expression: String!, entity: Entity!, duration: Duration!): ExpressionResult!
+    # Param, if debug is true will enable the query tracing and return DebuggingTrace in the ExpressionResult.
+    # Param, if dumpDBRsp is true the database response will dump into the DebuggingTrace span message.
+    execExpression(expression: String!, entity: Entity!, duration: Duration!, debug: Boolean, dumpDBRsp: Boolean): ExpressionResult!
 }
 ```
+About the query tracing, see [MQE Query Tracing](../debugging/query-tracing.md#debugging-with-graphql-bundled).
 
 ```graphql
 type ExpressionResult {
@@ -103,6 +106,7 @@ type ExpressionResult {
     # When type == ExpressionResultType.UNKNOWN,
     # the error message includes the expression resolving errors.
     error: String
+    debuggingTrace: DebuggingTrace
 }
 ```
 
diff --git a/docs/en/changes/changes.md b/docs/en/changes/changes.md
index bae2243468..6f97d08e17 100644
--- a/docs/en/changes/changes.md
+++ b/docs/en/changes/changes.md
@@ -11,6 +11,7 @@
 * Remove unnecessary BanyanDB flushing logs(info).
 * Increase `SW_CORE_GRPC_MAX_MESSAGE_SIZE` to 50MB.
 * Support to query relation metrics through PromQL.
+* Support trace MQE query for debugging.
 
 #### UI
 
diff --git a/docs/en/debugging/query-tracing.md b/docs/en/debugging/query-tracing.md
new file mode 100644
index 0000000000..3252456d83
--- /dev/null
+++ b/docs/en/debugging/query-tracing.md
@@ -0,0 +1,343 @@
+# Tracing MQE Execution
+SkyWalking OAP provides the metrics query tracing to help users debug and diagnose the query performance for the SkyWalking backend self.
+
+## Tracing Structure
+- Trace
+
+| Field     | Description                                     |
+|-----------|-------------------------------------------------|
+| traceId   | The unique ID of the trace                      |
+| condition | The query conditions                            |
+| startTime | The start time of the trace. In nanoseconds     |
+| endTime   | The end time of the trace. In nanoseconds       |
+| duration  | The operation name of the trace. in nanoseconds |
+| spans     | spans in this trace                             |
+
+- Span
+
+| Field        | Description                                                                                                      |
+|--------------|------------------------------------------------------------------------------------------------------------------|
+| spanId       | The unique ID of the span                                                                                        |
+| parentSpanId | The parent span ID of the span                                                                                   |
+| operation    | The operation name of the span                                                                                   |
+| startTime    | The start time of the span. In nanoseconds                                                                       |
+| endTime      | The end time of the span. In nanoseconds                                                                         |
+| duration     | The duration of the span. In nanoseconds                                                                         |
+| msg          | The message of the span, could include additional info such as request condition and response from the Database  |
+| error        | The error message of the span, if the span has an error.                                                         |
+
+
+## MQE Query Tracing
+MQE query tracing used to trace the query performance of the MQE query.
+
+### Debugging through HTTP APIs
+The MQE query tracing service is provided within the OAP rest server, 
+which could be accessed through HTTP GET `http://{core restHost}:{core restPort}/debugging/query/mqe?{parameters}`.
+
+- Parameters
+
+| Field               | Description                                                                 | Required                   | 
+|---------------------|-----------------------------------------------------------------------------|----------------------------|
+| dumpDBRsp           | Dump the response from the database, **support Elasticsearch and BanyanDB** | No, default value is false |
+| expression          | The MQE query expression                                                    | Yes                        |               
+| startTime           | The start time of the query                                                 | Yes                        |               
+| endTime             | The end time of the query                                                   | Yes                        |               
+| step                | The query step                                                              | Yes                        |               
+| service             | The service name                                                            | Yes                        |               
+| serviceLayer        | The service layer name                                                      | Yes                        |               
+| serviceInstance     | The service instance name                                                   | No                         |               
+| endpoint            | The endpoint name                                                           | No                         |               
+| process             | The process name                                                            | No                         |               
+| destService         | The destination service name                                                | No                         |               
+| destServiceInstance | The destination service instance name                                       | No                         |               
+| destServiceInstance | The destination service instance name                                       | No                         |               
+| destEndpoint        | The destination endpoint name                                               | No                         |               
+| destProcess         | The destination process name                                                | No                         |               
+
+
+The time and step parameters are follow the [Duration](../api/query-protocol.md#duration) format.
+
+- Example
+
+Tracing an avg query with the MQE query expression `avg(service_sla)` from 2024-06-18 11 to 2024-06-18 12 with the step HOUR for the service `mock_a_service` and the service layer `GENERAL`.
+
+```shell
+curl -X GET 'http://127.0.0.1:12800/debugging/query/mqe?dumpDBRsp=true&expression=avg(service_sla)&startTime=2024-06-18%2011&endTime=2024-06-18%2012&step=HOUR&service=mock_a_service&serviceLayer=GENERAL'
+```
+
+Response will include query result and the execTrace information:
+
+```yaml
+type: "SINGLE_VALUE"
+results:
+  - metric:
+      labels: []
+    values:
+      - id: null
+        value: "10000"
+        traceID: null
+        doubleValue: 10000.0
+        emptyValue: false
+error: null
+execTrace:
+  traceId: "a50910fe-c966-4249-87f9-e471e55cf92f"
+  condition: "Expression: avg(service_sla), Entity: Entity(scope=null, serviceName=mock_a_service,\
+    \ normal=true, serviceInstanceName=null, endpointName=null, processName=null,\
+    \ destServiceName=null, destNormal=null, destServiceInstanceName=null, destEndpointName=null,\
+    \ destProcessName=null), Duration: Duration(start=2024-06-18 11, end=2024-06-18\
+    \ 12, step=HOUR)"
+  startTime: 61636369350002
+  endTime: 61636374541189
+  duration: 5191187
+  rootSpan:
+    spanId: 0
+    parentSpanId: -1
+    operation: "MQE query"
+    startTime: 61636369352761
+    endTime: 61636374539893
+    duration: 5187132
+    msg: null
+    error: null
+    childSpans:
+      - spanId: 1
+        parentSpanId: 0
+        operation: "MQE syntax analysis"
+        startTime: 61636369357437
+        endTime: 61636369443631
+        duration: 86194
+        msg: null
+        error: null
+        childSpans: []
+      - spanId: 2
+        parentSpanId: 0
+        operation: "MQE Aggregation OP: avg(service_sla)"
+        startTime: 61636369458018
+        endTime: 61636374440560
+        duration: 4982542
+        msg: null
+        error: null
+        childSpans:
+          - spanId: 3
+            parentSpanId: 2
+            operation: "MQE Metric OP: service_sla"
+            startTime: 61636369468839
+            endTime: 61636374373771
+            duration: 4904932
+            msg: null
+            error: null
+            childSpans:
+              - spanId: 4
+                parentSpanId: 3
+                operation: "Query Service"
+                startTime: 61636369481077
+                endTime: 61636374187132
+                duration: 4706055
+                msg: "readMetricsValues, MetricsCondition: MetricsCondition(name=service_sla,\
+            \ entity=Entity(scope=null, serviceName=mock_a_service, normal=true, serviceInstanceName=null,\
+            \ endpointName=null, processName=null, destServiceName=null, destNormal=null,\
+            \ destServiceInstanceName=null, destEndpointName=null, destProcessName=null)),\
+            \ Duration: Duration(start=2024-06-18 11, end=2024-06-18 12, step=HOUR)"
+                error: null
+                childSpans:
+                  - spanId: 5
+                    parentSpanId: 4
+                    operation: "Query Dao: readMetricsValues"
+                    startTime: 61636369516511
+                    endTime: 61636374186023
+                    duration: 4669512
+                    msg: "MetricsCondition: MetricsCondition(name=service_sla, entity=Entity(scope=Service,\
+              \ serviceName=mock_a_service, normal=true, serviceInstanceName=null,\
+              \ endpointName=null, processName=null, destServiceName=null, destNormal=null,\
+              \ destServiceInstanceName=null, destEndpointName=null, destProcessName=null)),\
+              \ ValueColumnName: percentage, Duration: Duration(start=2024-06-18 11,\
+              \ end=2024-06-18 12, step=HOUR)"
+                    error: null
+                    childSpans:
+                      - spanId: 6
+                        parentSpanId: 5
+                        operation: "Query Elasticsearch"
+                        startTime: 61636369738832
+                        endTime: 61636374107033
+                        duration: 4368201
+                        msg: "Condition: indices: [metrics-all-20240618]\n Response: {\"docs\"\
+                :[{\"found\":true,\"id\":\"service_sla_2024061811_bW9ja19hX3NlcnZpY2U\\\
+                u003d.1\",\"source\":{\"metric_table\":\"service_sla\",\"total\":130,\"\
+                percentage\":10000,\"match\":130,\"time_bucket\":2024061811,\"entity_id\"\
+                :\"bW9ja19hX3NlcnZpY2U\\u003d.1\"}},{\"found\":false,\"id\":\"service_sla_2024061812_bW9ja19hX3NlcnZpY2U\\\
+                u003d.1\"}]}"
+                        error: null
+                        childSpans: []
+```
+
+### Debugging with GraphQL bundled
+When querying the metrics though the [Metrics V3 APIs](../api/query-protocol.md#v3-apis),
+the MQE query tracing service is also provided within the GraphQL bundled.
+
+```graphql
+extend type Query {
+    ...
+    # Param, if debug is true will enable the query tracing and return DebuggingTrace in the ExpressionResult.
+    # Param, if dumpDBRsp is true the database response will dump into the DebuggingTrace span message.
+    execExpression(expression: String!, entity: Entity!, duration: Duration!, debug: Boolean, dumpDBRsp: Boolean): ExpressionResult!
+}
+```
+
+```graphql
+type ExpressionResult {
+    ...
+    debuggingTrace: DebuggingTrace
+}
+```
+
+- Example
+
+Query the metrics using the above condition through the GraphQL and tracing the query:
+```text
+http://127.0.0.1:12800/graphql
+```
+
+```graphql
+{
+    execExpression(expression: "avg(service_sla)", entity: {serviceName: "mock_a_service", normal: true}, duration: {start: "2024-06-18 11", end: "2024-06-18 12", step: HOUR}, debug: true, dumpDBRsp: true) {
+        type
+        error
+        results {
+            metric {
+                labels {
+                    key
+                    value
+                }
+            }
+            values {
+                id
+                value
+                traceID
+            }
+        }
+        debuggingTrace {
+            traceId
+            condition
+            startTime
+            endTime
+            duration
+            spans {
+                spanId
+                parentSpanId
+                operation
+                startTime
+                endTime
+                duration
+                msg
+                error
+            }
+        }
+    }
+}
+
+
+```
+Response will include query result and the execTrace information:
+
+```json
+{
+  "data": {
+    "execExpression": {
+      "type": "SINGLE_VALUE",
+      "error": null,
+      "results": [
+        {
+          "metric": {
+            "labels": []
+          },
+          "values": [
+            {
+              "id": null,
+              "value": "10000",
+              "traceID": null
+            }
+          ]
+        }
+      ],
+      "debuggingTrace": {
+        "traceId": "7caa6529-9a72-4878-ab82-7a9ca032a97b",
+        "condition": "Expression: avg(service_sla), Entity: Entity(scope=null, serviceName=mock_a_service, normal=true, serviceInstanceName=null, endpointName=null, processName=null, destServiceName=null, destNormal=null, destServiceInstanceName=null, destEndpointName=null, destProcessName=null), Duration: Duration(start=2024-06-18 11, end=2024-06-18 12, step=HOUR)",
+        "startTime": 73040673584523,
+        "endTime": 73040678525541,
+        "duration": 4941018,
+        "spans": [
+          {
+            "spanId": 0,
+            "parentSpanId": -1,
+            "operation": "MQE query",
+            "startTime": 73040673586770,
+            "endTime": 73040678523624,
+            "duration": 4936854,
+            "msg": null,
+            "error": null
+          },
+          {
+            "spanId": 1,
+            "parentSpanId": 0,
+            "operation": "MQE syntax analysis",
+            "startTime": 73040673591566,
+            "endTime": 73040673677579,
+            "duration": 86013,
+            "msg": null,
+            "error": null
+          },
+          {
+            "spanId": 2,
+            "parentSpanId": 0,
+            "operation": "MQE Aggregation OP: avg(service_sla)",
+            "startTime": 73040673691700,
+            "endTime": 73040678270159,
+            "duration": 4578459,
+            "msg": null,
+            "error": null
+          },
+          {
+            "spanId": 3,
+            "parentSpanId": 2,
+            "operation": "MQE Metric OP: service_sla",
+            "startTime": 73040673704725,
+            "endTime": 73040678230548,
+            "duration": 4525823,
+            "msg": null,
+            "error": null
+          },
+          {
+            "spanId": 4,
+            "parentSpanId": 3,
+            "operation": "Query Service",
+            "startTime": 73040673717502,
+            "endTime": 73040678040891,
+            "duration": 4323389,
+            "msg": "readMetricsValues, MetricsCondition: MetricsCondition(name=service_sla, entity=Entity(scope=null, serviceName=mock_a_service, normal=true, serviceInstanceName=null, endpointName=null, processName=null, destServiceName=null, destNormal=null, destServiceInstanceName=null, destEndpointName=null, destProcessName=null)), Duration: Duration(start=2024-06-18 11, end=2024-06-18 12, step=HOUR)",
+            "error": null
+          },
+          {
+            "spanId": 5,
+            "parentSpanId": 4,
+            "operation": "Query Dao: readMetricsValues",
+            "startTime": 73040673744571,
+            "endTime": 73040678039850,
+            "duration": 4295279,
+            "msg": "MetricsCondition: MetricsCondition(name=service_sla, entity=Entity(scope=Service, serviceName=mock_a_service, normal=true, serviceInstanceName=null, endpointName=null, processName=null, destServiceName=null, destNormal=null, destServiceInstanceName=null, destEndpointName=null, destProcessName=null)), ValueColumnName: percentage, Duration: Duration(start=2024-06-18 11, end=2024-06-18 12, step=HOUR)",
+            "error": null
+          },
+          {
+            "spanId": 6,
+            "parentSpanId": 5,
+            "operation": "Query Elasticsearch",
+            "startTime": 73040673916132,
+            "endTime": 73040677997181,
+            "duration": 4081049,
+            "msg": "Condition: indices: [metrics-all-20240618]\n Response: {\"docs\":[{\"found\":true,\"id\":\"service_sla_2024061811_bW9ja19hX3NlcnZpY2U\\u003d.1\",\"source\":{\"metric_table\":\"service_sla\",\"total\":130,\"percentage\":10000,\"match\":130,\"time_bucket\":2024061811,\"entity_id\":\"bW9ja19hX3NlcnZpY2U\\u003d.1\"}},{\"found\":false,\"id\":\"service_sla_2024061812_bW9ja19hX3NlcnZpY2U\\u003d.1\"}]}",
+            "error": null
+          }
+        ]
+      }
+    }
+  }
+}
+```
diff --git a/docs/menu.yml b/docs/menu.yml
index 18fd50acbb..183edeeea3 100644
--- a/docs/menu.yml
+++ b/docs/menu.yml
@@ -340,6 +340,8 @@ catalog:
     catalog:
       - name: "Scratch The OAP Config Dump"
         path: "/en/debugging/config_dump"
+      - name: "Tracing Query Execution"
+        path: "/en/debugging/query-tracing.md"
   - name: "Academy"
     catalog:
       - name: "Scaling SkyWalking server automatically in kubernetes"
diff --git a/oap-server/mqe-rt/src/main/java/org/apache/skywalking/mqe/rt/MQEVisitorBase.java b/oap-server/mqe-rt/src/main/java/org/apache/skywalking/mqe/rt/MQEVisitorBase.java
index 4416176b83..ec1b6cfb60 100644
--- a/oap-server/mqe-rt/src/main/java/org/apache/skywalking/mqe/rt/MQEVisitorBase.java
+++ b/oap-server/mqe-rt/src/main/java/org/apache/skywalking/mqe/rt/MQEVisitorBase.java
@@ -43,8 +43,12 @@ import org.apache.skywalking.mqe.rt.type.MQEValues;
 import org.apache.skywalking.oap.server.core.Const;
 import org.apache.skywalking.oap.server.core.query.enumeration.Step;
 import org.apache.skywalking.oap.server.core.query.type.KeyValue;
+import org.apache.skywalking.oap.server.core.query.type.debugging.DebuggingSpan;
+import org.apache.skywalking.oap.server.core.query.type.debugging.DebuggingTraceContext;
 import org.apache.skywalking.oap.server.library.util.StringUtil;
 
+import static org.apache.skywalking.oap.server.core.query.type.debugging.DebuggingTrace.TRACE_CONTEXT;
+
 @Slf4j
 public abstract class MQEVisitorBase extends MQEParserBaseVisitor<ExpressionResult> {
     public final Step queryStep;
@@ -60,293 +64,343 @@ public abstract class MQEVisitorBase extends MQEParserBaseVisitor<ExpressionResu
 
     @Override
     public ExpressionResult visitAddSubOp(MQEParser.AddSubOpContext ctx) {
-        ExpressionResult left = visit(ctx.expression(0));
-        if (StringUtil.isNotBlank(left.getError())) {
-            return left;
-        }
-        ExpressionResult right = visit(ctx.expression(1));
-        if (StringUtil.isNotBlank(right.getError())) {
-            return right;
-        }
-        int opType = ctx.addSub().getStart().getType();
+        DebuggingTraceContext traceContext = TRACE_CONTEXT.get();
+        DebuggingSpan span = traceContext.createSpan("MQE Binary OP: " + ctx.getText());
         try {
-            return BinaryOp.doBinaryOp(left, right, opType);
-        } catch (IllegalExpressionException e) {
-            ExpressionResult result = new ExpressionResult();
-            result.setType(ExpressionResultType.UNKNOWN);
-            result.setError(e.getMessage());
-            return result;
+            ExpressionResult left = visit(ctx.expression(0));
+            if (StringUtil.isNotBlank(left.getError())) {
+                return left;
+            }
+            ExpressionResult right = visit(ctx.expression(1));
+            if (StringUtil.isNotBlank(right.getError())) {
+                return right;
+            }
+            int opType = ctx.addSub().getStart().getType();
+            try {
+                return BinaryOp.doBinaryOp(left, right, opType);
+            } catch (IllegalExpressionException e) {
+                return getErrorResult(e.getMessage());
+            }
+        } finally {
+            traceContext.stopSpan(span);
         }
     }
 
     @Override
     public ExpressionResult visitMulDivModOp(MQEParser.MulDivModOpContext ctx) {
-        ExpressionResult left = visit(ctx.expression(0));
-        if (StringUtil.isNotBlank(left.getError())) {
-            return left;
-        }
-        ExpressionResult right = visit(ctx.expression(1));
-        if (StringUtil.isNotBlank(right.getError())) {
-            return right;
-        }
-        int opType = ctx.mulDivMod().getStart().getType();
+        DebuggingTraceContext traceContext = TRACE_CONTEXT.get();
+        DebuggingSpan span = traceContext.createSpan("MQE Binary OP: " + ctx.getText());
         try {
-            return BinaryOp.doBinaryOp(left, right, opType);
-        } catch (IllegalExpressionException e) {
-            ExpressionResult result = new ExpressionResult();
-            result.setType(ExpressionResultType.UNKNOWN);
-            result.setError(e.getMessage());
-            return result;
+            ExpressionResult left = visit(ctx.expression(0));
+            if (StringUtil.isNotBlank(left.getError())) {
+                return left;
+            }
+            ExpressionResult right = visit(ctx.expression(1));
+            if (StringUtil.isNotBlank(right.getError())) {
+                return right;
+            }
+            int opType = ctx.mulDivMod().getStart().getType();
+            try {
+                return BinaryOp.doBinaryOp(left, right, opType);
+            } catch (IllegalExpressionException e) {
+                return getErrorResult(e.getMessage());
+            }
+        } finally {
+            traceContext.stopSpan(span);
         }
     }
 
     @Override
     public ExpressionResult visitScalar(MQEParser.ScalarContext ctx) {
-        ExpressionResult result = new ExpressionResult();
-        double value = Double.parseDouble(ctx.getText());
-        MQEValue mqeValue = new MQEValue();
-        mqeValue.setDoubleValue(value);
-        mqeValue.setEmptyValue(false);
-        MQEValues mqeValues = new MQEValues();
-        mqeValues.getValues().add(mqeValue);
-        result.getResults().add(mqeValues);
-        result.setType(ExpressionResultType.SINGLE_VALUE);
-        return result;
+        DebuggingTraceContext traceContext = TRACE_CONTEXT.get();
+        DebuggingSpan span = traceContext.createSpan("MQE Scalar: " + ctx.getText());
+        try {
+            ExpressionResult result = new ExpressionResult();
+            double value = Double.parseDouble(ctx.getText());
+            MQEValue mqeValue = new MQEValue();
+            mqeValue.setDoubleValue(value);
+            mqeValue.setEmptyValue(false);
+            MQEValues mqeValues = new MQEValues();
+            mqeValues.getValues().add(mqeValue);
+            result.getResults().add(mqeValues);
+            result.setType(ExpressionResultType.SINGLE_VALUE);
+            return result;
+        } finally {
+            traceContext.stopSpan(span);
+        }
     }
 
     @Override
     public ExpressionResult visitAggregationOp(MQEParser.AggregationOpContext ctx) {
-        int opType = ctx.aggregation().getStart().getType();
-        ExpressionResult expResult = visit(ctx.expression());
-        if (StringUtil.isNotEmpty(expResult.getError())) {
-            return expResult;
-        }
+        DebuggingTraceContext traceContext = TRACE_CONTEXT.get();
+        DebuggingSpan span = traceContext.createSpan("MQE Aggregation OP: " + ctx.getText());
         try {
-            return AggregationOp.doAggregationOp(expResult, opType);
-        } catch (IllegalExpressionException e) {
-            ExpressionResult result = new ExpressionResult();
-            result.setType(ExpressionResultType.UNKNOWN);
-            result.setError(e.getMessage());
-            return result;
+            int opType = ctx.aggregation().getStart().getType();
+            ExpressionResult expResult = visit(ctx.expression());
+            if (StringUtil.isNotEmpty(expResult.getError())) {
+                return expResult;
+            }
+            try {
+                return AggregationOp.doAggregationOp(expResult, opType);
+            } catch (IllegalExpressionException e) {
+                return getErrorResult(e.getMessage());
+            }
+        } finally {
+            traceContext.stopSpan(span);
         }
     }
 
     @Override
     public ExpressionResult visitAggregateLabelsOp(final MQEParser.AggregateLabelsOpContext ctx) {
-        int funcType = ctx.aggregateLabelsFunc().getStart().getType();
-        ExpressionResult expResult = visit(ctx.expression());
-        if (StringUtil.isNotEmpty(expResult.getError())) {
-            return expResult;
-        }
-
-        if (!expResult.isLabeledResult()) {
-            expResult.setError("The result of expression [" + ctx.expression().getText() + "] is not a labeled result.");
-            return expResult;
-        }
-
+        DebuggingTraceContext traceContext = TRACE_CONTEXT.get();
+        DebuggingSpan span = traceContext.createSpan("MQE Aggregate Labels OP: " + ctx.getText());
         try {
-            if (expResult.getResults().isEmpty()) {
+            int funcType = ctx.aggregateLabelsFunc().getStart().getType();
+            ExpressionResult expResult = visit(ctx.expression());
+            if (StringUtil.isNotEmpty(expResult.getError())) {
                 return expResult;
             }
-            List<String> labelNames = new ArrayList<>();
-            MQEParser.LabelNameListContext labelNameListContext = ctx.aggregateLabelsFunc().labelNameList();
-            if (null != labelNameListContext) {
-                for (MQEParser.LabelNameContext labelNameContext : labelNameListContext.labelName()) {
-                    // ignore the label name that does not exist in the result
-                    if (expResult.getResults()
-                                 .get(0)
-                                 .getMetric()
-                                 .getLabels()
-                                 .stream()
-                                 .anyMatch(label -> label.getKey().equals(labelNameContext.getText()))) {
-                        labelNames.add(labelNameContext.getText());
+
+            if (!expResult.isLabeledResult()) {
+                expResult.setError(
+                    "The result of expression [" + ctx.expression().getText() + "] is not a labeled result.");
+                return expResult;
+            }
+
+            try {
+                if (expResult.getResults().isEmpty()) {
+                    return expResult;
+                }
+                List<String> labelNames = new ArrayList<>();
+                MQEParser.LabelNameListContext labelNameListContext = ctx.aggregateLabelsFunc().labelNameList();
+                if (null != labelNameListContext) {
+                    for (MQEParser.LabelNameContext labelNameContext : labelNameListContext.labelName()) {
+                        // ignore the label name that does not exist in the result
+                        if (expResult.getResults()
+                                     .get(0)
+                                     .getMetric()
+                                     .getLabels()
+                                     .stream()
+                                     .anyMatch(label -> label.getKey().equals(labelNameContext.getText()))) {
+                            labelNames.add(labelNameContext.getText());
+                        }
                     }
                 }
+                return AggregateLabelsOp.doAggregateLabelsOp(expResult, funcType, labelNames);
+            } catch (IllegalExpressionException e) {
+                return getErrorResult(e.getMessage());
             }
-            return AggregateLabelsOp.doAggregateLabelsOp(expResult, funcType, labelNames);
-        } catch (IllegalExpressionException e) {
-            ExpressionResult result = new ExpressionResult();
-            result.setType(ExpressionResultType.UNKNOWN);
-            result.setError(e.getMessage());
-            return result;
+        } finally {
+            traceContext.stopSpan(span);
         }
     }
 
     @Override
     public ExpressionResult visitMathematicalOperator0OP(MQEParser.MathematicalOperator0OPContext ctx) {
-        int opType = ctx.mathematical_operator0().getStart().getType();
-        ExpressionResult expResult = visit(ctx.expression());
-        if (StringUtil.isNotEmpty(expResult.getError())) {
-            return expResult;
-        }
+        DebuggingTraceContext traceContext = TRACE_CONTEXT.get();
+        DebuggingSpan span = traceContext.createSpan("MQE Mathematical OP: " + ctx.getText());
         try {
-            return MathematicalFunctionOp.doFunction0Op(expResult, opType);
-        } catch (IllegalExpressionException e) {
-            ExpressionResult result = new ExpressionResult();
-            result.setType(ExpressionResultType.UNKNOWN);
-            result.setError(e.getMessage());
-            return result;
+            int opType = ctx.mathematical_operator0().getStart().getType();
+            ExpressionResult expResult = visit(ctx.expression());
+            if (StringUtil.isNotEmpty(expResult.getError())) {
+                return expResult;
+            }
+            try {
+                return MathematicalFunctionOp.doFunction0Op(expResult, opType);
+            } catch (IllegalExpressionException e) {
+                return getErrorResult(e.getMessage());
+            }
+        } finally {
+            traceContext.stopSpan(span);
         }
     }
 
     @Override
     public ExpressionResult visitMathematicalOperator1OP(MQEParser.MathematicalOperator1OPContext ctx) {
-        int opType = ctx.mathematical_operator1().getStart().getType();
-        ExpressionResult expResult = visit(ctx.expression());
-        if (StringUtil.isNotEmpty(expResult.getError())) {
-            return expResult;
-        }
+        DebuggingTraceContext traceContext = TRACE_CONTEXT.get();
+        DebuggingSpan span = traceContext.createSpan("MQE Mathematical OP: " + ctx.getText());
         try {
-            return MathematicalFunctionOp.doFunction1Op(expResult, opType, Integer.parseInt(ctx.parameter().INTEGER().getText()));
-        } catch (IllegalExpressionException e) {
-            ExpressionResult result = new ExpressionResult();
-            result.setType(ExpressionResultType.UNKNOWN);
-            result.setError(e.getMessage());
-            return result;
+            int opType = ctx.mathematical_operator1().getStart().getType();
+            ExpressionResult expResult = visit(ctx.expression());
+            if (StringUtil.isNotEmpty(expResult.getError())) {
+                return expResult;
+            }
+            try {
+                return MathematicalFunctionOp.doFunction1Op(expResult, opType, Integer.parseInt(ctx.parameter().INTEGER().getText()));
+            } catch (IllegalExpressionException e) {
+                return getErrorResult(e.getMessage());
+            }
+        } finally {
+            traceContext.stopSpan(span);
         }
     }
 
     @Override
     public ExpressionResult visitTopNOP(MQEParser.TopNOPContext ctx) {
-        return visit(ctx.metric());
+        DebuggingTraceContext traceContext = TRACE_CONTEXT.get();
+        DebuggingSpan span = traceContext.createSpan("MQE TopN OP: " + ctx.getText());
+        try {
+            return visit(ctx.metric());
+        } finally {
+            traceContext.stopSpan(span);
+        }
     }
 
     @Override
     public ExpressionResult visitRelablesOP(MQEParser.RelablesOPContext ctx) {
-        ExpressionResult result = visit(ctx.expression());
-        if (!result.isLabeledResult()) {
-            // Reserve the original result type
-            result.setError("The result of expression [" + ctx.expression().getText() + "] is not a labeled result.");
-            return result;
-        }
-        KeyValue targetLabel = buildLabel(ctx.label());
-        KeyValue replaceLabel = buildLabel(ctx.replaceLabel().label());
-        List<KeyValue> targetLabels = parseLabelValue(targetLabel, Const.COMMA);
-        List<KeyValue> replaceLabels = parseLabelValue(replaceLabel, Const.COMMA);
+        DebuggingTraceContext traceContext = TRACE_CONTEXT.get();
+        DebuggingSpan span = traceContext.createSpan("MQE Relabels OP: " + ctx.getText());
+        try {
+            ExpressionResult result = visit(ctx.expression());
+            if (!result.isLabeledResult()) {
+                // Reserve the original result type
+                result.setError("The result of expression [" + ctx.expression().getText() + "] is not a labeled result.");
+                return result;
+            }
+            KeyValue targetLabel = buildLabel(ctx.label());
+            KeyValue replaceLabel = buildLabel(ctx.replaceLabel().label());
+            List<KeyValue> targetLabels = parseLabelValue(targetLabel, Const.COMMA);
+            List<KeyValue> replaceLabels = parseLabelValue(replaceLabel, Const.COMMA);
 
-        Map<KeyValue, KeyValue> relabelMap = new HashMap<>();
-        if (targetLabels.isEmpty() || replaceLabels.isEmpty()) {
-            return result;
-        }
-        if (targetLabels.size() != replaceLabels.size()) {
-            result.setError(
-                "Targrt label [" + targetLabel.getKey() + "]: the number of relabel values is not equal to the number of replace label.");
-            return result;
-        }
-        for (int i = 0; i < targetLabels.size(); i++) {
-            relabelMap.put(targetLabels.get(i), replaceLabels.get(i));
-        }
-        for (MQEValues mqeValues : result.getResults()) {
-            for (KeyValue label : mqeValues.getMetric().getLabels()) {
-                if (relabelMap.containsKey(label)) {
-                    KeyValue replaceLabelValue = relabelMap.get(label);
-                    label.setKey(replaceLabelValue.getKey());
-                    label.setValue(replaceLabelValue.getValue());
+            Map<KeyValue, KeyValue> relabelMap = new HashMap<>();
+            if (targetLabels.isEmpty() || replaceLabels.isEmpty()) {
+                return result;
+            }
+            if (targetLabels.size() != replaceLabels.size()) {
+                result.setError(
+                    "Target label [" + targetLabel.getKey() + "]: the number of relabel values is not equal to the number of replace label.");
+                return result;
+            }
+            for (int i = 0; i < targetLabels.size(); i++) {
+                relabelMap.put(targetLabels.get(i), replaceLabels.get(i));
+            }
+            for (MQEValues mqeValues : result.getResults()) {
+                for (KeyValue label : mqeValues.getMetric().getLabels()) {
+                    if (relabelMap.containsKey(label)) {
+                        KeyValue replaceLabelValue = relabelMap.get(label);
+                        label.setKey(replaceLabelValue.getKey());
+                        label.setValue(replaceLabelValue.getValue());
+                    }
                 }
             }
+            return result;
+        } finally {
+            traceContext.stopSpan(span);
         }
-        return result;
     }
 
     @Override
     public ExpressionResult visitLogicalOperatorOP(MQEParser.LogicalOperatorOPContext ctx) {
-        int opType = ctx.logical_operator().getStart().getType();
+        DebuggingTraceContext traceContext = TRACE_CONTEXT.get();
+        DebuggingSpan span = traceContext.createSpan("MQE Logical OP: " + ctx.getText());
         try {
-            return LogicalFunctionOp.doOP(opType, ctx.expressionList(), this);
-        } catch (IllegalExpressionException e) {
-            ExpressionResult result = new ExpressionResult();
-            result.setType(ExpressionResultType.UNKNOWN);
-            result.setError(e.getMessage());
-            return result;
+            int opType = ctx.logical_operator().getStart().getType();
+            try {
+                return LogicalFunctionOp.doOP(opType, ctx.expressionList(), this);
+            } catch (IllegalExpressionException e) {
+                return getErrorResult(e.getMessage());
+            }
+        } finally {
+            traceContext.stopSpan(span);
         }
     }
 
     @Override
     public ExpressionResult visitCompareOp(MQEParser.CompareOpContext ctx) {
-        ExpressionResult left = visit(ctx.expression(0));
-        if (StringUtil.isNotBlank(left.getError())) {
-            return left;
-        }
-        ExpressionResult right = visit(ctx.expression(1));
-        if (StringUtil.isNotBlank(right.getError())) {
-            return right;
-        }
-        int opType = ctx.compare().getStart().getType();
+        DebuggingTraceContext traceContext = TRACE_CONTEXT.get();
+        DebuggingSpan span = traceContext.createSpan("MQE Compare OP: " + ctx.getText());
         try {
-            ExpressionResult result = CompareOp.doCompareOP(left, right, opType);
-            if (ctx.parent == null) {
-                result.setBoolResult(true);
+            ExpressionResult left = visit(ctx.expression(0));
+            if (StringUtil.isNotBlank(left.getError())) {
+                return left;
             }
-            return result;
-        } catch (IllegalExpressionException e) {
-            ExpressionResult result = new ExpressionResult();
-            result.setType(ExpressionResultType.UNKNOWN);
-            result.setError(e.getMessage());
-            return result;
+            ExpressionResult right = visit(ctx.expression(1));
+            if (StringUtil.isNotBlank(right.getError())) {
+                return right;
+            }
+            int opType = ctx.compare().getStart().getType();
+            try {
+                ExpressionResult result = CompareOp.doCompareOP(left, right, opType);
+                if (ctx.parent == null) {
+                    result.setBoolResult(true);
+                }
+                return result;
+            } catch (IllegalExpressionException e) {
+                return getErrorResult(e.getMessage());
+            }
+        } finally {
+            traceContext.stopSpan(span);
         }
     }
 
     @Override
     public ExpressionResult visitTrendOP(MQEParser.TrendOPContext ctx) {
-        int opType = ctx.trend().getStart().getType();
-        int trendRange = Integer.parseInt(ctx.INTEGER().getText());
-        if (trendRange < 1) {
-            ExpressionResult result = new ExpressionResult();
-            result.setType(ExpressionResultType.UNKNOWN);
-            result.setError("The trend range must be greater than 0.");
-            return result;
-        }
-        ExpressionResult expResult = visit(ctx.metric());
-        if (StringUtil.isNotEmpty(expResult.getError())) {
-            return expResult;
-        }
-        if (expResult.getType() != ExpressionResultType.TIME_SERIES_VALUES) {
-            expResult.setError("The result of expression [" + ctx.metric().getText() + "] is not a time series result.");
-            return expResult;
-        }
+        DebuggingTraceContext traceContext = TRACE_CONTEXT.get();
+        DebuggingSpan span = traceContext.createSpan("MQE Trend OP: " + ctx.getText());
         try {
-            return TrendOp.doTrendOp(expResult, opType, trendRange, queryStep);
-        } catch (IllegalExpressionException e) {
-            ExpressionResult result = new ExpressionResult();
-            result.setType(ExpressionResultType.UNKNOWN);
-            result.setError(e.getMessage());
-            return result;
+            int opType = ctx.trend().getStart().getType();
+            int trendRange = Integer.parseInt(ctx.INTEGER().getText());
+            if (trendRange < 1) {
+                return getErrorResult("The trend range must be greater than 0.");
+            }
+            ExpressionResult expResult = visit(ctx.metric());
+            if (StringUtil.isNotEmpty(expResult.getError())) {
+                return expResult;
+            }
+            if (expResult.getType() != ExpressionResultType.TIME_SERIES_VALUES) {
+                expResult.setError(
+                    "The result of expression [" + ctx.metric().getText() + "] is not a time series result.");
+                return expResult;
+            }
+            try {
+                return TrendOp.doTrendOp(expResult, opType, trendRange, queryStep);
+            } catch (IllegalExpressionException e) {
+                return getErrorResult(e.getMessage());
+            }
+        } finally {
+            traceContext.stopSpan(span);
         }
     }
 
     @Override
     public ExpressionResult visitSortValuesOP(MQEParser.SortValuesOPContext ctx) {
-        ExpressionResult result = visit(ctx.expression());
-        int order = ctx.order().getStart().getType();
-        Optional<Integer> limit = Optional.empty();
-        if (ctx.INTEGER() != null) {
-            limit = Optional.of(Integer.valueOf(ctx.INTEGER().getText()));
-        }
+        DebuggingTraceContext traceContext = TRACE_CONTEXT.get();
+        DebuggingSpan span = traceContext.createSpan("MQE Sort Values OP: " + ctx.getText());
         try {
-            return SortValuesOp.doSortValuesOp(result, limit, order);
-        } catch (IllegalExpressionException e) {
-            ExpressionResult errorResult = new ExpressionResult();
-            errorResult.setType(ExpressionResultType.UNKNOWN);
-            errorResult.setError(e.getMessage());
-            return errorResult;
+            ExpressionResult result = visit(ctx.expression());
+            int order = ctx.order().getStart().getType();
+            Optional<Integer> limit = Optional.empty();
+            if (ctx.INTEGER() != null) {
+                limit = Optional.of(Integer.valueOf(ctx.INTEGER().getText()));
+            }
+            try {
+                return SortValuesOp.doSortValuesOp(result, limit, order);
+            } catch (IllegalExpressionException e) {
+                return getErrorResult(e.getMessage());
+            }
+        } finally {
+            traceContext.stopSpan(span);
         }
     }
 
     @Override
     public ExpressionResult visitSortLabelValuesOP(MQEParser.SortLabelValuesOPContext ctx) {
-        ExpressionResult result = visit(ctx.expression());
-        int order = ctx.order().getStart().getType();
-        List<String> labelNames = new ArrayList<>();
-        for (MQEParser.LabelNameContext labelNameContext : ctx.labelNameList().labelName()) {
-            labelNames.add(labelNameContext.getText());
-        }
+        DebuggingTraceContext traceContext = TRACE_CONTEXT.get();
+        DebuggingSpan span = traceContext.createSpan("MQE Sort Label Values OP: " + ctx.getText());
         try {
-            return SortLabelValuesOp.doSortLabelValuesOp(result, order, labelNames);
-        } catch (IllegalExpressionException e) {
-            ExpressionResult errorResult = new ExpressionResult();
-            errorResult.setType(ExpressionResultType.UNKNOWN);
-            errorResult.setError(e.getMessage());
-            return errorResult;
+            ExpressionResult result = visit(ctx.expression());
+            int order = ctx.order().getStart().getType();
+            List<String> labelNames = new ArrayList<>();
+            for (MQEParser.LabelNameContext labelNameContext : ctx.labelNameList().labelName()) {
+                labelNames.add(labelNameContext.getText());
+            }
+            try {
+                return SortLabelValuesOp.doSortLabelValuesOp(result, order, labelNames);
+            } catch (IllegalExpressionException e) {
+                return getErrorResult(e.getMessage());
+            }
+        } finally {
+            traceContext.stopSpan(span);
         }
     }
 
@@ -386,4 +440,11 @@ public abstract class MQEVisitorBase extends MQEParserBaseVisitor<ExpressionResu
         }
         return labels;
     }
+
+    protected ExpressionResult getErrorResult(String error) {
+        ExpressionResult result = new ExpressionResult();
+        result.setType(ExpressionResultType.UNKNOWN);
+        result.setError(error);
+        return result;
+    }
 }
diff --git a/oap-server/mqe-rt/src/main/java/org/apache/skywalking/mqe/rt/type/ExpressionResult.java b/oap-server/mqe-rt/src/main/java/org/apache/skywalking/mqe/rt/type/ExpressionResult.java
index 8de0383a9c..350e1fd7fe 100644
--- a/oap-server/mqe-rt/src/main/java/org/apache/skywalking/mqe/rt/type/ExpressionResult.java
+++ b/oap-server/mqe-rt/src/main/java/org/apache/skywalking/mqe/rt/type/ExpressionResult.java
@@ -22,6 +22,7 @@ package org.apache.skywalking.mqe.rt.type;
 import java.util.ArrayList;
 import java.util.List;
 import lombok.Data;
+import org.apache.skywalking.oap.server.core.query.type.debugging.DebuggingTrace;
 
 @Data
 public class ExpressionResult {
@@ -30,4 +31,5 @@ public class ExpressionResult {
     private String error;
     private boolean isLabeledResult = false;
     private boolean isBoolResult = false;
+    private DebuggingTrace debuggingTrace;
 }
diff --git a/oap-server/server-alarm-plugin/src/main/java/org/apache/skywalking/oap/server/core/alarm/provider/AlarmRule.java b/oap-server/server-alarm-plugin/src/main/java/org/apache/skywalking/oap/server/core/alarm/provider/AlarmRule.java
index e5238719b7..6ca788412d 100644
--- a/oap-server/server-alarm-plugin/src/main/java/org/apache/skywalking/oap/server/core/alarm/provider/AlarmRule.java
+++ b/oap-server/server-alarm-plugin/src/main/java/org/apache/skywalking/oap/server/core/alarm/provider/AlarmRule.java
@@ -38,9 +38,12 @@ import org.apache.skywalking.mqe.rt.grammar.MQEParser;
 import org.apache.skywalking.mqe.rt.type.ExpressionResult;
 import org.apache.skywalking.mqe.rt.type.ExpressionResultType;
 import org.apache.skywalking.oap.server.core.alarm.provider.expr.rt.AlarmMQEVerifyVisitor;
+import org.apache.skywalking.oap.server.core.query.type.debugging.DebuggingTraceContext;
 import org.apache.skywalking.oap.server.core.storage.annotation.ValueColumnMetadata;
 import org.apache.skywalking.oap.server.library.util.StringUtil;
 
+import static org.apache.skywalking.oap.server.core.query.type.debugging.DebuggingTrace.TRACE_CONTEXT;
+
 @Data
 @ToString
 @EqualsAndHashCode
@@ -75,22 +78,29 @@ public class AlarmRule {
         } catch (ParseCancellationException e) {
             throw new IllegalExpressionException("Expression: " + expression + " error: " + e.getMessage());
         }
-        AlarmMQEVerifyVisitor visitor = new AlarmMQEVerifyVisitor();
-        ExpressionResult parseResult = visitor.visit(tree);
-        if (StringUtil.isNotBlank(parseResult.getError())) {
-            throw new IllegalExpressionException("Expression: " + expression + " error: " + parseResult.getError());
-        }
-        if (!parseResult.isBoolResult()) {
-            throw new IllegalExpressionException("Expression: " + expression + " root operation is not a Compare Operation.");
-        }
-        if (ExpressionResultType.SINGLE_VALUE != parseResult.getType()) {
-            throw new IllegalExpressionException("Expression: " + expression + " is not a SINGLE_VALUE result expression.");
-        }
+        try {
+            TRACE_CONTEXT.set(new DebuggingTraceContext(expression, false, false));
+            AlarmMQEVerifyVisitor visitor = new AlarmMQEVerifyVisitor();
+            ExpressionResult parseResult = visitor.visit(tree);
+            if (StringUtil.isNotBlank(parseResult.getError())) {
+                throw new IllegalExpressionException("Expression: " + expression + " error: " + parseResult.getError());
+            }
+            if (!parseResult.isBoolResult()) {
+                throw new IllegalExpressionException(
+                    "Expression: " + expression + " root operation is not a Compare Operation.");
+            }
+            if (ExpressionResultType.SINGLE_VALUE != parseResult.getType()) {
+                throw new IllegalExpressionException(
+                    "Expression: " + expression + " is not a SINGLE_VALUE result expression.");
+            }
 
-        verifyIncludeMetrics(visitor.getIncludeMetrics(), expression);
-        this.expression = expression;
-        this.includeMetrics = visitor.getIncludeMetrics();
-        this.maxTrendRange = visitor.getMaxTrendRange();
+            verifyIncludeMetrics(visitor.getIncludeMetrics(), expression);
+            this.expression = expression;
+            this.includeMetrics = visitor.getIncludeMetrics();
+            this.maxTrendRange = visitor.getMaxTrendRange();
+        } finally {
+            TRACE_CONTEXT.remove();
+        }
     }
 
     private void verifyIncludeMetrics(Set<String> includeMetrics, String expression) throws IllegalExpressionException {
diff --git a/oap-server/server-alarm-plugin/src/main/java/org/apache/skywalking/oap/server/core/alarm/provider/RunningRule.java b/oap-server/server-alarm-plugin/src/main/java/org/apache/skywalking/oap/server/core/alarm/provider/RunningRule.java
index 7bd3b6ea5e..4eb03d81bb 100644
--- a/oap-server/server-alarm-plugin/src/main/java/org/apache/skywalking/oap/server/core/alarm/provider/RunningRule.java
+++ b/oap-server/server-alarm-plugin/src/main/java/org/apache/skywalking/oap/server/core/alarm/provider/RunningRule.java
@@ -43,6 +43,7 @@ import org.apache.skywalking.mqe.rt.type.ExpressionResult;
 import org.apache.skywalking.mqe.rt.type.ExpressionResultType;
 import org.apache.skywalking.mqe.rt.type.MQEValues;
 import org.apache.skywalking.oap.server.core.alarm.provider.expr.rt.AlarmMQEVisitor;
+import org.apache.skywalking.oap.server.core.query.type.debugging.DebuggingTraceContext;
 import org.apache.skywalking.oap.server.library.util.StringUtil;
 import org.apache.skywalking.oap.server.core.alarm.AlarmMessage;
 import org.apache.skywalking.oap.server.core.alarm.MetaInAlarm;
@@ -59,6 +60,8 @@ import org.joda.time.Minutes;
 import org.joda.time.format.DateTimeFormat;
 import org.joda.time.format.DateTimeFormatter;
 
+import static org.apache.skywalking.oap.server.core.query.type.debugging.DebuggingTrace.TRACE_CONTEXT;
+
 /**
  * RunningRule represents each rule in running status. Based on the {@link AlarmRule} definition,
  */
@@ -345,52 +348,57 @@ public class RunningRule {
 
         private boolean isMatch() {
             int isMatch = 0;
-            AlarmMQEVisitor visitor = new AlarmMQEVisitor(this.values, this.endTime, this.additionalPeriod);
-            ExpressionResult parseResult = visitor.visit(exprTree);
-            if (StringUtil.isNotBlank(parseResult.getError())) {
-                log.error("expression:" + expression + " error: " + parseResult.getError());
-                return false;
-            }
-            if (!parseResult.isBoolResult() ||
-                ExpressionResultType.SINGLE_VALUE != parseResult.getType() ||
-                CollectionUtils.isEmpty(parseResult.getResults())) {
-                return false;
-            }
-            if (!parseResult.isLabeledResult()) {
-                MQEValues mqeValues = parseResult.getResults().get(0);
-                if (mqeValues != null &&
-                    CollectionUtils.isNotEmpty(mqeValues.getValues()) &&
-                    mqeValues.getValues().get(0) != null) {
-                    isMatch = (int) mqeValues.getValues().get(0).getDoubleValue();
+            try {
+                TRACE_CONTEXT.set(new DebuggingTraceContext(expression, false, false));
+                AlarmMQEVisitor visitor = new AlarmMQEVisitor(this.values, this.endTime, this.additionalPeriod);
+                ExpressionResult parseResult = visitor.visit(exprTree);
+                if (StringUtil.isNotBlank(parseResult.getError())) {
+                    log.error("expression:" + expression + " error: " + parseResult.getError());
+                    return false;
                 }
-            } else {
-                // if the result has multiple labels, when there is one label match, then the result is match
-                // for example in 5 minutes, the sum(percentile{p='50,75'} > 1000) >= 3
-                // percentile{p='50,75'} result is:
-                // P50(1000,1100,1200,1000,500), > 1000 2 times
-                // P75(2000,1500,1200,1000,500), > 1000 3 times
-                // percentile{p='50,75'} > 1000 result is:
-                // P50(0,1,1,0,0)
-                // P75(1,1,1,0,0)
-                // sum(percentile{p='50,75'} > 1000) >= 3 result is:
-                // P50(0)
-                // P75(1)
-                // then the isMatch is 1
-                for (MQEValues mqeValues : parseResult.getResults()) {
+                if (!parseResult.isBoolResult() ||
+                    ExpressionResultType.SINGLE_VALUE != parseResult.getType() ||
+                    CollectionUtils.isEmpty(parseResult.getResults())) {
+                    return false;
+                }
+                if (!parseResult.isLabeledResult()) {
+                    MQEValues mqeValues = parseResult.getResults().get(0);
                     if (mqeValues != null &&
                         CollectionUtils.isNotEmpty(mqeValues.getValues()) &&
                         mqeValues.getValues().get(0) != null) {
                         isMatch = (int) mqeValues.getValues().get(0).getDoubleValue();
-                        if (isMatch == 1) {
-                            break;
+                    }
+                } else {
+                    // if the result has multiple labels, when there is one label match, then the result is match
+                    // for example in 5 minutes, the sum(percentile{p='50,75'} > 1000) >= 3
+                    // percentile{p='50,75'} result is:
+                    // P50(1000,1100,1200,1000,500), > 1000 2 times
+                    // P75(2000,1500,1200,1000,500), > 1000 3 times
+                    // percentile{p='50,75'} > 1000 result is:
+                    // P50(0,1,1,0,0)
+                    // P75(1,1,1,0,0)
+                    // sum(percentile{p='50,75'} > 1000) >= 3 result is:
+                    // P50(0)
+                    // P75(1)
+                    // then the isMatch is 1
+                    for (MQEValues mqeValues : parseResult.getResults()) {
+                        if (mqeValues != null &&
+                            CollectionUtils.isNotEmpty(mqeValues.getValues()) &&
+                            mqeValues.getValues().get(0) != null) {
+                            isMatch = (int) mqeValues.getValues().get(0).getDoubleValue();
+                            if (isMatch == 1) {
+                                break;
+                            }
                         }
                     }
                 }
+                if (log.isTraceEnabled()) {
+                    log.trace("Match expression is {}", expression);
+                }
+                return isMatch == 1;
+            } finally {
+                TRACE_CONTEXT.remove();
             }
-            if (log.isTraceEnabled()) {
-                log.trace("Match expression is {}", expression);
-            }
-            return isMatch == 1;
         }
 
         public boolean isExpired() {
diff --git a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/query/AggregationQueryService.java b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/query/AggregationQueryService.java
index bac6dfbb99..e065a7e408 100644
--- a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/query/AggregationQueryService.java
+++ b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/query/AggregationQueryService.java
@@ -22,6 +22,9 @@ import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
+import org.apache.skywalking.oap.server.core.query.type.debugging.DebuggingTrace;
+import org.apache.skywalking.oap.server.core.query.type.debugging.DebuggingSpan;
+import org.apache.skywalking.oap.server.core.query.type.debugging.DebuggingTraceContext;
 import org.apache.skywalking.oap.server.library.util.StringUtil;
 import org.apache.skywalking.oap.server.core.Const;
 import org.apache.skywalking.oap.server.core.analysis.IDManager;
@@ -53,7 +56,7 @@ public class AggregationQueryService implements Service {
         return aggregationQueryDAO;
     }
 
-    public List<SelectedRecord> sortMetrics(TopNCondition condition, Duration duration) throws IOException {
+    private List<SelectedRecord> invokeSortMetrics(TopNCondition condition, Duration duration) throws IOException {
         if (!condition.senseScope()) {
             return Collections.emptyList();
         }
@@ -109,4 +112,21 @@ public class AggregationQueryService implements Service {
         });
         return selectedRecords;
     }
+
+    public List<SelectedRecord> sortMetrics(TopNCondition condition,
+                                            Duration duration) throws IOException {
+        DebuggingTraceContext traceContext = DebuggingTrace.TRACE_CONTEXT.get();
+        DebuggingSpan span = null;
+        try {
+            if (traceContext != null) {
+                span = traceContext.createSpan("Query Service: sortMetrics");
+                span.setMsg("TopNCondition: " + condition + ", Duration: " + duration);
+            }
+            return invokeSortMetrics(condition, duration);
+        } finally {
+            if (traceContext != null && span != null) {
+                traceContext.stopSpan(span);
+            }
+        }
+    }
 }
diff --git a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/query/MetricsQueryService.java b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/query/MetricsQueryService.java
index 375c8b5e4e..d8035b9446 100644
--- a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/query/MetricsQueryService.java
+++ b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/query/MetricsQueryService.java
@@ -30,12 +30,16 @@ import org.apache.skywalking.oap.server.core.query.type.KVInt;
 import org.apache.skywalking.oap.server.core.query.type.KeyValue;
 import org.apache.skywalking.oap.server.core.query.type.MetricsValues;
 import org.apache.skywalking.oap.server.core.query.type.NullableValue;
+import org.apache.skywalking.oap.server.core.query.type.debugging.DebuggingSpan;
+import org.apache.skywalking.oap.server.core.query.type.debugging.DebuggingTraceContext;
 import org.apache.skywalking.oap.server.core.storage.StorageModule;
 import org.apache.skywalking.oap.server.core.storage.annotation.ValueColumnMetadata;
 import org.apache.skywalking.oap.server.core.storage.query.IMetricsQueryDAO;
 import org.apache.skywalking.oap.server.library.module.ModuleManager;
 import org.apache.skywalking.oap.server.library.module.Service;
 
+import static org.apache.skywalking.oap.server.core.query.type.debugging.DebuggingTrace.TRACE_CONTEXT;
+
 @Slf4j
 public class MetricsQueryService implements Service {
     private final ModuleManager moduleManager;
@@ -72,10 +76,7 @@ public class MetricsQueryService implements Service {
         return new NullableValue(defaultValue, true);
     }
 
-    /**
-     * Read time-series values in the duration of required metrics
-     */
-    public MetricsValues readMetricsValues(MetricsCondition condition, Duration duration) throws IOException {
+    private MetricsValues invokeReadMetricsValues(MetricsCondition condition, Duration duration) throws IOException {
         if (!condition.senseScope() || !condition.getEntity().isValid()) {
             return new MetricsValues();
         }
@@ -84,11 +85,25 @@ public class MetricsQueryService implements Service {
     }
 
     /**
-     * Read value in the given time duration, usually as a linear.
-     *
-     * @param labels the labels you need to query.
+     * Read time-series values in the duration of required metrics
      */
-    public List<MetricsValues> readLabeledMetricsValues(MetricsCondition condition,
+    public MetricsValues readMetricsValues(MetricsCondition condition, Duration duration) throws IOException {
+        DebuggingTraceContext traceContext = TRACE_CONTEXT.get();
+        DebuggingSpan span = null;
+        try {
+            if (traceContext != null) {
+                span = traceContext.createSpan("Query Service");
+                span.setMsg("readMetricsValues, MetricsCondition: " + condition + ", Duration: " + duration);
+            }
+            return invokeReadMetricsValues(condition, duration);
+        } finally {
+            if (traceContext != null && span != null) {
+                traceContext.stopSpan(span);
+            }
+        }
+    }
+
+    private List<MetricsValues> invokeReadLabeledMetricsValues(MetricsCondition condition,
                                                         List<KeyValue> labels,
                                                         Duration duration) throws IOException {
         if (!condition.senseScope() || !condition.getEntity().isValid()) {
@@ -98,6 +113,29 @@ public class MetricsQueryService implements Service {
             condition, ValueColumnMetadata.INSTANCE.getValueCName(condition.getName()), labels, duration);
     }
 
+    /**
+     * Read value in the given time duration, usually as a linear.
+     *
+     * @param labels the labels you need to query.
+     */
+    public List<MetricsValues> readLabeledMetricsValues(MetricsCondition condition,
+                                                         List<KeyValue> labels,
+                                                         Duration duration) throws IOException {
+        DebuggingTraceContext traceContext = TRACE_CONTEXT.get();
+        DebuggingSpan span = null;
+        try {
+            if (traceContext != null) {
+                span = traceContext.createSpan("Query Service");
+                span.setMsg("readLabeledMetricsValues, MetricsCondition: " + condition + ", Labels: " + labels + ", Duration: " + duration);
+            }
+            return invokeReadLabeledMetricsValues(condition, labels, duration);
+        } finally {
+            if (traceContext != null && span != null) {
+                traceContext.stopSpan(span);
+            }
+        }
+    }
+
     public List<MetricsValues> readLabeledMetricsValuesWithoutEntity(String metricsName,
                                          List<KeyValue> labels,
                                          Duration duration) throws IOException {
diff --git a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/query/RecordQueryService.java b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/query/RecordQueryService.java
index c645909ec4..759e57fc59 100644
--- a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/query/RecordQueryService.java
+++ b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/query/RecordQueryService.java
@@ -22,6 +22,8 @@ import java.util.Collections;
 import org.apache.skywalking.oap.server.core.query.input.Duration;
 import org.apache.skywalking.oap.server.core.query.input.RecordCondition;
 import org.apache.skywalking.oap.server.core.query.type.Record;
+import org.apache.skywalking.oap.server.core.query.type.debugging.DebuggingSpan;
+import org.apache.skywalking.oap.server.core.query.type.debugging.DebuggingTraceContext;
 import org.apache.skywalking.oap.server.core.storage.StorageModule;
 import org.apache.skywalking.oap.server.core.storage.annotation.ValueColumnMetadata;
 import org.apache.skywalking.oap.server.core.storage.query.IRecordsQueryDAO;
@@ -31,6 +33,8 @@ import org.apache.skywalking.oap.server.library.module.Service;
 import java.io.IOException;
 import java.util.List;
 
+import static org.apache.skywalking.oap.server.core.query.type.debugging.DebuggingTrace.TRACE_CONTEXT;
+
 public class RecordQueryService implements Service {
     private final ModuleManager moduleManager;
     private IRecordsQueryDAO recordsQueryDAO;
@@ -48,11 +52,27 @@ public class RecordQueryService implements Service {
         return recordsQueryDAO;
     }
 
-    public List<Record> readRecords(RecordCondition condition, Duration duration) throws IOException {
+    private List<Record> invokeReadRecords(RecordCondition condition, Duration duration) throws IOException {
         if (!condition.senseScope() || !condition.getParentEntity().isValid()) {
             return Collections.emptyList();
         }
         return getRecordsQueryDAO().readRecords(
             condition, ValueColumnMetadata.INSTANCE.getValueCName(condition.getName()), duration);
     }
+
+    public List<Record> readRecords(RecordCondition condition, Duration duration) throws IOException {
+        DebuggingTraceContext traceContext = TRACE_CONTEXT.get();
+        DebuggingSpan span = null;
+        try {
+            if (traceContext != null) {
+                span = traceContext.createSpan("Query Service");
+                span.setMsg("readRecords, RecordCondition: " + condition + ", Duration: " + duration);
+            }
+            return invokeReadRecords(condition, duration);
+        } finally {
+            if (traceContext != null && span != null) {
+                traceContext.stopSpan(span);
+            }
+        }
+    }
 }
diff --git a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/query/input/Duration.java b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/query/input/Duration.java
index ff71638e1b..eecbc3a596 100644
--- a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/query/input/Duration.java
+++ b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/query/input/Duration.java
@@ -20,6 +20,7 @@ package org.apache.skywalking.oap.server.core.query.input;
 
 import lombok.Getter;
 import lombok.Setter;
+import lombok.ToString;
 import org.apache.skywalking.oap.server.core.query.DurationUtils;
 import org.apache.skywalking.oap.server.core.query.PointOfTime;
 import org.apache.skywalking.oap.server.core.query.enumeration.Step;
@@ -28,6 +29,7 @@ import java.util.List;
 
 @Getter
 @Setter
+@ToString
 public class Duration {
     private String start;
     private String end;
diff --git a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/query/input/Entity.java b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/query/input/Entity.java
index d7864fedf2..f440419017 100644
--- a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/query/input/Entity.java
+++ b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/query/input/Entity.java
@@ -21,6 +21,7 @@ package org.apache.skywalking.oap.server.core.query.input;
 import java.util.Objects;
 import lombok.Getter;
 import lombok.Setter;
+import lombok.ToString;
 import org.apache.skywalking.oap.server.core.analysis.IDManager;
 import org.apache.skywalking.oap.server.core.query.enumeration.Scope;
 
@@ -31,6 +32,7 @@ import org.apache.skywalking.oap.server.core.query.enumeration.Scope;
  */
 @Setter
 @Getter
+@ToString
 public class Entity {
     /**
      * <pre>
diff --git a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/query/input/MetricsCondition.java b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/query/input/MetricsCondition.java
index 53a2a0abec..2c5bd6650d 100644
--- a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/query/input/MetricsCondition.java
+++ b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/query/input/MetricsCondition.java
@@ -20,6 +20,7 @@ package org.apache.skywalking.oap.server.core.query.input;
 
 import lombok.Getter;
 import lombok.Setter;
+import lombok.ToString;
 import org.apache.skywalking.oap.server.core.query.MetricsMetadataQueryService;
 import org.apache.skywalking.oap.server.core.query.enumeration.MetricsType;
 import org.apache.skywalking.oap.server.core.storage.annotation.ValueColumnMetadata;
@@ -29,6 +30,7 @@ import org.apache.skywalking.oap.server.core.storage.annotation.ValueColumnMetad
  */
 @Getter
 @Setter
+@ToString
 public class MetricsCondition {
     /**
      * Metrics name
diff --git a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/query/input/RecordCondition.java b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/query/input/RecordCondition.java
index eab7a1de3e..2a620a94c6 100644
--- a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/query/input/RecordCondition.java
+++ b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/query/input/RecordCondition.java
@@ -20,6 +20,7 @@ package org.apache.skywalking.oap.server.core.query.input;
 
 import lombok.Getter;
 import lombok.Setter;
+import lombok.ToString;
 import org.apache.skywalking.oap.server.core.query.MetricsMetadataQueryService;
 import org.apache.skywalking.oap.server.core.query.enumeration.MetricsType;
 import org.apache.skywalking.oap.server.core.query.enumeration.Order;
@@ -34,6 +35,7 @@ import org.apache.skywalking.oap.server.library.util.StringUtil;
  */
 @Setter
 @Getter
+@ToString
 public class RecordCondition {
     /**
      * Metrics name
diff --git a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/query/input/TopNCondition.java b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/query/input/TopNCondition.java
index 8ced87aead..33f2984277 100644
--- a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/query/input/TopNCondition.java
+++ b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/query/input/TopNCondition.java
@@ -20,6 +20,7 @@ package org.apache.skywalking.oap.server.core.query.input;
 
 import lombok.Getter;
 import lombok.Setter;
+import lombok.ToString;
 import org.apache.skywalking.oap.server.core.query.MetricsMetadataQueryService;
 import org.apache.skywalking.oap.server.core.query.enumeration.MetricsType;
 import org.apache.skywalking.oap.server.core.query.enumeration.Order;
@@ -33,6 +34,7 @@ import org.apache.skywalking.oap.server.core.storage.annotation.ValueColumnMetad
  */
 @Setter
 @Getter
+@ToString
 public class TopNCondition {
     /**
      * Metrics name
diff --git a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/query/type/KeyValue.java b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/query/type/KeyValue.java
index 419776a955..85dcc8eb02 100644
--- a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/query/type/KeyValue.java
+++ b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/query/type/KeyValue.java
@@ -20,9 +20,11 @@ package org.apache.skywalking.oap.server.core.query.type;
 
 import lombok.Data;
 import lombok.EqualsAndHashCode;
+import lombok.ToString;
 
 @Data
 @EqualsAndHashCode
+@ToString
 public class KeyValue {
     private String key;
     private String value;
diff --git a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/query/type/KeyValue.java b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/query/type/debugging/DebuggingSpan.java
similarity index 52%
copy from oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/query/type/KeyValue.java
copy to oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/query/type/debugging/DebuggingSpan.java
index 419776a955..7b836aca0a 100644
--- a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/query/type/KeyValue.java
+++ b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/query/type/debugging/DebuggingSpan.java
@@ -16,22 +16,36 @@
  *
  */
 
-package org.apache.skywalking.oap.server.core.query.type;
+package org.apache.skywalking.oap.server.core.query.type.debugging;
 
-import lombok.Data;
-import lombok.EqualsAndHashCode;
+import lombok.Getter;
+import lombok.Setter;
 
-@Data
-@EqualsAndHashCode
-public class KeyValue {
-    private String key;
-    private String value;
+@Getter
+public class DebuggingSpan {
+    private final int spanId;
+    @Setter
+    private int parentSpanId;
+    private final String operation;
+    //nano seconds
+    private final long startTime;
+    //nano seconds
+    private long endTime;
+    //nano seconds
+    private long duration;
+    @Setter
+    private String msg;
+    @Setter
+    private String error;
 
-    public KeyValue(String key, String value) {
-        this.key = key;
-        this.value = value;
+    public DebuggingSpan(int spanId, String operation) {
+        this.spanId = spanId;
+        this.operation = operation;
+        this.startTime = System.nanoTime();
     }
 
-    public KeyValue() {
+    public void stopSpan() {
+        this.endTime = System.nanoTime();
+        this.duration = endTime - startTime;
     }
 }
diff --git a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/query/type/debugging/DebuggingTrace.java b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/query/type/debugging/DebuggingTrace.java
new file mode 100644
index 0000000000..3159e67cdc
--- /dev/null
+++ b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/query/type/debugging/DebuggingTrace.java
@@ -0,0 +1,50 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.skywalking.oap.server.core.query.type.debugging;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.UUID;
+import lombok.Getter;
+
+@Getter
+public class DebuggingTrace {
+    public final static ThreadLocal<DebuggingTraceContext> TRACE_CONTEXT = new ThreadLocal<>();
+    private final String traceId;
+    private final String condition;
+    private final long startTime;
+    private long endTime;
+    private long duration;
+    private final List<DebuggingSpan> spans = new ArrayList<>();
+
+    public DebuggingTrace(String condition) {
+        this.condition = condition;
+        this.traceId = UUID.randomUUID().toString();
+        this.startTime = System.nanoTime();
+    }
+
+    public void addSpan(DebuggingSpan span) {
+        spans.add(span);
+    }
+
+    public void stopTrace() {
+        this.endTime = System.nanoTime();
+        this.duration = endTime - startTime;
+    }
+}
diff --git a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/query/type/debugging/DebuggingTraceContext.java b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/query/type/debugging/DebuggingTraceContext.java
new file mode 100644
index 0000000000..5f1e9e7996
--- /dev/null
+++ b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/query/type/debugging/DebuggingTraceContext.java
@@ -0,0 +1,68 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.skywalking.oap.server.core.query.type.debugging;
+
+import java.util.Stack;
+import lombok.Getter;
+
+@Getter
+public class DebuggingTraceContext {
+    private final DebuggingTrace execTrace;
+    private final Stack<DebuggingSpan> spanStack = new Stack<>();
+    private int spanIdGenerator;
+    private final boolean debug;
+    private final boolean dumpStorageRsp;
+
+    public DebuggingTraceContext(String condition, boolean debug, boolean dumpStorageRsp) {
+        this.execTrace = new DebuggingTrace(condition);
+        this.debug = debug;
+        this.dumpStorageRsp = dumpStorageRsp;
+    }
+
+    public DebuggingSpan createSpan(String operation) {
+        DebuggingSpan span = new DebuggingSpan(spanIdGenerator++, operation);
+        if (debug) {
+            DebuggingSpan parentSpan = spanStack.isEmpty() ? null : spanStack.peek();
+            if (parentSpan != null) {
+                span.setParentSpanId(parentSpan.getSpanId());
+            } else {
+                span.setParentSpanId(-1);
+            }
+            spanStack.push(span);
+            execTrace.addSpan(span);
+        }
+        return span;
+    }
+
+    public void stopSpan(DebuggingSpan span) {
+        if (debug) {
+            span.stopSpan();
+            if (spanStack.isEmpty()) {
+                return;
+            }
+            spanStack.pop();
+        }
+    }
+
+    public void stopTrace() {
+        if (debug) {
+            execTrace.stopTrace();
+        }
+    }
+}
diff --git a/oap-server/server-query-plugin/debugging-query-plugin/pom.xml b/oap-server/server-query-plugin/debugging-query-plugin/pom.xml
index b67b392902..99965098fd 100644
--- a/oap-server/server-query-plugin/debugging-query-plugin/pom.xml
+++ b/oap-server/server-query-plugin/debugging-query-plugin/pom.xml
@@ -34,5 +34,10 @@
             <artifactId>server-core</artifactId>
             <version>${project.version}</version>
         </dependency>
+        <dependency>
+            <groupId>org.apache.skywalking</groupId>
+            <artifactId>query-graphql-plugin</artifactId>
+            <version>${project.version}</version>
+        </dependency>
     </dependencies>
-</project>
\ No newline at end of file
+</project>
diff --git a/oap-server/server-query-plugin/debugging-query-plugin/src/main/java/org/apache/skywalking/oap/query/debug/DebuggingHTTPHandler.java b/oap-server/server-query-plugin/debugging-query-plugin/src/main/java/org/apache/skywalking/oap/query/debug/DebuggingHTTPHandler.java
index cd6ef86f5f..3c41d1a2f2 100644
--- a/oap-server/server-query-plugin/debugging-query-plugin/src/main/java/org/apache/skywalking/oap/query/debug/DebuggingHTTPHandler.java
+++ b/oap-server/server-query-plugin/debugging-query-plugin/src/main/java/org/apache/skywalking/oap/query/debug/DebuggingHTTPHandler.java
@@ -18,15 +18,32 @@
 
 package org.apache.skywalking.oap.query.debug;
 
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
 import com.linecorp.armeria.server.annotation.Get;
+import com.linecorp.armeria.server.annotation.Param;
+import java.util.Map;
+import java.util.Optional;
+import java.util.stream.Collectors;
+import lombok.SneakyThrows;
 import lombok.extern.slf4j.Slf4j;
+import org.apache.skywalking.mqe.rt.type.ExpressionResult;
+import org.apache.skywalking.oap.query.debug.mqe.DebuggingMQERsp;
+import org.apache.skywalking.oap.query.graphql.resolver.MetricsExpressionQuery;
 import org.apache.skywalking.oap.server.core.CoreModule;
+import org.apache.skywalking.oap.server.core.analysis.Layer;
+import org.apache.skywalking.oap.server.core.query.enumeration.Step;
+import org.apache.skywalking.oap.server.core.query.input.Duration;
+import org.apache.skywalking.oap.server.core.query.input.Entity;
+import org.apache.skywalking.oap.server.core.query.type.debugging.DebuggingTrace;
+import org.apache.skywalking.oap.server.core.query.type.debugging.DebuggingSpan;
 import org.apache.skywalking.oap.server.core.status.ServerStatusService;
 import org.apache.skywalking.oap.server.library.module.ModuleManager;
 
 @Slf4j
 public class DebuggingHTTPHandler {
     private final ServerStatusService serverStatusService;
+    private final MetricsExpressionQuery mqeQuery;
     final DebuggingQueryConfig config;
 
     public DebuggingHTTPHandler(final ModuleManager manager, final DebuggingQueryConfig config) {
@@ -34,10 +51,82 @@ public class DebuggingHTTPHandler {
                                      .provider()
                                      .getService(ServerStatusService.class);
         this.config = config;
+        this.mqeQuery = new MetricsExpressionQuery(manager);
     }
 
     @Get("/debugging/config/dump")
     public String dumpConfigurations() {
         return serverStatusService.dumpBootingConfigurations(config.getKeywords4MaskingSecretsOfConfig());
     }
+
+    @SneakyThrows
+    @Get("/debugging/query/mqe")
+    public String execExpression(@Param("dumpDBRsp") boolean dumpStorageRsp,
+                                 @Param("expression") String expression,
+                                 @Param("startTime") String startTime,
+                                 @Param("endTime") String endTime,
+                                 @Param("step") String step,
+                                 @Param("service") String service,
+                                 @Param("serviceLayer") String serviceLayer,
+                                 @Param("serviceInstance") Optional<String> serviceInstance,
+                                 @Param("endpoint") Optional<String> endpoint,
+                                 @Param("process") Optional<String> process,
+                                 @Param("destService") Optional<String> destService,
+                                 @Param("destServiceLayer") Optional<String> destServiceLayer,
+                                 @Param("destServiceInstance") Optional<String> destServiceInstance,
+                                 @Param("destEndpoint") Optional<String> destEndpoint,
+                                 @Param("destProcess") Optional<String> destProcess
+    ) {
+        Entity entity = new Entity();
+        entity.setServiceName(service);
+        entity.setServiceInstanceName(serviceInstance.orElse(null));
+        entity.setEndpointName(endpoint.orElse(null));
+        entity.setProcessName(process.orElse(null));
+        entity.setDestServiceName(destService.orElse(null));
+        entity.setDestServiceInstanceName(destServiceInstance.orElse(null));
+        entity.setDestEndpointName(destEndpoint.orElse(null));
+        entity.setDestProcessName(destProcess.orElse(null));
+        entity.setNormal(Layer.nameOf(serviceLayer).isNormal());
+        destServiceLayer.ifPresent(layer -> {
+            entity.setDestNormal(Layer.nameOf(layer).isNormal());
+        });
+        Duration duration = new Duration();
+        duration.setStart(startTime);
+        duration.setEnd(endTime);
+        duration.setStep(Step.valueOf(step));
+        ExpressionResult expressionResult = mqeQuery.execExpression(expression, entity, duration, true, dumpStorageRsp);
+        DebuggingTrace execTrace = expressionResult.getDebuggingTrace();
+
+        Map<Integer, DebuggingSpanRsp> spanMap = execTrace.getSpans().stream()
+                                                          .collect(Collectors.toMap(
+                                                              DebuggingSpan::getSpanId,
+                                                              this::transformSpan
+                                                          ));
+
+        execTrace.getSpans().forEach(span -> {
+            if (span.getParentSpanId() != -1) {
+                DebuggingSpanRsp parentSpan = spanMap.get(span.getParentSpanId());
+                parentSpan.getChildSpans().add(spanMap.get(span.getSpanId()));
+            }
+        });
+        DebuggingMQERsp result = new DebuggingMQERsp(
+            expressionResult.getType(), expressionResult.getResults(), expressionResult.getError(),
+            new DebuggingTraceRsp(
+                execTrace.getTraceId(), execTrace.getCondition(), execTrace.getStartTime(), execTrace.getEndTime(),
+                execTrace.getDuration(), spanMap.get(0)
+            )
+        );
+
+        YAMLFactory yamlFactory = new YAMLFactory();
+        ObjectMapper mapper = new ObjectMapper(yamlFactory);
+
+        return mapper.writeValueAsString(result);
+    }
+
+    private DebuggingSpanRsp transformSpan(DebuggingSpan span) {
+        return new DebuggingSpanRsp(
+            span.getSpanId(), span.getParentSpanId(), span.getOperation(), span.getStartTime(), span.getEndTime(),
+            span.getDuration(), span.getMsg(), span.getError()
+        );
+    }
 }
diff --git a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/query/type/KeyValue.java b/oap-server/server-query-plugin/debugging-query-plugin/src/main/java/org/apache/skywalking/oap/query/debug/DebuggingSpanRsp.java
similarity index 59%
copy from oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/query/type/KeyValue.java
copy to oap-server/server-query-plugin/debugging-query-plugin/src/main/java/org/apache/skywalking/oap/query/debug/DebuggingSpanRsp.java
index 419776a955..0d62d84063 100644
--- a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/query/type/KeyValue.java
+++ b/oap-server/server-query-plugin/debugging-query-plugin/src/main/java/org/apache/skywalking/oap/query/debug/DebuggingSpanRsp.java
@@ -16,22 +16,23 @@
  *
  */
 
-package org.apache.skywalking.oap.server.core.query.type;
+package org.apache.skywalking.oap.query.debug;
 
-import lombok.Data;
-import lombok.EqualsAndHashCode;
+import java.util.ArrayList;
+import java.util.List;
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
 
-@Data
-@EqualsAndHashCode
-public class KeyValue {
-    private String key;
-    private String value;
-
-    public KeyValue(String key, String value) {
-        this.key = key;
-        this.value = value;
-    }
-
-    public KeyValue() {
-    }
+@RequiredArgsConstructor
+@Getter
+public class DebuggingSpanRsp {
+    private final int spanId;
+    private final int parentSpanId;
+    private final String operation;
+    private final long startTime;
+    private final long endTime;
+    private final long duration;
+    private final String msg;
+    private final String error;
+    private final List<DebuggingSpanRsp> childSpans = new ArrayList<>();
 }
diff --git a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/query/type/KeyValue.java b/oap-server/server-query-plugin/debugging-query-plugin/src/main/java/org/apache/skywalking/oap/query/debug/DebuggingTraceRsp.java
similarity index 67%
copy from oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/query/type/KeyValue.java
copy to oap-server/server-query-plugin/debugging-query-plugin/src/main/java/org/apache/skywalking/oap/query/debug/DebuggingTraceRsp.java
index 419776a955..aa77970913 100644
--- a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/query/type/KeyValue.java
+++ b/oap-server/server-query-plugin/debugging-query-plugin/src/main/java/org/apache/skywalking/oap/query/debug/DebuggingTraceRsp.java
@@ -16,22 +16,18 @@
  *
  */
 
-package org.apache.skywalking.oap.server.core.query.type;
+package org.apache.skywalking.oap.query.debug;
 
-import lombok.Data;
-import lombok.EqualsAndHashCode;
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
 
-@Data
-@EqualsAndHashCode
-public class KeyValue {
-    private String key;
-    private String value;
-
-    public KeyValue(String key, String value) {
-        this.key = key;
-        this.value = value;
-    }
-
-    public KeyValue() {
-    }
+@RequiredArgsConstructor
+@Getter
+public class DebuggingTraceRsp {
+    private final String traceId;
+    private final String condition;
+    private final long startTime;
+    private final long endTime;
+    private final long duration;
+    private final DebuggingSpanRsp rootSpan;
 }
diff --git a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/query/type/KeyValue.java b/oap-server/server-query-plugin/debugging-query-plugin/src/main/java/org/apache/skywalking/oap/query/debug/mqe/DebuggingMQERsp.java
similarity index 59%
copy from oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/query/type/KeyValue.java
copy to oap-server/server-query-plugin/debugging-query-plugin/src/main/java/org/apache/skywalking/oap/query/debug/mqe/DebuggingMQERsp.java
index 419776a955..e8002bda33 100644
--- a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/query/type/KeyValue.java
+++ b/oap-server/server-query-plugin/debugging-query-plugin/src/main/java/org/apache/skywalking/oap/query/debug/mqe/DebuggingMQERsp.java
@@ -16,22 +16,20 @@
  *
  */
 
-package org.apache.skywalking.oap.server.core.query.type;
+package org.apache.skywalking.oap.query.debug.mqe;
 
-import lombok.Data;
-import lombok.EqualsAndHashCode;
+import java.util.List;
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+import org.apache.skywalking.mqe.rt.type.ExpressionResultType;
+import org.apache.skywalking.mqe.rt.type.MQEValues;
+import org.apache.skywalking.oap.query.debug.DebuggingTraceRsp;
 
-@Data
-@EqualsAndHashCode
-public class KeyValue {
-    private String key;
-    private String value;
-
-    public KeyValue(String key, String value) {
-        this.key = key;
-        this.value = value;
-    }
-
-    public KeyValue() {
-    }
+@RequiredArgsConstructor
+@Getter
+public class DebuggingMQERsp {
+    private final ExpressionResultType type;
+    private final List<MQEValues> results;
+    private final String error;
+    private final DebuggingTraceRsp execTrace;
 }
diff --git a/oap-server/server-query-plugin/query-graphql-plugin/src/main/java/org/apache/skywalking/oap/query/graphql/mqe/rt/MQEVisitor.java b/oap-server/server-query-plugin/query-graphql-plugin/src/main/java/org/apache/skywalking/oap/query/graphql/mqe/rt/MQEVisitor.java
index 5a83f17aed..8d62ef48bb 100644
--- a/oap-server/server-query-plugin/query-graphql-plugin/src/main/java/org/apache/skywalking/oap/query/graphql/mqe/rt/MQEVisitor.java
+++ b/oap-server/server-query-plugin/query-graphql-plugin/src/main/java/org/apache/skywalking/oap/query/graphql/mqe/rt/MQEVisitor.java
@@ -47,6 +47,8 @@ import org.apache.skywalking.oap.server.core.query.type.KeyValue;
 import org.apache.skywalking.oap.server.core.query.type.MetricsValues;
 import org.apache.skywalking.oap.server.core.query.type.Record;
 import org.apache.skywalking.oap.server.core.query.type.SelectedRecord;
+import org.apache.skywalking.oap.server.core.query.type.debugging.DebuggingSpan;
+import org.apache.skywalking.oap.server.core.query.type.debugging.DebuggingTraceContext;
 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.library.module.ModuleManager;
@@ -55,6 +57,8 @@ import org.apache.skywalking.mqe.rt.type.ExpressionResult;
 import org.apache.skywalking.mqe.rt.type.ExpressionResultType;
 import org.joda.time.DateTime;
 
+import static org.apache.skywalking.oap.server.core.query.type.debugging.DebuggingTrace.TRACE_CONTEXT;
+
 @Slf4j
 public class MQEVisitor extends MQEVisitorBase {
     private final Entity entity;
@@ -102,77 +106,76 @@ public class MQEVisitor extends MQEVisitorBase {
 
     @Override
     public ExpressionResult visitMetric(MQEParser.MetricContext ctx) {
-        ExpressionResult result = new ExpressionResult();
-        String metricName = ctx.metricName().getText();
-        Optional<ValueColumnMetadata.ValueColumn> valueColumn = ValueColumnMetadata.INSTANCE.readValueColumnDefinition(
-            metricName);
-        if (valueColumn.isEmpty()) {
-            result.setType(ExpressionResultType.UNKNOWN);
-            result.setError("Metric: [" + metricName + "] dose not exist.");
-            return result;
-        }
-
-        Column.ValueDataType dataType = valueColumn.get().getDataType();
+        DebuggingTraceContext traceContext = TRACE_CONTEXT.get();
+        DebuggingSpan span = traceContext.createSpan("MQE Metric OP: " + ctx.getText());
         try {
-            if (Column.ValueDataType.COMMON_VALUE == dataType) {
-                if (ctx.parent instanceof MQEParser.TopNOPContext) {
-                    MQEParser.TopNOPContext parent = (MQEParser.TopNOPContext) ctx.parent;
-                    int topN = Integer.parseInt(parent.INTEGER().getText());
-                    if (topN <= 0) {
-                        throw new IllegalExpressionException("TopN value must be > 0.");
+            ExpressionResult result = new ExpressionResult();
+            String metricName = ctx.metricName().getText();
+            Optional<ValueColumnMetadata.ValueColumn> valueColumn = ValueColumnMetadata.INSTANCE.readValueColumnDefinition(
+                metricName);
+            if (valueColumn.isEmpty()) {
+                result.setType(ExpressionResultType.UNKNOWN);
+                result.setError("Metric: [" + metricName + "] dose not exist.");
+                return result;
+            }
+
+            Column.ValueDataType dataType = valueColumn.get().getDataType();
+            try {
+                if (Column.ValueDataType.COMMON_VALUE == dataType) {
+                    if (ctx.parent instanceof MQEParser.TopNOPContext) {
+                        MQEParser.TopNOPContext parent = (MQEParser.TopNOPContext) ctx.parent;
+                        int topN = Integer.parseInt(parent.INTEGER().getText());
+                        if (topN <= 0) {
+                            throw new IllegalExpressionException("TopN value must be > 0.");
+                        }
+                        querySortMetrics(metricName, Integer.parseInt(parent.INTEGER().getText()),
+                                         Order.valueOf(parent.order().getText().toUpperCase()), result);
+                    } else if (ctx.parent instanceof MQEParser.TrendOPContext) {
+                        //trend query requires get previous data according to the trend range
+                        MQEParser.TrendOPContext parent = (MQEParser.TrendOPContext) ctx.parent;
+                        int trendRange = Integer.parseInt(parent.INTEGER().getText());
+                        queryMetrics(metricName, getTrendQueryDuration(trendRange), result);
+                    } else {
+                        queryMetrics(metricName, this.duration, result);
                     }
-                    querySortMetrics(metricName, Integer.parseInt(parent.INTEGER().getText()),
-                                     Order.valueOf(parent.order().getText().toUpperCase()), result
-                    );
-                } else if (ctx.parent instanceof MQEParser.TrendOPContext) {
-                    //trend query requires get previous data according to the trend range
-                    MQEParser.TrendOPContext parent = (MQEParser.TrendOPContext) ctx.parent;
-                    int trendRange = Integer.parseInt(parent.INTEGER().getText());
-                    queryMetrics(metricName, getTrendQueryDuration(trendRange), result);
-                } else {
-                    queryMetrics(metricName, this.duration, result);
-                }
-            } else if (Column.ValueDataType.LABELED_VALUE == dataType) {
-                if (ctx.parent instanceof MQEParser.TopNOPContext) {
-                    throw new IllegalExpressionException(
-                        "Metric: [" + metricName + "] is labeled value, dose not support top_n query.");
-                }
-                List<KeyValue> queryLabels = super.buildLabels(ctx.labelList());
-                if (ctx.parent instanceof MQEParser.TrendOPContext) {
-                    MQEParser.TrendOPContext parent = (MQEParser.TrendOPContext) ctx.parent;
-                    int trendRange = Integer.parseInt(parent.INTEGER().getText());
-                    queryLabeledMetrics(metricName, queryLabels, getTrendQueryDuration(trendRange), result);
-                } else {
-                    queryLabeledMetrics(metricName, queryLabels, this.duration, result);
-                }
-            } else if (Column.ValueDataType.SAMPLED_RECORD == dataType) {
-                if (ctx.parent instanceof MQEParser.TopNOPContext) {
-                    MQEParser.TopNOPContext parent = (MQEParser.TopNOPContext) ctx.parent;
-                    int topN = Integer.parseInt(parent.INTEGER().getText());
-                    if (topN <= 0) {
-                        throw new IllegalExpressionException("TopN value must be > 0.");
+                } else if (Column.ValueDataType.LABELED_VALUE == dataType) {
+                    if (ctx.parent instanceof MQEParser.TopNOPContext) {
+                        throw new IllegalExpressionException(
+                            "Metric: [" + metricName + "] is labeled value, dose not support top_n query.");
+                    }
+                    List<KeyValue> queryLabels = super.buildLabels(ctx.labelList());
+                    if (ctx.parent instanceof MQEParser.TrendOPContext) {
+                        MQEParser.TrendOPContext parent = (MQEParser.TrendOPContext) ctx.parent;
+                        int trendRange = Integer.parseInt(parent.INTEGER().getText());
+                        queryLabeledMetrics(metricName, queryLabels, getTrendQueryDuration(trendRange), result);
+                    } else {
+                        queryLabeledMetrics(metricName, queryLabels, this.duration, result);
+                    }
+                } else if (Column.ValueDataType.SAMPLED_RECORD == dataType) {
+                    if (ctx.parent instanceof MQEParser.TopNOPContext) {
+                        MQEParser.TopNOPContext parent = (MQEParser.TopNOPContext) ctx.parent;
+                        int topN = Integer.parseInt(parent.INTEGER().getText());
+                        if (topN <= 0) {
+                            throw new IllegalExpressionException("TopN value must be > 0.");
+                        }
+                        queryRecords(metricName, Integer.parseInt(parent.INTEGER().getText()),
+                                     Order.valueOf(parent.order().getText().toUpperCase()), result);
+                    } else {
+                        throw new IllegalExpressionException(
+                            "Metric: [" + metricName + "] is topN record, need top_n function for query.");
                     }
-                    queryRecords(metricName, Integer.parseInt(parent.INTEGER().getText()),
-                                 Order.valueOf(parent.order().getText().toUpperCase()), result
-                    );
-                } else {
-                    throw new IllegalExpressionException(
-                        "Metric: [" + metricName + "] is topN record, need top_n function for query.");
                 }
+            } catch (IllegalExpressionException e) {
+                return getErrorResult(e.getMessage());
+            } catch (IOException e) {
+                ExpressionResult errorResult = getErrorResult("Internal IO exception, query metrics error.");
+                log.error("Query metrics from backend error.", e);
+                return errorResult;
             }
-        } catch (IllegalExpressionException e) {
-            ExpressionResult errorResult = new ExpressionResult();
-            errorResult.setType(ExpressionResultType.UNKNOWN);
-            errorResult.setError(e.getMessage());
-            return errorResult;
-        } catch (IOException e) {
-            ExpressionResult errorResult = new ExpressionResult();
-            errorResult.setType(ExpressionResultType.UNKNOWN);
-            errorResult.setError("Internal IO exception, query metrics error.");
-            log.error("Query metrics from backend error.", e);
-            return errorResult;
+            return result;
+        } finally {
+            traceContext.stopSpan(span);
         }
-        return result;
     }
 
     private void querySortMetrics(String metricName,
diff --git a/oap-server/server-query-plugin/query-graphql-plugin/src/main/java/org/apache/skywalking/oap/query/graphql/resolver/MetricsExpressionQuery.java b/oap-server/server-query-plugin/query-graphql-plugin/src/main/java/org/apache/skywalking/oap/query/graphql/resolver/MetricsExpressionQuery.java
index 1522278fb1..37a45b654f 100644
--- a/oap-server/server-query-plugin/query-graphql-plugin/src/main/java/org/apache/skywalking/oap/query/graphql/resolver/MetricsExpressionQuery.java
+++ b/oap-server/server-query-plugin/query-graphql-plugin/src/main/java/org/apache/skywalking/oap/query/graphql/resolver/MetricsExpressionQuery.java
@@ -30,10 +30,15 @@ import org.apache.skywalking.mqe.rt.type.ExpressionResult;
 import org.apache.skywalking.mqe.rt.type.ExpressionResultType;
 import org.apache.skywalking.oap.server.core.query.input.Entity;
 import org.apache.skywalking.oap.server.core.query.input.Duration;
+import org.apache.skywalking.oap.server.core.query.type.debugging.DebuggingTrace;
+import org.apache.skywalking.oap.server.core.query.type.debugging.DebuggingSpan;
+import org.apache.skywalking.oap.server.core.query.type.debugging.DebuggingTraceContext;
 import org.apache.skywalking.oap.server.library.module.ModuleManager;
 import org.apache.skywalking.mqe.rt.grammar.MQELexer;
 import org.apache.skywalking.mqe.rt.grammar.MQEParser;
 
+import static org.apache.skywalking.oap.server.core.query.type.debugging.DebuggingTrace.TRACE_CONTEXT;
+
 public class MetricsExpressionQuery implements GraphQLQueryResolver {
     private final ModuleManager moduleManager;
     private final DecimalFormat valueFormat = new DecimalFormat();
@@ -43,32 +48,53 @@ public class MetricsExpressionQuery implements GraphQLQueryResolver {
         this.valueFormat.setGroupingUsed(false);
     }
 
-    public ExpressionResult execExpression(String expression, Entity entity, Duration duration) {
-        MQELexer lexer = new MQELexer(
-            CharStreams.fromString(expression));
-        lexer.addErrorListener(new ParseErrorListener());
-        MQEParser parser = new MQEParser(new CommonTokenStream(lexer));
-        parser.addErrorListener(new ParseErrorListener());
-        ParseTree tree;
+    public ExpressionResult execExpression(String expression,
+                                           Entity entity,
+                                           Duration duration,
+                                           boolean debug,
+                                           boolean dumpStorageRsp) {
+        DebuggingTraceContext traceContext = new DebuggingTraceContext(
+            "Expression: " + expression + ", Entity: " + entity + ", Duration: " + duration, debug, dumpStorageRsp);
+        TRACE_CONTEXT.set(traceContext);
+        DebuggingSpan span = traceContext.createSpan("MQE query");
         try {
-            tree = parser.expression();
-        } catch (ParseCancellationException e) {
-            ExpressionResult errorResult = new ExpressionResult();
-            errorResult.setType(ExpressionResultType.UNKNOWN);
-            errorResult.setError(e.getMessage());
-            return errorResult;
-        }
-        MQEVisitor visitor = new MQEVisitor(moduleManager, entity, duration);
-        ExpressionResult parseResult = visitor.visit(tree);
-
-        parseResult.getResults().forEach(mqeValues -> {
-            mqeValues.getValues().forEach(mqeValue -> {
-                if (!mqeValue.isEmptyValue()) {
-                    mqeValue.setValue(valueFormat.format(mqeValue.getDoubleValue()));
+            MQEVisitor visitor = new MQEVisitor(moduleManager, entity, duration);
+            DebuggingTrace execTrace = traceContext.getExecTrace();
+            DebuggingSpan syntaxSpan = traceContext.createSpan("MQE syntax analysis");
+            ParseTree tree;
+            try {
+                MQELexer lexer = new MQELexer(
+                    CharStreams.fromString(expression));
+                lexer.addErrorListener(new ParseErrorListener());
+                MQEParser parser = new MQEParser(new CommonTokenStream(lexer));
+                parser.addErrorListener(new ParseErrorListener());
+                try {
+                    tree = parser.expression();
+                } catch (ParseCancellationException e) {
+                    ExpressionResult errorResult = new ExpressionResult();
+                    errorResult.setType(ExpressionResultType.UNKNOWN);
+                    errorResult.setError(e.getMessage());
+                    return errorResult;
                 }
+            } finally {
+                traceContext.stopSpan(syntaxSpan);
+            }
+            ExpressionResult parseResult = visitor.visit(tree);
+            parseResult.getResults().forEach(mqeValues -> {
+                mqeValues.getValues().forEach(mqeValue -> {
+                    if (!mqeValue.isEmptyValue()) {
+                        mqeValue.setValue(valueFormat.format(mqeValue.getDoubleValue()));
+                    }
+                });
             });
-        });
-
-        return parseResult;
+            if (debug) {
+                parseResult.setDebuggingTrace(execTrace);
+            }
+            return parseResult;
+        } finally {
+            traceContext.stopSpan(span);
+            traceContext.stopTrace();
+            TRACE_CONTEXT.remove();
+        }
     }
 }
diff --git a/oap-server/server-query-plugin/query-graphql-plugin/src/main/resources/query-protocol b/oap-server/server-query-plugin/query-graphql-plugin/src/main/resources/query-protocol
index e0be806ea4..b4a0234eda 160000
--- a/oap-server/server-query-plugin/query-graphql-plugin/src/main/resources/query-protocol
+++ b/oap-server/server-query-plugin/query-graphql-plugin/src/main/resources/query-protocol
@@ -1 +1 @@
-Subproject commit e0be806ea4f5e5e324e4ba57c2ca414931d9e512
+Subproject commit b4a0234edab6c06e31cfac9c95f3ba16b20f7912
diff --git a/oap-server/server-storage-plugin/storage-banyandb-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/banyandb/BanyanDBAggregationQueryDAO.java b/oap-server/server-storage-plugin/storage-banyandb-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/banyandb/BanyanDBAggregationQueryDAO.java
index a83d0e68df..27d1c3e88e 100644
--- a/oap-server/server-storage-plugin/storage-banyandb-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/banyandb/BanyanDBAggregationQueryDAO.java
+++ b/oap-server/server-storage-plugin/storage-banyandb-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/banyandb/BanyanDBAggregationQueryDAO.java
@@ -19,6 +19,7 @@
 package org.apache.skywalking.oap.server.storage.plugin.banyandb;
 
 import com.google.common.collect.ImmutableSet;
+import com.google.gson.Gson;
 import org.apache.skywalking.banyandb.v1.client.DataPoint;
 import org.apache.skywalking.banyandb.v1.client.MeasureQuery;
 import org.apache.skywalking.banyandb.v1.client.MeasureQueryResponse;
@@ -30,6 +31,9 @@ import org.apache.skywalking.oap.server.core.query.input.Duration;
 import org.apache.skywalking.oap.server.core.query.input.TopNCondition;
 import org.apache.skywalking.oap.server.core.query.type.KeyValue;
 import org.apache.skywalking.oap.server.core.query.type.SelectedRecord;
+import org.apache.skywalking.oap.server.core.query.type.debugging.DebuggingTrace;
+import org.apache.skywalking.oap.server.core.query.type.debugging.DebuggingSpan;
+import org.apache.skywalking.oap.server.core.query.type.debugging.DebuggingTraceContext;
 import org.apache.skywalking.oap.server.core.storage.query.IAggregationQueryDAO;
 import org.apache.skywalking.oap.server.library.util.CollectionUtils;
 import org.apache.skywalking.oap.server.storage.plugin.banyandb.stream.AbstractBanyanDBDAO;
@@ -50,7 +54,27 @@ public class BanyanDBAggregationQueryDAO extends AbstractBanyanDBDAO implements
     }
 
     @Override
-    public List<SelectedRecord> sortMetrics(TopNCondition condition, String valueColumnName, Duration duration, List<KeyValue> additionalConditions) throws IOException {
+    public List<SelectedRecord> sortMetrics(TopNCondition condition,
+                                            String valueColumnName,
+                                            Duration duration,
+                                            List<KeyValue> additionalConditions) throws IOException {
+        DebuggingTraceContext traceContext = DebuggingTrace.TRACE_CONTEXT.get();
+        DebuggingSpan span = null;
+        try {
+            if (traceContext != null) {
+                span = traceContext.createSpan("Query Dao: sortMetrics");
+                span.setMsg("TopNCondition: " + condition + ", valueColumnName: " + valueColumnName + ", duration: " + duration + ", additionalConditions: " + additionalConditions);
+
+            }
+            return invokeSortMetrics(condition, valueColumnName, duration, additionalConditions);
+        } finally {
+            if (traceContext != null && span != null) {
+                traceContext.stopSpan(span);
+            }
+        }
+    }
+
+    private List<SelectedRecord> invokeSortMetrics(TopNCondition condition, String valueColumnName, Duration duration, List<KeyValue> additionalConditions) throws IOException {
         final String modelName = condition.getName();
         final TimestampRange timestampRange = new TimestampRange(duration.getStartTimestamp(), duration.getEndTimestamp());
         MetadataRegistry.Schema schema = MetadataRegistry.INSTANCE.findMetadata(modelName, duration.getStep());
@@ -79,13 +103,7 @@ public class BanyanDBAggregationQueryDAO extends AbstractBanyanDBDAO implements
 
     List<SelectedRecord> serverSideTopN(TopNCondition condition, MetadataRegistry.Schema schema, MetadataRegistry.ColumnSpec valueColumnSpec,
                                         TimestampRange timestampRange, List<KeyValue> additionalConditions) throws IOException {
-        TopNQueryResponse resp = null;
-        if (condition.getOrder() == Order.DES) {
-            resp = topN(schema, timestampRange, condition.getTopN(), additionalConditions);
-        } else {
-            resp = bottomN(schema, timestampRange, condition.getTopN(), additionalConditions);
-        }
-
+        TopNQueryResponse resp = traceServerSideTopN(schema, timestampRange, condition.getTopN(), additionalConditions, condition.getOrder());
         if (resp.size() == 0) {
             return Collections.emptyList();
         } else if (resp.size() > 1) { // since we have done aggregation, i.e. MEAN
@@ -105,8 +123,8 @@ public class BanyanDBAggregationQueryDAO extends AbstractBanyanDBDAO implements
 
     List<SelectedRecord> directMetricsTopN(TopNCondition condition, String valueColumnName, MetadataRegistry.ColumnSpec valueColumnSpec,
                                            TimestampRange timestampRange, List<KeyValue> additionalConditions) throws IOException {
-        MeasureQueryResponse resp = query(condition.getName(), TAGS, Collections.singleton(valueColumnName),
-                timestampRange, new QueryBuilder<MeasureQuery>() {
+        MeasureQueryResponse resp = traceDirectMetricsTopN(condition.getName(), TAGS, Collections.singleton(valueColumnName),
+                timestampRange, valueColumnName, condition.getTopN(), condition.getOrder(), new QueryBuilder<MeasureQuery>() {
                     @Override
                     protected void apply(MeasureQuery query) {
                         query.meanBy(valueColumnName, ImmutableSet.of(Metrics.ENTITY_ID));
@@ -149,4 +167,90 @@ public class BanyanDBAggregationQueryDAO extends AbstractBanyanDBDAO implements
             return String.valueOf(((Number) fieldValue).longValue());
         }
     }
+
+    private MeasureQueryResponse traceDirectMetricsTopN(String measureModelName,
+                                                        Set<String> tags,
+                                                        Set<String> fields,
+                                                        TimestampRange timestampRange,
+                                                        String valueColumnName,
+                                                        int topN,
+                                                        Order order,
+                                                        QueryBuilder<MeasureQuery> queryBuilder) throws IOException {
+        DebuggingTraceContext traceContext = DebuggingTrace.TRACE_CONTEXT.get();
+        DebuggingSpan span = null;
+        try {
+            StringBuilder builder = new StringBuilder();
+            if (traceContext != null) {
+                span = traceContext.createSpan("Query BanyanDB direct metrics TopN");
+                builder.append("Condition: ")
+                       .append("measureModelName: ")
+                       .append(measureModelName)
+                       .append(", tags: ")
+                       .append(tags)
+                       .append(", fields: ")
+                       .append(fields)
+                       .append(", timestampRange: ")
+                       .append(timestampRange)
+                       .append(", valueColumnName: ")
+                       .append(valueColumnName)
+                       .append(", topN: ")
+                       .append(topN)
+                       .append(", order: ")
+                       .append(order);
+                span.setMsg(builder.toString());
+            }
+            MeasureQueryResponse response = query(measureModelName, tags, fields, timestampRange, queryBuilder);
+            if (traceContext != null && traceContext.isDumpStorageRsp()) {
+                builder.append("\n").append(" Response: ").append(new Gson().toJson(response));
+                span.setMsg(builder.toString());
+            }
+            return response;
+        } finally {
+            if (traceContext != null && span != null) {
+                traceContext.stopSpan(span);
+            }
+        }
+    }
+
+    private TopNQueryResponse traceServerSideTopN(MetadataRegistry.Schema schema,
+                                                  TimestampRange timestampRange,
+                                                  int topN,
+                                                  List<KeyValue> additionalConditions,
+                                                  Order order) throws IOException {
+        DebuggingTraceContext traceContext = DebuggingTrace.TRACE_CONTEXT.get();
+        DebuggingSpan span = null;
+        try {
+            StringBuilder builder = new StringBuilder();
+            if (traceContext != null) {
+                span = traceContext.createSpan("Query BanyanDB server-side TopN");
+                builder.append("Condition: ")
+                       .append("schema: ")
+                       .append(schema)
+                       .append(", timestampRange: ")
+                       .append(timestampRange)
+                       .append(", topN: ")
+                       .append(topN)
+                       .append(", additionalConditions: ")
+                       .append(additionalConditions)
+                       .append(", order: ")
+                       .append(order);
+                span.setMsg(builder.toString());
+            }
+            TopNQueryResponse response;
+            if (order == Order.DES) {
+                response = topN(schema, timestampRange, topN, additionalConditions);
+            } else {
+                response = bottomN(schema, timestampRange, topN, additionalConditions);
+            }
+            if (traceContext != null && traceContext.isDumpStorageRsp()) {
+                builder.append("\n").append(" Response: ").append(new Gson().toJson(response));
+                span.setMsg(builder.toString());
+            }
+            return response;
+        } finally {
+            if (traceContext != null && span != null) {
+                traceContext.stopSpan(span);
+            }
+        }
+    }
 }
diff --git a/oap-server/server-storage-plugin/storage-banyandb-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/banyandb/BanyanDBRecordsQueryDAO.java b/oap-server/server-storage-plugin/storage-banyandb-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/banyandb/BanyanDBRecordsQueryDAO.java
index 8bf59354bc..cf030fca22 100644
--- a/oap-server/server-storage-plugin/storage-banyandb-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/banyandb/BanyanDBRecordsQueryDAO.java
+++ b/oap-server/server-storage-plugin/storage-banyandb-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/banyandb/BanyanDBRecordsQueryDAO.java
@@ -19,6 +19,7 @@
 package org.apache.skywalking.oap.server.storage.plugin.banyandb;
 
 import com.google.common.collect.ImmutableSet;
+import com.google.gson.Gson;
 import org.apache.skywalking.banyandb.v1.client.AbstractQuery;
 import org.apache.skywalking.banyandb.v1.client.RowEntity;
 import org.apache.skywalking.banyandb.v1.client.StreamQuery;
@@ -29,6 +30,9 @@ import org.apache.skywalking.oap.server.core.query.enumeration.Order;
 import org.apache.skywalking.oap.server.core.query.input.Duration;
 import org.apache.skywalking.oap.server.core.query.input.RecordCondition;
 import org.apache.skywalking.oap.server.core.query.type.Record;
+import org.apache.skywalking.oap.server.core.query.type.debugging.DebuggingTrace;
+import org.apache.skywalking.oap.server.core.query.type.debugging.DebuggingSpan;
+import org.apache.skywalking.oap.server.core.query.type.debugging.DebuggingTraceContext;
 import org.apache.skywalking.oap.server.core.storage.query.IRecordsQueryDAO;
 import org.apache.skywalking.oap.server.library.util.StringUtil;
 import org.apache.skywalking.oap.server.storage.plugin.banyandb.stream.AbstractBanyanDBDAO;
@@ -48,11 +52,28 @@ public class BanyanDBRecordsQueryDAO extends AbstractBanyanDBDAO implements IRec
 
     @Override
     public List<Record> readRecords(RecordCondition condition, String valueColumnName, Duration duration) throws IOException {
+        DebuggingTraceContext traceContext = DebuggingTrace.TRACE_CONTEXT.get();
+        DebuggingSpan span = null;
+        try {
+            if (traceContext != null) {
+                span = traceContext.createSpan("Query BanyanDB records");
+                span.setMsg(
+                    "Condition: " + condition + ", ValueColumnName: " + valueColumnName + ", Duration: " + duration);
+            }
+            return invokeReadRecords(condition, valueColumnName, duration);
+        } finally {
+            if (traceContext != null && span != null) {
+                traceContext.stopSpan(span);
+            }
+        }
+    }
+
+    public List<Record> invokeReadRecords(RecordCondition condition, String valueColumnName, Duration duration) throws IOException {
         final String modelName = condition.getName();
         final TimestampRange timestampRange = new TimestampRange(duration.getStartTimestamp(), duration.getEndTimestamp());
         final Set<String> tags = ImmutableSet.of(TopN.ENTITY_ID, TopN.STATEMENT, TopN.TRACE_ID, valueColumnName);
-        StreamQueryResponse resp = query(modelName, tags,
-                timestampRange, new QueryBuilder<StreamQuery>() {
+        StreamQueryResponse resp = traceRecordsTopN(modelName, tags,
+                timestampRange, valueColumnName, condition.getTopN(), condition.getOrder(), new QueryBuilder<StreamQuery>() {
                     @Override
                     protected void apply(StreamQuery query) {
                         query.and(eq(TopN.ENTITY_ID, condition.getParentEntity().buildId()));
@@ -103,4 +124,45 @@ public class BanyanDBRecordsQueryDAO extends AbstractBanyanDBDAO implements IRec
             return String.valueOf(((Number) e.getTagValue(fieldName)).longValue());
         }
     }
+
+    private StreamQueryResponse traceRecordsTopN(String modelName,
+                                                 Set<String> tags,
+                                                 TimestampRange timestampRange,
+                                                 String valueColumnName,
+                                                 int topN,
+                                                 Order order,
+                                                 QueryBuilder<StreamQuery> queryBuilder) throws IOException {
+        DebuggingTraceContext traceContext = DebuggingTrace.TRACE_CONTEXT.get();
+        DebuggingSpan span = null;
+        try {
+            StringBuilder builder = new StringBuilder();
+            if (traceContext != null) {
+                span = traceContext.createSpan("Query BanyanDB records TopN");
+                builder.append("Condition: ")
+                       .append(modelName)
+                       .append(", Tags: ")
+                       .append(tags)
+                       .append(", TimestampRange: ")
+                       .append(timestampRange)
+                       .append(", ValueColumnName: ")
+                       .append(valueColumnName)
+                       .append(", TopN: ")
+                       .append(topN)
+                       .append(", Order: ")
+                       .append(order);
+                span.setMsg(builder.toString());
+            }
+
+            StreamQueryResponse response = query(modelName, tags, timestampRange, queryBuilder);
+            if (traceContext != null && traceContext.isDumpStorageRsp()) {
+                builder.append("\n").append(" Response: ").append(new Gson().toJson(response));
+                span.setMsg(builder.toString());
+            }
+            return response;
+        } finally {
+            if (traceContext != null && span != null) {
+                traceContext.stopSpan(span);
+            }
+        }
+    }
 }
diff --git a/oap-server/server-storage-plugin/storage-banyandb-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/banyandb/MetadataRegistry.java b/oap-server/server-storage-plugin/storage-banyandb-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/banyandb/MetadataRegistry.java
index bc3dc3de83..0f94173f4d 100644
--- a/oap-server/server-storage-plugin/storage-banyandb-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/banyandb/MetadataRegistry.java
+++ b/oap-server/server-storage-plugin/storage-banyandb-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/banyandb/MetadataRegistry.java
@@ -624,6 +624,7 @@ public enum MetadataRegistry {
 
     @Builder
     @EqualsAndHashCode
+    @ToString
     public static class Schema {
         @Getter
         private final SchemaMetadata metadata;
@@ -671,6 +672,7 @@ public enum MetadataRegistry {
     @Builder
     @EqualsAndHashCode
     @Getter
+    @ToString
     public static class TopNSpec {
         private final String name;
         @Singular
@@ -683,6 +685,7 @@ public enum MetadataRegistry {
 
     @RequiredArgsConstructor
     @Getter
+    @ToString
     public static class ColumnSpec {
         private final ColumnType columnType;
         private final Class<?> columnClass;
diff --git a/oap-server/server-storage-plugin/storage-banyandb-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/banyandb/measure/BanyanDBMetricsQueryDAO.java b/oap-server/server-storage-plugin/storage-banyandb-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/banyandb/measure/BanyanDBMetricsQueryDAO.java
index d64f674038..5a8375ebe9 100644
--- a/oap-server/server-storage-plugin/storage-banyandb-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/banyandb/measure/BanyanDBMetricsQueryDAO.java
+++ b/oap-server/server-storage-plugin/storage-banyandb-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/banyandb/measure/BanyanDBMetricsQueryDAO.java
@@ -20,6 +20,7 @@ package org.apache.skywalking.oap.server.storage.plugin.banyandb.measure;
 
 import com.google.common.collect.ImmutableSet;
 
+import com.google.gson.Gson;
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Collections;
@@ -27,6 +28,7 @@ import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
+import java.util.Set;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.skywalking.banyandb.v1.client.DataPoint;
 import org.apache.skywalking.banyandb.v1.client.MeasureQuery;
@@ -44,6 +46,9 @@ import org.apache.skywalking.oap.server.core.query.type.IntValues;
 import org.apache.skywalking.oap.server.core.query.type.KVInt;
 import org.apache.skywalking.oap.server.core.query.type.KeyValue;
 import org.apache.skywalking.oap.server.core.query.type.MetricsValues;
+import org.apache.skywalking.oap.server.core.query.type.debugging.DebuggingTrace;
+import org.apache.skywalking.oap.server.core.query.type.debugging.DebuggingSpan;
+import org.apache.skywalking.oap.server.core.query.type.debugging.DebuggingTraceContext;
 import org.apache.skywalking.oap.server.core.storage.annotation.ValueColumnMetadata;
 import org.apache.skywalking.oap.server.core.storage.query.IMetricsQueryDAO;
 import org.apache.skywalking.oap.server.storage.plugin.banyandb.BanyanDBStorageClient;
@@ -60,7 +65,47 @@ public class BanyanDBMetricsQueryDAO extends AbstractBanyanDBDAO implements IMet
     }
 
     @Override
-    public MetricsValues readMetricsValues(MetricsCondition condition, String valueColumnName, Duration duration) throws IOException {
+    public MetricsValues readMetricsValues(MetricsCondition condition,
+                                           String valueColumnName,
+                                           Duration duration) throws IOException {
+        DebuggingTraceContext traceContext = DebuggingTrace.TRACE_CONTEXT.get();
+        DebuggingSpan span = null;
+        try {
+            if (traceContext != null) {
+                span = traceContext.createSpan("Query Dao: readMetricsValues");
+                span.setMsg(
+                    "MetricsCondition: " + condition + ", valueColumnName: " + valueColumnName + ", duration: " + duration);
+            }
+            return invokeReadMetricsValues(condition, valueColumnName, duration);
+        } finally {
+            if (traceContext != null && span != null) {
+                traceContext.stopSpan(span);
+            }
+        }
+    }
+
+    @Override
+    public List<MetricsValues> readLabeledMetricsValues(MetricsCondition condition,
+                                                        String valueColumnName,
+                                                        List<KeyValue> labels,
+                                                        Duration duration) throws IOException {
+        DebuggingTraceContext traceContext = DebuggingTrace.TRACE_CONTEXT.get();
+        DebuggingSpan span = null;
+        try {
+            if (traceContext != null) {
+                span = traceContext.createSpan("Query Dao: readLabeledMetricsValues");
+                span.setMsg(
+                    "MetricsCondition: " + condition + ", valueColumnName: " + valueColumnName + ", labels: " + labels + ", duration: " + duration);
+            }
+            return invokeReadLabeledMetricsValues(condition, valueColumnName, labels, duration);
+        } finally {
+            if (traceContext != null && span != null) {
+                traceContext.stopSpan(span);
+            }
+        }
+    }
+
+    private MetricsValues invokeReadMetricsValues(MetricsCondition condition, String valueColumnName, Duration duration) throws IOException {
         String modelName = condition.getName();
         MetadataRegistry.Schema schema = MetadataRegistry.INSTANCE.findMetadata(modelName, duration.getStep());
         if (schema == null) {
@@ -104,8 +149,7 @@ public class BanyanDBMetricsQueryDAO extends AbstractBanyanDBDAO implements IMet
         }
     }
 
-    @Override
-    public List<MetricsValues> readLabeledMetricsValues(MetricsCondition condition, String valueColumnName, List<KeyValue> labels, Duration duration) throws IOException {
+    private List<MetricsValues> invokeReadLabeledMetricsValues(MetricsCondition condition, String valueColumnName, List<KeyValue> labels, Duration duration) throws IOException {
         Map<Long, DataPoint> idMap = queryByEntityID(condition, valueColumnName, duration);
 
         List<PointOfTime> tsPoints = duration.assembleDurationPoints();
@@ -219,7 +263,7 @@ public class BanyanDBMetricsQueryDAO extends AbstractBanyanDBDAO implements IMet
         TimestampRange timestampRange = new TimestampRange(duration.getStartTimestamp(), duration.getEndTimestamp());
 
         Map<Long, DataPoint> map = new HashMap<>();
-        MeasureQueryResponse resp = query(schema, ImmutableSet.of(Metrics.ENTITY_ID), ImmutableSet.of(valueColumnName), timestampRange, new QueryBuilder<MeasureQuery>() {
+        MeasureQueryResponse resp = traceQueryResponse(schema, ImmutableSet.of(Metrics.ENTITY_ID), ImmutableSet.of(valueColumnName), timestampRange, new QueryBuilder<MeasureQuery>() {
             @Override
             protected void apply(MeasureQuery query) {
                 query.and(eq(Metrics.ENTITY_ID, entityID));
@@ -235,4 +279,38 @@ public class BanyanDBMetricsQueryDAO extends AbstractBanyanDBDAO implements IMet
 
         return map;
     }
+
+    private MeasureQueryResponse traceQueryResponse(MetadataRegistry.Schema schema,
+                                                    Set<String> tags,
+                                                    Set<String> fields,
+                                                    TimestampRange timestampRange,
+                                                    QueryBuilder<MeasureQuery> queryBuilder) throws IOException {
+        DebuggingTraceContext traceContext = DebuggingTrace.TRACE_CONTEXT.get();
+        DebuggingSpan span = null;
+        try {
+            StringBuilder builder = new StringBuilder();
+            if (traceContext != null) {
+                span = traceContext.createSpan("Query BanyanDB metrics");
+                builder.append("Condition: schema: ")
+                       .append(schema)
+                       .append(", tags: ")
+                       .append(tags)
+                       .append(", fields: ")
+                       .append(fields)
+                       .append(", timestampRange: ")
+                       .append(timestampRange);
+                span.setMsg(builder.toString());
+            }
+            MeasureQueryResponse response = query(schema, tags, fields, timestampRange, queryBuilder);
+            if (traceContext != null && traceContext.isDumpStorageRsp()) {
+                builder.append("\n").append(" Response: ").append(new Gson().toJson(response));
+                span.setMsg(builder.toString());
+            }
+            return response;
+        } finally {
+            if (traceContext != null && span != null) {
+                traceContext.stopSpan(span);
+            }
+        }
+    }
 }
diff --git a/oap-server/server-storage-plugin/storage-elasticsearch-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/elasticsearch/query/AggregationQueryEsDAO.java b/oap-server/server-storage-plugin/storage-elasticsearch-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/elasticsearch/query/AggregationQueryEsDAO.java
index 6d881829f7..40e48257ab 100644
--- a/oap-server/server-storage-plugin/storage-elasticsearch-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/elasticsearch/query/AggregationQueryEsDAO.java
+++ b/oap-server/server-storage-plugin/storage-elasticsearch-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/elasticsearch/query/AggregationQueryEsDAO.java
@@ -18,7 +18,10 @@
 
 package org.apache.skywalking.oap.server.storage.plugin.elasticsearch.query;
 
+import com.google.gson.Gson;
+import java.util.Arrays;
 import java.util.Objects;
+import java.util.function.Supplier;
 import org.apache.skywalking.library.elasticsearch.requests.search.BoolQueryBuilder;
 import org.apache.skywalking.library.elasticsearch.requests.search.Query;
 import org.apache.skywalking.library.elasticsearch.requests.search.RangeQueryBuilder;
@@ -34,6 +37,9 @@ import org.apache.skywalking.oap.server.core.query.input.Duration;
 import org.apache.skywalking.oap.server.core.query.input.TopNCondition;
 import org.apache.skywalking.oap.server.core.query.type.KeyValue;
 import org.apache.skywalking.oap.server.core.query.type.SelectedRecord;
+import org.apache.skywalking.oap.server.core.query.type.debugging.DebuggingTrace;
+import org.apache.skywalking.oap.server.core.query.type.debugging.DebuggingSpan;
+import org.apache.skywalking.oap.server.core.query.type.debugging.DebuggingTraceContext;
 import org.apache.skywalking.oap.server.core.storage.query.IAggregationQueryDAO;
 import org.apache.skywalking.oap.server.library.client.elasticsearch.ElasticSearchClient;
 import org.apache.skywalking.oap.server.library.util.CollectionUtils;
@@ -56,6 +62,25 @@ public class AggregationQueryEsDAO extends EsDAO implements IAggregationQueryDAO
                                             final String valueColumnName,
                                             final Duration duration,
                                             final List<KeyValue> additionalConditions) {
+        DebuggingTraceContext traceContext = DebuggingTrace.TRACE_CONTEXT.get();
+        DebuggingSpan span = null;
+        try {
+            if (traceContext != null) {
+                span = traceContext.createSpan("Query Dao: sortMetrics");
+                span.setMsg("TopNCondition: " + condition + ", ValueColumnName: " + valueColumnName + ", Duration: " + duration + ", AdditionalConditions: " + additionalConditions);
+            }
+            return invokeSortMetrics(condition, valueColumnName, duration, additionalConditions);
+        } finally {
+            if (traceContext != null && span != null) {
+                traceContext.stopSpan(span);
+            }
+        }
+    }
+
+    private List<SelectedRecord> invokeSortMetrics(final TopNCondition condition,
+                                            final String valueColumnName,
+                                            final Duration duration,
+                                            final List<KeyValue> additionalConditions) {
         final String realValueColumn = IndexController.LogicIndicesRegister.getPhysicalColumnName(condition.getName(), valueColumnName);
         final RangeQueryBuilder basicQuery = Query.range(Metrics.TIME_BUCKET)
                                                   .lte(duration.getEndTimeBucket())
@@ -112,7 +137,7 @@ public class AggregationQueryEsDAO extends EsDAO implements IAggregationQueryDAO
                        .collectMode(TermsAggregationBuilder.CollectMode.BREADTH_FIRST)
                        .build());
 
-        final SearchResponse response = getClient().search(new TimeRangeIndexNameGenerator(
+        final SearchResponse response = traceSearchResponse(new TimeRangeIndexNameGenerator(
             IndexController.LogicIndicesRegister.getPhysicalTableName(condition.getName()),
             duration.getStartTimeBucketInSec(),
             duration.getEndTimeBucketInSec()), search.build());
@@ -133,4 +158,27 @@ public class AggregationQueryEsDAO extends EsDAO implements IAggregationQueryDAO
         }
         return topNList;
     }
+
+    private SearchResponse traceSearchResponse(Supplier<String[]> indices, Search search) {
+        DebuggingTraceContext traceContext = DebuggingTrace.TRACE_CONTEXT.get();
+        DebuggingSpan span = null;
+        try {
+            StringBuilder builder = new StringBuilder();
+            if (traceContext != null) {
+                span = traceContext.createSpan("Query Elasticsearch");
+                builder.append("Condition: ").append("indices: ").append(Arrays.toString(indices.get()));
+                span.setMsg(builder.toString());
+            }
+            SearchResponse response = getClient().search(indices, search);
+            if (traceContext != null && traceContext.isDumpStorageRsp()) {
+                builder.append("\n").append(" Response: ").append(new Gson().toJson(response));
+                span.setMsg(builder.toString());
+            }
+            return response;
+        } finally {
+            if (traceContext != null && span != null) {
+                traceContext.stopSpan(span);
+            }
+        }
+    }
 }
diff --git a/oap-server/server-storage-plugin/storage-elasticsearch-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/elasticsearch/query/MetricsQueryEsDAO.java b/oap-server/server-storage-plugin/storage-elasticsearch-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/elasticsearch/query/MetricsQueryEsDAO.java
index 48a9e014d7..c33be4c102 100644
--- a/oap-server/server-storage-plugin/storage-elasticsearch-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/elasticsearch/query/MetricsQueryEsDAO.java
+++ b/oap-server/server-storage-plugin/storage-elasticsearch-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/elasticsearch/query/MetricsQueryEsDAO.java
@@ -18,6 +18,7 @@
 
 package org.apache.skywalking.oap.server.storage.plugin.elasticsearch.query;
 
+import com.google.gson.Gson;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
@@ -44,6 +45,9 @@ import org.apache.skywalking.oap.server.core.query.type.IntValues;
 import org.apache.skywalking.oap.server.core.query.type.KVInt;
 import org.apache.skywalking.oap.server.core.query.type.KeyValue;
 import org.apache.skywalking.oap.server.core.query.type.MetricsValues;
+import org.apache.skywalking.oap.server.core.query.type.debugging.DebuggingTrace;
+import org.apache.skywalking.oap.server.core.query.type.debugging.DebuggingSpan;
+import org.apache.skywalking.oap.server.core.query.type.debugging.DebuggingTraceContext;
 import org.apache.skywalking.oap.server.core.storage.annotation.ValueColumnMetadata;
 import org.apache.skywalking.oap.server.core.storage.query.IMetricsQueryDAO;
 import org.apache.skywalking.oap.server.library.client.elasticsearch.ElasticSearchClient;
@@ -62,6 +66,46 @@ public class MetricsQueryEsDAO extends EsDAO implements IMetricsQueryDAO {
     public MetricsValues readMetricsValues(final MetricsCondition condition,
                                            final String valueColumnName,
                                            final Duration duration) {
+        DebuggingTraceContext traceContext = DebuggingTrace.TRACE_CONTEXT.get();
+        DebuggingSpan span = null;
+        try {
+            if (traceContext != null) {
+                span = traceContext.createSpan("Query Dao: readMetricsValues");
+                span.setMsg(
+                    "MetricsCondition: " + condition + ", ValueColumnName: " + valueColumnName + ", Duration: " + duration);
+            }
+            return invokeReadMetricsValues(condition, valueColumnName, duration);
+        } finally {
+            if (traceContext != null && span != null) {
+                traceContext.stopSpan(span);
+            }
+        }
+    }
+
+    @Override
+    public List<MetricsValues> readLabeledMetricsValues(final MetricsCondition condition,
+                                                        final String valueColumnName,
+                                                        final List<KeyValue> labels,
+                                                        final Duration duration) {
+        DebuggingTraceContext traceContext = DebuggingTrace.TRACE_CONTEXT.get();
+        DebuggingSpan span = null;
+        try {
+            if (traceContext != null) {
+                span = traceContext.createSpan("Query Dao: readLabeledMetricsValues");
+                span.setMsg(
+                    "MetricsCondition: " + condition + ", ValueColumnName: " + valueColumnName + ", Labels: " + labels + ", Duration: " + duration);
+            }
+            return invokeReadLabeledMetricsValues(condition, valueColumnName, labels, duration);
+        } finally {
+            if (traceContext != null && span != null) {
+                traceContext.stopSpan(span);
+            }
+        }
+    }
+
+    private MetricsValues invokeReadMetricsValues(final MetricsCondition condition,
+                                           final String valueColumnName,
+                                           final Duration duration) {
         final String realValueColumn = IndexController.LogicIndicesRegister.getPhysicalColumnName(condition.getName(), valueColumnName);
         String tableName =
             IndexController.LogicIndicesRegister.getPhysicalTableName(condition.getName());
@@ -80,7 +124,7 @@ public class MetricsQueryEsDAO extends EsDAO implements IMetricsQueryDAO {
         }).collect(Collectors.toList());
 
         MetricsValues metricsValues = new MetricsValues();
-        Optional<Documents> response = getClient().ids(indexIdsGroup);
+        Optional<Documents> response = traceQueryResponse(indexIdsGroup);
         if (response.isPresent()) {
             Map<String, Map<String, Object>> idMap = toMap(response.get());
 
@@ -109,8 +153,7 @@ public class MetricsQueryEsDAO extends EsDAO implements IMetricsQueryDAO {
         return metricsValues;
     }
 
-    @Override
-    public List<MetricsValues> readLabeledMetricsValues(final MetricsCondition condition,
+    private List<MetricsValues> invokeReadLabeledMetricsValues(final MetricsCondition condition,
                                                         final String valueColumnName,
                                                         final List<KeyValue> labels,
                                                         final Duration duration) {
@@ -133,7 +176,7 @@ public class MetricsQueryEsDAO extends EsDAO implements IMetricsQueryDAO {
             indexIdsGroup.computeIfAbsent(indexName, v -> new ArrayList<>()).add(id);
         });
 
-        Optional<Documents> response = getClient().ids(indexIdsGroup);
+        Optional<Documents> response = traceQueryResponse(indexIdsGroup);
         Map<String, DataTable> idMap = new HashMap<>();
 
         if (response.isPresent()) {
@@ -153,6 +196,29 @@ public class MetricsQueryEsDAO extends EsDAO implements IMetricsQueryDAO {
         );
     }
 
+    private Optional<Documents> traceQueryResponse(Map<String, List<String>> indexIdsGroup) {
+        DebuggingTraceContext traceContext = DebuggingTrace.TRACE_CONTEXT.get();
+        DebuggingSpan span = null;
+        try {
+            StringBuilder builder = new StringBuilder();
+            if (traceContext != null) {
+                span = traceContext.createSpan("Query Elasticsearch");
+                builder.append("Condition: ").append("indices: ").append(indexIdsGroup.keySet());
+                span.setMsg(builder.toString());
+            }
+            Optional<Documents> response = getClient().ids(indexIdsGroup);
+            if (traceContext != null && traceContext.isDumpStorageRsp()) {
+                builder.append("\n").append(" Response: ").append(response.isPresent() ? new Gson().toJson(response.get()) : "null");
+                span.setMsg(builder.toString());
+            }
+            return response;
+        } finally {
+            if (traceContext != null && span != null) {
+                traceContext.stopSpan(span);
+            }
+        }
+    }
+
     public List<MetricsValues> readLabeledMetricsValuesWithoutEntity(final String metricName,
                                                     final String valueColumnName,
                                                     final List<KeyValue> labels,
diff --git a/oap-server/server-storage-plugin/storage-elasticsearch-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/elasticsearch/query/RecordsQueryEsDAO.java b/oap-server/server-storage-plugin/storage-elasticsearch-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/elasticsearch/query/RecordsQueryEsDAO.java
index e2e0be0a62..ffd6f23081 100644
--- a/oap-server/server-storage-plugin/storage-elasticsearch-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/elasticsearch/query/RecordsQueryEsDAO.java
+++ b/oap-server/server-storage-plugin/storage-elasticsearch-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/elasticsearch/query/RecordsQueryEsDAO.java
@@ -18,6 +18,7 @@
 
 package org.apache.skywalking.oap.server.storage.plugin.elasticsearch.query;
 
+import com.google.gson.Gson;
 import org.apache.skywalking.library.elasticsearch.requests.search.BoolQueryBuilder;
 import org.apache.skywalking.library.elasticsearch.requests.search.Query;
 import org.apache.skywalking.library.elasticsearch.requests.search.Search;
@@ -30,6 +31,9 @@ import org.apache.skywalking.oap.server.core.query.enumeration.Order;
 import org.apache.skywalking.oap.server.core.query.input.Duration;
 import org.apache.skywalking.oap.server.core.query.input.RecordCondition;
 import org.apache.skywalking.oap.server.core.query.type.Record;
+import org.apache.skywalking.oap.server.core.query.type.debugging.DebuggingTrace;
+import org.apache.skywalking.oap.server.core.query.type.debugging.DebuggingSpan;
+import org.apache.skywalking.oap.server.core.query.type.debugging.DebuggingTraceContext;
 import org.apache.skywalking.oap.server.core.storage.query.IRecordsQueryDAO;
 import org.apache.skywalking.oap.server.library.client.elasticsearch.ElasticSearchClient;
 import org.apache.skywalking.oap.server.library.util.StringUtil;
@@ -48,6 +52,24 @@ public class RecordsQueryEsDAO extends EsDAO implements IRecordsQueryDAO {
 
     @Override
     public List<Record> readRecords(final RecordCondition condition,
+                                    final String valueColumnName,
+                                    final Duration duration) throws IOException {
+        DebuggingTraceContext traceContext = DebuggingTrace.TRACE_CONTEXT.get();
+        DebuggingSpan span = null;
+        try {
+            if (traceContext != null) {
+                span = traceContext.createSpan("Query Dao: readRecords");
+                span.setMsg("RecordCondition: " + condition + ", ValueColumnName: " + valueColumnName + ", Duration: " + duration);
+            }
+            return invokeReadRecords(condition, valueColumnName, duration);
+        } finally {
+            if (traceContext != null && span != null) {
+                traceContext.stopSpan(span);
+            }
+        }
+    }
+
+    private List<Record> invokeReadRecords(final RecordCondition condition,
                                            final String valueColumnName,
                                            final Duration duration) throws IOException {
         final BoolQueryBuilder query =
@@ -69,7 +91,7 @@ public class RecordsQueryEsDAO extends EsDAO implements IRecordsQueryDAO {
                       condition.getOrder().equals(Order.DES) ?
                           Sort.Order.DESC : Sort.Order.ASC
                   );
-        final SearchResponse response = getClient().search(
+        final SearchResponse response = traceSearchResponse(
             IndexController.LogicIndicesRegister.getPhysicalTableName(condition.getName()),
             search.build()
         );
@@ -89,4 +111,27 @@ public class RecordsQueryEsDAO extends EsDAO implements IRecordsQueryDAO {
 
         return results;
     }
+
+    private SearchResponse traceSearchResponse(String indexName, Search search) throws IOException {
+        DebuggingTraceContext traceContext = DebuggingTrace.TRACE_CONTEXT.get();
+        DebuggingSpan span = null;
+        try {
+            StringBuilder builder = new StringBuilder();
+            if (traceContext != null) {
+                span = traceContext.createSpan("Query Elasticsearch");
+                builder.append("Condition: ").append("indices: ").append(indexName);
+                span.setMsg(builder.toString());
+            }
+            SearchResponse response = getClient().search(indexName, search);
+            if (traceContext != null && traceContext.isDumpStorageRsp()) {
+                builder.append("\n").append(" Response: ").append(new Gson().toJson(response));
+                span.setMsg(builder.toString());
+            }
+            return response;
+        } finally {
+            if (traceContext != null && span != null) {
+                traceContext.stopSpan(span);
+            }
+        }
+    }
 }