Skip to content

Commit d9b40be

Browse files
authored
Add missing OTEL_PYTHON_EXCLUDED_URLS documentation (open-telemetry#3836)
* tornado: cite the instrumentation also handles OTEL_PYTHON_EXCLUDED_URLS * aiohttp-server: document OTEL_PYTHON_EXCLUDED_URLS configuration * Add test for aiohttp-server And make the implementation testable * Assert metrics in aiohttp-server tests * Please pylint * Initialize tracer, meter and excluded urls at instrument time So they are testable * Add changelog * Update instrumentation/opentelemetry-instrumentation-tornado/src/opentelemetry/instrumentation/tornado/__init__.py
1 parent 5a15f57 commit d9b40be

File tree

4 files changed

+113
-5
lines changed

4 files changed

+113
-5
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
3636
([#3894](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3894))
3737
- `opentelemetry-exporter-richconsole`: Prevent deadlock when parent span is not part of the batch
3838
([#3900](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3900))
39+
- `opentelemetry-instrumentation-aiohttp-server`: delay initialization of tracer, meter and excluded urls to instrumentation for testability
40+
([#3836](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3836))
3941

4042
## Version 1.38.0/0.59b0 (2025-10-16)
4143

instrumentation/opentelemetry-instrumentation-aiohttp-server/src/opentelemetry/instrumentation/aiohttp_server/__init__.py

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,25 @@ async def hello(request):
3535
app.add_routes([web.get('/', hello)])
3636
3737
web.run_app(app)
38+
39+
40+
Configuration
41+
-------------
42+
43+
Exclude lists
44+
*************
45+
To exclude certain URLs from tracking, set the environment variable ``OTEL_PYTHON_AIOHTTP_SERVER_EXCLUDED_URLS``
46+
(or ``OTEL_PYTHON_EXCLUDED_URLS`` to cover all instrumentations) to a string of comma delimited regexes that match the
47+
URLs.
48+
49+
For example,
50+
51+
::
52+
53+
export OTEL_PYTHON_AIOHTTP_SERVER_EXCLUDED_URLS="client/.*/info,healthcheck"
54+
55+
will exclude requests such as ``https://site/client/123/info`` and ``https://site/xyz/healthcheck``.
56+
3857
"""
3958

4059
import urllib
@@ -94,9 +113,9 @@ async def hello(request):
94113
HTTP_SERVER_NAME,
95114
]
96115

97-
tracer = trace.get_tracer(__name__)
98-
meter = metrics.get_meter(__name__, __version__)
99-
_excluded_urls = get_excluded_urls("AIOHTTP_SERVER")
116+
tracer = None
117+
meter = None
118+
_excluded_urls = None
100119

101120

102121
def _parse_duration_attrs(req_attrs):
@@ -296,6 +315,16 @@ class AioHttpServerInstrumentor(BaseInstrumentor):
296315
"""
297316

298317
def _instrument(self, **kwargs):
318+
# update global values at instrument time so we can test them
319+
global _excluded_urls # pylint: disable=global-statement
320+
_excluded_urls = get_excluded_urls("AIOHTTP_SERVER")
321+
322+
global tracer # pylint: disable=global-statement
323+
tracer = trace.get_tracer(__name__)
324+
325+
global meter # pylint: disable=global-statement
326+
meter = metrics.get_meter(__name__, __version__)
327+
299328
self._original_app = web.Application
300329
setattr(web, "Application", _InstrumentedApplication)
301330

instrumentation/opentelemetry-instrumentation-aiohttp-server/tests/test_aiohttp_server_integration.py

Lines changed: 78 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import pytest
2020
import pytest_asyncio
2121

22+
from opentelemetry import metrics as metrics_api
2223
from opentelemetry import trace as trace_api
2324
from opentelemetry.instrumentation.aiohttp_server import (
2425
AioHttpServerInstrumentor,
@@ -29,7 +30,10 @@
2930
HTTP_STATUS_CODE,
3031
HTTP_URL,
3132
)
32-
from opentelemetry.test.globals_test import reset_trace_globals
33+
from opentelemetry.test.globals_test import (
34+
reset_metrics_globals,
35+
reset_trace_globals,
36+
)
3337
from opentelemetry.test.test_base import TestBase
3438
from opentelemetry.util._importlib_metadata import entry_points
3539

@@ -65,6 +69,20 @@ def fixture_tracer():
6569
reset_trace_globals()
6670

6771

72+
@pytest.fixture(name="meter", scope="function")
73+
def fixture_meter():
74+
test_base = TestBase()
75+
76+
meter_provider, memory_reader = test_base.create_meter_provider()
77+
78+
reset_metrics_globals()
79+
metrics_api.set_meter_provider(meter_provider)
80+
81+
yield meter_provider, memory_reader
82+
83+
reset_metrics_globals()
84+
85+
6886
async def default_handler(request, status=200):
6987
return aiohttp.web.Response(status=status)
7088

@@ -113,21 +131,27 @@ def test_checking_instrumentor_pkg_installed():
113131
)
114132
async def test_status_code_instrumentation(
115133
tracer,
134+
meter,
116135
server_fixture,
117136
aiohttp_client,
118137
url,
119138
expected_method,
120139
expected_status_code,
121140
):
122141
_, memory_exporter = tracer
142+
_, metrics_reader = meter
123143
server, _ = server_fixture
124144

125145
assert len(memory_exporter.get_finished_spans()) == 0
146+
metrics = _get_sorted_metrics(metrics_reader.get_metrics_data())
147+
assert len(metrics) == 0
126148

127149
client = await aiohttp_client(server)
128150
await client.get(url)
129151

130152
assert len(memory_exporter.get_finished_spans()) == 1
153+
metrics = _get_sorted_metrics(metrics_reader.get_metrics_data())
154+
assert len(metrics) == 2
131155

132156
[span] = memory_exporter.get_finished_spans()
133157

@@ -195,3 +219,56 @@ async def handler(request):
195219
# Clean up
196220
AioHttpServerInstrumentor().uninstrument()
197221
memory_exporter.clear()
222+
223+
224+
def _get_sorted_metrics(metrics_data):
225+
resource_metrics = metrics_data.resource_metrics if metrics_data else []
226+
227+
all_metrics = []
228+
for metrics in resource_metrics:
229+
for scope_metrics in metrics.scope_metrics:
230+
all_metrics.extend(scope_metrics.metrics)
231+
232+
return sorted(
233+
all_metrics,
234+
key=lambda m: m.name,
235+
)
236+
237+
238+
@pytest.mark.asyncio
239+
@pytest.mark.parametrize(
240+
"env_var",
241+
["OTEL_PYTHON_AIOHTTP_SERVER_EXCLUDED_URLS", "OTEL_PYTHON_EXCLUDED_URLS"],
242+
)
243+
async def test_excluded_urls(
244+
tracer, meter, aiohttp_server, monkeypatch, env_var
245+
):
246+
"""Test that excluded env vars are taken into account."""
247+
_, memory_exporter = tracer
248+
_, metrics_reader = meter
249+
250+
monkeypatch.setenv(env_var, "/status/200")
251+
AioHttpServerInstrumentor().instrument()
252+
253+
app = aiohttp.web.Application()
254+
255+
async def handler(request):
256+
return aiohttp.web.Response(text="hello")
257+
258+
app.router.add_get("/status/200", handler)
259+
260+
server = await aiohttp_server(app)
261+
262+
url = f"http://{server.host}:{server.port}/status/200"
263+
async with aiohttp.ClientSession() as session:
264+
async with session.get(url) as response:
265+
assert response.status == 200
266+
assert await response.text() == "hello"
267+
268+
spans = memory_exporter.get_finished_spans()
269+
assert len(spans) == 0
270+
271+
metrics = _get_sorted_metrics(metrics_reader.get_metrics_data())
272+
assert len(metrics) == 0
273+
274+
AioHttpServerInstrumentor().uninstrument()

instrumentation/opentelemetry-instrumentation-tornado/src/opentelemetry/instrumentation/tornado/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ def get(self):
3939
4040
The following environment variables are supported as configuration options:
4141
42-
- OTEL_PYTHON_TORNADO_EXCLUDED_URLS
42+
- ``OTEL_PYTHON_TORNADO_EXCLUDED_URLS`` (or ``OTEL_PYTHON_EXCLUDED_URLS`` to cover all instrumentations)
4343
4444
A comma separated list of paths that should not be automatically traced. For example, if this is set to
4545

0 commit comments

Comments
 (0)