bzp2010 commented on code in PR #12686:
URL: https://github.com/apache/apisix/pull/12686#discussion_r2741867364


##########
docs/en/latest/plugins/opentelemetry.md:
##########
@@ -153,37 +168,152 @@ In OpenTelemetry collector's log, you should see 
information similar to the foll
 
 ```text
 2024-02-18T17:14:03.825Z info ResourceSpans #0
-Resource SchemaURL:
-Resource attributes:
-     -> telemetry.sdk.language: Str(lua)
-     -> telemetry.sdk.name: Str(opentelemetry-lua)
-     -> telemetry.sdk.version: Str(0.1.1)
-     -> hostname: Str(e34673e24631)
-     -> service.name: Str(APISIX)
 ScopeSpans #0
 ScopeSpans SchemaURL:
 InstrumentationScope opentelemetry-lua
 Span #0
-    Trace ID       : fbd0a38d4ea4a128ff1a688197bc58b0
+    Trace ID       : 95a1644afaaf65e1f0193b1f193b990a
+    Parent ID      : 905f850f13e32bfb
+    ID             : 5a3835b61110d942
+    Name           : http_router_match
+    Kind           : Internal
+    Start time     : 2025-10-24 06:58:04.430430976 +0000 UTC
+    End time       : 2025-10-24 06:58:04.431542016 +0000 UTC
+    Status code    : Unset
+    Status message :
+Span #1
+    Trace ID       : 95a1644afaaf65e1f0193b1f193b990a
+    Parent ID      : 905f850f13e32bfb
+    ID             : 4ab25e2b92f394e1
+    Name           : resolve_dns
+    Kind           : Internal
+    Start time     : 2025-10-24 06:58:04.432521984 +0000 UTC
+    End time       : 2025-10-24 06:58:04.44903296 +0000 UTC
+    Status code    : Unset
+    Status message :
+Span #2
+    Trace ID       : 95a1644afaaf65e1f0193b1f193b990a
+    Parent ID      : 905f850f13e32bfb
+    ID             : 3620c0f05dd2be4f
+    Name           : apisix.phase.header_filter
+    Kind           : Internal
+    Start time     : 2025-10-24 06:58:06.960481024 +0000 UTC
+    End time       : 2025-10-24 06:58:06.960510976 +0000 UTC
+    Status code    : Unset
+    Status message :
+Span #3
+    Trace ID       : 95a1644afaaf65e1f0193b1f193b990a
+    Parent ID      : 4c5f3476f62a7e8a
+    ID             : a9bfad7bb6986e41
+    Name           : apisix.phase.body_filter
+    Kind           : Internal
+    Start time     : 2025-10-24 06:58:06.960579072 +0000 UTC
+    End time       : 2025-10-24 06:58:06.96059008 +0000 UTC
+    Status code    : Unset
+    Status message :
+Span #4
+    Trace ID       : 95a1644afaaf65e1f0193b1f193b990a
+    Parent ID      : b2994675df6baa83
+    ID             : 26705f9c47584a5b
+    Name           : apisix.phase.delayed_body_filter.opentelemetry
+    Kind           : Internal
+    Start time     : 2025-10-24 06:58:06.960613888 +0000 UTC
+    End time       : 2025-10-24 06:58:06.960687104 +0000 UTC
+    Status code    : Unset
+    Status message :
+Span #5
+    Trace ID       : 95a1644afaaf65e1f0193b1f193b990a
+    Parent ID      : 4c5f3476f62a7e8a
+    ID             : b2994675df6baa83
+    Name           : apisix.phase.delayed_body_filter
+    Kind           : Internal
+    Start time     : 2025-10-24 06:58:06.96059904 +0000 UTC
+    End time       : 2025-10-24 06:58:06.960692992 +0000 UTC
+    Status code    : Unset
+    Status message :
+Span #6
+    Trace ID       : 95a1644afaaf65e1f0193b1f193b990a
+    Parent ID      : 905f850f13e32bfb
+    ID             : 4c5f3476f62a7e8a
+    Name           : apisix.phase.body_filter
+    Kind           : Server
+    Start time     : 2025-10-24 06:58:06.96056704 +0000 UTC
+    End time       : 2025-10-24 06:58:06.960698112 +0000 UTC
+    Status code    : Unset
+    Status message :
+Span #7
+    Trace ID       : 95a1644afaaf65e1f0193b1f193b990a
+    Parent ID      : 2024d73d32cbd81b
+    ID             : 223c64fb691a24e8
+    Name           : apisix.phase.body_filter
+    Kind           : Internal
+    Start time     : 2025-10-24 06:58:06.961624064 +0000 UTC
+    End time       : 2025-10-24 06:58:06.961635072 +0000 UTC
+    Status code    : Unset
+    Status message :
+Span #8
+    Trace ID       : 95a1644afaaf65e1f0193b1f193b990a
+    Parent ID      : fd193dd24c618f60
+    ID             : 8729ad6e0d94a23b
+    Name           : apisix.phase.delayed_body_filter.opentelemetry
+    Kind           : Internal
+    Start time     : 2025-10-24 06:58:06.961648896 +0000 UTC
+    End time       : 1970-01-01 00:00:00 +0000 UTC
+    Status code    : Unset
+    Status message :
+Span #9
+    Trace ID       : 95a1644afaaf65e1f0193b1f193b990a
+    Parent ID      : 2024d73d32cbd81b
+    ID             : fd193dd24c618f60
+    Name           : apisix.phase.delayed_body_filter
+    Kind           : Internal
+    Start time     : 2025-10-24 06:58:06.961641984 +0000 UTC
+    End time       : 1970-01-01 00:00:00 +0000 UTC
+    Status code    : Unset
+    Status message :
+Span #10
+    Trace ID       : 95a1644afaaf65e1f0193b1f193b990a
+    Parent ID      : 905f850f13e32bfb
+    ID             : 2024d73d32cbd81b
+    Name           : apisix.phase.body_filter
+    Kind           : Server
+    Start time     : 2025-10-24 06:58:06.960980992 +0000 UTC
+    End time       : 1970-01-01 00:00:00 +0000 UTC
+    Status code    : Unset
+    Status message :
+Span #11
+    Trace ID       : 95a1644afaaf65e1f0193b1f193b990a
+    Parent ID      : cfb0b4603dc2e385
+    ID             : 905f850f13e32bfb
+    Name           : apisix.phase.access
+    Kind           : Server
+    Start time     : 2025-10-24 06:58:04.427932928 +0000 UTC
+    End time       : 1970-01-01 00:00:00 +0000 UTC
+    Status code    : Unset
+    Status message :
+Span #12
+    Trace ID       : 95a1644afaaf65e1f0193b1f193b990a
     Parent ID      :
-    ID             : af3dc7642104748a
-    Name           : GET /anything
+    ID             : cfb0b4603dc2e385
+    Name           : GET /headers
     Kind           : Server
-    Start time     : 2024-02-18 17:14:03.763244032 +0000 UTC
-    End time       : 2024-02-18 17:14:03.920229888 +0000 UTC
+    Start time     : 2025-10-24 06:58:04.432427008 +0000 UTC
+    End time       : 2025-10-24 06:58:06.962299904 +0000 UTC
     Status code    : Unset
     Status message :
 Attributes:
      -> net.host.name: Str(127.0.0.1)
      -> http.method: Str(GET)
      -> http.scheme: Str(http)
-     -> http.target: Str(/anything)
-     -> http.user_agent: Str(curl/7.64.1)
+     -> http.target: Str(/headers)
+     -> http.user_agent: Str(curl/8.16.0)
      -> apisix.route_id: Str(otel-tracing-route)
      -> apisix.route_name: Empty()
-     -> http.route: Str(/anything)
+     -> http.route: Str(/headers)
      -> http.status_code: Int(200)

Review Comment:
   Ditto. check them again against the conventions 
https://opentelemetry.io/docs/specs/semconv/registry/attributes/http/
   
   For previously added attributes that do not comply with the convention, you 
may retain them, but please add a new attribute that complies with the 
convention.



##########
docs/zh/latest/plugins/opentelemetry.md:
##########
@@ -152,37 +167,152 @@ curl "http://127.0.0.1:9080/anything";
 
 ```text
 2024-02-18T17:14:03.825Z info ResourceSpans #0
-Resource SchemaURL:
-Resource attributes:
-    -> telemetry.sdk.language: Str(lua)
-    -> telemetry.sdk.name: Str(opentelemetry-lua)
-    -> telemetry.sdk.version: Str(0.1.1)
-    -> hostname: Str(e34673e24631)
-    -> service.name: Str(APISIX)
 ScopeSpans #0
 ScopeSpans SchemaURL:
 InstrumentationScope opentelemetry-lua
 Span #0
-    Trace ID       : fbd0a38d4ea4a128ff1a688197bc58b0
+    Trace ID       : 95a1644afaaf65e1f0193b1f193b990a
+    Parent ID      : 905f850f13e32bfb
+    ID             : 5a3835b61110d942
+    Name           : http_router_match
+    Kind           : Internal
+    Start time     : 2025-10-24 06:58:04.430430976 +0000 UTC
+    End time       : 2025-10-24 06:58:04.431542016 +0000 UTC
+    Status code    : Unset
+    Status message :
+Span #1
+    Trace ID       : 95a1644afaaf65e1f0193b1f193b990a
+    Parent ID      : 905f850f13e32bfb
+    ID             : 4ab25e2b92f394e1
+    Name           : resolve_dns
+    Kind           : Internal
+    Start time     : 2025-10-24 06:58:04.432521984 +0000 UTC
+    End time       : 2025-10-24 06:58:04.44903296 +0000 UTC
+    Status code    : Unset
+    Status message :
+Span #2
+    Trace ID       : 95a1644afaaf65e1f0193b1f193b990a
+    Parent ID      : 905f850f13e32bfb
+    ID             : 3620c0f05dd2be4f
+    Name           : apisix.phase.header_filter
+    Kind           : Internal
+    Start time     : 2025-10-24 06:58:06.960481024 +0000 UTC
+    End time       : 2025-10-24 06:58:06.960510976 +0000 UTC
+    Status code    : Unset
+    Status message :
+Span #3
+    Trace ID       : 95a1644afaaf65e1f0193b1f193b990a
+    Parent ID      : 4c5f3476f62a7e8a
+    ID             : a9bfad7bb6986e41
+    Name           : apisix.phase.body_filter
+    Kind           : Internal
+    Start time     : 2025-10-24 06:58:06.960579072 +0000 UTC
+    End time       : 2025-10-24 06:58:06.96059008 +0000 UTC
+    Status code    : Unset
+    Status message :
+Span #4
+    Trace ID       : 95a1644afaaf65e1f0193b1f193b990a
+    Parent ID      : b2994675df6baa83
+    ID             : 26705f9c47584a5b
+    Name           : apisix.phase.delayed_body_filter.opentelemetry
+    Kind           : Internal
+    Start time     : 2025-10-24 06:58:06.960613888 +0000 UTC
+    End time       : 2025-10-24 06:58:06.960687104 +0000 UTC
+    Status code    : Unset
+    Status message :
+Span #5
+    Trace ID       : 95a1644afaaf65e1f0193b1f193b990a
+    Parent ID      : 4c5f3476f62a7e8a
+    ID             : b2994675df6baa83
+    Name           : apisix.phase.delayed_body_filter
+    Kind           : Internal
+    Start time     : 2025-10-24 06:58:06.96059904 +0000 UTC
+    End time       : 2025-10-24 06:58:06.960692992 +0000 UTC
+    Status code    : Unset
+    Status message :
+Span #6
+    Trace ID       : 95a1644afaaf65e1f0193b1f193b990a
+    Parent ID      : 905f850f13e32bfb
+    ID             : 4c5f3476f62a7e8a
+    Name           : apisix.phase.body_filter
+    Kind           : Server
+    Start time     : 2025-10-24 06:58:06.96056704 +0000 UTC
+    End time       : 2025-10-24 06:58:06.960698112 +0000 UTC
+    Status code    : Unset
+    Status message :
+Span #7
+    Trace ID       : 95a1644afaaf65e1f0193b1f193b990a
+    Parent ID      : 2024d73d32cbd81b
+    ID             : 223c64fb691a24e8
+    Name           : apisix.phase.body_filter
+    Kind           : Internal
+    Start time     : 2025-10-24 06:58:06.961624064 +0000 UTC
+    End time       : 2025-10-24 06:58:06.961635072 +0000 UTC
+    Status code    : Unset
+    Status message :
+Span #8
+    Trace ID       : 95a1644afaaf65e1f0193b1f193b990a
+    Parent ID      : fd193dd24c618f60
+    ID             : 8729ad6e0d94a23b
+    Name           : apisix.phase.delayed_body_filter.opentelemetry
+    Kind           : Internal
+    Start time     : 2025-10-24 06:58:06.961648896 +0000 UTC
+    End time       : 1970-01-01 00:00:00 +0000 UTC
+    Status code    : Unset
+    Status message :
+Span #9
+    Trace ID       : 95a1644afaaf65e1f0193b1f193b990a
+    Parent ID      : 2024d73d32cbd81b
+    ID             : fd193dd24c618f60
+    Name           : apisix.phase.delayed_body_filter
+    Kind           : Internal
+    Start time     : 2025-10-24 06:58:06.961641984 +0000 UTC
+    End time       : 1970-01-01 00:00:00 +0000 UTC
+    Status code    : Unset
+    Status message :
+Span #10
+    Trace ID       : 95a1644afaaf65e1f0193b1f193b990a
+    Parent ID      : 905f850f13e32bfb
+    ID             : 2024d73d32cbd81b
+    Name           : apisix.phase.body_filter
+    Kind           : Server
+    Start time     : 2025-10-24 06:58:06.960980992 +0000 UTC
+    End time       : 1970-01-01 00:00:00 +0000 UTC
+    Status code    : Unset
+    Status message :
+Span #11
+    Trace ID       : 95a1644afaaf65e1f0193b1f193b990a
+    Parent ID      : cfb0b4603dc2e385
+    ID             : 905f850f13e32bfb
+    Name           : apisix.phase.access
+    Kind           : Server
+    Start time     : 2025-10-24 06:58:04.427932928 +0000 UTC
+    End time       : 1970-01-01 00:00:00 +0000 UTC
+    Status code    : Unset
+    Status message :
+Span #12
+    Trace ID       : 95a1644afaaf65e1f0193b1f193b990a
     Parent ID      :
-    ID             : af3dc7642104748a
-    Name           : GET /anything
+    ID             : cfb0b4603dc2e385
+    Name           : GET /headers
     Kind           : Server
-    Start time     : 2024-02-18 17:14:03.763244032 +0000 UTC
-    End time       : 2024-02-18 17:14:03.920229888 +0000 UTC
+    Start time     : 2025-10-24 06:58:04.432427008 +0000 UTC
+    End time       : 2025-10-24 06:58:06.962299904 +0000 UTC
     Status code    : Unset
     Status message :
 Attributes:
-    -> net.host.name: Str(127.0.0.1)
-    -> http.method: Str(GET)
-    -> http.scheme: Str(http)
-    -> http.target: Str(/anything)
-    -> http.user_agent: Str(curl/7.64.1)
-    -> apisix.route_id: Str(otel-tracing-route)
-    -> apisix.route_name: Empty()
-    -> http.route: Str(/anything)
-    -> http.status_code: Int(200)
-{"kind": "exporter", "data_type": "traces", "name": "debug"}
+     -> net.host.name: Str(127.0.0.1)
+     -> http.method: Str(GET)
+     -> http.scheme: Str(http)
+     -> http.target: Str(/headers)
+     -> http.user_agent: Str(curl/8.16.0)
+     -> apisix.route_id: Str(otel-tracing-route)
+     -> apisix.route_name: Empty()
+     -> http.route: Str(/headers)
+     -> http.status_code: Int(200)

Review Comment:
   ditto 
   
   https://opentelemetry.io/docs/specs/semconv/registry/attributes/http/



##########
docs/zh/latest/plugins/opentelemetry.md:
##########
@@ -152,37 +167,152 @@ curl "http://127.0.0.1:9080/anything";
 
 ```text
 2024-02-18T17:14:03.825Z info ResourceSpans #0
-Resource SchemaURL:
-Resource attributes:
-    -> telemetry.sdk.language: Str(lua)
-    -> telemetry.sdk.name: Str(opentelemetry-lua)
-    -> telemetry.sdk.version: Str(0.1.1)
-    -> hostname: Str(e34673e24631)
-    -> service.name: Str(APISIX)
 ScopeSpans #0
 ScopeSpans SchemaURL:
 InstrumentationScope opentelemetry-lua
 Span #0
-    Trace ID       : fbd0a38d4ea4a128ff1a688197bc58b0
+    Trace ID       : 95a1644afaaf65e1f0193b1f193b990a
+    Parent ID      : 905f850f13e32bfb
+    ID             : 5a3835b61110d942
+    Name           : http_router_match
+    Kind           : Internal
+    Start time     : 2025-10-24 06:58:04.430430976 +0000 UTC
+    End time       : 2025-10-24 06:58:04.431542016 +0000 UTC
+    Status code    : Unset
+    Status message :
+Span #1
+    Trace ID       : 95a1644afaaf65e1f0193b1f193b990a
+    Parent ID      : 905f850f13e32bfb
+    ID             : 4ab25e2b92f394e1
+    Name           : resolve_dns
+    Kind           : Internal
+    Start time     : 2025-10-24 06:58:04.432521984 +0000 UTC
+    End time       : 2025-10-24 06:58:04.44903296 +0000 UTC
+    Status code    : Unset
+    Status message :
+Span #2
+    Trace ID       : 95a1644afaaf65e1f0193b1f193b990a
+    Parent ID      : 905f850f13e32bfb
+    ID             : 3620c0f05dd2be4f
+    Name           : apisix.phase.header_filter
+    Kind           : Internal
+    Start time     : 2025-10-24 06:58:06.960481024 +0000 UTC
+    End time       : 2025-10-24 06:58:06.960510976 +0000 UTC
+    Status code    : Unset
+    Status message :
+Span #3
+    Trace ID       : 95a1644afaaf65e1f0193b1f193b990a
+    Parent ID      : 4c5f3476f62a7e8a
+    ID             : a9bfad7bb6986e41
+    Name           : apisix.phase.body_filter
+    Kind           : Internal
+    Start time     : 2025-10-24 06:58:06.960579072 +0000 UTC
+    End time       : 2025-10-24 06:58:06.96059008 +0000 UTC
+    Status code    : Unset
+    Status message :
+Span #4
+    Trace ID       : 95a1644afaaf65e1f0193b1f193b990a
+    Parent ID      : b2994675df6baa83
+    ID             : 26705f9c47584a5b
+    Name           : apisix.phase.delayed_body_filter.opentelemetry
+    Kind           : Internal
+    Start time     : 2025-10-24 06:58:06.960613888 +0000 UTC
+    End time       : 2025-10-24 06:58:06.960687104 +0000 UTC
+    Status code    : Unset
+    Status message :
+Span #5
+    Trace ID       : 95a1644afaaf65e1f0193b1f193b990a
+    Parent ID      : 4c5f3476f62a7e8a
+    ID             : b2994675df6baa83
+    Name           : apisix.phase.delayed_body_filter
+    Kind           : Internal
+    Start time     : 2025-10-24 06:58:06.96059904 +0000 UTC
+    End time       : 2025-10-24 06:58:06.960692992 +0000 UTC
+    Status code    : Unset
+    Status message :
+Span #6
+    Trace ID       : 95a1644afaaf65e1f0193b1f193b990a
+    Parent ID      : 905f850f13e32bfb
+    ID             : 4c5f3476f62a7e8a
+    Name           : apisix.phase.body_filter
+    Kind           : Server
+    Start time     : 2025-10-24 06:58:06.96056704 +0000 UTC
+    End time       : 2025-10-24 06:58:06.960698112 +0000 UTC
+    Status code    : Unset
+    Status message :
+Span #7
+    Trace ID       : 95a1644afaaf65e1f0193b1f193b990a
+    Parent ID      : 2024d73d32cbd81b
+    ID             : 223c64fb691a24e8
+    Name           : apisix.phase.body_filter
+    Kind           : Internal
+    Start time     : 2025-10-24 06:58:06.961624064 +0000 UTC
+    End time       : 2025-10-24 06:58:06.961635072 +0000 UTC
+    Status code    : Unset
+    Status message :
+Span #8
+    Trace ID       : 95a1644afaaf65e1f0193b1f193b990a
+    Parent ID      : fd193dd24c618f60
+    ID             : 8729ad6e0d94a23b
+    Name           : apisix.phase.delayed_body_filter.opentelemetry
+    Kind           : Internal
+    Start time     : 2025-10-24 06:58:06.961648896 +0000 UTC
+    End time       : 1970-01-01 00:00:00 +0000 UTC
+    Status code    : Unset
+    Status message :
+Span #9
+    Trace ID       : 95a1644afaaf65e1f0193b1f193b990a
+    Parent ID      : 2024d73d32cbd81b
+    ID             : fd193dd24c618f60
+    Name           : apisix.phase.delayed_body_filter
+    Kind           : Internal
+    Start time     : 2025-10-24 06:58:06.961641984 +0000 UTC
+    End time       : 1970-01-01 00:00:00 +0000 UTC
+    Status code    : Unset
+    Status message :
+Span #10
+    Trace ID       : 95a1644afaaf65e1f0193b1f193b990a
+    Parent ID      : 905f850f13e32bfb
+    ID             : 2024d73d32cbd81b
+    Name           : apisix.phase.body_filter
+    Kind           : Server
+    Start time     : 2025-10-24 06:58:06.960980992 +0000 UTC
+    End time       : 1970-01-01 00:00:00 +0000 UTC
+    Status code    : Unset
+    Status message :
+Span #11
+    Trace ID       : 95a1644afaaf65e1f0193b1f193b990a
+    Parent ID      : cfb0b4603dc2e385
+    ID             : 905f850f13e32bfb
+    Name           : apisix.phase.access
+    Kind           : Server
+    Start time     : 2025-10-24 06:58:04.427932928 +0000 UTC
+    End time       : 1970-01-01 00:00:00 +0000 UTC
+    Status code    : Unset
+    Status message :
+Span #12
+    Trace ID       : 95a1644afaaf65e1f0193b1f193b990a
     Parent ID      :
-    ID             : af3dc7642104748a
-    Name           : GET /anything
+    ID             : cfb0b4603dc2e385
+    Name           : GET /headers
     Kind           : Server
-    Start time     : 2024-02-18 17:14:03.763244032 +0000 UTC
-    End time       : 2024-02-18 17:14:03.920229888 +0000 UTC
+    Start time     : 2025-10-24 06:58:04.432427008 +0000 UTC
+    End time       : 2025-10-24 06:58:06.962299904 +0000 UTC
     Status code    : Unset
     Status message :
 Attributes:
-    -> net.host.name: Str(127.0.0.1)
-    -> http.method: Str(GET)
-    -> http.scheme: Str(http)
-    -> http.target: Str(/anything)
-    -> http.user_agent: Str(curl/7.64.1)
-    -> apisix.route_id: Str(otel-tracing-route)
-    -> apisix.route_name: Empty()
-    -> http.route: Str(/anything)
-    -> http.status_code: Int(200)
-{"kind": "exporter", "data_type": "traces", "name": "debug"}
+     -> net.host.name: Str(127.0.0.1)
+     -> http.method: Str(GET)
+     -> http.scheme: Str(http)
+     -> http.target: Str(/headers)
+     -> http.user_agent: Str(curl/8.16.0)
+     -> apisix.route_id: Str(otel-tracing-route)
+     -> apisix.route_name: Empty()
+     -> http.route: Str(/headers)
+     -> http.status_code: Int(200)

Review Comment:
   ditto



##########
apisix/ssl/router/radixtree_sni.lua:
##########
@@ -177,9 +179,10 @@ function _M.match_and_set(api_ctx, match_only, alt_sni)
             -- with it sometimes
             core.log.error("failed to find any SSL certificate by SNI: ", sni)
         end
+        tracer.finish(api_ctx.ngx_ctx, tracer.status.ERROR, "failed match SNI")
         return false
     end
-
+    tracer.finish(api_ctx.ngx_ctx)

Review Comment:
   Strictly, this API design is still strange, with many internal details 
hidden within the stack data structure. For developers, it presents an opaque 
data structure.
   If any span of code begins with `tracer.start` but fails to conclude with 
`tracer.finish`, the entire tracing process will encounter an error. This 
unclosed span will encompass all subsequent spans, which is not the intended 
behavior.
   
   If we examine OTEL implementations in other major programming languages, we 
find that many of them follow the following pattern:
   
   Use the tracer's method to create a span, then obtain an instance of that 
span. After internal operations complete, use the span instance's methods to 
actively terminate the specified span. The ctx will be used to track the 
current span, ensuring that parent-child relationships are correctly maintained 
when creating child spans.
   
   ```lua
   -- context-based
   local parent_span = tracer.start(ctx, "parent")
   
   local child_span = tracer.start(ctx, "child")
   
   child_span:end()
   
   parent_span:end()
   ```
   
   ref: 
https://opentelemetry.io/docs/languages/go/instrumentation/#create-nested-spans
   ref: 
https://opentelemetry.io/docs/languages/php/instrumentation/#create-nested-spans
   ref: 
https://opentelemetry.io/docs/languages/js/instrumentation/#create-nested-spans
   ref: https://opentelemetry.io/docs/languages/java/api/#span
   ref: 
https://opentelemetry.io/docs/languages/cpp/instrumentation/#create-nested-spans
   
   Alternatively, explicitly pass the parent span instance to the child span to 
achieve precise hierarchical control. 
   
   ```lua
   -- pass span directly
   local parent_span = tracer.start("parent")
   
   local child_span = tracer.start("child", parent_span)
   
   child_span:end()
   
   parent_span:end()
   ```
   
   Note that the following Rust code typically terminates spans automatically 
via drop calls when the span instance's variable scopes end.
   
   ref: 
https://docs.rs/tracing/latest/tracing/span/index.html#span-relationships
   
   None of the OTEL libraries for these languages organize and manage the 
entire span stack using only global state. Each one terminates spans by 
returning span instances and requiring developers to explicitly call span.end() 
when operations conclude.
   
   Our current approach requires any developer writing trace code to be 
familiar with the stack mechanism we've created and to have complete clarity 
about where their spans appear in the stack, whether parent spans exist, 
whether they've closed as expected, or any other state. Otherwise, they cannot 
write correct code. Therefore, I disagree with the current API design.
   
   I have already pointed this out in 
https://github.com/apache/apisix/pull/12686#discussion_r2471659658, and 
@membphis has clearly reiterated this point through images.
   
   
![](https://private-user-images.githubusercontent.com/6814606/507373141-8a75f5fe-e0a4-4375-8e97-84c80445be5a.png?jwt=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3Njk2OTY1NDIsIm5iZiI6MTc2OTY5NjI0MiwicGF0aCI6Ii82ODE0NjA2LzUwNzM3MzE0MS04YTc1ZjVmZS1lMGE0LTQzNzUtOGU5Ny04NGM4MDQ0NWJlNWEucG5nP1gtQW16LUFsZ29yaXRobT1BV1M0LUhNQUMtU0hBMjU2JlgtQW16LUNyZWRlbnRpYWw9QUtJQVZDT0RZTFNBNTNQUUs0WkElMkYyMDI2MDEyOSUyRnVzLWVhc3QtMSUyRnMzJTJGYXdzNF9yZXF1ZXN0JlgtQW16LURhdGU9MjAyNjAxMjlUMTQxNzIyWiZYLUFtei1FeHBpcmVzPTMwMCZYLUFtei1TaWduYXR1cmU9MGQ2MmNhM2RkZmUzMTkxZDJmMjVmYWU3OWE1ZDFiYWI2OTRjZjFiMzA5YTE5Y2Y4ZmExMmFjOTZkOGQzZmVjMSZYLUFtei1TaWduZWRIZWFkZXJzPWhvc3QifQ.YV-yZUolOcov273ML-vqQfSEmcNLL74SOB_7Cl5rDCw)



##########
apisix/plugins/opentelemetry.lua:
##########
@@ -376,49 +377,101 @@ function _M.rewrite(conf, api_ctx)
       ngx_var.opentelemetry_span_id = span_context.span_id
     end
 
+    if not ctx:span():is_recording() and ngx.ctx.tracing then
+        ngx.ctx.tracing.skip = true
+    end
+
     api_ctx.otel_context_token = ctx:attach()
 
     -- inject trace context into the headers of upstream HTTP request
     trace_context_propagator:inject(ctx, ngx.req)
 end
 
 
-function _M.delayed_body_filter(conf, api_ctx)
-    if api_ctx.otel_context_token and ngx.arg[2] then
-        local ctx = context:current()
-        ctx:detach(api_ctx.otel_context_token)
-        api_ctx.otel_context_token = nil
+local function create_child_span(tracer, parent_span_ctx, spans, span_idx)
+    local span = spans[span_idx]
+    if not span or span.finished then
+        return
+    end
+    span.finished = true
+    local new_span_ctx, new_span = tracer:start(parent_span_ctx, span.name,
+                                    {
+                                        kind = span.kind,
+                                        attributes = span.attributes,
+                                    })
+    new_span.start_time = span.start_time
+
+    for _, idx in ipairs(span.child_ids or {}) do
+        create_child_span(tracer, new_span_ctx, spans, idx)
+    end
+    if span.status then
+        new_span:set_status(span.status.code, span.status.message)
+    end
+    new_span:finish(span.end_time)
+end
 
-        -- get span from current context
-        local span = ctx:span()
-        local upstream_status = core.response.get_upstream_status(api_ctx)
-        if upstream_status and upstream_status >= 500 then
-            span:set_status(span_status.ERROR,
-                            "upstream response status: " .. upstream_status)
-        end
 
-        span:set_attributes(attr.int("http.status_code", upstream_status))
+local function inject_core_spans(root_span_ctx, api_ctx, conf)
+    local tracing = api_ctx.ngx_ctx.tracing
+    if not tracing then
+        return
+    end
 
-        span:finish()
+    local span = root_span_ctx:span()
+
+    local metadata = plugin.plugin_metadata(plugin_name)
+    local plugin_info = metadata.value
+    if span and not span:is_recording() then
+        return
+    end
+    local inject_conf = {
+        sampler = {
+            name = "always_on",
+            options = conf.sampler.options
+        },
+        additional_attributes = conf.additional_attributes,
+        additional_header_prefix_attributes = 
conf.additional_header_prefix_attributes
+    }
+    local tracer, err = core.lrucache.plugin_ctx(lrucache, api_ctx, nil,
+                                                create_tracer_obj, 
inject_conf, plugin_info)
+    if not tracer then
+        core.log.error("failed to fetch tracer object: ", err)
+        return
+    end
+
+    if #tracing.spans == 0 then
+        return
+    end
+    span.start_time = tracing.spans[1].start_time
+    for i, _ in ipairs(tracing.spans or {}) do
+        create_child_span(tracer, root_span_ctx, tracing.spans, i)
     end
 end
 
 
--- body_filter maybe not called because of empty http body response
--- so we need to check if the span has finished in log phase
 function _M.log(conf, api_ctx)
     if api_ctx.otel_context_token then
         -- ctx:detach() is not necessary, because of ctx is stored in ngx.ctx
         local upstream_status = core.response.get_upstream_status(api_ctx)
 
         -- get span from current context
-        local span = context:current():span()
+        local ctx = context:current()
+        local span = ctx:span()
         if upstream_status and upstream_status >= 500 then
             span:set_status(span_status.ERROR,
                     "upstream response status: " .. upstream_status)
         end
 
+        inject_core_spans(ctx, api_ctx, conf)
+        span:set_attributes(attr.int("http.status_code", upstream_status))

Review Comment:
   Use `http.response.status_code` to comply with the OTEL semantic conventions.
   
   https://opentelemetry.io/docs/specs/semconv/registry/attributes/http/



##########
apisix/plugins/opentelemetry.lua:
##########
@@ -376,49 +377,101 @@ function _M.rewrite(conf, api_ctx)
       ngx_var.opentelemetry_span_id = span_context.span_id
     end
 
+    if not ctx:span():is_recording() and ngx.ctx.tracing then
+        ngx.ctx.tracing.skip = true
+    end
+
     api_ctx.otel_context_token = ctx:attach()
 
     -- inject trace context into the headers of upstream HTTP request
     trace_context_propagator:inject(ctx, ngx.req)
 end
 
 
-function _M.delayed_body_filter(conf, api_ctx)
-    if api_ctx.otel_context_token and ngx.arg[2] then
-        local ctx = context:current()
-        ctx:detach(api_ctx.otel_context_token)
-        api_ctx.otel_context_token = nil
+local function create_child_span(tracer, parent_span_ctx, spans, span_idx)
+    local span = spans[span_idx]
+    if not span or span.finished then
+        return
+    end
+    span.finished = true
+    local new_span_ctx, new_span = tracer:start(parent_span_ctx, span.name,
+                                    {
+                                        kind = span.kind,
+                                        attributes = span.attributes,
+                                    })
+    new_span.start_time = span.start_time
+
+    for _, idx in ipairs(span.child_ids or {}) do
+        create_child_span(tracer, new_span_ctx, spans, idx)
+    end
+    if span.status then
+        new_span:set_status(span.status.code, span.status.message)
+    end
+    new_span:finish(span.end_time)
+end
 
-        -- get span from current context
-        local span = ctx:span()
-        local upstream_status = core.response.get_upstream_status(api_ctx)
-        if upstream_status and upstream_status >= 500 then
-            span:set_status(span_status.ERROR,
-                            "upstream response status: " .. upstream_status)
-        end
 
-        span:set_attributes(attr.int("http.status_code", upstream_status))
+local function inject_core_spans(root_span_ctx, api_ctx, conf)
+    local tracing = api_ctx.ngx_ctx.tracing
+    if not tracing then
+        return
+    end
 
-        span:finish()
+    local span = root_span_ctx:span()
+
+    local metadata = plugin.plugin_metadata(plugin_name)
+    local plugin_info = metadata.value
+    if span and not span:is_recording() then
+        return
+    end
+    local inject_conf = {
+        sampler = {
+            name = "always_on",
+            options = conf.sampler.options
+        },
+        additional_attributes = conf.additional_attributes,
+        additional_header_prefix_attributes = 
conf.additional_header_prefix_attributes
+    }
+    local tracer, err = core.lrucache.plugin_ctx(lrucache, api_ctx, nil,
+                                                create_tracer_obj, 
inject_conf, plugin_info)
+    if not tracer then
+        core.log.error("failed to fetch tracer object: ", err)
+        return
+    end
+
+    if #tracing.spans == 0 then
+        return
+    end
+    span.start_time = tracing.spans[1].start_time
+    for i, _ in ipairs(tracing.spans or {}) do
+        create_child_span(tracer, root_span_ctx, tracing.spans, i)
     end
 end
 
 
--- body_filter maybe not called because of empty http body response
--- so we need to check if the span has finished in log phase
 function _M.log(conf, api_ctx)
     if api_ctx.otel_context_token then
         -- ctx:detach() is not necessary, because of ctx is stored in ngx.ctx
         local upstream_status = core.response.get_upstream_status(api_ctx)
 
         -- get span from current context
-        local span = context:current():span()
+        local ctx = context:current()
+        local span = ctx:span()
         if upstream_status and upstream_status >= 500 then
             span:set_status(span_status.ERROR,
                     "upstream response status: " .. upstream_status)
         end
 
+        inject_core_spans(ctx, api_ctx, conf)
+        span:set_attributes(attr.int("http.status_code", upstream_status))

Review Comment:
   Pls use `http.response.status_code` to comply with the OTEL semantic 
conventions.
   
   https://opentelemetry.io/docs/specs/semconv/registry/attributes/http/



##########
docs/en/latest/plugins/opentelemetry.md:
##########
@@ -153,37 +168,152 @@ In OpenTelemetry collector's log, you should see 
information similar to the foll
 
 ```text
 2024-02-18T17:14:03.825Z info ResourceSpans #0
-Resource SchemaURL:
-Resource attributes:
-     -> telemetry.sdk.language: Str(lua)
-     -> telemetry.sdk.name: Str(opentelemetry-lua)
-     -> telemetry.sdk.version: Str(0.1.1)
-     -> hostname: Str(e34673e24631)
-     -> service.name: Str(APISIX)
 ScopeSpans #0
 ScopeSpans SchemaURL:
 InstrumentationScope opentelemetry-lua
 Span #0
-    Trace ID       : fbd0a38d4ea4a128ff1a688197bc58b0
+    Trace ID       : 95a1644afaaf65e1f0193b1f193b990a
+    Parent ID      : 905f850f13e32bfb
+    ID             : 5a3835b61110d942
+    Name           : http_router_match
+    Kind           : Internal
+    Start time     : 2025-10-24 06:58:04.430430976 +0000 UTC
+    End time       : 2025-10-24 06:58:04.431542016 +0000 UTC
+    Status code    : Unset
+    Status message :
+Span #1
+    Trace ID       : 95a1644afaaf65e1f0193b1f193b990a
+    Parent ID      : 905f850f13e32bfb
+    ID             : 4ab25e2b92f394e1
+    Name           : resolve_dns
+    Kind           : Internal
+    Start time     : 2025-10-24 06:58:04.432521984 +0000 UTC
+    End time       : 2025-10-24 06:58:04.44903296 +0000 UTC
+    Status code    : Unset
+    Status message :
+Span #2
+    Trace ID       : 95a1644afaaf65e1f0193b1f193b990a
+    Parent ID      : 905f850f13e32bfb
+    ID             : 3620c0f05dd2be4f
+    Name           : apisix.phase.header_filter
+    Kind           : Internal
+    Start time     : 2025-10-24 06:58:06.960481024 +0000 UTC
+    End time       : 2025-10-24 06:58:06.960510976 +0000 UTC
+    Status code    : Unset
+    Status message :
+Span #3
+    Trace ID       : 95a1644afaaf65e1f0193b1f193b990a
+    Parent ID      : 4c5f3476f62a7e8a
+    ID             : a9bfad7bb6986e41
+    Name           : apisix.phase.body_filter
+    Kind           : Internal
+    Start time     : 2025-10-24 06:58:06.960579072 +0000 UTC
+    End time       : 2025-10-24 06:58:06.96059008 +0000 UTC
+    Status code    : Unset
+    Status message :
+Span #4
+    Trace ID       : 95a1644afaaf65e1f0193b1f193b990a
+    Parent ID      : b2994675df6baa83
+    ID             : 26705f9c47584a5b
+    Name           : apisix.phase.delayed_body_filter.opentelemetry
+    Kind           : Internal
+    Start time     : 2025-10-24 06:58:06.960613888 +0000 UTC
+    End time       : 2025-10-24 06:58:06.960687104 +0000 UTC
+    Status code    : Unset
+    Status message :
+Span #5
+    Trace ID       : 95a1644afaaf65e1f0193b1f193b990a
+    Parent ID      : 4c5f3476f62a7e8a
+    ID             : b2994675df6baa83
+    Name           : apisix.phase.delayed_body_filter
+    Kind           : Internal
+    Start time     : 2025-10-24 06:58:06.96059904 +0000 UTC
+    End time       : 2025-10-24 06:58:06.960692992 +0000 UTC
+    Status code    : Unset
+    Status message :
+Span #6
+    Trace ID       : 95a1644afaaf65e1f0193b1f193b990a
+    Parent ID      : 905f850f13e32bfb
+    ID             : 4c5f3476f62a7e8a
+    Name           : apisix.phase.body_filter
+    Kind           : Server
+    Start time     : 2025-10-24 06:58:06.96056704 +0000 UTC
+    End time       : 2025-10-24 06:58:06.960698112 +0000 UTC
+    Status code    : Unset
+    Status message :
+Span #7
+    Trace ID       : 95a1644afaaf65e1f0193b1f193b990a
+    Parent ID      : 2024d73d32cbd81b
+    ID             : 223c64fb691a24e8
+    Name           : apisix.phase.body_filter
+    Kind           : Internal
+    Start time     : 2025-10-24 06:58:06.961624064 +0000 UTC
+    End time       : 2025-10-24 06:58:06.961635072 +0000 UTC
+    Status code    : Unset
+    Status message :
+Span #8
+    Trace ID       : 95a1644afaaf65e1f0193b1f193b990a
+    Parent ID      : fd193dd24c618f60
+    ID             : 8729ad6e0d94a23b
+    Name           : apisix.phase.delayed_body_filter.opentelemetry
+    Kind           : Internal
+    Start time     : 2025-10-24 06:58:06.961648896 +0000 UTC
+    End time       : 1970-01-01 00:00:00 +0000 UTC
+    Status code    : Unset
+    Status message :
+Span #9
+    Trace ID       : 95a1644afaaf65e1f0193b1f193b990a
+    Parent ID      : 2024d73d32cbd81b
+    ID             : fd193dd24c618f60
+    Name           : apisix.phase.delayed_body_filter
+    Kind           : Internal
+    Start time     : 2025-10-24 06:58:06.961641984 +0000 UTC
+    End time       : 1970-01-01 00:00:00 +0000 UTC
+    Status code    : Unset
+    Status message :
+Span #10
+    Trace ID       : 95a1644afaaf65e1f0193b1f193b990a
+    Parent ID      : 905f850f13e32bfb
+    ID             : 2024d73d32cbd81b
+    Name           : apisix.phase.body_filter
+    Kind           : Server
+    Start time     : 2025-10-24 06:58:06.960980992 +0000 UTC
+    End time       : 1970-01-01 00:00:00 +0000 UTC
+    Status code    : Unset
+    Status message :
+Span #11
+    Trace ID       : 95a1644afaaf65e1f0193b1f193b990a
+    Parent ID      : cfb0b4603dc2e385
+    ID             : 905f850f13e32bfb
+    Name           : apisix.phase.access
+    Kind           : Server
+    Start time     : 2025-10-24 06:58:04.427932928 +0000 UTC
+    End time       : 1970-01-01 00:00:00 +0000 UTC
+    Status code    : Unset
+    Status message :
+Span #12
+    Trace ID       : 95a1644afaaf65e1f0193b1f193b990a
     Parent ID      :
-    ID             : af3dc7642104748a
-    Name           : GET /anything
+    ID             : cfb0b4603dc2e385
+    Name           : GET /headers
     Kind           : Server
-    Start time     : 2024-02-18 17:14:03.763244032 +0000 UTC
-    End time       : 2024-02-18 17:14:03.920229888 +0000 UTC
+    Start time     : 2025-10-24 06:58:04.432427008 +0000 UTC
+    End time       : 2025-10-24 06:58:06.962299904 +0000 UTC
     Status code    : Unset
     Status message :
 Attributes:
      -> net.host.name: Str(127.0.0.1)
      -> http.method: Str(GET)
      -> http.scheme: Str(http)
-     -> http.target: Str(/anything)
-     -> http.user_agent: Str(curl/7.64.1)
+     -> http.target: Str(/headers)
+     -> http.user_agent: Str(curl/8.16.0)
      -> apisix.route_id: Str(otel-tracing-route)
      -> apisix.route_name: Empty()
-     -> http.route: Str(/anything)
+     -> http.route: Str(/headers)
      -> http.status_code: Int(200)

Review Comment:
   ditto



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


Reply via email to