Skip to content

Commit 1ac4d7b

Browse files
Add tests
1 parent 49ba67c commit 1ac4d7b

File tree

1 file changed

+325
-5
lines changed

1 file changed

+325
-5
lines changed

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

Lines changed: 325 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,14 +22,54 @@
2222

2323
from opentelemetry import metrics as metrics_api
2424
from opentelemetry import trace as trace_api
25+
from opentelemetry.instrumentation._semconv import (
26+
HTTP_DURATION_HISTOGRAM_BUCKETS_NEW,
27+
OTEL_SEMCONV_STABILITY_OPT_IN,
28+
_OpenTelemetrySemanticConventionStability,
29+
_server_active_requests_count_attrs_new,
30+
_server_active_requests_count_attrs_old,
31+
_server_duration_attrs_new,
32+
_server_duration_attrs_old,
33+
_StabilityMode,
34+
)
2535
from opentelemetry.instrumentation.aiohttp_server import (
2636
AioHttpServerInstrumentor,
2737
)
2838
from opentelemetry.instrumentation.utils import suppress_http_instrumentation
39+
from opentelemetry.sdk.metrics.export import (
40+
HistogramDataPoint,
41+
)
2942
from opentelemetry.semconv._incubating.attributes.http_attributes import (
43+
HTTP_FLAVOR,
3044
HTTP_METHOD,
45+
HTTP_SCHEME,
3146
HTTP_STATUS_CODE,
47+
HTTP_TARGET,
3248
HTTP_URL,
49+
HTTP_USER_AGENT,
50+
)
51+
from opentelemetry.semconv._incubating.attributes.net_attributes import (
52+
NET_HOST_NAME,
53+
NET_HOST_PORT,
54+
)
55+
from opentelemetry.semconv.attributes.http_attributes import (
56+
HTTP_REQUEST_METHOD,
57+
HTTP_RESPONSE_STATUS_CODE,
58+
)
59+
from opentelemetry.semconv.attributes.network_attributes import (
60+
NETWORK_PROTOCOL_VERSION,
61+
)
62+
from opentelemetry.semconv.attributes.server_attributes import (
63+
SERVER_ADDRESS,
64+
SERVER_PORT,
65+
)
66+
from opentelemetry.semconv.attributes.url_attributes import (
67+
URL_PATH,
68+
URL_QUERY,
69+
URL_SCHEME,
70+
)
71+
from opentelemetry.semconv.attributes.user_agent_attributes import (
72+
USER_AGENT_ORIGINAL,
3373
)
3474
from opentelemetry.test.globals_test import (
3575
reset_metrics_globals,
@@ -164,10 +204,7 @@ async def test_status_code_instrumentation(
164204

165205
assert expected_method.value == span.attributes[HTTP_METHOD]
166206
assert expected_status_code == span.attributes[HTTP_STATUS_CODE]
167-
168-
assert (
169-
f"http://{server.host}:{server.port}{url}" == span.attributes[HTTP_URL]
170-
)
207+
assert url == span.attributes[HTTP_TARGET]
171208

172209

173210
@pytest.mark.asyncio
@@ -186,10 +223,16 @@ async def test_suppress_instrumentation(
186223

187224

188225
@pytest.mark.asyncio
189-
async def test_remove_sensitive_params(tracer, aiohttp_server):
226+
async def test_remove_sensitive_params(tracer, aiohttp_server, monkeypatch):
190227
"""Test that sensitive information in URLs is properly redacted."""
191228
_, memory_exporter = tracer
192229

230+
# Use old semconv to test HTTP_URL redaction
231+
monkeypatch.setenv(
232+
OTEL_SEMCONV_STABILITY_OPT_IN, _StabilityMode.DEFAULT.value
233+
)
234+
_OpenTelemetrySemanticConventionStability._initialized = False
235+
193236
# Set up instrumentation
194237
AioHttpServerInstrumentor().instrument()
195238

@@ -228,6 +271,56 @@ async def handler(request):
228271
memory_exporter.clear()
229272

230273

274+
@pytest.mark.asyncio
275+
async def test_remove_sensitive_params_new(
276+
tracer, aiohttp_server, monkeypatch
277+
):
278+
"""Test URL handling with new semantic conventions (no redaction for URL_PATH/URL_QUERY)."""
279+
_, memory_exporter = tracer
280+
281+
# Use new semconv
282+
monkeypatch.setenv(
283+
OTEL_SEMCONV_STABILITY_OPT_IN, _StabilityMode.HTTP.value
284+
)
285+
_OpenTelemetrySemanticConventionStability._initialized = False
286+
287+
# Set up instrumentation
288+
AioHttpServerInstrumentor().instrument()
289+
290+
# Create app with test route
291+
app = aiohttp.web.Application()
292+
293+
async def handler(request):
294+
return aiohttp.web.Response(text="hello")
295+
296+
app.router.add_get("/status/200", handler)
297+
298+
# Start the server
299+
server = await aiohttp_server(app)
300+
301+
# Make request with sensitive data in URL
302+
url = f"http://username:password@{server.host}:{server.port}/status/200?Signature=secret"
303+
async with aiohttp.ClientSession() as session:
304+
async with session.get(url) as response:
305+
assert response.status == 200
306+
assert await response.text() == "hello"
307+
308+
# Verify span attributes with new semconv
309+
spans = memory_exporter.get_finished_spans()
310+
assert len(spans) == 1
311+
312+
span = spans[0]
313+
assert span.attributes[HTTP_REQUEST_METHOD] == "GET"
314+
assert span.attributes[HTTP_RESPONSE_STATUS_CODE] == 200
315+
assert span.attributes[URL_PATH] == "/status/200"
316+
assert span.attributes[URL_QUERY] == "Signature=REDACTED"
317+
assert HTTP_URL not in span.attributes
318+
319+
# Clean up
320+
AioHttpServerInstrumentor().uninstrument()
321+
memory_exporter.clear()
322+
323+
231324
def _get_sorted_metrics(metrics_data):
232325
resource_metrics = metrics_data.resource_metrics if metrics_data else []
233326

@@ -400,3 +493,230 @@ async def handler(request):
400493
assert "http.response.header.custom_test_header_3" not in span.attributes
401494

402495
AioHttpServerInstrumentor().uninstrument()
496+
497+
498+
@pytest.mark.asyncio
499+
async def test_semantic_conventions_metrics_old_default(
500+
tracer, meter, aiohttp_server, monkeypatch
501+
):
502+
_, memory_exporter = tracer
503+
_, metrics_reader = meter
504+
monkeypatch.setenv(
505+
OTEL_SEMCONV_STABILITY_OPT_IN, _StabilityMode.DEFAULT.value
506+
)
507+
_OpenTelemetrySemanticConventionStability._initialized = False
508+
509+
AioHttpServerInstrumentor().instrument()
510+
app = aiohttp.web.Application()
511+
app.router.add_get("/test-path", default_handler)
512+
server = await aiohttp_server(app)
513+
client_session = aiohttp.ClientSession()
514+
try:
515+
url = f"http://{server.host}:{server.port}/test-path?query=test"
516+
async with client_session.get(
517+
url, headers={"User-Agent": "test-agent"}
518+
) as response:
519+
assert response.status == 200
520+
spans = memory_exporter.get_finished_spans()
521+
assert len(spans) == 1
522+
span = spans[0]
523+
524+
# Old semconv span attributes present
525+
assert span.attributes.get(HTTP_METHOD) == "GET"
526+
assert span.attributes.get(HTTP_SCHEME) == "http"
527+
assert span.attributes.get(NET_HOST_NAME) == server.host
528+
assert span.attributes.get(NET_HOST_PORT) == server.port
529+
assert span.attributes.get(HTTP_TARGET) == "/test-path?query=test"
530+
assert span.attributes.get(HTTP_USER_AGENT) == "test-agent"
531+
assert span.attributes.get(HTTP_FLAVOR) == "1.1"
532+
assert span.attributes.get(HTTP_STATUS_CODE) == 200
533+
# New semconv span attributes NOT present
534+
assert HTTP_REQUEST_METHOD not in span.attributes
535+
assert URL_SCHEME not in span.attributes
536+
assert SERVER_ADDRESS not in span.attributes
537+
assert SERVER_PORT not in span.attributes
538+
assert URL_PATH not in span.attributes
539+
assert URL_QUERY not in span.attributes
540+
assert USER_AGENT_ORIGINAL not in span.attributes
541+
assert NETWORK_PROTOCOL_VERSION not in span.attributes
542+
assert HTTP_RESPONSE_STATUS_CODE not in span.attributes
543+
544+
metrics = _get_sorted_metrics(metrics_reader.get_metrics_data())
545+
expected_metric_names = [
546+
"http.server.active_requests",
547+
"http.server.duration",
548+
]
549+
recommended_metrics_attrs = {
550+
"http.server.active_requests": _server_active_requests_count_attrs_old,
551+
"http.server.duration": _server_duration_attrs_old,
552+
}
553+
for metric in metrics:
554+
assert metric.name in expected_metric_names
555+
if metric.name == "http.server.duration":
556+
assert metric.unit == "ms"
557+
for point in metric.data.data_points:
558+
for attr in point.attributes:
559+
assert attr in recommended_metrics_attrs[metric.name]
560+
561+
finally:
562+
await client_session.close()
563+
AioHttpServerInstrumentor().uninstrument()
564+
565+
566+
@pytest.mark.asyncio
567+
async def test_semantic_conventions_metrics_new(
568+
tracer, meter, aiohttp_server, monkeypatch
569+
):
570+
_, memory_exporter = tracer
571+
_, metrics_reader = meter
572+
monkeypatch.setenv(
573+
OTEL_SEMCONV_STABILITY_OPT_IN, _StabilityMode.HTTP.value
574+
)
575+
_OpenTelemetrySemanticConventionStability._initialized = False
576+
577+
AioHttpServerInstrumentor().instrument()
578+
app = aiohttp.web.Application()
579+
app.router.add_get("/test-path", default_handler)
580+
server = await aiohttp_server(app)
581+
client_session = aiohttp.ClientSession()
582+
try:
583+
url = f"http://{server.host}:{server.port}/test-path?query=test"
584+
async with client_session.get(
585+
url, headers={"User-Agent": "test-agent"}
586+
) as response:
587+
assert response.status == 200
588+
spans = memory_exporter.get_finished_spans()
589+
assert len(spans) == 1
590+
span = spans[0]
591+
592+
# New semconv span attributes present
593+
assert span.attributes.get(HTTP_REQUEST_METHOD) == "GET"
594+
assert span.attributes.get(URL_SCHEME) == "http"
595+
assert span.attributes.get(SERVER_ADDRESS) == server.host
596+
assert span.attributes.get(SERVER_PORT) == server.port
597+
assert span.attributes.get(URL_PATH) == "/test-path"
598+
assert span.attributes.get(URL_QUERY) == "query=test"
599+
assert span.attributes.get(USER_AGENT_ORIGINAL) == "test-agent"
600+
assert span.attributes.get(NETWORK_PROTOCOL_VERSION) == "1.1"
601+
assert span.attributes.get(HTTP_RESPONSE_STATUS_CODE) == 200
602+
# Old semconv span attributes NOT present
603+
assert HTTP_METHOD not in span.attributes
604+
assert HTTP_SCHEME not in span.attributes
605+
assert NET_HOST_NAME not in span.attributes
606+
assert NET_HOST_PORT not in span.attributes
607+
assert HTTP_TARGET not in span.attributes
608+
assert HTTP_USER_AGENT not in span.attributes
609+
assert HTTP_FLAVOR not in span.attributes
610+
assert HTTP_STATUS_CODE not in span.attributes
611+
612+
metrics = _get_sorted_metrics(metrics_reader.get_metrics_data())
613+
expected_metric_names = [
614+
"http.server.active_requests",
615+
"http.server.request.duration",
616+
]
617+
recommended_metrics_attrs = {
618+
"http.server.active_requests": _server_active_requests_count_attrs_new,
619+
"http.server.request.duration": _server_duration_attrs_new,
620+
}
621+
for metric in metrics:
622+
assert metric.name in expected_metric_names
623+
if metric.name == "http.server.request.duration":
624+
assert metric.unit == "s"
625+
for point in metric.data.data_points:
626+
if (
627+
isinstance(point, HistogramDataPoint)
628+
and metric.name == "http.server.request.duration"
629+
):
630+
assert (
631+
point.explicit_bounds
632+
== HTTP_DURATION_HISTOGRAM_BUCKETS_NEW
633+
)
634+
for attr in point.attributes:
635+
assert attr in recommended_metrics_attrs[metric.name]
636+
637+
finally:
638+
await client_session.close()
639+
AioHttpServerInstrumentor().uninstrument()
640+
641+
642+
@pytest.mark.asyncio
643+
async def test_semantic_conventions_metrics_both(
644+
tracer, meter, aiohttp_server, monkeypatch
645+
):
646+
_, memory_exporter = tracer
647+
_, metrics_reader = meter
648+
monkeypatch.setenv(
649+
OTEL_SEMCONV_STABILITY_OPT_IN, _StabilityMode.HTTP_DUP.value
650+
)
651+
_OpenTelemetrySemanticConventionStability._initialized = False
652+
653+
AioHttpServerInstrumentor().instrument()
654+
app = aiohttp.web.Application()
655+
app.router.add_get("/test-path", default_handler)
656+
server = await aiohttp_server(app)
657+
client_session = aiohttp.ClientSession()
658+
try:
659+
url = f"http://{server.host}:{server.port}/test-path?query=test"
660+
async with client_session.get(
661+
url, headers={"User-Agent": "test-agent"}
662+
) as response:
663+
assert response.status == 200
664+
spans = memory_exporter.get_finished_spans()
665+
assert len(spans) == 1
666+
span = spans[0]
667+
668+
# Both old and new semconv span attributes present
669+
assert span.attributes.get(HTTP_METHOD) == "GET"
670+
assert span.attributes.get(HTTP_REQUEST_METHOD) == "GET"
671+
assert span.attributes.get(HTTP_SCHEME) == "http"
672+
assert span.attributes.get(URL_SCHEME) == "http"
673+
assert span.attributes.get(NET_HOST_NAME) == server.host
674+
assert span.attributes.get(SERVER_ADDRESS) == server.host
675+
assert span.attributes.get(NET_HOST_PORT) == server.port
676+
assert span.attributes.get(SERVER_PORT) == server.port
677+
assert span.attributes.get(HTTP_TARGET) == "/test-path?query=test"
678+
assert span.attributes.get(URL_PATH) == "/test-path"
679+
assert span.attributes.get(URL_QUERY) == "query=test"
680+
assert span.attributes.get(HTTP_USER_AGENT) == "test-agent"
681+
assert span.attributes.get(USER_AGENT_ORIGINAL) == "test-agent"
682+
assert span.attributes.get(HTTP_FLAVOR) == "1.1"
683+
assert span.attributes.get(NETWORK_PROTOCOL_VERSION) == "1.1"
684+
assert span.attributes.get(HTTP_STATUS_CODE) == 200
685+
assert span.attributes.get(HTTP_RESPONSE_STATUS_CODE) == 200
686+
687+
metrics = _get_sorted_metrics(metrics_reader.get_metrics_data())
688+
assert len(metrics) == 3 # Both duration metrics + active requests
689+
server_active_requests_count_attrs_both = list(
690+
_server_active_requests_count_attrs_old
691+
)
692+
server_active_requests_count_attrs_both.extend(
693+
_server_active_requests_count_attrs_new
694+
)
695+
recommended_metrics_attrs = {
696+
"http.server.active_requests": server_active_requests_count_attrs_both,
697+
"http.server.duration": _server_duration_attrs_old,
698+
"http.server.request.duration": _server_duration_attrs_new,
699+
}
700+
for metric in metrics:
701+
if metric.unit == "ms":
702+
assert metric.name == "http.server.duration"
703+
elif metric.unit == "s":
704+
assert metric.name == "http.server.request.duration"
705+
else:
706+
assert metric.name == "http.server.active_requests"
707+
708+
for point in metric.data.data_points:
709+
if (
710+
isinstance(point, HistogramDataPoint)
711+
and metric.name == "http.server.request.duration"
712+
):
713+
assert (
714+
point.explicit_bounds
715+
== HTTP_DURATION_HISTOGRAM_BUCKETS_NEW
716+
)
717+
for attr in point.attributes:
718+
assert attr in recommended_metrics_attrs[metric.name]
719+
720+
finally:
721+
await client_session.close()
722+
AioHttpServerInstrumentor().uninstrument()

0 commit comments

Comments
 (0)