Skip to content

Commit 620abb5

Browse files
aiohttp-client: add support for url exclusions (#3850)
* Stop using deprecated span.instrumentation_info * aiohttp-client: add support for OTEL_PYTHON_EXCLUDED_URLS / OTEL_PYTHON_HTTPX_EXCLUDED_URLS * Add docs * Add changelog * Please lint * Update CHANGELOG.md Co-authored-by: Tammy Baylis <[email protected]> * Test for both env vars * Assert at each iteration --------- Co-authored-by: Tammy Baylis <[email protected]>
1 parent d31e20e commit 620abb5

File tree

3 files changed

+74
-5
lines changed

3 files changed

+74
-5
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1111
1212
## Unreleased
1313

14+
### Added
15+
16+
- `opentelemetry-instrumentation-aiohttp-client`: add support for url exclusions via `OTEL_PYTHON_EXCLUDED_URLS` / `OTEL_PYTHON_AIOHTTP_CLIENT_EXCLUDED_URLS`
17+
([#3850](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3850))
18+
1419
### Fixed
1520

1621
- `opentelemetry-instrumentation-botocore`: Handle dict input in _decode_tool_use for Bedrock streaming

instrumentation/opentelemetry-instrumentation-aiohttp-client/src/opentelemetry/instrumentation/aiohttp_client/__init__.py

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,20 @@ def response_hook(span: Span, params: typing.Union[
8484
8585
AioHttpClientInstrumentor().instrument(request_hook=request_hook, response_hook=response_hook)
8686
87+
Exclude lists
88+
*************
89+
To exclude certain URLs from tracking, set the environment variable ``OTEL_PYTHON_AIOHTTP_CLIENT_EXCLUDED_URLS``
90+
(or ``OTEL_PYTHON_EXCLUDED_URLS`` to cover all instrumentations) to a string of comma delimited regexes that match the
91+
URLs.
92+
93+
For example,
94+
95+
::
96+
97+
export OTEL_PYTHON_AIOHTTP_CLIENT_EXCLUDED_URLS="client/.*/info,healthcheck"
98+
99+
will exclude requests such as ``https://site/client/123/info`` and ``https://site/xyz/healthcheck``.
100+
87101
API
88102
---
89103
"""
@@ -135,7 +149,11 @@ def response_hook(span: Span, params: typing.Union[
135149
)
136150
from opentelemetry.trace import Span, SpanKind, TracerProvider, get_tracer
137151
from opentelemetry.trace.status import Status, StatusCode
138-
from opentelemetry.util.http import redact_url, sanitize_method
152+
from opentelemetry.util.http import (
153+
get_excluded_urls,
154+
redact_url,
155+
sanitize_method,
156+
)
139157

140158
_UrlFilterT = typing.Optional[typing.Callable[[yarl.URL], str]]
141159
_RequestHookT = typing.Optional[
@@ -271,6 +289,8 @@ def create_trace_config(
271289

272290
metric_attributes = {}
273291

292+
excluded_urls = get_excluded_urls("AIOHTTP_CLIENT")
293+
274294
def _end_trace(trace_config_ctx: types.SimpleNamespace):
275295
elapsed_time = max(default_timer() - trace_config_ctx.start_time, 0)
276296
if trace_config_ctx.token:
@@ -304,7 +324,10 @@ async def on_request_start(
304324
trace_config_ctx: types.SimpleNamespace,
305325
params: aiohttp.TraceRequestStartParams,
306326
):
307-
if not is_instrumentation_enabled():
327+
if (
328+
not is_instrumentation_enabled()
329+
or trace_config_ctx.excluded_urls.url_disabled(str(params.url))
330+
):
308331
trace_config_ctx.span = None
309332
return
310333

@@ -426,6 +449,7 @@ def _trace_config_ctx_factory(**kwargs):
426449
start_time=start_time,
427450
duration_histogram_old=duration_histogram_old,
428451
duration_histogram_new=duration_histogram_new,
452+
excluded_urls=excluded_urls,
429453
**kwargs,
430454
)
431455

instrumentation/opentelemetry-instrumentation-aiohttp-client/tests/test_aiohttp_client_integration.py

Lines changed: 43 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
import asyncio
1818
import contextlib
19+
import os
1920
import typing
2021
import unittest
2122
import urllib.parse
@@ -87,6 +88,7 @@ async def do_request():
8788
return loop.run_until_complete(do_request())
8889

8990

91+
# pylint: disable=too-many-public-methods
9092
class TestAioHttpIntegration(TestBase):
9193
_test_status_codes = (
9294
(HTTPStatus.OK, StatusCode.UNSET),
@@ -332,7 +334,7 @@ def test_schema_url(self):
332334

333335
span = self.memory_exporter.get_finished_spans()[0]
334336
self.assertEqual(
335-
span.instrumentation_info.schema_url,
337+
span.instrumentation_scope.schema_url,
336338
"https://opentelemetry.io/schemas/1.11.0",
337339
)
338340
self.memory_exporter.clear()
@@ -349,7 +351,7 @@ def test_schema_url_new_semconv(self):
349351

350352
span = self.memory_exporter.get_finished_spans()[0]
351353
self.assertEqual(
352-
span.instrumentation_info.schema_url,
354+
span.instrumentation_scope.schema_url,
353355
"https://opentelemetry.io/schemas/1.21.0",
354356
)
355357
self.memory_exporter.clear()
@@ -366,7 +368,7 @@ def test_schema_url_both_semconv(self):
366368

367369
span = self.memory_exporter.get_finished_spans()[0]
368370
self.assertEqual(
369-
span.instrumentation_info.schema_url,
371+
span.instrumentation_scope.schema_url,
370372
"https://opentelemetry.io/schemas/1.21.0",
371373
)
372374
self.memory_exporter.clear()
@@ -803,6 +805,29 @@ async def do_request(url):
803805
)
804806
self.memory_exporter.clear()
805807

808+
def test_ignores_excluded_urls(self):
809+
async def request_handler(request):
810+
assert "traceparent" not in request.headers
811+
return aiohttp.web.Response(status=HTTPStatus.OK)
812+
813+
for env_var in (
814+
"OTEL_PYTHON_AIOHTTP_CLIENT_EXCLUDED_URLS",
815+
"OTEL_PYTHON_EXCLUDED_URLS",
816+
):
817+
with self.subTest(env_var=env_var):
818+
with mock.patch.dict(
819+
os.environ, {env_var: "/some/path"}, clear=True
820+
):
821+
self._http_request(
822+
trace_config=aiohttp_client.create_trace_config(),
823+
request_handler=request_handler,
824+
url="/some/path?query=param&other=param2",
825+
status_code=HTTPStatus.OK,
826+
)
827+
828+
self._assert_spans([], 0)
829+
self._assert_metrics(0)
830+
806831

807832
class TestAioHttpClientInstrumentor(TestBase):
808833
URL = "/test-path"
@@ -1115,6 +1140,21 @@ def response_hook(
11151140
self.assertIn("response_hook_attr", span.attributes)
11161141
self.assertEqual(span.attributes["response_hook_attr"], "value")
11171142

1143+
@mock.patch.dict(
1144+
os.environ, {"OTEL_PYTHON_AIOHTTP_CLIENT_EXCLUDED_URLS": "/test-path"}
1145+
)
1146+
def test_ignores_excluded_urls(self):
1147+
# need the env var set at instrument time
1148+
AioHttpClientInstrumentor().uninstrument()
1149+
AioHttpClientInstrumentor().instrument()
1150+
1151+
url = "/test-path?query=params"
1152+
run_with_test_server(
1153+
self.get_default_request(url), url, self.default_handler
1154+
)
1155+
self._assert_spans(0)
1156+
self._assert_metrics(0)
1157+
11181158

11191159
class TestLoadingAioHttpInstrumentor(unittest.TestCase):
11201160
def test_loading_instrumentor(self):

0 commit comments

Comments
 (0)