From fbdd32e16a3ec3ba5a76edcfd4e4c9df13eac094 Mon Sep 17 00:00:00 2001 From: Steve Liu Date: Sun, 29 Mar 2026 22:05:04 -0700 Subject: [PATCH] feat(asgi): respect suppress_http_instrumentation in ASGI middleware Add is_http_instrumentation_enabled() check in OpenTelemetryMiddleware.__call__ so ASGI server spans are skipped when HTTP instrumentation is suppressed. Every other HTTP instrumentor (client and server) already honors this flag. ASGI was the only one that did not. --- CHANGELOG.md | 2 ++ .../opentelemetry/instrumentation/asgi/__init__.py | 10 ++++++++-- .../tests/test_asgi_middleware.py | 14 ++++++++++++++ 3 files changed, 24 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3c9c67be4bb..08226bf539a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +- `opentelemetry-instrumentation-asgi`: Respect `suppress_http_instrumentation` context in ASGI middleware to skip server span creation when HTTP instrumentation is suppressed + ([#4375](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/4375)) - `opentelemetry-instrumentation-confluent-kafka`: Loosen confluent-kafka upper bound to <3.0.0 ([#4289](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/4289)) - `opentelemetry-instrumentation`: Add support for wrapt 2.x 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 59c5083ada8..e28315f502e 100644 --- a/instrumentation/opentelemetry-instrumentation-asgi/src/opentelemetry/instrumentation/asgi/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-asgi/src/opentelemetry/instrumentation/asgi/__init__.py @@ -255,7 +255,10 @@ def client_response_hook(span: Span, scope: Scope, message: dict[str, Any]): from opentelemetry.instrumentation.propagators import ( get_global_response_propagator, ) -from opentelemetry.instrumentation.utils import _start_internal_or_server_span +from opentelemetry.instrumentation.utils import ( + _start_internal_or_server_span, + is_http_instrumentation_enabled, +) from opentelemetry.metrics import get_meter from opentelemetry.propagators.textmap import Getter, Setter from opentelemetry.semconv._incubating.attributes.http_attributes import ( @@ -745,7 +748,10 @@ async def __call__( send: An awaitable callable taking a single dictionary as argument. """ start = default_timer() - if scope["type"] not in ("http", "websocket"): + if not is_http_instrumentation_enabled() or scope["type"] not in ( + "http", + "websocket", + ): return await self.app(scope, receive, send) _, _, url = get_host_port_url_tuple(scope) diff --git a/instrumentation/opentelemetry-instrumentation-asgi/tests/test_asgi_middleware.py b/instrumentation/opentelemetry-instrumentation-asgi/tests/test_asgi_middleware.py index f716c9f2f1e..b14f207d8a5 100644 --- a/instrumentation/opentelemetry-instrumentation-asgi/tests/test_asgi_middleware.py +++ b/instrumentation/opentelemetry-instrumentation-asgi/tests/test_asgi_middleware.py @@ -37,6 +37,7 @@ get_global_response_propagator, set_global_response_propagator, ) +from opentelemetry.instrumentation.utils import suppress_http_instrumentation from opentelemetry.sdk import resources from opentelemetry.sdk.metrics.export import ( HistogramDataPoint, @@ -1880,6 +1881,19 @@ async def test_no_excluded_urls(self): spans = self.get_finished_spans() self.assertGreater(len(spans), 0) + async def test_suppress_http_instrumentation(self): + app = otel_asgi.OpenTelemetryMiddleware(simple_asgi) + + async def suppression_wrapper(scope, receive, send): + with suppress_http_instrumentation(): + await app(scope, receive, send) + + self.seed_app(suppression_wrapper) + await self.send_default_request() + await self.get_all_output() + spans = self.get_finished_spans() + self.assertEqual(len(spans), 0) + class TestAsgiAttributes(unittest.TestCase): def setUp(self):