From 1eab5597417f78072b444c2a12853665b6e865d9 Mon Sep 17 00:00:00 2001 From: Riccardo Magliocchetti Date: Wed, 15 Oct 2025 12:06:57 +0200 Subject: [PATCH 1/8] Stop using deprecated span.instrumentation_info --- .../tests/test_aiohttp_client_integration.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/instrumentation/opentelemetry-instrumentation-aiohttp-client/tests/test_aiohttp_client_integration.py b/instrumentation/opentelemetry-instrumentation-aiohttp-client/tests/test_aiohttp_client_integration.py index ec608e6a67..a4b3a9d5fe 100644 --- a/instrumentation/opentelemetry-instrumentation-aiohttp-client/tests/test_aiohttp_client_integration.py +++ b/instrumentation/opentelemetry-instrumentation-aiohttp-client/tests/test_aiohttp_client_integration.py @@ -332,7 +332,7 @@ def test_schema_url(self): span = self.memory_exporter.get_finished_spans()[0] self.assertEqual( - span.instrumentation_info.schema_url, + span.instrumentation_scope.schema_url, "https://opentelemetry.io/schemas/1.11.0", ) self.memory_exporter.clear() @@ -349,7 +349,7 @@ def test_schema_url_new_semconv(self): span = self.memory_exporter.get_finished_spans()[0] self.assertEqual( - span.instrumentation_info.schema_url, + span.instrumentation_scope.schema_url, "https://opentelemetry.io/schemas/1.21.0", ) self.memory_exporter.clear() @@ -366,7 +366,7 @@ def test_schema_url_both_semconv(self): span = self.memory_exporter.get_finished_spans()[0] self.assertEqual( - span.instrumentation_info.schema_url, + span.instrumentation_scope.schema_url, "https://opentelemetry.io/schemas/1.21.0", ) self.memory_exporter.clear() From 4355be534334fb7e994d16a26e80038c4303bef8 Mon Sep 17 00:00:00 2001 From: Riccardo Magliocchetti Date: Wed, 15 Oct 2025 12:37:04 +0200 Subject: [PATCH 2/8] aiohttp-client: add support for OTEL_PYTHON_EXCLUDED_URLS / OTEL_PYTHON_HTTPX_EXCLUDED_URLS --- .../aiohttp_client/__init__.py | 14 ++++++-- .../tests/test_aiohttp_client_integration.py | 34 +++++++++++++++++++ 2 files changed, 46 insertions(+), 2 deletions(-) diff --git a/instrumentation/opentelemetry-instrumentation-aiohttp-client/src/opentelemetry/instrumentation/aiohttp_client/__init__.py b/instrumentation/opentelemetry-instrumentation-aiohttp-client/src/opentelemetry/instrumentation/aiohttp_client/__init__.py index c84839deb7..3b240235c9 100644 --- a/instrumentation/opentelemetry-instrumentation-aiohttp-client/src/opentelemetry/instrumentation/aiohttp_client/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-aiohttp-client/src/opentelemetry/instrumentation/aiohttp_client/__init__.py @@ -135,7 +135,11 @@ def response_hook(span: Span, params: typing.Union[ ) from opentelemetry.trace import Span, SpanKind, TracerProvider, get_tracer from opentelemetry.trace.status import Status, StatusCode -from opentelemetry.util.http import redact_url, sanitize_method +from opentelemetry.util.http import ( + get_excluded_urls, + redact_url, + sanitize_method, +) _UrlFilterT = typing.Optional[typing.Callable[[yarl.URL], str]] _RequestHookT = typing.Optional[ @@ -271,6 +275,8 @@ def create_trace_config( metric_attributes = {} + excluded_urls = get_excluded_urls("AIOHTTP_CLIENT") + def _end_trace(trace_config_ctx: types.SimpleNamespace): elapsed_time = max(default_timer() - trace_config_ctx.start_time, 0) if trace_config_ctx.token: @@ -304,7 +310,10 @@ async def on_request_start( trace_config_ctx: types.SimpleNamespace, params: aiohttp.TraceRequestStartParams, ): - if not is_instrumentation_enabled(): + if ( + not is_instrumentation_enabled() + or trace_config_ctx.excluded_urls.url_disabled(str(params.url)) + ): trace_config_ctx.span = None return @@ -426,6 +435,7 @@ def _trace_config_ctx_factory(**kwargs): start_time=start_time, duration_histogram_old=duration_histogram_old, duration_histogram_new=duration_histogram_new, + excluded_urls=excluded_urls, **kwargs, ) diff --git a/instrumentation/opentelemetry-instrumentation-aiohttp-client/tests/test_aiohttp_client_integration.py b/instrumentation/opentelemetry-instrumentation-aiohttp-client/tests/test_aiohttp_client_integration.py index a4b3a9d5fe..8fadcbacc7 100644 --- a/instrumentation/opentelemetry-instrumentation-aiohttp-client/tests/test_aiohttp_client_integration.py +++ b/instrumentation/opentelemetry-instrumentation-aiohttp-client/tests/test_aiohttp_client_integration.py @@ -16,6 +16,7 @@ import asyncio import contextlib +import os import typing import unittest import urllib.parse @@ -803,6 +804,24 @@ async def do_request(url): ) self.memory_exporter.clear() + @mock.patch.dict( + os.environ, {"OTEL_PYTHON_AIOHTTP_CLIENT_EXCLUDED_URLS": "/some/path"} + ) + def test_ignores_excluded_urls(self): + async def request_handler(request): + assert "traceparent" not in request.headers + return aiohttp.web.Response(status=HTTPStatus.OK) + + host, port = self._http_request( + trace_config=aiohttp_client.create_trace_config(), + request_handler=request_handler, + url="/some/path?query=param&other=param2", + status_code=HTTPStatus.OK, + ) + + self._assert_spans([], 0) + self._assert_metrics(0) + class TestAioHttpClientInstrumentor(TestBase): URL = "/test-path" @@ -1115,6 +1134,21 @@ def response_hook( self.assertIn("response_hook_attr", span.attributes) self.assertEqual(span.attributes["response_hook_attr"], "value") + @mock.patch.dict( + os.environ, {"OTEL_PYTHON_AIOHTTP_CLIENT_EXCLUDED_URLS": "/test-path"} + ) + def test_ignores_excluded_urls(self): + # need the env var set at instrument time + AioHttpClientInstrumentor().uninstrument() + AioHttpClientInstrumentor().instrument() + + url = "/test-path?query=params" + host, port = run_with_test_server( + self.get_default_request(url), url, self.default_handler + ) + self._assert_spans(0) + self._assert_metrics(0) + class TestLoadingAioHttpInstrumentor(unittest.TestCase): def test_loading_instrumentor(self): From adc8d5e71472269290b2f9e06419c4f7cecc1879 Mon Sep 17 00:00:00 2001 From: Riccardo Magliocchetti Date: Wed, 15 Oct 2025 12:40:13 +0200 Subject: [PATCH 3/8] Add docs --- .../instrumentation/aiohttp_client/__init__.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/instrumentation/opentelemetry-instrumentation-aiohttp-client/src/opentelemetry/instrumentation/aiohttp_client/__init__.py b/instrumentation/opentelemetry-instrumentation-aiohttp-client/src/opentelemetry/instrumentation/aiohttp_client/__init__.py index 3b240235c9..d6869bcd36 100644 --- a/instrumentation/opentelemetry-instrumentation-aiohttp-client/src/opentelemetry/instrumentation/aiohttp_client/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-aiohttp-client/src/opentelemetry/instrumentation/aiohttp_client/__init__.py @@ -84,6 +84,20 @@ def response_hook(span: Span, params: typing.Union[ AioHttpClientInstrumentor().instrument(request_hook=request_hook, response_hook=response_hook) +Exclude lists +************* +To exclude certain URLs from tracking, set the environment variable ``OTEL_PYTHON_AIOHTTP_CLIENT_EXCLUDED_URLS`` +(or ``OTEL_PYTHON_EXCLUDED_URLS`` to cover all instrumentations) to a string of comma delimited regexes that match the +URLs. + +For example, + +:: + + export OTEL_PYTHON_AIOHTTP_CLIENT_EXCLUDED_URLS="client/.*/info,healthcheck" + +will exclude requests such as ``https://site/client/123/info`` and ``https://site/xyz/healthcheck``. + API --- """ From f0c2080b93079407b97da38b6dbc22677d93208b Mon Sep 17 00:00:00 2001 From: Riccardo Magliocchetti Date: Wed, 15 Oct 2025 12:41:10 +0200 Subject: [PATCH 4/8] Add changelog --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 024990c91d..b19b936259 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased +### Added + +- `opentelemetry-instrumentation-aiohttp-client`: add support for url exclusions via `OTEL_PYTHON_EXCLUDED_URLS` / `OTEL_PYTHON_HTTPX_EXCLUDED_URLS` + ([#3850](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3850)) + ## Version 1.38.0/0.59b0 (2025-10-16) ### Fixed From 06e2fc3ff25202697528bb227ff6ff06fce684ce Mon Sep 17 00:00:00 2001 From: Riccardo Magliocchetti Date: Wed, 15 Oct 2025 12:43:01 +0200 Subject: [PATCH 5/8] Please lint --- .../tests/test_aiohttp_client_integration.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/instrumentation/opentelemetry-instrumentation-aiohttp-client/tests/test_aiohttp_client_integration.py b/instrumentation/opentelemetry-instrumentation-aiohttp-client/tests/test_aiohttp_client_integration.py index 8fadcbacc7..f7029c793f 100644 --- a/instrumentation/opentelemetry-instrumentation-aiohttp-client/tests/test_aiohttp_client_integration.py +++ b/instrumentation/opentelemetry-instrumentation-aiohttp-client/tests/test_aiohttp_client_integration.py @@ -88,6 +88,7 @@ async def do_request(): return loop.run_until_complete(do_request()) +# pylint: disable=too-many-public-methods class TestAioHttpIntegration(TestBase): _test_status_codes = ( (HTTPStatus.OK, StatusCode.UNSET), @@ -812,7 +813,7 @@ async def request_handler(request): assert "traceparent" not in request.headers return aiohttp.web.Response(status=HTTPStatus.OK) - host, port = self._http_request( + self._http_request( trace_config=aiohttp_client.create_trace_config(), request_handler=request_handler, url="/some/path?query=param&other=param2", @@ -1143,7 +1144,7 @@ def test_ignores_excluded_urls(self): AioHttpClientInstrumentor().instrument() url = "/test-path?query=params" - host, port = run_with_test_server( + run_with_test_server( self.get_default_request(url), url, self.default_handler ) self._assert_spans(0) From c1eb7220cb8e24b63451a21f2c396891f85f3357 Mon Sep 17 00:00:00 2001 From: Riccardo Magliocchetti Date: Wed, 29 Oct 2025 11:23:11 +0100 Subject: [PATCH 6/8] Update CHANGELOG.md Co-authored-by: Tammy Baylis <96076570+tammy-baylis-swi@users.noreply.github.com> --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b19b936259..65855341ea 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,7 +13,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added -- `opentelemetry-instrumentation-aiohttp-client`: add support for url exclusions via `OTEL_PYTHON_EXCLUDED_URLS` / `OTEL_PYTHON_HTTPX_EXCLUDED_URLS` +- `opentelemetry-instrumentation-aiohttp-client`: add support for url exclusions via `OTEL_PYTHON_EXCLUDED_URLS` / `OTEL_PYTHON_AIOHTTP_CLIENT_EXCLUDED_URLS` ([#3850](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3850)) ## Version 1.38.0/0.59b0 (2025-10-16) From 850d6be843aba7fbe57193f9601c1c553ae2d8fd Mon Sep 17 00:00:00 2001 From: Riccardo Magliocchetti Date: Wed, 29 Oct 2025 11:32:26 +0100 Subject: [PATCH 7/8] Test for both env vars --- .../tests/test_aiohttp_client_integration.py | 23 +++++++++++-------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/instrumentation/opentelemetry-instrumentation-aiohttp-client/tests/test_aiohttp_client_integration.py b/instrumentation/opentelemetry-instrumentation-aiohttp-client/tests/test_aiohttp_client_integration.py index f7029c793f..2851ad80fa 100644 --- a/instrumentation/opentelemetry-instrumentation-aiohttp-client/tests/test_aiohttp_client_integration.py +++ b/instrumentation/opentelemetry-instrumentation-aiohttp-client/tests/test_aiohttp_client_integration.py @@ -805,20 +805,25 @@ async def do_request(url): ) self.memory_exporter.clear() - @mock.patch.dict( - os.environ, {"OTEL_PYTHON_AIOHTTP_CLIENT_EXCLUDED_URLS": "/some/path"} - ) def test_ignores_excluded_urls(self): async def request_handler(request): assert "traceparent" not in request.headers return aiohttp.web.Response(status=HTTPStatus.OK) - self._http_request( - trace_config=aiohttp_client.create_trace_config(), - request_handler=request_handler, - url="/some/path?query=param&other=param2", - status_code=HTTPStatus.OK, - ) + for env_var in ( + "OTEL_PYTHON_AIOHTTP_CLIENT_EXCLUDED_URLS", + "OTEL_PYTHON_EXCLUDED_URLS", + ): + with self.subTest(env_var=env_var): + with mock.patch.dict( + os.environ, {env_var: "/some/path"}, clear=True + ): + self._http_request( + trace_config=aiohttp_client.create_trace_config(), + request_handler=request_handler, + url="/some/path?query=param&other=param2", + status_code=HTTPStatus.OK, + ) self._assert_spans([], 0) self._assert_metrics(0) From e1d7a8e07f3f03b91e211cbf92abe51608793142 Mon Sep 17 00:00:00 2001 From: Riccardo Magliocchetti Date: Wed, 29 Oct 2025 11:34:01 +0100 Subject: [PATCH 8/8] Assert at each iteration --- .../tests/test_aiohttp_client_integration.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/instrumentation/opentelemetry-instrumentation-aiohttp-client/tests/test_aiohttp_client_integration.py b/instrumentation/opentelemetry-instrumentation-aiohttp-client/tests/test_aiohttp_client_integration.py index 2851ad80fa..87d75afc70 100644 --- a/instrumentation/opentelemetry-instrumentation-aiohttp-client/tests/test_aiohttp_client_integration.py +++ b/instrumentation/opentelemetry-instrumentation-aiohttp-client/tests/test_aiohttp_client_integration.py @@ -825,8 +825,8 @@ async def request_handler(request): status_code=HTTPStatus.OK, ) - self._assert_spans([], 0) - self._assert_metrics(0) + self._assert_spans([], 0) + self._assert_metrics(0) class TestAioHttpClientInstrumentor(TestBase):