From f4762063f1d6e8f9e7e6b33da64b1983e398e453 Mon Sep 17 00:00:00 2001 From: rite7sh Date: Wed, 17 Dec 2025 16:44:37 +0530 Subject: [PATCH 1/4] refactor(asgi): replace HTTP_SERVER_NAME SpanAttribute with semconv attribute Refs #3475 --- .../src/opentelemetry/instrumentation/asgi/__init__.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/instrumentation/opentelemetry-instrumentation-asgi/src/opentelemetry/instrumentation/asgi/__init__.py b/instrumentation/opentelemetry-instrumentation-asgi/src/opentelemetry/instrumentation/asgi/__init__.py index 60c43f6db0..edb2300f01 100644 --- a/instrumentation/opentelemetry-instrumentation-asgi/src/opentelemetry/instrumentation/asgi/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-asgi/src/opentelemetry/instrumentation/asgi/__init__.py @@ -219,6 +219,7 @@ def client_response_hook(span: Span, scope: Scope, message: dict[str, Any]): from asgiref.compatibility import guarantee_single_callable + from opentelemetry import context, trace from opentelemetry.instrumentation._semconv import ( HTTP_DURATION_HISTOGRAM_BUCKETS_NEW, @@ -288,7 +289,9 @@ def client_response_hook(span: Span, scope: Scope, message: dict[str, Any]): redact_url, sanitize_method, ) - +from opentelemetry.semconv._incubating.attributes.http_attributes import ( + HTTP_SERVER_NAME, +) class ASGIGetter(Getter[dict]): def get( @@ -397,7 +400,7 @@ def collect_request_attributes( http_host_value_list = asgi_getter.get(scope, "host") if http_host_value_list: if _report_old(sem_conv_opt_in_mode): - result[SpanAttributes.HTTP_SERVER_NAME] = ",".join( + result[HTTP_SERVER_NAME] = ",".join( http_host_value_list ) http_user_agent = asgi_getter.get(scope, "user-agent") From 9789577bfee08b258f564fbf44482aefff62e038 Mon Sep 17 00:00:00 2001 From: Ritesh Traipathi Date: Fri, 19 Dec 2025 15:40:21 +0530 Subject: [PATCH 2/4] Update instrumentation/opentelemetry-instrumentation-asgi/src/opentelemetry/instrumentation/asgi/__init__.py Co-authored-by: Riccardo Magliocchetti --- .../src/opentelemetry/instrumentation/asgi/__init__.py | 1 - 1 file changed, 1 deletion(-) diff --git a/instrumentation/opentelemetry-instrumentation-asgi/src/opentelemetry/instrumentation/asgi/__init__.py b/instrumentation/opentelemetry-instrumentation-asgi/src/opentelemetry/instrumentation/asgi/__init__.py index edb2300f01..8e1c1df799 100644 --- a/instrumentation/opentelemetry-instrumentation-asgi/src/opentelemetry/instrumentation/asgi/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-asgi/src/opentelemetry/instrumentation/asgi/__init__.py @@ -219,7 +219,6 @@ def client_response_hook(span: Span, scope: Scope, message: dict[str, Any]): from asgiref.compatibility import guarantee_single_callable - from opentelemetry import context, trace from opentelemetry.instrumentation._semconv import ( HTTP_DURATION_HISTOGRAM_BUCKETS_NEW, From a6bd905a54e40a9c1df20d6232b076a784313340 Mon Sep 17 00:00:00 2001 From: Ritesh Traipathi Date: Fri, 19 Dec 2025 15:46:51 +0530 Subject: [PATCH 3/4] Update instrumentation/opentelemetry-instrumentation-asgi/src/opentelemetry/instrumentation/asgi/__init__.py Co-authored-by: Riccardo Magliocchetti --- .../src/opentelemetry/instrumentation/asgi/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/instrumentation/opentelemetry-instrumentation-asgi/src/opentelemetry/instrumentation/asgi/__init__.py b/instrumentation/opentelemetry-instrumentation-asgi/src/opentelemetry/instrumentation/asgi/__init__.py index 8e1c1df799..541c28d1ab 100644 --- a/instrumentation/opentelemetry-instrumentation-asgi/src/opentelemetry/instrumentation/asgi/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-asgi/src/opentelemetry/instrumentation/asgi/__init__.py @@ -399,7 +399,7 @@ def collect_request_attributes( http_host_value_list = asgi_getter.get(scope, "host") if http_host_value_list: if _report_old(sem_conv_opt_in_mode): - result[HTTP_SERVER_NAME] = ",".join( + result[HTTP_SERVER_NAME] = ",".join( http_host_value_list ) http_user_agent = asgi_getter.get(scope, "user-agent") From 8f45599d499a9a843647af7b3aa32b3f09d6cf60 Mon Sep 17 00:00:00 2001 From: rite7sh Date: Tue, 23 Dec 2025 01:32:28 +0530 Subject: [PATCH 4/4] tests(asgi): remove duplicate expected attribute keys --- .../instrumentation/asgi/__init__.py | 11 ++- .../tests/test_asgi_middleware.py | 72 ++++++++----------- 2 files changed, 35 insertions(+), 48 deletions(-) diff --git a/instrumentation/opentelemetry-instrumentation-asgi/src/opentelemetry/instrumentation/asgi/__init__.py b/instrumentation/opentelemetry-instrumentation-asgi/src/opentelemetry/instrumentation/asgi/__init__.py index 541c28d1ab..20b0ac31e5 100644 --- a/instrumentation/opentelemetry-instrumentation-asgi/src/opentelemetry/instrumentation/asgi/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-asgi/src/opentelemetry/instrumentation/asgi/__init__.py @@ -258,6 +258,9 @@ def client_response_hook(span: Span, scope: Scope, message: dict[str, Any]): from opentelemetry.instrumentation.utils import _start_internal_or_server_span from opentelemetry.metrics import get_meter from opentelemetry.propagators.textmap import Getter, Setter +from opentelemetry.semconv._incubating.attributes.http_attributes import ( + HTTP_SERVER_NAME, +) from opentelemetry.semconv._incubating.attributes.user_agent_attributes import ( USER_AGENT_SYNTHETIC_TYPE, ) @@ -288,9 +291,7 @@ def client_response_hook(span: Span, scope: Scope, message: dict[str, Any]): redact_url, sanitize_method, ) -from opentelemetry.semconv._incubating.attributes.http_attributes import ( - HTTP_SERVER_NAME, -) + class ASGIGetter(Getter[dict]): def get( @@ -399,9 +400,7 @@ def collect_request_attributes( http_host_value_list = asgi_getter.get(scope, "host") if http_host_value_list: if _report_old(sem_conv_opt_in_mode): - result[HTTP_SERVER_NAME] = ",".join( - http_host_value_list - ) + result[HTTP_SERVER_NAME] = ",".join(http_host_value_list) http_user_agent = asgi_getter.get(scope, "user-agent") if http_user_agent: user_agent_raw = http_user_agent[0] diff --git a/instrumentation/opentelemetry-instrumentation-asgi/tests/test_asgi_middleware.py b/instrumentation/opentelemetry-instrumentation-asgi/tests/test_asgi_middleware.py index fdf328498b..ca65e70472 100644 --- a/instrumentation/opentelemetry-instrumentation-asgi/tests/test_asgi_middleware.py +++ b/instrumentation/opentelemetry-instrumentation-asgi/tests/test_asgi_middleware.py @@ -42,6 +42,9 @@ HistogramDataPoint, NumberDataPoint, ) +from opentelemetry.semconv._incubating.attributes.http_attributes import ( + HTTP_SERVER_NAME, +) from opentelemetry.semconv._incubating.attributes.user_agent_attributes import ( USER_AGENT_SYNTHETIC_TYPE, ) @@ -61,6 +64,7 @@ SERVER_PORT, ) from opentelemetry.semconv.attributes.url_attributes import ( + URL_FULL, URL_PATH, URL_QUERY, URL_SCHEME, @@ -368,7 +372,7 @@ def validate_outputs( "name": "GET / http send", "kind": trace_api.SpanKind.INTERNAL, "attributes": { - SpanAttributes.HTTP_STATUS_CODE: 200, + HTTP_RESPONSE_STATUS_CODE: 200, "asgi.event.type": "http.response.start", }, }, @@ -381,16 +385,16 @@ def validate_outputs( "name": "GET /", "kind": trace_api.SpanKind.SERVER, "attributes": { - SpanAttributes.HTTP_METHOD: "GET", + HTTP_REQUEST_METHOD: "GET", SpanAttributes.HTTP_SCHEME: "http", - SpanAttributes.NET_HOST_PORT: 80, - SpanAttributes.HTTP_HOST: "127.0.0.1", + SERVER_PORT: 80, + SERVER_ADDRESS: "127.0.0.1", SpanAttributes.HTTP_FLAVOR: "1.0", SpanAttributes.HTTP_TARGET: "/", - SpanAttributes.HTTP_URL: "http://127.0.0.1/", - SpanAttributes.NET_PEER_IP: "127.0.0.1", - SpanAttributes.NET_PEER_PORT: 32767, - SpanAttributes.HTTP_STATUS_CODE: 200, + URL_FULL: "http://127.0.0.1/", + CLIENT_ADDRESS: "127.0.0.1", + CLIENT_PORT: 32767, + HTTP_RESPONSE_STATUS_CODE: 200, }, }, ] @@ -439,7 +443,6 @@ def validate_outputs( "name": "GET / http send", "kind": trace_api.SpanKind.INTERNAL, "attributes": { - SpanAttributes.HTTP_STATUS_CODE: 200, HTTP_RESPONSE_STATUS_CODE: 200, "asgi.event.type": "http.response.start", }, @@ -462,16 +465,10 @@ def validate_outputs( CLIENT_ADDRESS: "127.0.0.1", CLIENT_PORT: 32767, HTTP_RESPONSE_STATUS_CODE: 200, - SpanAttributes.HTTP_METHOD: "GET", SpanAttributes.HTTP_SCHEME: "http", - SpanAttributes.NET_HOST_PORT: 80, - SpanAttributes.HTTP_HOST: "127.0.0.1", SpanAttributes.HTTP_FLAVOR: "1.0", SpanAttributes.HTTP_TARGET: "/", - SpanAttributes.HTTP_URL: "http://127.0.0.1/", - SpanAttributes.NET_PEER_IP: "127.0.0.1", - SpanAttributes.NET_PEER_PORT: 32767, - SpanAttributes.HTTP_STATUS_CODE: 200, + URL_FULL: "http://127.0.0.1/", }, }, ] @@ -713,7 +710,7 @@ def update_expected_server(expected): expected[3]["attributes"].update( { SpanAttributes.HTTP_HOST: "0.0.0.0", - SpanAttributes.NET_HOST_PORT: 80, + SERVER_PORT: 80, SpanAttributes.HTTP_URL: "http://0.0.0.0/", } ) @@ -757,10 +754,9 @@ def update_expected_server(expected): expected[3]["attributes"].update( { SpanAttributes.HTTP_HOST: "0.0.0.0", - SpanAttributes.NET_HOST_PORT: 80, + SERVER_PORT: 80, SpanAttributes.HTTP_URL: "http://0.0.0.0/", SERVER_ADDRESS: "0.0.0.0", - SERVER_PORT: 80, } ) return expected @@ -784,7 +780,7 @@ async def test_host_header(self): def update_expected_server(expected): expected[3]["attributes"].update( { - SpanAttributes.HTTP_SERVER_NAME: hostname.decode("utf8"), + HTTP_SERVER_NAME: hostname.decode("utf8"), SpanAttributes.HTTP_URL: f"http://{hostname.decode('utf8')}/", } ) @@ -1094,7 +1090,7 @@ async def test_websocket(self): "kind": trace_api.SpanKind.INTERNAL, "attributes": { "asgi.event.type": "websocket.receive", - SpanAttributes.HTTP_STATUS_CODE: 200, + HTTP_RESPONSE_STATUS_CODE: 200, }, }, { @@ -1102,7 +1098,7 @@ async def test_websocket(self): "kind": trace_api.SpanKind.INTERNAL, "attributes": { "asgi.event.type": "websocket.send", - SpanAttributes.HTTP_STATUS_CODE: 200, + HTTP_RESPONSE_STATUS_CODE: 200, }, }, { @@ -1122,7 +1118,7 @@ async def test_websocket(self): SpanAttributes.HTTP_URL: f"{self.scope['scheme']}://{self.scope['server'][0]}{self.scope['path']}", SpanAttributes.NET_PEER_IP: self.scope["client"][0], SpanAttributes.NET_PEER_PORT: self.scope["client"][1], - SpanAttributes.HTTP_STATUS_CODE: 200, + HTTP_RESPONSE_STATUS_CODE: 200, SpanAttributes.HTTP_METHOD: self.scope["method"], }, }, @@ -1242,7 +1238,6 @@ async def test_websocket_both_semconv(self): "attributes": { "asgi.event.type": "websocket.receive", HTTP_RESPONSE_STATUS_CODE: 200, - SpanAttributes.HTTP_STATUS_CODE: 200, }, }, { @@ -1251,7 +1246,6 @@ async def test_websocket_both_semconv(self): "attributes": { "asgi.event.type": "websocket.send", HTTP_RESPONSE_STATUS_CODE: 200, - SpanAttributes.HTTP_STATUS_CODE: 200, }, }, { @@ -1271,7 +1265,7 @@ async def test_websocket_both_semconv(self): SpanAttributes.HTTP_URL: f"{self.scope['scheme']}://{self.scope['server'][0]}{self.scope['path']}", SpanAttributes.NET_PEER_IP: self.scope["client"][0], SpanAttributes.NET_PEER_PORT: self.scope["client"][1], - SpanAttributes.HTTP_STATUS_CODE: 200, + HTTP_RESPONSE_STATUS_CODE: 200, SpanAttributes.HTTP_METHOD: self.scope["method"], URL_SCHEME: self.scope["scheme"], SERVER_ADDRESS: self.scope["server"][0], @@ -1280,7 +1274,6 @@ async def test_websocket_both_semconv(self): URL_PATH: self.scope["path"], CLIENT_ADDRESS: self.scope["client"][0], CLIENT_PORT: self.scope["client"][1], - HTTP_RESPONSE_STATUS_CODE: 200, HTTP_REQUEST_METHOD: self.scope["method"], }, }, @@ -1873,16 +1866,16 @@ def test_request_attributes(self): self.assertDictEqual( attrs, { - SpanAttributes.HTTP_METHOD: "GET", - SpanAttributes.HTTP_HOST: "127.0.0.1", + HTTP_REQUEST_METHOD: "GET", + SERVER_ADDRESS: "127.0.0.1", SpanAttributes.HTTP_TARGET: "/", SpanAttributes.HTTP_URL: "http://test/?foo=bar", - SpanAttributes.NET_HOST_PORT: 80, + SERVER_PORT: 80, SpanAttributes.HTTP_SCHEME: "http", SpanAttributes.HTTP_SERVER_NAME: "test", SpanAttributes.HTTP_FLAVOR: "1.0", - SpanAttributes.NET_PEER_IP: "127.0.0.1", - SpanAttributes.NET_PEER_PORT: 32767, + CLIENT_ADDRESS: "127.0.0.1", + CLIENT_PORT: 32767, }, ) @@ -1926,25 +1919,20 @@ def test_request_attributes_both_semconv(self): self.assertDictEqual( attrs, { - SpanAttributes.HTTP_METHOD: "GET", - SpanAttributes.HTTP_HOST: "127.0.0.1", + HTTP_REQUEST_METHOD: "GET", + SERVER_ADDRESS: "127.0.0.1", SpanAttributes.HTTP_TARGET: "/", SpanAttributes.HTTP_URL: "http://test/?foo=bar", - SpanAttributes.NET_HOST_PORT: 80, + SERVER_PORT: 80, SpanAttributes.HTTP_SCHEME: "http", SpanAttributes.HTTP_SERVER_NAME: "test", SpanAttributes.HTTP_FLAVOR: "1.0", - SpanAttributes.NET_PEER_IP: "127.0.0.1", - SpanAttributes.NET_PEER_PORT: 32767, - HTTP_REQUEST_METHOD: "GET", + CLIENT_ADDRESS: "127.0.0.1", + CLIENT_PORT: 32767, URL_PATH: "/", URL_QUERY: "foo=bar", - SERVER_ADDRESS: "127.0.0.1", - SERVER_PORT: 80, URL_SCHEME: "http", NETWORK_PROTOCOL_VERSION: "1.0", - CLIENT_ADDRESS: "127.0.0.1", - CLIENT_PORT: 32767, }, )