Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
([#3894](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3894))
- `opentelemetry-exporter-richconsole`: Prevent deadlock when parent span is not part of the batch
([#3900](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3900))
- `opentelemetry-instrumentation-aiohttp-server`: delay initialization of tracer, meter and excluded urls to instrumentation for testability
([#3836](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3836))

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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,25 @@ async def hello(request):
app.add_routes([web.get('/', hello)])

web.run_app(app)


Configuration
-------------

Exclude lists
*************
To exclude certain URLs from tracking, set the environment variable ``OTEL_PYTHON_AIOHTTP_SERVER_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_SERVER_EXCLUDED_URLS="client/.*/info,healthcheck"

will exclude requests such as ``https://site/client/123/info`` and ``https://site/xyz/healthcheck``.

"""

import urllib
Expand Down Expand Up @@ -94,9 +113,9 @@ async def hello(request):
HTTP_SERVER_NAME,
]

tracer = trace.get_tracer(__name__)
meter = metrics.get_meter(__name__, __version__)
_excluded_urls = get_excluded_urls("AIOHTTP_SERVER")
tracer = None
meter = None
_excluded_urls = None


def _parse_duration_attrs(req_attrs):
Expand Down Expand Up @@ -296,6 +315,16 @@ class AioHttpServerInstrumentor(BaseInstrumentor):
"""

def _instrument(self, **kwargs):
# update global values at instrument time so we can test them
global _excluded_urls # pylint: disable=global-statement
_excluded_urls = get_excluded_urls("AIOHTTP_SERVER")

global tracer # pylint: disable=global-statement
tracer = trace.get_tracer(__name__)

global meter # pylint: disable=global-statement
meter = metrics.get_meter(__name__, __version__)

self._original_app = web.Application
setattr(web, "Application", _InstrumentedApplication)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import pytest
import pytest_asyncio

from opentelemetry import metrics as metrics_api
from opentelemetry import trace as trace_api
from opentelemetry.instrumentation.aiohttp_server import (
AioHttpServerInstrumentor,
Expand All @@ -29,7 +30,10 @@
HTTP_STATUS_CODE,
HTTP_URL,
)
from opentelemetry.test.globals_test import reset_trace_globals
from opentelemetry.test.globals_test import (
reset_metrics_globals,
reset_trace_globals,
)
from opentelemetry.test.test_base import TestBase
from opentelemetry.util._importlib_metadata import entry_points

Expand Down Expand Up @@ -65,6 +69,20 @@ def fixture_tracer():
reset_trace_globals()


@pytest.fixture(name="meter", scope="function")
def fixture_meter():
test_base = TestBase()

meter_provider, memory_reader = test_base.create_meter_provider()

reset_metrics_globals()
metrics_api.set_meter_provider(meter_provider)

yield meter_provider, memory_reader

reset_metrics_globals()


async def default_handler(request, status=200):
return aiohttp.web.Response(status=status)

Expand Down Expand Up @@ -113,21 +131,27 @@ def test_checking_instrumentor_pkg_installed():
)
async def test_status_code_instrumentation(
tracer,
meter,
server_fixture,
aiohttp_client,
url,
expected_method,
expected_status_code,
):
_, memory_exporter = tracer
_, metrics_reader = meter
server, _ = server_fixture

assert len(memory_exporter.get_finished_spans()) == 0
metrics = _get_sorted_metrics(metrics_reader.get_metrics_data())
assert len(metrics) == 0

client = await aiohttp_client(server)
await client.get(url)

assert len(memory_exporter.get_finished_spans()) == 1
metrics = _get_sorted_metrics(metrics_reader.get_metrics_data())
assert len(metrics) == 2

[span] = memory_exporter.get_finished_spans()

Expand Down Expand Up @@ -195,3 +219,56 @@ async def handler(request):
# Clean up
AioHttpServerInstrumentor().uninstrument()
memory_exporter.clear()


def _get_sorted_metrics(metrics_data):
resource_metrics = metrics_data.resource_metrics if metrics_data else []

all_metrics = []
for metrics in resource_metrics:
for scope_metrics in metrics.scope_metrics:
all_metrics.extend(scope_metrics.metrics)

return sorted(
all_metrics,
key=lambda m: m.name,
)


@pytest.mark.asyncio
@pytest.mark.parametrize(
"env_var",
["OTEL_PYTHON_AIOHTTP_SERVER_EXCLUDED_URLS", "OTEL_PYTHON_EXCLUDED_URLS"],
)
async def test_excluded_urls(
tracer, meter, aiohttp_server, monkeypatch, env_var
):
"""Test that excluded env vars are taken into account."""
_, memory_exporter = tracer
_, metrics_reader = meter

monkeypatch.setenv(env_var, "/status/200")
AioHttpServerInstrumentor().instrument()

app = aiohttp.web.Application()

async def handler(request):
return aiohttp.web.Response(text="hello")

app.router.add_get("/status/200", handler)

server = await aiohttp_server(app)

url = f"http://{server.host}:{server.port}/status/200"
async with aiohttp.ClientSession() as session:
async with session.get(url) as response:
assert response.status == 200
assert await response.text() == "hello"

spans = memory_exporter.get_finished_spans()
assert len(spans) == 0

metrics = _get_sorted_metrics(metrics_reader.get_metrics_data())
assert len(metrics) == 0

AioHttpServerInstrumentor().uninstrument()
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ def get(self):

The following environment variables are supported as configuration options:

- OTEL_PYTHON_TORNADO_EXCLUDED_URLS
- OTEL_PYTHON_TORNADO_EXCLUDED_URLS (or ``OTEL_PYTHON_EXCLUDED_URLS`` to cover all instrumentations)

A comma separated list of paths that should not be automatically traced. For example, if this is set to

Expand Down