From 765e9941c4953037655c9e978b3163a49f3b22c5 Mon Sep 17 00:00:00 2001 From: VimalN2005 Date: Thu, 29 Oct 2026 14:40:18 +0530 Subject: [PATCH 1/2] fix(aiohttp-server): resolve tracer dynamically for legacy middleware --- .changelog/5164.fixed | 1 + .../aiohttp_server/__init__.py | 4 +- .../tests/test_aiohttp_server_integration.py | 115 ++++++++++++++++++ 3 files changed, 119 insertions(+), 1 deletion(-) create mode 100644 .changelog/5164.fixed diff --git a/.changelog/5164.fixed b/.changelog/5164.fixed new file mode 100644 index 0000000000..92c966db56 --- /dev/null +++ b/.changelog/5164.fixed @@ -0,0 +1 @@ +`opentelemetry-instrumentation-aiohttp-server`: Fix HTTP 500 responses when using the backwards-compatible `middleware` imported before instrumentation. diff --git a/instrumentation/opentelemetry-instrumentation-aiohttp-server/src/opentelemetry/instrumentation/aiohttp_server/__init__.py b/instrumentation/opentelemetry-instrumentation-aiohttp-server/src/opentelemetry/instrumentation/aiohttp_server/__init__.py index a4a027c955..b2f8d40f53 100644 --- a/instrumentation/opentelemetry-instrumentation-aiohttp-server/src/opentelemetry/instrumentation/aiohttp_server/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-aiohttp-server/src/opentelemetry/instrumentation/aiohttp_server/__init__.py @@ -433,7 +433,9 @@ async def _middleware(request, handler): _sem_conv_opt_in_mode, ) - with _tracer.start_as_current_span( + # The backwards-compatible middleware is created before instrumentation. + request_tracer = _tracer if _tracer is not None else (tracer or trace.get_tracer(__name__, __version__)) + with request_tracer.start_as_current_span( span_name, context=extract(request, getter=getter), kind=trace.SpanKind.SERVER, diff --git a/instrumentation/opentelemetry-instrumentation-aiohttp-server/tests/test_aiohttp_server_integration.py b/instrumentation/opentelemetry-instrumentation-aiohttp-server/tests/test_aiohttp_server_integration.py index a49c72e4cc..4173dd0438 100644 --- a/instrumentation/opentelemetry-instrumentation-aiohttp-server/tests/test_aiohttp_server_integration.py +++ b/instrumentation/opentelemetry-instrumentation-aiohttp-server/tests/test_aiohttp_server_integration.py @@ -10,6 +10,9 @@ import pytest import pytest_asyncio from multidict import CIMultiDict +from pytest_aiohttp.plugin import AiohttpClient + +from opentelemetry import trace from opentelemetry.instrumentation._semconv import ( HTTP_DURATION_HISTOGRAM_BUCKETS_NEW, @@ -23,6 +26,8 @@ ) from opentelemetry.instrumentation.aiohttp_server import ( AioHttpServerInstrumentor, + create_aiohttp_middleware, + middleware, ) from opentelemetry.instrumentation.utils import suppress_http_instrumentation from opentelemetry.sdk.metrics.export import ( @@ -64,6 +69,8 @@ from opentelemetry.semconv.attributes.user_agent_attributes import ( USER_AGENT_ORIGINAL, ) +from opentelemetry.semconv.metrics import MetricInstruments +from opentelemetry.test.globals_test import reset_trace_globals from opentelemetry.test.samplertestutil import CapturingSampler from opentelemetry.test.test_base import TestBase from opentelemetry.trace import SpanKind, StatusCode @@ -166,6 +173,114 @@ def test_checking_instrumentor_pkg_installed(): assert isinstance(instrumentor, AioHttpServerInstrumentor) +@pytest.mark.asyncio +async def test_legacy_middleware(test_base: TestBase, aiohttp_client: AiohttpClient) -> None: + app = aiohttp.web.Application(middlewares=[middleware]) + app.router.add_get("/test-path", default_handler) + + AioHttpServerInstrumentor().instrument() + try: + client = await aiohttp_client(app) + response = await client.get("/test-path") + + assert response.status == HTTPStatus.OK + spans = test_base.get_finished_spans() + assert len(spans) == 1 + assert spans[0].name == "GET /test-path" + assert spans[0].kind == trace.SpanKind.SERVER + assert spans[0].attributes[HTTP_METHOD] == "GET" + assert spans[0].attributes[HTTP_STATUS_CODE] == HTTPStatus.OK + assert isinstance(spans[0].attributes[HTTP_STATUS_CODE], int) + metrics = test_base.get_sorted_metrics(SCOPE) + assert len(metrics) == 2 + assert metrics[0].name == MetricInstruments.HTTP_SERVER_ACTIVE_REQUESTS + assert metrics[0].data.data_points[0].value == 0 + assert metrics[1].name == MetricInstruments.HTTP_SERVER_DURATION + assert metrics[1].data.data_points[0].count == 1 + finally: + AioHttpServerInstrumentor().uninstrument() + + +@pytest.mark.asyncio +async def test_legacy_middleware_after_reinstrumenting(test_base: TestBase, aiohttp_client: AiohttpClient) -> None: + app = aiohttp.web.Application(middlewares=[middleware]) + app.router.add_get("/test-path", default_handler) + provider, exporter = TestBase.create_tracer_provider() + + AioHttpServerInstrumentor().instrument() + try: + client = await aiohttp_client(app) + assert (await client.get("/test-path")).status == HTTPStatus.OK + assert len(test_base.get_finished_spans()) == 1 + + AioHttpServerInstrumentor().uninstrument() + reset_trace_globals() + trace.set_tracer_provider(provider) + AioHttpServerInstrumentor().instrument() + + assert (await client.get("/test-path")).status == HTTPStatus.OK + assert len(test_base.get_finished_spans()) == 1 + assert len(exporter.get_finished_spans()) == 1 + finally: + AioHttpServerInstrumentor().uninstrument() + provider.shutdown() + + +@pytest.mark.asyncio +@pytest.mark.parametrize("explicit_provider", [False, True]) +async def test_middleware_factory_keeps_tracer_provider( + test_base: TestBase, aiohttp_client: AiohttpClient, explicit_provider: bool +) -> None: + provider, exporter = TestBase.create_tracer_provider() + AioHttpServerInstrumentor().instrument() + selected_provider = provider if explicit_provider else None + bound_middleware = create_aiohttp_middleware(tracer_provider=selected_provider) + AioHttpServerInstrumentor().uninstrument() + + app = aiohttp.web.Application(middlewares=[bound_middleware]) + app.router.add_get("/test-path", default_handler) + reset_trace_globals() + trace.set_tracer_provider(trace.NoOpTracerProvider()) + AioHttpServerInstrumentor().instrument() + try: + client = await aiohttp_client(app) + assert (await client.get("/test-path")).status == HTTPStatus.OK + assert len(exporter.get_finished_spans()) == int(explicit_provider) + assert len(test_base.get_finished_spans()) == int(not explicit_provider) + finally: + AioHttpServerInstrumentor().uninstrument() + provider.shutdown() + + +@pytest.mark.asyncio +async def test_legacy_middleware_without_sdk(test_base: TestBase, aiohttp_client: AiohttpClient) -> None: + app = aiohttp.web.Application(middlewares=[middleware]) + app.router.add_get("/test-path", default_handler) + reset_trace_globals() + + AioHttpServerInstrumentor().instrument() + try: + client = await aiohttp_client(app) + assert (await client.get("/test-path")).status == HTTPStatus.OK + assert len(test_base.get_finished_spans()) == 0 + finally: + AioHttpServerInstrumentor().uninstrument() + + +@pytest.mark.asyncio +async def test_suppressed_legacy_middleware_without_instrumenting( + test_base: TestBase, aiohttp_client: AiohttpClient +) -> None: + app = aiohttp.web.Application(middlewares=[middleware]) + app.router.add_get("/test-path", default_handler) + with suppress_http_instrumentation(): + client = await aiohttp_client(app) + + assert (await client.get("/test-path")).status == HTTPStatus.OK + assert len(test_base.get_finished_spans()) == 0 + assert len(test_base.get_sorted_metrics(SCOPE)) == 0 + + @pytest.mark.asyncio @pytest.mark.parametrize( "url, expected_method, expected_status_code", From 22807a0b1d7ad03dbd1521daa47f3677a430556d Mon Sep 17 00:00:00 2001 From: VimalN2005 Date: Fri, 30 Oct 2026 22:56:14 +0530 Subject: [PATCH 2/2] fix(aiohttp-server): rename changelog to PR number and fix ruff I001 --- .changelog/{5164.fixed => 5256.fixed} | 0 .../tests/test_aiohttp_server_integration.py | 1 - 2 files changed, 1 deletion(-) rename .changelog/{5164.fixed => 5256.fixed} (100%) diff --git a/.changelog/5164.fixed b/.changelog/5256.fixed similarity index 100% rename from .changelog/5164.fixed rename to .changelog/5256.fixed diff --git a/instrumentation/opentelemetry-instrumentation-aiohttp-server/tests/test_aiohttp_server_integration.py b/instrumentation/opentelemetry-instrumentation-aiohttp-server/tests/test_aiohttp_server_integration.py index 4173dd0438..f3f2e78991 100644 --- a/instrumentation/opentelemetry-instrumentation-aiohttp-server/tests/test_aiohttp_server_integration.py +++ b/instrumentation/opentelemetry-instrumentation-aiohttp-server/tests/test_aiohttp_server_integration.py @@ -13,7 +13,6 @@ from pytest_aiohttp.plugin import AiohttpClient from opentelemetry import trace - from opentelemetry.instrumentation._semconv import ( HTTP_DURATION_HISTOGRAM_BUCKETS_NEW, OTEL_SEMCONV_STABILITY_OPT_IN,