Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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