Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 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 @@ -50,6 +50,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
([#3904](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3904))
- build: bump ruff to 0.14.1
([#3842](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3842))
- `opentelemetry-instrumentation-aiohttp-client`: Fix metric attribute leakage
([#3936](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3936))

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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -287,8 +287,6 @@ def create_trace_config(
explicit_bucket_boundaries_advisory=HTTP_DURATION_HISTOGRAM_BUCKETS_NEW,
)

metric_attributes = {}

excluded_urls = get_excluded_urls("AIOHTTP_CLIENT")

def _end_trace(trace_config_ctx: types.SimpleNamespace):
Expand All @@ -299,7 +297,7 @@ def _end_trace(trace_config_ctx: types.SimpleNamespace):

if trace_config_ctx.duration_histogram_old is not None:
duration_attrs_old = _filter_semconv_duration_attrs(
metric_attributes,
trace_config_ctx.metric_attributes,
_client_duration_attrs_old,
_client_duration_attrs_new,
_StabilityMode.DEFAULT,
Expand All @@ -310,7 +308,7 @@ def _end_trace(trace_config_ctx: types.SimpleNamespace):
)
if trace_config_ctx.duration_histogram_new is not None:
duration_attrs_new = _filter_semconv_duration_attrs(
metric_attributes,
trace_config_ctx.metric_attributes,
_client_duration_attrs_old,
_client_duration_attrs_new,
_StabilityMode.HTTP,
Expand Down Expand Up @@ -348,7 +346,7 @@ async def on_request_start(
sem_conv_opt_in_mode,
)
_set_http_method(
metric_attributes,
trace_config_ctx.metric_attributes,
method,
sanitize_method(method),
sem_conv_opt_in_mode,
Expand All @@ -359,12 +357,12 @@ async def on_request_start(
parsed_url = urlparse(request_url)
if parsed_url.hostname:
_set_http_host_client(
metric_attributes,
trace_config_ctx.metric_attributes,
parsed_url.hostname,
sem_conv_opt_in_mode,
)
_set_http_net_peer_name_client(
metric_attributes,
trace_config_ctx.metric_attributes,
parsed_url.hostname,
sem_conv_opt_in_mode,
)
Expand All @@ -376,7 +374,9 @@ async def on_request_start(
)
if parsed_url.port:
_set_http_peer_port_client(
metric_attributes, parsed_url.port, sem_conv_opt_in_mode
trace_config_ctx.metric_attributes,
parsed_url.port,
sem_conv_opt_in_mode,
)
if _report_new(sem_conv_opt_in_mode):
_set_http_peer_port_client(
Expand Down Expand Up @@ -411,7 +411,7 @@ async def on_request_end(
_set_http_status_code_attribute(
trace_config_ctx.span,
params.response.status,
metric_attributes,
trace_config_ctx.metric_attributes,
sem_conv_opt_in_mode,
)

Expand All @@ -429,7 +429,7 @@ async def on_request_exception(
exc_type = type(params.exception).__qualname__
if _report_new(sem_conv_opt_in_mode):
trace_config_ctx.span.set_attribute(ERROR_TYPE, exc_type)
metric_attributes[ERROR_TYPE] = exc_type
trace_config_ctx.metric_attributes[ERROR_TYPE] = exc_type

trace_config_ctx.span.set_status(
Status(StatusCode.ERROR, exc_type)
Expand All @@ -450,6 +450,7 @@ def _trace_config_ctx_factory(**kwargs):
duration_histogram_old=duration_histogram_old,
duration_histogram_new=duration_histogram_new,
excluded_urls=excluded_urls,
metric_attributes={},
**kwargs,
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -828,6 +828,56 @@ async def request_handler(request):
self._assert_spans([], 0)
self._assert_metrics(0)

def test_metric_attributes_isolation(self):
async def success_handler(request):
assert "traceparent" in request.headers
return aiohttp.web.Response(status=HTTPStatus.OK)

async def timeout_handler(request):
await asyncio.sleep(60)
assert "traceparent" in request.headers
return aiohttp.web.Response(status=HTTPStatus.OK)

trace_config: aiohttp.TraceConfig = (
aiohttp_client.create_trace_config()
)

success_host, success_port = self._http_request(
trace_config=trace_config,
url="/success",
request_handler=success_handler,
)

timeout_host, timeout_port = self._http_request(
trace_config=trace_config,
url="/timeout",
request_handler=timeout_handler,
timeout=aiohttp.ClientTimeout(sock_read=0.01),
)

metrics = self._assert_metrics(1)
duration_dp_attributes = [
dict(dp.attributes) for dp in metrics[0].data.data_points
]
self.assertEqual(
[
{
HTTP_METHOD: "GET",
HTTP_HOST: success_host,
HTTP_STATUS_CODE: int(HTTPStatus.OK),
NET_PEER_NAME: success_host,
NET_PEER_PORT: success_port,
},
{
HTTP_METHOD: "GET",
HTTP_HOST: timeout_host,
NET_PEER_NAME: timeout_host,
NET_PEER_PORT: timeout_port,
},
],
duration_dp_attributes,
)


class TestAioHttpClientInstrumentor(TestBase):
URL = "/test-path"
Expand Down