diff --git a/instrumentation/opentelemetry-instrumentation-aiohttp-client/tests/test_aiohttp_client_integration.py b/instrumentation/opentelemetry-instrumentation-aiohttp-client/tests/test_aiohttp_client_integration.py index f9a7325ea..9c9dd3e85 100644 --- a/instrumentation/opentelemetry-instrumentation-aiohttp-client/tests/test_aiohttp_client_integration.py +++ b/instrumentation/opentelemetry-instrumentation-aiohttp-client/tests/test_aiohttp_client_integration.py @@ -91,6 +91,9 @@ async def do_request(): return loop.run_until_complete(do_request()) +SCOPE = "opentelemetry.instrumentation.aiohttp_client" + + # pylint: disable=too-many-public-methods class TestAioHttpIntegration(TestBase): _test_status_codes = ( @@ -122,7 +125,7 @@ def _assert_spans(self, spans, num_spans=1): ) def _assert_metrics(self, num_metrics: int = 1): - metrics = self.get_sorted_metrics() + metrics = self.get_sorted_metrics(SCOPE) self.assertEqual(len(metrics), num_metrics) return metrics @@ -917,7 +920,7 @@ def _assert_spans(self, num_spans: int): return finished_spans def _assert_metrics(self, num_metrics: int = 1): - metrics = self.get_sorted_metrics() + metrics = self.get_sorted_metrics(SCOPE) self.assertEqual(len(metrics), num_metrics) return metrics diff --git a/instrumentation/opentelemetry-instrumentation-aiohttp-server/tests/test_aiohttp_server_integration.py b/instrumentation/opentelemetry-instrumentation-aiohttp-server/tests/test_aiohttp_server_integration.py index 1528edf01..98867ae65 100644 --- a/instrumentation/opentelemetry-instrumentation-aiohttp-server/tests/test_aiohttp_server_integration.py +++ b/instrumentation/opentelemetry-instrumentation-aiohttp-server/tests/test_aiohttp_server_integration.py @@ -61,6 +61,19 @@ def __repr__(self): TRACE = "TRACE" +SCOPE = "opentelemetry.instrumentation.aiohttp_server" + + +@pytest.fixture(name="test_base", scope="function") +def fixture_test_base(): + test_base = TestBase() + test_base.setUp() + try: + yield test_base + finally: + test_base.tearDown() + + @pytest.fixture(name="tracer", scope="function") def fixture_tracer(): test_base = TestBase() @@ -149,15 +162,15 @@ async def test_status_code_instrumentation( _, 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(test_base.get_finished_spans()) == 0 + metrics = test_base.get_sorted_metrics(SCOPE) 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(test_base.get_finished_spans()) == 1 + metrics = test_base.get_sorted_metrics(SCOPE) assert len(metrics) == 2 [span] = memory_exporter.get_finished_spans() @@ -275,7 +288,7 @@ async def handler(request): spans = memory_exporter.get_finished_spans() assert len(spans) == 0 - metrics = _get_sorted_metrics(metrics_reader.get_metrics_data()) + metrics = test_base.get_sorted_metrics(SCOPE) assert len(metrics) == 0 AioHttpServerInstrumentor().uninstrument() @@ -400,3 +413,249 @@ async def handler(request): assert "http.response.header.custom_test_header_3" not in span.attributes AioHttpServerInstrumentor().uninstrument() + + +# pylint: disable=too-many-locals +@pytest.mark.asyncio +async def test_semantic_conventions_metrics_old_default( + test_base: TestBase, aiohttp_server, monkeypatch +): + monkeypatch.setenv( + OTEL_SEMCONV_STABILITY_OPT_IN, _StabilityMode.DEFAULT.value + ) + _OpenTelemetrySemanticConventionStability._initialized = False + + AioHttpServerInstrumentor().instrument() + app = aiohttp.web.Application() + app.router.add_get("/test-path", default_handler) + server = await aiohttp_server(app) + client_session = aiohttp.ClientSession() + try: + url = f"http://{server.host}:{server.port}/test-path?query=test" + async with client_session.get( # pylint: disable=not-async-context-manager + url, headers={"User-Agent": "test-agent"} + ) as response: + assert response.status == 200 + spans = test_base.get_finished_spans() + assert len(spans) == 1 + span = spans[0] + + # Verify old semconv schema URL + assert ( + span.instrumentation_scope.schema_url + == "https://opentelemetry.io/schemas/1.11.0" + ) + + # Old semconv span attributes present + assert span.attributes.get(HTTP_METHOD) == "GET" + assert span.attributes.get(HTTP_SCHEME) == "http" + assert span.attributes.get(NET_HOST_NAME) == server.host + assert span.attributes.get(NET_HOST_PORT) == server.port + assert span.attributes.get(HTTP_TARGET) == "/test-path?query=test" + assert span.attributes.get(HTTP_USER_AGENT) == "test-agent" + assert span.attributes.get(HTTP_FLAVOR) == "1.1" + assert span.attributes.get(HTTP_STATUS_CODE) == 200 + assert span.attributes.get(HTTP_ROUTE) == "default_handler" + # New semconv span attributes NOT present + assert HTTP_REQUEST_METHOD not in span.attributes + assert URL_SCHEME not in span.attributes + assert SERVER_ADDRESS not in span.attributes + assert SERVER_PORT not in span.attributes + assert URL_PATH not in span.attributes + assert URL_QUERY not in span.attributes + assert USER_AGENT_ORIGINAL not in span.attributes + assert NETWORK_PROTOCOL_VERSION not in span.attributes + assert HTTP_RESPONSE_STATUS_CODE not in span.attributes + + metrics = test_base.get_sorted_metrics(SCOPE) + expected_metric_names = [ + "http.server.active_requests", + "http.server.duration", + ] + recommended_metrics_attrs = { + "http.server.active_requests": _server_active_requests_count_attrs_old, + "http.server.duration": _server_duration_attrs_old, + } + for metric in metrics: + assert metric.name in expected_metric_names + if metric.name == "http.server.duration": + assert metric.unit == "ms" + for point in metric.data.data_points: + for attr in point.attributes: + assert attr in recommended_metrics_attrs[metric.name] + + finally: + await client_session.close() + AioHttpServerInstrumentor().uninstrument() + + +# pylint: disable=too-many-locals +@pytest.mark.asyncio +async def test_semantic_conventions_metrics_new( + test_base: TestBase, meter, aiohttp_server, monkeypatch +): + monkeypatch.setenv( + OTEL_SEMCONV_STABILITY_OPT_IN, _StabilityMode.HTTP.value + ) + _OpenTelemetrySemanticConventionStability._initialized = False + + AioHttpServerInstrumentor().instrument() + app = aiohttp.web.Application() + app.router.add_get("/test-path", default_handler) + server = await aiohttp_server(app) + client_session = aiohttp.ClientSession() + try: + url = f"http://{server.host}:{server.port}/test-path?query=test" + async with client_session.get( # pylint: disable=not-async-context-manager + url, headers={"User-Agent": "test-agent"} + ) as response: + assert response.status == 200 + spans = test_base.get_finished_spans() + assert len(spans) == 1 + span = spans[0] + + # Verify new semconv schema URL + assert ( + span.instrumentation_scope.schema_url + == "https://opentelemetry.io/schemas/1.21.0" + ) + + # New semconv span attributes present + assert span.attributes.get(HTTP_REQUEST_METHOD) == "GET" + assert span.attributes.get(URL_SCHEME) == "http" + assert span.attributes.get(SERVER_ADDRESS) == server.host + assert span.attributes.get(SERVER_PORT) == server.port + assert span.attributes.get(URL_PATH) == "/test-path" + assert span.attributes.get(URL_QUERY) == "query=test" + assert span.attributes.get(USER_AGENT_ORIGINAL) == "test-agent" + assert span.attributes.get(NETWORK_PROTOCOL_VERSION) == "1.1" + assert span.attributes.get(HTTP_RESPONSE_STATUS_CODE) == 200 + assert span.attributes.get(HTTP_ROUTE) == "default_handler" + # Old semconv span attributes NOT present + assert HTTP_METHOD not in span.attributes + assert HTTP_SCHEME not in span.attributes + assert NET_HOST_NAME not in span.attributes + assert NET_HOST_PORT not in span.attributes + assert HTTP_TARGET not in span.attributes + assert HTTP_USER_AGENT not in span.attributes + assert HTTP_FLAVOR not in span.attributes + assert HTTP_STATUS_CODE not in span.attributes + + metrics = test_base.get_sorted_metrics(SCOPE) + expected_metric_names = [ + "http.server.active_requests", + "http.server.request.duration", + ] + recommended_metrics_attrs = { + "http.server.active_requests": _server_active_requests_count_attrs_new, + "http.server.request.duration": _server_duration_attrs_new, + } + for metric in metrics: + assert metric.name in expected_metric_names + if metric.name == "http.server.request.duration": + assert metric.unit == "s" + for point in metric.data.data_points: + if ( + isinstance(point, HistogramDataPoint) + and metric.name == "http.server.request.duration" + ): + assert ( + point.explicit_bounds + == HTTP_DURATION_HISTOGRAM_BUCKETS_NEW + ) + for attr in point.attributes: + assert attr in recommended_metrics_attrs[metric.name] + + finally: + await client_session.close() + AioHttpServerInstrumentor().uninstrument() + + +# pylint: disable=too-many-locals +# pylint: disable=too-many-statements +@pytest.mark.asyncio +async def test_semantic_conventions_metrics_both( + test_base: TestBase, meter, aiohttp_server, monkeypatch +): + monkeypatch.setenv( + OTEL_SEMCONV_STABILITY_OPT_IN, _StabilityMode.HTTP_DUP.value + ) + _OpenTelemetrySemanticConventionStability._initialized = False + + AioHttpServerInstrumentor().instrument() + app = aiohttp.web.Application() + app.router.add_get("/test-path", default_handler) + server = await aiohttp_server(app) + client_session = aiohttp.ClientSession() + try: + url = f"http://{server.host}:{server.port}/test-path?query=test" + async with client_session.get( # pylint: disable=not-async-context-manager + url, headers={"User-Agent": "test-agent"} + ) as response: + assert response.status == 200 + spans = test_base.get_finished_spans() + assert len(spans) == 1 + span = spans[0] + + # Verify new semconv schema URL (both mode uses new schema) + assert ( + span.instrumentation_scope.schema_url + == "https://opentelemetry.io/schemas/1.21.0" + ) + + # Both old and new semconv span attributes present + assert span.attributes.get(HTTP_METHOD) == "GET" + assert span.attributes.get(HTTP_REQUEST_METHOD) == "GET" + assert span.attributes.get(HTTP_SCHEME) == "http" + assert span.attributes.get(URL_SCHEME) == "http" + assert span.attributes.get(NET_HOST_NAME) == server.host + assert span.attributes.get(SERVER_ADDRESS) == server.host + assert span.attributes.get(NET_HOST_PORT) == server.port + assert span.attributes.get(SERVER_PORT) == server.port + assert span.attributes.get(HTTP_TARGET) == "/test-path?query=test" + assert span.attributes.get(URL_PATH) == "/test-path" + assert span.attributes.get(URL_QUERY) == "query=test" + assert span.attributes.get(HTTP_USER_AGENT) == "test-agent" + assert span.attributes.get(USER_AGENT_ORIGINAL) == "test-agent" + assert span.attributes.get(HTTP_FLAVOR) == "1.1" + assert span.attributes.get(NETWORK_PROTOCOL_VERSION) == "1.1" + assert span.attributes.get(HTTP_STATUS_CODE) == 200 + assert span.attributes.get(HTTP_RESPONSE_STATUS_CODE) == 200 + assert span.attributes.get(HTTP_ROUTE) == "default_handler" + + metrics = test_base.get_sorted_metrics(SCOPE) + assert len(metrics) == 3 # Both duration metrics + active requests + server_active_requests_count_attrs_both = list( + _server_active_requests_count_attrs_old + ) + server_active_requests_count_attrs_both.extend( + _server_active_requests_count_attrs_new + ) + recommended_metrics_attrs = { + "http.server.active_requests": server_active_requests_count_attrs_both, + "http.server.duration": _server_duration_attrs_old, + "http.server.request.duration": _server_duration_attrs_new, + } + for metric in metrics: + if metric.unit == "ms": + assert metric.name == "http.server.duration" + elif metric.unit == "s": + assert metric.name == "http.server.request.duration" + else: + assert metric.name == "http.server.active_requests" + + for point in metric.data.data_points: + if ( + isinstance(point, HistogramDataPoint) + and metric.name == "http.server.request.duration" + ): + assert ( + point.explicit_bounds + == HTTP_DURATION_HISTOGRAM_BUCKETS_NEW + ) + for attr in point.attributes: + assert attr in recommended_metrics_attrs[metric.name] + + finally: + await client_session.close() + AioHttpServerInstrumentor().uninstrument() diff --git a/instrumentation/opentelemetry-instrumentation-asyncio/tests/test_asyncio_future_cancellation.py b/instrumentation/opentelemetry-instrumentation-asyncio/tests/test_asyncio_future_cancellation.py index f8f4e5f23..b3d4bbc22 100644 --- a/instrumentation/opentelemetry-instrumentation-asyncio/tests/test_asyncio_future_cancellation.py +++ b/instrumentation/opentelemetry-instrumentation-asyncio/tests/test_asyncio_future_cancellation.py @@ -8,6 +8,8 @@ from opentelemetry.test.test_base import TestBase from opentelemetry.trace import get_tracer +SCOPE = "opentelemetry.instrumentation.asyncio" + class TestTraceFuture(TestBase): @patch.dict( @@ -41,12 +43,7 @@ async def future_cancelled(): self.assertEqual(spans[0].name, "root") self.assertEqual(spans[1].name, "asyncio future") - metrics = ( - self.memory_metrics_reader.get_metrics_data() - .resource_metrics[0] - .scope_metrics[0] - .metrics - ) + metrics = self.get_sorted_metrics(SCOPE) self.assertEqual(len(metrics), 2) self.assertEqual(metrics[0].name, "asyncio.process.duration") diff --git a/instrumentation/opentelemetry-instrumentation-celery/tests/test_metrics.py b/instrumentation/opentelemetry-instrumentation-celery/tests/test_metrics.py index f83759317..28c7876d4 100644 --- a/instrumentation/opentelemetry-instrumentation-celery/tests/test_metrics.py +++ b/instrumentation/opentelemetry-instrumentation-celery/tests/test_metrics.py @@ -10,6 +10,8 @@ from .celery_test_tasks import app, task_add +SCOPE = "opentelemetry.instrumentation.celery" + class TestMetrics(TestBase): def setUp(self): @@ -34,7 +36,7 @@ def get_metrics(self): if time.time() > timeout: break time.sleep(0.05) - return self.get_sorted_metrics() + return self.get_sorted_metrics(SCOPE) def test_basic_metric(self): CeleryInstrumentor().instrument() diff --git a/instrumentation/opentelemetry-instrumentation-django/tests/test_middleware.py b/instrumentation/opentelemetry-instrumentation-django/tests/test_middleware.py index 960bf97bc..ef336ee9b 100644 --- a/instrumentation/opentelemetry-instrumentation-django/tests/test_middleware.py +++ b/instrumentation/opentelemetry-instrumentation-django/tests/test_middleware.py @@ -99,6 +99,8 @@ def path(path_argument, *args, **kwargs): ] _django_instrumentor = DjangoInstrumentor() +SCOPE = "opentelemetry.instrumentation.django" + # pylint: disable=too-many-public-methods class TestMiddleware(WsgiTestBase): @@ -737,7 +739,7 @@ def test_wsgi_metrics(self): response = Client().get("/span_name/1234/") self.assertEqual(response.status_code, 200) duration = max(round((default_timer() - start) * 1000), 0) - metrics_list = self.memory_metrics_reader.get_metrics_data() + metrics = self.get_sorted_metrics(SCOPE) number_data_point_seen = False histrogram_data_point_seen = False @@ -792,7 +794,7 @@ def test_wsgi_metrics_new_semconv(self): response = Client().get("/span_name/1234/") self.assertEqual(response.status_code, 200) duration_s = default_timer() - start - metrics_list = self.memory_metrics_reader.get_metrics_data() + metrics = self.get_sorted_metrics(SCOPE) number_data_point_seen = False histrogram_data_point_seen = False @@ -867,7 +869,7 @@ def test_wsgi_metrics_both_semconv(self): self.assertEqual(response.status_code, 200) duration_s = max(default_timer() - start, 0) duration = max(round(duration_s * 1000), 0) - metrics_list = self.memory_metrics_reader.get_metrics_data() + metrics = self.get_sorted_metrics(SCOPE) number_data_point_seen = False histrogram_data_point_seen = False @@ -921,15 +923,13 @@ def test_wsgi_metrics_unistrument(self): Client().get("/span_name/1234/") _django_instrumentor.uninstrument() Client().get("/span_name/1234/") - metrics_list = self.memory_metrics_reader.get_metrics_data() - for resource_metric in metrics_list.resource_metrics: - for scope_metric in resource_metric.scope_metrics: - for metric in scope_metric.metrics: - for point in list(metric.data.data_points): - if isinstance(point, HistogramDataPoint): - self.assertEqual(1, point.count) - if isinstance(point, NumberDataPoint): - self.assertEqual(0, point.value) + metrics = self.get_sorted_metrics(SCOPE) + for metric in metrics: + for point in list(metric.data.data_points): + if isinstance(point, HistogramDataPoint): + self.assertEqual(1, point.count) + if isinstance(point, NumberDataPoint): + self.assertEqual(0, point.value) class TestMiddlewareWithTracerProvider(WsgiTestBase): diff --git a/instrumentation/opentelemetry-instrumentation-falcon/tests/test_falcon.py b/instrumentation/opentelemetry-instrumentation-falcon/tests/test_falcon.py index bf41fb020..188cb28f8 100644 --- a/instrumentation/opentelemetry-instrumentation-falcon/tests/test_falcon.py +++ b/instrumentation/opentelemetry-instrumentation-falcon/tests/test_falcon.py @@ -106,6 +106,9 @@ _parsed_falcon_version = package_version.parse(_falcon_version) +SCOPE = "opentelemetry.instrumentation.falcon" + + class TestFalconBase(TestBase): def setUp(self): super().setUp() @@ -483,26 +486,21 @@ def test_traced_not_recording(self): self.assertFalse(mock_span.set_attribute.called) self.assertFalse(mock_span.set_status.called) - metrics_list = self.memory_metrics_reader.get_metrics_data() - self.assertTrue(len(metrics_list.resource_metrics) != 0) - - for resource_metric in metrics_list.resource_metrics: - for scope_metric in resource_metric.scope_metrics: - for metric in scope_metric.metrics: - data_points = list(metric.data.data_points) - self.assertEqual(len(data_points), 1) - for point in list(metric.data.data_points): - if isinstance(point, HistogramDataPoint): - self.assertEqual(point.count, 1) - if isinstance(point, NumberDataPoint): - self.assertEqual(point.value, 0) - for attr in point.attributes: - self.assertIn( - attr, - _recommended_metrics_attrs_old[ - metric.name - ], - ) + metrics = self.get_sorted_metrics(SCOPE) + self.assertTrue(len(metrics) != 0) + for metric in metrics: + data_points = list(metric.data.data_points) + self.assertEqual(len(data_points), 1) + for point in list(metric.data.data_points): + if isinstance(point, HistogramDataPoint): + self.assertEqual(point.count, 1) + if isinstance(point, NumberDataPoint): + self.assertEqual(point.value, 0) + for attr in point.attributes: + self.assertIn( + attr, + _recommended_metrics_attrs_old[metric.name], + ) def test_uninstrument_after_instrument(self): self.client().simulate_get(path="/hello") @@ -520,7 +518,7 @@ def test_falcon_metrics(self): self.client().simulate_get("/hello/756") self.client().simulate_get("/hello/756") self.client().simulate_get("/hello/756") - metrics_list = self.memory_metrics_reader.get_metrics_data() + metrics = self.get_sorted_metrics(SCOPE) number_data_point_seen = False histogram_data_point_seen = False self.assertTrue(len(metrics_list.resource_metrics) != 0) @@ -552,31 +550,27 @@ def test_falcon_metric_values_new_semconv(self): self.client().simulate_get("/hello/756") duration = max(default_timer() - start, 0) - metrics_list = self.memory_metrics_reader.get_metrics_data() - for resource_metric in metrics_list.resource_metrics: - for scope_metric in resource_metric.scope_metrics: - for metric in scope_metric.metrics: - data_points = list(metric.data.data_points) - self.assertEqual(len(data_points), 1) - for point in data_points: - if isinstance(point, HistogramDataPoint): - self.assertEqual(point.count, 1) - histogram_data_point_seen = True - self.assertAlmostEqual( - duration, point.sum, delta=10 - ) - self.assertEqual( - point.explicit_bounds, - HTTP_DURATION_HISTOGRAM_BUCKETS_NEW, - ) - if isinstance(point, NumberDataPoint): - self.assertEqual(point.value, 0) - number_data_point_seen = True - for attr in point.attributes: - self.assertIn( - attr, - _recommended_metrics_attrs_new[metric.name], - ) + metrics = self.get_sorted_metrics(SCOPE) + for metric in metrics: + data_points = list(metric.data.data_points) + self.assertEqual(len(data_points), 1) + for point in data_points: + if isinstance(point, HistogramDataPoint): + self.assertEqual(point.count, 1) + histogram_data_point_seen = True + self.assertAlmostEqual(duration, point.sum, delta=10) + self.assertEqual( + point.explicit_bounds, + HTTP_DURATION_HISTOGRAM_BUCKETS_NEW, + ) + if isinstance(point, NumberDataPoint): + self.assertEqual(point.value, 0) + number_data_point_seen = True + for attr in point.attributes: + self.assertIn( + attr, + _recommended_metrics_attrs_new[metric.name], + ) self.assertTrue(number_data_point_seen and histogram_data_point_seen) @@ -588,7 +582,7 @@ def test_falcon_metric_values_both_semconv(self): self.client().simulate_get("/hello/756") duration_s = default_timer() - start - metrics_list = self.memory_metrics_reader.get_metrics_data() + metrics = self.get_sorted_metrics(SCOPE) # pylint: disable=too-many-nested-blocks for resource_metric in metrics_list.resource_metrics: @@ -643,27 +637,23 @@ def test_falcon_metric_values(self): self.client().simulate_get("/hello/756") duration = max(round((default_timer() - start) * 1000), 0) - metrics_list = self.memory_metrics_reader.get_metrics_data() - for resource_metric in metrics_list.resource_metrics: - for scope_metric in resource_metric.scope_metrics: - for metric in scope_metric.metrics: - data_points = list(metric.data.data_points) - self.assertEqual(len(data_points), 1) - for point in list(metric.data.data_points): - if isinstance(point, HistogramDataPoint): - self.assertEqual(point.count, 1) - histogram_data_point_seen = True - self.assertAlmostEqual( - duration, point.sum, delta=10 - ) - if isinstance(point, NumberDataPoint): - self.assertEqual(point.value, 0) - number_data_point_seen = True - for attr in point.attributes: - self.assertIn( - attr, - _recommended_metrics_attrs_old[metric.name], - ) + metrics = self.get_sorted_metrics(SCOPE) + for metric in metrics: + data_points = list(metric.data.data_points) + self.assertEqual(len(data_points), 1) + for point in list(metric.data.data_points): + if isinstance(point, HistogramDataPoint): + self.assertEqual(point.count, 1) + histogram_data_point_seen = True + self.assertAlmostEqual(duration, point.sum, delta=10) + if isinstance(point, NumberDataPoint): + self.assertEqual(point.value, 0) + number_data_point_seen = True + for attr in point.attributes: + self.assertIn( + attr, + _recommended_metrics_attrs_old[metric.name], + ) self.assertTrue(number_data_point_seen and histogram_data_point_seen) @@ -671,13 +661,11 @@ def test_metric_uninstrument(self): self.client().simulate_request(method="POST", path="/hello/756") FalconInstrumentor().uninstrument() self.client().simulate_request(method="POST", path="/hello/756") - metrics_list = self.memory_metrics_reader.get_metrics_data() - for resource_metric in metrics_list.resource_metrics: - for scope_metric in resource_metric.scope_metrics: - for metric in scope_metric.metrics: - for point in list(metric.data.data_points): - if isinstance(point, HistogramDataPoint): - self.assertEqual(point.count, 1) + metrics = self.get_sorted_metrics(SCOPE) + for metric in metrics: + for point in list(metric.data.data_points): + if isinstance(point, HistogramDataPoint): + self.assertEqual(point.count, 1) class TestFalconInstrumentationWithTracerProvider(TestBase): diff --git a/instrumentation/opentelemetry-instrumentation-flask/tests/test_programmatic.py b/instrumentation/opentelemetry-instrumentation-flask/tests/test_programmatic.py index 922f2c1eb..edb01e352 100644 --- a/instrumentation/opentelemetry-instrumentation-flask/tests/test_programmatic.py +++ b/instrumentation/opentelemetry-instrumentation-flask/tests/test_programmatic.py @@ -146,6 +146,8 @@ def expected_attributes_new(override_attributes): "http.server.request.duration": _server_duration_attrs_new_copy, } +SCOPE = "opentelemetry.instrumentation.flask" + # pylint: disable=too-many-public-methods class TestProgrammatic(InstrumentationTest, WsgiTestBase): @@ -497,7 +499,7 @@ def test_flask_metrics(self): self.client.get("/hello/321") self.client.get("/hello/756") duration = max(round((default_timer() - start) * 1000), 0) - metrics_list = self.memory_metrics_reader.get_metrics_data() + metrics = self.get_sorted_metrics(SCOPE) number_data_point_seen = False histogram_data_point_seen = False self.assertTrue(len(metrics_list.resource_metrics) != 0) @@ -531,7 +533,7 @@ def test_flask_metrics_new_semconv(self): self.client.get("/hello/321") self.client.get("/hello/756") duration_s = max(default_timer() - start, 0) - metrics_list = self.memory_metrics_reader.get_metrics_data() + metrics = self.get_sorted_metrics(SCOPE) number_data_point_seen = False histogram_data_point_seen = False self.assertTrue(len(metrics_list.resource_metrics) != 0) @@ -569,18 +571,14 @@ def test_flask_metric_values(self): self.client.post("/hello/756") self.client.post("/hello/756") duration = max(round((default_timer() - start) * 1000), 0) - metrics_list = self.memory_metrics_reader.get_metrics_data() - for resource_metric in metrics_list.resource_metrics: - for scope_metric in resource_metric.scope_metrics: - for metric in scope_metric.metrics: - for point in list(metric.data.data_points): - if isinstance(point, HistogramDataPoint): - self.assertEqual(point.count, 3) - self.assertAlmostEqual( - duration, point.sum, delta=10 - ) - if isinstance(point, NumberDataPoint): - self.assertEqual(point.value, 0) + metrics = self.get_sorted_metrics(SCOPE) + for metric in metrics: + for point in list(metric.data.data_points): + if isinstance(point, HistogramDataPoint): + self.assertEqual(point.count, 3) + self.assertAlmostEqual(duration, point.sum, delta=10) + if isinstance(point, NumberDataPoint): + self.assertEqual(point.value, 0) def _assert_basic_metric( self, @@ -589,28 +587,26 @@ def _assert_basic_metric( expected_histogram_explicit_bounds=None, ): # pylint: disable=too-many-nested-blocks - metrics_list = self.memory_metrics_reader.get_metrics_data() - for resource_metric in metrics_list.resource_metrics: - for scope_metrics in resource_metric.scope_metrics: - for metric in scope_metrics.metrics: - for point in list(metric.data.data_points): - if isinstance(point, HistogramDataPoint): - self.assertDictEqual( - expected_duration_attributes, - dict(point.attributes), - ) - if expected_histogram_explicit_bounds is not None: - self.assertEqual( - expected_histogram_explicit_bounds, - point.explicit_bounds, - ) - self.assertEqual(point.count, 1) - elif isinstance(point, NumberDataPoint): - self.assertDictEqual( - expected_requests_count_attributes, - dict(point.attributes), - ) - self.assertEqual(point.value, 0) + metrics = self.get_sorted_metrics(SCOPE) + for metric in metrics: + for point in list(metric.data.data_points): + if isinstance(point, HistogramDataPoint): + self.assertDictEqual( + expected_duration_attributes, + dict(point.attributes), + ) + if expected_histogram_explicit_bounds is not None: + self.assertEqual( + expected_histogram_explicit_bounds, + point.explicit_bounds, + ) + self.assertEqual(point.count, 1) + elif isinstance(point, NumberDataPoint): + self.assertDictEqual( + expected_requests_count_attributes, + dict(point.attributes), + ) + self.assertEqual(point.value, 0) def test_basic_metric_success(self): self.client.get("/hello/756") diff --git a/instrumentation/opentelemetry-instrumentation-httpx/tests/test_httpx_integration.py b/instrumentation/opentelemetry-instrumentation-httpx/tests/test_httpx_integration.py index 909ec3300..1cc02acc4 100644 --- a/instrumentation/opentelemetry-instrumentation-httpx/tests/test_httpx_integration.py +++ b/instrumentation/opentelemetry-instrumentation-httpx/tests/test_httpx_integration.py @@ -142,6 +142,8 @@ async def _async_no_update_request_hook(span: "Span", request: "RequestInfo"): # pylint: disable=too-many-public-methods +SCOPE = "opentelemetry.instrumentation.httpx" + # Using this wrapper class to have a base class for the tests while also not # angering pylint or mypy when calling methods not in the class when only @@ -205,7 +207,7 @@ def assert_span( return span_list def assert_metrics(self, num_metrics: int = 1): - metrics = self.get_sorted_metrics() + metrics = self.get_sorted_metrics(SCOPE) self.assertEqual(len(metrics), num_metrics) return metrics @@ -244,7 +246,7 @@ def test_basic(self): def test_basic_metrics(self): self.perform_request(self.URL) - metrics = self.get_sorted_metrics() + metrics = self.get_sorted_metrics(SCOPE) self.assertEqual(len(metrics), 1) duration_data_point = metrics[0].data.data_points[0] self.assertEqual(duration_data_point.count, 1) @@ -289,7 +291,7 @@ def test_nonstandard_http_method(self): span, opentelemetry.instrumentation.httpx ) # Validate metrics - metrics = self.get_sorted_metrics() + metrics = self.get_sorted_metrics(SCOPE) self.assertEqual(len(metrics), 1) duration_data_point = metrics[0].data.data_points[0] self.assertEqual(duration_data_point.count, 1) @@ -331,7 +333,7 @@ def test_nonstandard_http_method_new_semconv(self): span, opentelemetry.instrumentation.httpx ) # Validate metrics - metrics = self.get_sorted_metrics() + metrics = self.get_sorted_metrics(SCOPE) self.assertEqual(len(metrics), 1) duration_data_point = metrics[0].data.data_points[0] self.assertEqual(duration_data_point.count, 1) @@ -391,7 +393,7 @@ def test_basic_new_semconv(self): ) # Validate metrics - metrics = self.get_sorted_metrics() + metrics = self.get_sorted_metrics(SCOPE) self.assertEqual(len(metrics), 1) duration_data_point = metrics[0].data.data_points[0] self.assertEqual(duration_data_point.count, 1) @@ -448,7 +450,7 @@ def test_basic_both_semconv(self): ) # Validate metrics - metrics = self.get_sorted_metrics() + metrics = self.get_sorted_metrics(SCOPE) self.assertEqual(len(metrics), 2) # Old convention self.assertEqual( @@ -497,7 +499,7 @@ def test_not_foundbasic(self): trace.StatusCode.ERROR, ) # Validate metrics - metrics = self.get_sorted_metrics() + metrics = self.get_sorted_metrics(SCOPE) self.assertEqual(len(metrics), 1) duration_data_point = metrics[0].data.data_points[0] self.assertEqual( @@ -525,7 +527,7 @@ def test_not_foundbasic_new_semconv(self): trace.StatusCode.ERROR, ) # Validate metrics - metrics = self.get_sorted_metrics() + metrics = self.get_sorted_metrics(SCOPE) self.assertEqual(len(metrics), 1) duration_data_point = metrics[0].data.data_points[0] self.assertEqual( @@ -556,7 +558,7 @@ def test_not_foundbasic_both_semconv(self): trace.StatusCode.ERROR, ) # Validate metrics - metrics = self.get_sorted_metrics() + metrics = self.get_sorted_metrics(SCOPE) self.assertEqual(len(metrics), 2) # Old convention self.assertEqual( diff --git a/instrumentation/opentelemetry-instrumentation-pyramid/tests/test_automatic.py b/instrumentation/opentelemetry-instrumentation-pyramid/tests/test_automatic.py index b40cf3355..9e0479a8f 100644 --- a/instrumentation/opentelemetry-instrumentation-pyramid/tests/test_automatic.py +++ b/instrumentation/opentelemetry-instrumentation-pyramid/tests/test_automatic.py @@ -49,6 +49,8 @@ "http.server.duration": _server_duration_attrs_old, } +SCOPE = "opentelemetry.instrumentation.pyramid.callbacks" + class TestAutomatic(InstrumentationTest, WsgiTestBase): def setUp(self): @@ -180,7 +182,7 @@ def test_pyramid_metric(self): self.client.get("/hello/756") self.client.get("/hello/756") self.client.get("/hello/756") - metrics_list = self.memory_metrics_reader.get_metrics_data() + metrics_list = self.get_sorted_metrics(SCOPE) number_data_point_seen = False histogram_data_point_seen = False self.assertTrue(len(metrics_list.resource_metrics) == 1) @@ -225,10 +227,8 @@ def test_basic_metric_success(self): "http.flavor": "1.1", "http.server_name": "localhost", } - metrics_list = self.memory_metrics_reader.get_metrics_data() - for metric in ( - metrics_list.resource_metrics[0].scope_metrics[0].metrics - ): + metrics = self.get_sorted_metrics(SCOPE) + for metric in metrics: for point in list(metric.data.data_points): if isinstance(point, HistogramDataPoint): self.assertDictEqual( @@ -250,10 +250,8 @@ def test_metric_uninstrument(self): self.config = Configurator() self._common_initialization(self.config) self.client.get("/hello/756") - metrics_list = self.memory_metrics_reader.get_metrics_data() - for metric in ( - metrics_list.resource_metrics[0].scope_metrics[0].metrics - ): + metrics = self.get_sorted_metrics(SCOPE) + for metric in metrics: for point in list(metric.data.data_points): if isinstance(point, HistogramDataPoint): self.assertEqual(point.count, 1) @@ -409,6 +407,220 @@ def test_custom_response_header_not_added_in_internal_span(self): self.assertNotIn(key, span.attributes) +class _SemConvTestBase(InstrumentationTest, WsgiTestBase): + semconv_mode = _StabilityMode.DEFAULT + + def setUp(self): + super().setUp() + self.env_patch = patch.dict( + "os.environ", + {OTEL_SEMCONV_STABILITY_OPT_IN: self.semconv_mode.value}, + ) + _OpenTelemetrySemanticConventionStability._initialized = False + self.env_patch.start() + + PyramidInstrumentor().instrument() + self.config = Configurator() + self._common_initialization(self.config) + + def tearDown(self): + super().tearDown() + self.env_patch.stop() + with self.disable_logging(): + PyramidInstrumentor().uninstrument() + + def _verify_metric_names( + self, metrics, expected_names, not_expected_names=None + ): + metric_names = [] + for metric in metrics: + metric_names.append(metric.name) + if expected_names: + self.assertIn(metric.name, expected_names) + if not_expected_names: + self.assertNotIn(metric.name, not_expected_names) + return metric_names + + def _verify_duration_point(self, point): + self.assertIn(HTTP_REQUEST_METHOD, point.attributes) + self.assertIn(URL_SCHEME, point.attributes) + self.assertNotIn(HTTP_METHOD, point.attributes) + self.assertNotIn(HTTP_SCHEME, point.attributes) + + def _verify_metric_duration(self, metric): + if "duration" in metric.name: + for point in metric.data.data_points: + if isinstance(point, HistogramDataPoint): + self._verify_duration_point(point) + + def _verify_duration_attributes(self, metrics): + for metric in metrics: + self._verify_metric_duration(metric) + + +class TestSemConvDefault(_SemConvTestBase): + semconv_mode = _StabilityMode.DEFAULT + + def test_basic_old_semconv(self): + resp = self.client.get("/hello/123") + self.assertEqual(200, resp.status_code) + + span = self.memory_exporter.get_finished_spans()[0] + + old_attrs = { + HTTP_METHOD: "GET", + HTTP_SCHEME: "http", + HTTP_HOST: "localhost", + HTTP_TARGET: "/hello/123", + HTTP_URL: "http://localhost/hello/123", + NET_HOST_PORT: 80, + HTTP_STATUS_CODE: 200, + HTTP_FLAVOR: "1.1", + HTTP_ROUTE: "/hello/{helloid}", + } + for attr, value in old_attrs.items(): + self.assertEqual(span.attributes[attr], value) + + for attr in [SERVER_ADDRESS, SERVER_PORT, URL_SCHEME, URL_FULL]: + self.assertNotIn(attr, span.attributes) + + self.assertEqual( + span.instrumentation_scope.schema_url, + "https://opentelemetry.io/schemas/1.11.0", + ) + + def test_metrics_old_semconv(self): + self.client.get("/hello/123") + + metrics = self.get_sorted_metrics(SCOPE) + self.assertEqual(len(metrics), 2) + + expected_metrics = [ + HTTP_SERVER_ACTIVE_REQUESTS, + MetricInstruments.HTTP_SERVER_DURATION, + ] + self._verify_metric_names( + metrics, expected_metrics, [HTTP_SERVER_REQUEST_DURATION] + ) + + for metric in metrics: + for point in metric.data.data_points: + if isinstance(point, HistogramDataPoint): + self.assertIn("http.method", point.attributes) + self.assertIn("http.scheme", point.attributes) + self.assertNotIn(HTTP_REQUEST_METHOD, point.attributes) + + +class TestSemConvNew(_SemConvTestBase): + semconv_mode = _StabilityMode.HTTP + + def test_basic_new_semconv(self): + resp = self.client.get( + "/hello/456?query=test", headers={"User-Agent": "test-agent"} + ) + self.assertEqual(200, resp.status_code) + + span = self.memory_exporter.get_finished_spans()[0] + + new_attrs = { + HTTP_REQUEST_METHOD: "GET", + URL_SCHEME: "http", + URL_PATH: "/hello/456", + URL_QUERY: "query=test", + URL_FULL: "http://localhost/hello/456?query=test", + SERVER_ADDRESS: "localhost", + SERVER_PORT: 80, + HTTP_RESPONSE_STATUS_CODE: 200, + NETWORK_PROTOCOL_VERSION: "1.1", + HTTP_ROUTE: "/hello/{helloid}", + USER_AGENT_ORIGINAL: "test-agent", + } + for attr, value in new_attrs.items(): + self.assertEqual(span.attributes[attr], value) + + old_attrs = [ + HTTP_METHOD, + HTTP_HOST, + HTTP_TARGET, + HTTP_URL, + ] + for attr in old_attrs: + self.assertNotIn(attr, span.attributes) + + self.assertEqual( + span.instrumentation_scope.schema_url, + "https://opentelemetry.io/schemas/1.21.0", + ) + + def test_metrics_new_semconv(self): + self.client.get("/hello/456") + metrics = self.get_sorted_metrics(SCOPE) + self.assertEqual(len(metrics), 2) + + expected_metrics = [ + HTTP_SERVER_REQUEST_DURATION, + HTTP_SERVER_ACTIVE_REQUESTS, + ] + metric_names = self._verify_metric_names(metrics, expected_metrics) + + self.assertIn(HTTP_SERVER_REQUEST_DURATION, metric_names) + self.assertIn(HTTP_SERVER_ACTIVE_REQUESTS, metric_names) + + self._verify_duration_attributes(metrics) + + +class TestSemConvDup(_SemConvTestBase): + semconv_mode = _StabilityMode.HTTP_DUP + + def test_basic_both_semconv(self): + resp = self.client.get( + "/hello/789?query=test", headers={"User-Agent": "test-agent"} + ) + self.assertEqual(200, resp.status_code) + + span = self.memory_exporter.get_finished_spans()[0] + + expected_attrs = { + HTTP_METHOD: "GET", + HTTP_SCHEME: "http", + HTTP_HOST: "localhost", + HTTP_URL: "http://localhost/hello/789?query=test", + HTTP_STATUS_CODE: 200, + HTTP_REQUEST_METHOD: "GET", + URL_SCHEME: "http", + URL_PATH: "/hello/789", + URL_QUERY: "query=test", + URL_FULL: "http://localhost/hello/789?query=test", + SERVER_ADDRESS: "localhost", + HTTP_RESPONSE_STATUS_CODE: 200, + HTTP_ROUTE: "/hello/{helloid}", + USER_AGENT_ORIGINAL: "test-agent", + } + for attr, value in expected_attrs.items(): + self.assertEqual(span.attributes[attr], value) + + self.assertEqual( + span.instrumentation_scope.schema_url, + "https://opentelemetry.io/schemas/1.21.0", + ) + + def test_metrics_both_semconv(self): + self.client.get("/hello/789") + + metrics = self.get_sorted_metrics(SCOPE) + self.assertEqual(len(metrics), 3) + + expected_metrics = [ + MetricInstruments.HTTP_SERVER_DURATION, + HTTP_SERVER_REQUEST_DURATION, + HTTP_SERVER_ACTIVE_REQUESTS, + ] + metric_names = self._verify_metric_names(metrics, None) + + for metric_name in expected_metrics: + self.assertIn(metric_name, metric_names) + + @patch.dict( "os.environ", { diff --git a/instrumentation/opentelemetry-instrumentation-requests/tests/test_requests_integration.py b/instrumentation/opentelemetry-instrumentation-requests/tests/test_requests_integration.py index ac9c0529f..ae152ec56 100644 --- a/instrumentation/opentelemetry-instrumentation-requests/tests/test_requests_integration.py +++ b/instrumentation/opentelemetry-instrumentation-requests/tests/test_requests_integration.py @@ -95,6 +95,9 @@ def __init__(self): self.response = {} +SCOPE = "opentelemetry.instrumentation.requests" + + class RequestsIntegrationTestBase(abc.ABC): # pylint: disable=no-member # pylint: disable=too-many-public-methods @@ -780,26 +783,23 @@ def test_basic_metric_success(self): HTTP_SCHEME: "http", } - for ( - resource_metrics - ) in self.memory_metrics_reader.get_metrics_data().resource_metrics: - for scope_metrics in resource_metrics.scope_metrics: - self.assertEqual(len(scope_metrics.metrics), 1) - for metric in scope_metrics.metrics: - self.assertEqual(metric.unit, "ms") - self.assertEqual( - metric.description, - "measures the duration of the outbound HTTP request", - ) - for data_point in metric.data.data_points: - self.assertEqual( - data_point.explicit_bounds, - HTTP_DURATION_HISTOGRAM_BUCKETS_OLD, - ) - self.assertDictEqual( - expected_attributes, dict(data_point.attributes) - ) - self.assertEqual(data_point.count, 1) + metrics = self.get_sorted_metrics(SCOPE) + self.assertEqual(len(metrics), 1) + for metric in metrics: + self.assertEqual(metric.unit, "ms") + self.assertEqual( + metric.description, + "measures the duration of the outbound HTTP request", + ) + for data_point in metric.data.data_points: + self.assertEqual( + data_point.explicit_bounds, + HTTP_DURATION_HISTOGRAM_BUCKETS_OLD, + ) + self.assertDictEqual( + expected_attributes, dict(data_point.attributes) + ) + self.assertEqual(data_point.count, 1) def test_basic_metric_new_semconv(self): self.perform_request(self.URL) @@ -811,25 +811,22 @@ def test_basic_metric_new_semconv(self): HTTP_REQUEST_METHOD: "GET", NETWORK_PROTOCOL_VERSION: "1.1", } - for ( - resource_metrics - ) in self.memory_metrics_reader.get_metrics_data().resource_metrics: - for scope_metrics in resource_metrics.scope_metrics: - self.assertEqual(len(scope_metrics.metrics), 1) - for metric in scope_metrics.metrics: - self.assertEqual(metric.unit, "s") - self.assertEqual( - metric.description, "Duration of HTTP client requests." - ) - for data_point in metric.data.data_points: - self.assertEqual( - data_point.explicit_bounds, - HTTP_DURATION_HISTOGRAM_BUCKETS_NEW, - ) - self.assertDictEqual( - expected_attributes, dict(data_point.attributes) - ) - self.assertEqual(data_point.count, 1) + metrics = self.get_sorted_metrics(SCOPE) + self.assertEqual(len(metrics), 1) + for metric in metrics: + self.assertEqual(metric.unit, "s") + self.assertEqual( + metric.description, "Duration of HTTP client requests." + ) + for data_point in metric.data.data_points: + self.assertEqual( + data_point.explicit_bounds, + HTTP_DURATION_HISTOGRAM_BUCKETS_NEW, + ) + self.assertDictEqual( + expected_attributes, dict(data_point.attributes) + ) + self.assertEqual(data_point.count, 1) def test_basic_metric_both_semconv(self): self.perform_request(self.URL) @@ -852,24 +849,21 @@ def test_basic_metric_both_semconv(self): NETWORK_PROTOCOL_VERSION: "1.1", } - for ( - resource_metrics - ) in self.memory_metrics_reader.get_metrics_data().resource_metrics: - for scope_metrics in resource_metrics.scope_metrics: - self.assertEqual(len(scope_metrics.metrics), 2) - for metric in scope_metrics.metrics: - for data_point in metric.data.data_points: - if metric.unit == "ms": - self.assertDictEqual( - expected_attributes_old, - dict(data_point.attributes), - ) - else: - self.assertDictEqual( - expected_attributes_new, - dict(data_point.attributes), - ) - self.assertEqual(data_point.count, 1) + metrics = self.get_sorted_metrics(SCOPE) + self.assertEqual(len(metrics), 2) + for metric in metrics: + for data_point in metric.data.data_points: + if metric.unit == "ms": + self.assertDictEqual( + expected_attributes_old, + dict(data_point.attributes), + ) + else: + self.assertDictEqual( + expected_attributes_new, + dict(data_point.attributes), + ) + self.assertEqual(data_point.count, 1) def test_custom_histogram_boundaries(self): RequestsInstrumentor().uninstrument() @@ -926,7 +920,7 @@ def test_basic_metric_non_recording_span(self): self.assertTrue(mock_span.is_recording.called) self.assertFalse(mock_span.set_attribute.called) self.assertFalse(mock_span.set_status.called) - metrics = self.get_sorted_metrics() + metrics = self.get_sorted_metrics(SCOPE) self.assertEqual(len(metrics), 1) duration_data_point = metrics[0].data.data_points[0] self.assertDictEqual( diff --git a/instrumentation/opentelemetry-instrumentation-sqlalchemy/tests/test_sqlalchemy_metrics.py b/instrumentation/opentelemetry-instrumentation-sqlalchemy/tests/test_sqlalchemy_metrics.py index 8d8995942..d8ccaeaf1 100644 --- a/instrumentation/opentelemetry-instrumentation-sqlalchemy/tests/test_sqlalchemy_metrics.py +++ b/instrumentation/opentelemetry-instrumentation-sqlalchemy/tests/test_sqlalchemy_metrics.py @@ -18,6 +18,8 @@ from opentelemetry.instrumentation.sqlalchemy import SQLAlchemyInstrumentor from opentelemetry.test.test_base import TestBase +SCOPE = "opentelemetry.instrumentation.sqlalchemy" + class TestSqlalchemyMetricsInstrumentation(TestBase): def setUp(self): @@ -31,7 +33,7 @@ def tearDown(self): SQLAlchemyInstrumentor().uninstrument() def assert_pool_idle_used_expected(self, pool_name, idle, used): - metrics = self.get_sorted_metrics() + metrics = self.get_sorted_metrics(SCOPE) self.assertEqual(len(metrics), 1) self.assert_metric_expected( metrics[0], @@ -56,7 +58,7 @@ def test_metrics_one_connection(self): pool_logging_name=pool_name, ) - self.assertIsNone(self.memory_metrics_reader.get_metrics_data()) + self.assertEqual(len(self.get_sorted_metrics(SCOPE)), 0) with engine.connect(): self.assert_pool_idle_used_expected( @@ -77,7 +79,7 @@ def test_metrics_without_pool_name(self): pool_logging_name=pool_name, ) - self.assertIsNone(self.memory_metrics_reader.get_metrics_data()) + self.assertEqual(len(self.get_sorted_metrics(SCOPE)), 0) with engine.connect(): self.assert_pool_idle_used_expected( @@ -98,7 +100,7 @@ def test_metrics_two_connections(self): pool_logging_name=pool_name, ) - self.assertIsNone(self.memory_metrics_reader.get_metrics_data()) + self.assertEqual(len(self.get_sorted_metrics(SCOPE)), 0) with engine.connect(): with engine.connect(): @@ -119,7 +121,7 @@ def test_metrics_connections(self): pool_logging_name=pool_name, ) - self.assertIsNone(self.memory_metrics_reader.get_metrics_data()) + self.assertEqual(len(self.get_sorted_metrics(SCOPE)), 0) with engine.connect(): with engine.connect(): @@ -152,4 +154,4 @@ def test_metric_uninstrument(self): engine.connect() - self.assertIsNone(self.memory_metrics_reader.get_metrics_data()) + self.assertEqual(len(self.get_sorted_metrics(SCOPE)), 0) diff --git a/instrumentation/opentelemetry-instrumentation-starlette/tests/test_starlette_instrumentation.py b/instrumentation/opentelemetry-instrumentation-starlette/tests/test_starlette_instrumentation.py index 8c326e83b..1e547ed1f 100644 --- a/instrumentation/opentelemetry-instrumentation-starlette/tests/test_starlette_instrumentation.py +++ b/instrumentation/opentelemetry-instrumentation-starlette/tests/test_starlette_instrumentation.py @@ -64,6 +64,8 @@ "http.server.request.size": _duration_attrs, } +SCOPE = "opentelemetry.instrumentation.starlette" + class TestStarletteManualInstrumentation(TestBase): def _create_app(self): @@ -183,29 +185,20 @@ def test_starlette_metrics(self): metrics_list = self.memory_metrics_reader.get_metrics_data() number_data_point_seen = False histogram_data_point_seen = False - self.assertTrue(len(metrics_list.resource_metrics) == 1) - for resource_metric in metrics_list.resource_metrics: - self.assertTrue(len(resource_metric.scope_metrics) == 1) - for scope_metric in resource_metric.scope_metrics: - self.assertEqual( - scope_metric.scope.name, - "opentelemetry.instrumentation.starlette", - ) - self.assertTrue(len(scope_metric.metrics) == 3) - for metric in scope_metric.metrics: - self.assertIn(metric.name, _expected_metric_names) - data_points = list(metric.data.data_points) - self.assertEqual(len(data_points), 1) - for point in data_points: - if isinstance(point, HistogramDataPoint): - self.assertEqual(point.count, 3) - histogram_data_point_seen = True - if isinstance(point, NumberDataPoint): - number_data_point_seen = True - for attr in point.attributes: - self.assertIn( - attr, _recommended_attrs[metric.name] - ) + metrics = self.get_sorted_metrics(SCOPE) + self.assertTrue(len(metrics) == 3) + for metric in metrics: + self.assertIn(metric.name, _expected_metric_names) + data_points = list(metric.data.data_points) + self.assertEqual(len(data_points), 1) + for point in data_points: + if isinstance(point, HistogramDataPoint): + self.assertEqual(point.count, 3) + histogram_data_point_seen = True + if isinstance(point, NumberDataPoint): + number_data_point_seen = True + for attr in point.attributes: + self.assertIn(attr, _recommended_attrs[metric.name]) self.assertTrue(number_data_point_seen and histogram_data_point_seen) def test_basic_post_request_metric_success(self): @@ -233,10 +226,8 @@ def test_basic_post_request_metric_success(self): duration = max(round((default_timer() - start) * 1000), 0) response_size = int(response.headers.get("content-length")) request_size = int(response.request.headers.get("content-length")) - metrics_list = self.memory_metrics_reader.get_metrics_data() - for metric in ( - metrics_list.resource_metrics[0].scope_metrics[0].metrics - ): + metrics = self.get_sorted_metrics(SCOPE) + for metric in metrics: for point in list(metric.data.data_points): if isinstance(point, HistogramDataPoint): self.assertEqual(point.count, 1) @@ -262,10 +253,8 @@ def test_metric_for_uninstrment_app_method(self): self._instrumentor.uninstrument_app(self._app) self._client.get("/foobar") self._client.get("/foobar") - metrics_list = self.memory_metrics_reader.get_metrics_data() - for metric in ( - metrics_list.resource_metrics[0].scope_metrics[0].metrics - ): + metrics = self.get_sorted_metrics(SCOPE) + for metric in metrics: for point in list(metric.data.data_points): if isinstance(point, HistogramDataPoint): self.assertEqual(point.count, 1) @@ -283,10 +272,8 @@ def test_metric_uninstrument_inherited_by_base(self): client.get("/foobar") client.get("/foobar") client.get("/foobar") - metrics_list = self.memory_metrics_reader.get_metrics_data() - for metric in ( - metrics_list.resource_metrics[0].scope_metrics[0].metrics - ): + metrics = self.get_sorted_metrics(SCOPE) + for metric in metrics: for point in list(metric.data.data_points): if isinstance(point, HistogramDataPoint): self.assertEqual(point.count, 1) diff --git a/instrumentation/opentelemetry-instrumentation-tornado/tests/test_metrics_instrumentation.py b/instrumentation/opentelemetry-instrumentation-tornado/tests/test_metrics_instrumentation.py index 859c38a05..ffdbc6184 100644 --- a/instrumentation/opentelemetry-instrumentation-tornado/tests/test_metrics_instrumentation.py +++ b/instrumentation/opentelemetry-instrumentation-tornado/tests/test_metrics_instrumentation.py @@ -25,6 +25,8 @@ TornadoTest, ) +SCOPE = "opentelemetry.instrumentation.tornado" + class TestTornadoMetricsInstrumentation(TornadoTest): # Return Sequence with one histogram @@ -40,7 +42,7 @@ def test_basic_metrics(self): response = self.fetch("/") client_duration_estimated = (default_timer() - start_time) * 1000 - metrics = self.get_sorted_metrics() + metrics = self.get_sorted_metrics(SCOPE) self.assertEqual(len(metrics), 7) ( @@ -177,7 +179,7 @@ async def test_metrics_concurrent_requests(self): req2 = self.http_client.fetch(self.get_url("/async")) await asyncio.gather(req1, req2) - metrics = self.get_sorted_metrics() + metrics = self.get_sorted_metrics(SCOPE) self.assertEqual(len(metrics), 7) client_duration = metrics[0] @@ -245,7 +247,7 @@ def test_metric_uninstrument(self): TornadoInstrumentor().uninstrument() self.fetch("/") - metrics = self.get_sorted_metrics() + metrics = self.get_sorted_metrics(SCOPE) self.assertEqual(len(metrics), 7) for metric in metrics: @@ -258,10 +260,345 @@ def test_excluded(path): self.fetch(path) # Verify no server metrics written (only client ones should exist) - metrics = self.get_sorted_metrics() + metrics = self.get_sorted_metrics(SCOPE) for metric in metrics: self.assertTrue("http.server" not in metric.name, metric) self.assertEqual(len(metrics), 3, metrics) test_excluded("/healthz") test_excluded("/ping") + + +class TornadoSemconvTestBase(AsyncHTTPTestCase, TestBase): + def get_app(self): + tracer = trace.get_tracer(__name__) + app = make_app(tracer) + return app + + def tearDown(self): + TornadoInstrumentor().uninstrument() + super().tearDown() + + @staticmethod + def _get_server_span(spans): + for span in spans: + if span.kind == SpanKind.SERVER: + return span + return None + + @staticmethod + def _get_client_span(spans): + for span in spans: + if span.kind == SpanKind.CLIENT: + return span + return None + + +class TestTornadoSemconvDefault(TornadoSemconvTestBase): + def setUp(self): + super().setUp() + _OpenTelemetrySemanticConventionStability._initialized = False + TornadoInstrumentor().instrument() + + def test_server_span_attributes_old_semconv(self): + response = self.fetch("/") + self.assertEqual(response.code, 201) + spans = self.memory_exporter.get_finished_spans() + server_span = self._get_server_span(spans) + self.assertIsNotNone(server_span) + + # Verify old semconv attributes are present + self.assertIn(HTTP_METHOD, server_span.attributes) + self.assertIn(HTTP_SCHEME, server_span.attributes) + self.assertIn(HTTP_HOST, server_span.attributes) + self.assertIn(HTTP_TARGET, server_span.attributes) + self.assertIn(HTTP_STATUS_CODE, server_span.attributes) + self.assertIn(HTTP_URL, server_span.attributes) + self.assertIn(HTTP_USER_AGENT, server_span.attributes) + # Verify new semconv attributes are NOT present + self.assertNotIn(HTTP_REQUEST_METHOD, server_span.attributes) + self.assertNotIn(URL_SCHEME, server_span.attributes) + self.assertNotIn(URL_PATH, server_span.attributes) + self.assertNotIn(HTTP_RESPONSE_STATUS_CODE, server_span.attributes) + self.assertNotIn(URL_FULL, server_span.attributes) + self.assertNotIn(USER_AGENT_ORIGINAL, server_span.attributes) + # Verify schema URL + self.assertEqual( + server_span.instrumentation_scope.schema_url, + "https://opentelemetry.io/schemas/1.11.0", + ) + + def test_client_span_attributes_old_semconv(self): + response = self.fetch("/") + self.assertEqual(response.code, 201) + spans = self.memory_exporter.get_finished_spans() + client_span = self._get_client_span(spans) + self.assertIsNotNone(client_span) + + # Verify old semconv attributes are present + self.assertIn(HTTP_METHOD, client_span.attributes) + self.assertIn(HTTP_URL, client_span.attributes) + self.assertIn(HTTP_STATUS_CODE, client_span.attributes) + # Verify new semconv attributes are NOT present + self.assertNotIn(HTTP_REQUEST_METHOD, client_span.attributes) + self.assertNotIn(URL_FULL, client_span.attributes) + self.assertNotIn(HTTP_RESPONSE_STATUS_CODE, client_span.attributes) + + def test_server_metrics_old_semconv(self): + """Test that server metrics use old semantic conventions by default.""" + response = self.fetch("/") + self.assertEqual(response.code, 201) + metrics = self.get_sorted_metrics(SCOPE) + + # Find old semconv metrics + old_duration_found = False + new_duration_found = False + for metric in metrics: + if metric.name == "http.server.duration": + old_duration_found = True + # Verify unit is milliseconds for old semconv + self.assertEqual(metric.unit, "ms") + elif metric.name == "http.server.request.duration": + new_duration_found = True + self.assertTrue(old_duration_found, "Old semconv metric not found") + self.assertFalse( + new_duration_found, "New semconv metric should not be present" + ) + + +class TestTornadoSemconvHttpNew(TornadoSemconvTestBase): + def setUp(self): + super().setUp() + _OpenTelemetrySemanticConventionStability._initialized = False + with patch.dict("os.environ", {OTEL_SEMCONV_STABILITY_OPT_IN: "http"}): + TornadoInstrumentor().instrument() + + def test_server_span_attributes_new_semconv(self): + response = self.fetch("/") + self.assertEqual(response.code, 201) + spans = self.memory_exporter.get_finished_spans() + server_span = self._get_server_span(spans) + self.assertIsNotNone(server_span) + + # Verify new semconv attributes are present + self.assertIn(HTTP_REQUEST_METHOD, server_span.attributes) + self.assertIn(URL_SCHEME, server_span.attributes) + self.assertIn(HTTP_RESPONSE_STATUS_CODE, server_span.attributes) + self.assertIn(URL_FULL, server_span.attributes) + self.assertIn(USER_AGENT_ORIGINAL, server_span.attributes) + self.assertIn(URL_PATH, server_span.attributes) + # URL_QUERY should not be present for requests without query strings + self.assertNotIn(URL_QUERY, server_span.attributes) + # Verify old semconv attributes are NOT present + self.assertNotIn(HTTP_METHOD, server_span.attributes) + self.assertNotIn(HTTP_SCHEME, server_span.attributes) + self.assertNotIn(HTTP_TARGET, server_span.attributes) + self.assertNotIn(HTTP_STATUS_CODE, server_span.attributes) + self.assertNotIn(HTTP_URL, server_span.attributes) + self.assertNotIn(HTTP_USER_AGENT, server_span.attributes) + # Verify schema URL + self.assertEqual( + server_span.instrumentation_scope.schema_url, + "https://opentelemetry.io/schemas/1.21.0", + ) + + def test_client_span_attributes_new_semconv(self): + """Test that client spans use new semantic conventions in http mode.""" + response = self.fetch("/") + self.assertEqual(response.code, 201) + spans = self.memory_exporter.get_finished_spans() + client_span = self._get_client_span(spans) + self.assertIsNotNone(client_span) + + # Verify new semconv attributes are present + self.assertIn(HTTP_REQUEST_METHOD, client_span.attributes) + self.assertIn(URL_FULL, client_span.attributes) + self.assertIn(HTTP_RESPONSE_STATUS_CODE, client_span.attributes) + # Verify old semconv attributes are NOT present + self.assertNotIn(HTTP_METHOD, client_span.attributes) + self.assertNotIn(HTTP_URL, client_span.attributes) + self.assertNotIn(HTTP_STATUS_CODE, client_span.attributes) + + def test_server_metrics_new_semconv(self): + """Test that server metrics use new semantic conventions in http mode.""" + response = self.fetch("/") + self.assertEqual(response.code, 201) + metrics = self.get_sorted_metrics(SCOPE) + + # Find new semconv metrics + old_duration_found = False + new_duration_found = False + for metric in metrics: + if metric.name == "http.server.duration": + old_duration_found = True + elif metric.name == "http.server.request.duration": + new_duration_found = True + # Verify unit is seconds for new semconv + self.assertEqual(metric.unit, "s") + self.assertFalse( + old_duration_found, "Old semconv metric should not be present" + ) + self.assertTrue(new_duration_found, "New semconv metric not found") + + def test_url_query_attribute_new_semconv(self): + """Test that URL_QUERY is set when request has query string.""" + response = self.fetch("/?foo=bar&baz=qux") + self.assertEqual(response.code, 201) + spans = self.memory_exporter.get_finished_spans() + server_span = self._get_server_span(spans) + self.assertIsNotNone(server_span) + + # Verify URL_QUERY is present when there's a query string + self.assertIn(URL_QUERY, server_span.attributes) + self.assertEqual(server_span.attributes[URL_QUERY], "foo=bar&baz=qux") + # Verify URL_PATH is also present + self.assertIn(URL_PATH, server_span.attributes) + self.assertEqual(server_span.attributes[URL_PATH], "/") + + +class TestTornadoSemconvHttpDup(TornadoSemconvTestBase): + def setUp(self): + super().setUp() + _OpenTelemetrySemanticConventionStability._initialized = False + with patch.dict( + "os.environ", {OTEL_SEMCONV_STABILITY_OPT_IN: "http/dup"} + ): + TornadoInstrumentor().instrument() + + def test_server_span_attributes_both_semconv(self): + response = self.fetch("/") + self.assertEqual(response.code, 201) + spans = self.memory_exporter.get_finished_spans() + server_span = self._get_server_span(spans) + self.assertIsNotNone(server_span) + + # Verify old semconv attributes are present + self.assertIn(HTTP_METHOD, server_span.attributes) + self.assertIn(HTTP_SCHEME, server_span.attributes) + self.assertIn(HTTP_HOST, server_span.attributes) + self.assertIn(HTTP_TARGET, server_span.attributes) + self.assertIn(HTTP_STATUS_CODE, server_span.attributes) + self.assertIn(HTTP_URL, server_span.attributes) + self.assertIn(HTTP_USER_AGENT, server_span.attributes) + # Verify new semconv attributes are also present + self.assertIn(HTTP_REQUEST_METHOD, server_span.attributes) + self.assertIn(URL_SCHEME, server_span.attributes) + self.assertIn(HTTP_RESPONSE_STATUS_CODE, server_span.attributes) + self.assertIn(URL_FULL, server_span.attributes) + self.assertIn(USER_AGENT_ORIGINAL, server_span.attributes) + self.assertIn(URL_PATH, server_span.attributes) + # URL_QUERY should not be present for requests without query strings + self.assertNotIn(URL_QUERY, server_span.attributes) + # Verify values match between old and new + self.assertEqual( + server_span.attributes[HTTP_METHOD], + server_span.attributes[HTTP_REQUEST_METHOD], + ) + self.assertEqual( + server_span.attributes[HTTP_STATUS_CODE], + server_span.attributes[HTTP_RESPONSE_STATUS_CODE], + ) + self.assertEqual( + server_span.attributes[HTTP_SCHEME], + server_span.attributes[URL_SCHEME], + ) + self.assertEqual( + server_span.attributes[HTTP_URL], + server_span.attributes[URL_FULL], + ) + self.assertEqual( + server_span.attributes[HTTP_USER_AGENT], + server_span.attributes[USER_AGENT_ORIGINAL], + ) + # Verify schema URL (in dup mode, schema_url should be the new one) + self.assertEqual( + server_span.instrumentation_scope.schema_url, + "https://opentelemetry.io/schemas/1.21.0", + ) + + def test_client_span_attributes_both_semconv(self): + response = self.fetch("/") + self.assertEqual(response.code, 201) + spans = self.memory_exporter.get_finished_spans() + client_span = self._get_client_span(spans) + self.assertIsNotNone(client_span) + + # Verify old semconv attributes are present + self.assertIn(HTTP_METHOD, client_span.attributes) + self.assertIn(HTTP_URL, client_span.attributes) + self.assertIn(HTTP_STATUS_CODE, client_span.attributes) + # Verify new semconv attributes are also present + self.assertIn(HTTP_REQUEST_METHOD, client_span.attributes) + self.assertIn(URL_FULL, client_span.attributes) + self.assertIn(HTTP_RESPONSE_STATUS_CODE, client_span.attributes) + # Verify values match between old and new + self.assertEqual( + client_span.attributes[HTTP_METHOD], + client_span.attributes[HTTP_REQUEST_METHOD], + ) + self.assertEqual( + client_span.attributes[HTTP_STATUS_CODE], + client_span.attributes[HTTP_RESPONSE_STATUS_CODE], + ) + + def test_server_metrics_both_semconv(self): + response = self.fetch("/") + self.assertEqual(response.code, 201) + metrics = self.get_sorted_metrics(SCOPE) + + # Find both old and new semconv metrics + old_duration_found = False + new_duration_found = False + for metric in metrics: + if metric.name == "http.server.duration": + old_duration_found = True + self.assertEqual(metric.unit, "ms") + elif metric.name == "http.server.request.duration": + new_duration_found = True + self.assertEqual(metric.unit, "s") + self.assertTrue(old_duration_found, "Old semconv metric not found") + self.assertTrue(new_duration_found, "New semconv metric not found") + + def test_url_query_attribute_both_semconv(self): + """Test that URL_QUERY is set in dup mode when request has query string.""" + response = self.fetch("/?test=value&another=param") + self.assertEqual(response.code, 201) + spans = self.memory_exporter.get_finished_spans() + server_span = self._get_server_span(spans) + self.assertIsNotNone(server_span) + + # Verify URL_QUERY is present in new semconv + self.assertIn(URL_QUERY, server_span.attributes) + self.assertEqual( + server_span.attributes[URL_QUERY], "test=value&another=param" + ) + # Verify URL_PATH is also present + self.assertIn(URL_PATH, server_span.attributes) + self.assertEqual(server_span.attributes[URL_PATH], "/") + # Verify HTTP_TARGET still contains the full target with query + self.assertIn(HTTP_TARGET, server_span.attributes) + self.assertEqual( + server_span.attributes[HTTP_TARGET], "/?test=value&another=param" + ) + + def test_client_metrics_both_semconv(self): + response = self.fetch("/") + self.assertEqual(response.code, 201) + metrics = self.get_sorted_metrics(SCOPE) + + # Find both old and new semconv metrics + old_duration_found = False + new_duration_found = False + for metric in metrics: + if metric.name == "http.client.duration": + old_duration_found = True + self.assertEqual(metric.unit, "ms") + elif metric.name == "http.client.request.duration": + new_duration_found = True + self.assertEqual(metric.unit, "s") + self.assertTrue( + old_duration_found, "Old semconv client metric not found" + ) + self.assertTrue( + new_duration_found, "New semconv client metric not found" + ) diff --git a/instrumentation/opentelemetry-instrumentation-urllib/tests/test_metrics_instrumentation.py b/instrumentation/opentelemetry-instrumentation-urllib/tests/test_metrics_instrumentation.py index ee9dd1db8..65b2b78cc 100644 --- a/instrumentation/opentelemetry-instrumentation-urllib/tests/test_metrics_instrumentation.py +++ b/instrumentation/opentelemetry-instrumentation-urllib/tests/test_metrics_instrumentation.py @@ -40,6 +40,8 @@ ) from opentelemetry.test.test_base import TestBase +SCOPE = "opentelemetry.instrumentation.urllib" + class TestUrllibMetricsInstrumentation(TestBase): URL = "http://mock/status/200" @@ -96,7 +98,7 @@ def test_basic_metric(self): with request.urlopen(self.URL) as result: client_duration_estimated = (default_timer() - start_time) * 1000 - metrics = self.get_sorted_metrics() + metrics = self.get_sorted_metrics(SCOPE) self.assertEqual(len(metrics), 3) ( @@ -162,7 +164,7 @@ def test_basic_metric_new_semconv(self): with request.urlopen(self.URL) as result: duration_s = default_timer() - start_time - metrics = self.get_sorted_metrics() + metrics = self.get_sorted_metrics(SCOPE) self.assertEqual(len(metrics), 3) ( @@ -227,7 +229,7 @@ def test_basic_metric_both_semconv(self): duration_s = default_timer() - start_time duration = max(round(duration_s * 1000), 0) - metrics = self.get_sorted_metrics() + metrics = self.get_sorted_metrics(SCOPE) self.assertEqual(len(metrics), 6) ( @@ -349,7 +351,7 @@ def test_basic_metric_request_not_empty(self): with request.urlopen(self.URL_POST, data=data_encoded) as result: client_duration_estimated = (default_timer() - start_time) * 1000 - metrics = self.get_sorted_metrics() + metrics = self.get_sorted_metrics(SCOPE) self.assertEqual(len(metrics), 3) ( @@ -414,7 +416,7 @@ def test_basic_metric_request_not_empty(self): ) def test_metric_uninstrument(self): with request.urlopen(self.URL): - metrics = self.get_sorted_metrics() + metrics = self.get_sorted_metrics(SCOPE) self.assertEqual(len(metrics), 3) ( @@ -438,7 +440,7 @@ def test_metric_uninstrument(self): ) with request.urlopen(self.URL): - metrics = self.get_sorted_metrics() + metrics = self.get_sorted_metrics(SCOPE) self.assertEqual(len(metrics), 3) @@ -464,7 +466,7 @@ def test_metric_uninstrument(self): URLLibInstrumentor().uninstrument() with request.urlopen(self.URL): - metrics = self.get_sorted_metrics() + metrics = self.get_sorted_metrics(SCOPE) self.assertEqual(len(metrics), 3) diff --git a/instrumentation/opentelemetry-instrumentation-urllib3/tests/test_urllib3_metrics.py b/instrumentation/opentelemetry-instrumentation-urllib3/tests/test_urllib3_metrics.py index 7672b6523..6c87aa7e3 100644 --- a/instrumentation/opentelemetry-instrumentation-urllib3/tests/test_urllib3_metrics.py +++ b/instrumentation/opentelemetry-instrumentation-urllib3/tests/test_urllib3_metrics.py @@ -30,6 +30,8 @@ from opentelemetry.test.httptest import HttpTestBase from opentelemetry.test.test_base import TestBase +SCOPE = "opentelemetry.instrumentation.urllib3" + class TestURLLib3InstrumentorMetric(HttpTestBase, TestBase): HTTP_URL = "http://mock/status/200" @@ -73,7 +75,7 @@ def test_basic_metrics(self): start_time = default_timer() response = self.pool.request("GET", self.HTTP_URL) duration_ms = max(round((default_timer() - start_time) * 1000), 0) - metrics = self.get_sorted_metrics() + metrics = self.get_sorted_metrics(SCOPE) self.assertEqual(len(metrics), 3) ( @@ -143,7 +145,7 @@ def test_basic_metrics_new_semconv(self): response = self.pool.request("GET", self.HTTP_URL) duration_s = max(default_timer() - start_time, 0) - metrics = self.get_sorted_metrics() + metrics = self.get_sorted_metrics(SCOPE) self.assertEqual(len(metrics), 3) ( client_request_size, @@ -216,7 +218,7 @@ def test_basic_metrics_both_semconv(self): duration = max(round(duration_s * 1000), 0) expected_size = len(response.data) - metrics = self.get_sorted_metrics() + metrics = self.get_sorted_metrics(SCOPE) self.assertEqual(len(metrics), 6) ( @@ -353,7 +355,7 @@ def test_basic_metrics_nonstandard_http_method(self): response = self.pool.request("NONSTANDARD", self.HTTP_URL) duration_ms = max(round((default_timer() - start_time) * 1000), 0) - metrics = self.get_sorted_metrics() + metrics = self.get_sorted_metrics(SCOPE) ( client_duration, @@ -426,7 +428,7 @@ def test_basic_metrics_nonstandard_http_method_new_semconv(self): response = self.pool.request("NONSTANDARD", self.HTTP_URL) duration_s = max(default_timer() - start_time, 0) - metrics = self.get_sorted_metrics() + metrics = self.get_sorted_metrics(SCOPE) ( client_request_size, @@ -496,7 +498,7 @@ def test_basic_metrics_nonstandard_http_method_new_semconv(self): def test_str_request_body_size_metrics(self): self.pool.request("POST", self.HTTP_URL, body="foobar") - metrics = self.get_sorted_metrics() + metrics = self.get_sorted_metrics(SCOPE) (_, client_request_size, _) = metrics self.assertEqual(client_request_size.name, "http.client.request.size") @@ -529,7 +531,10 @@ def test_schema_url(self): ) for metrics in resource_metrics: - for scope_metrics in metrics.scope_metrics: + scope_metrics_list = [ + sm for sm in metrics.scope_metrics if sm.scope.name == SCOPE + ] + for scope_metrics in scope_metrics_list: self.assertEqual( scope_metrics.scope.schema_url, "https://opentelemetry.io/schemas/1.11.0", @@ -538,7 +543,7 @@ def test_schema_url(self): def test_bytes_request_body_size_metrics(self): self.pool.request("POST", self.HTTP_URL, body=b"foobar") - metrics = self.get_sorted_metrics() + metrics = self.get_sorted_metrics(SCOPE) (_, client_request_size, _) = metrics self.assertEqual(client_request_size.name, "http.client.request.size") @@ -566,7 +571,7 @@ def test_bytes_request_body_size_metrics(self): def test_fields_request_body_size_metrics(self): self.pool.request("POST", self.HTTP_URL, fields={"foo": "bar"}) - metrics = self.get_sorted_metrics() + metrics = self.get_sorted_metrics(SCOPE) (_, client_request_size, _) = metrics self.assertEqual(client_request_size.name, "http.client.request.size") @@ -595,7 +600,7 @@ def test_fields_request_body_size_metrics(self): def test_bytesio_request_body_size_metrics(self): self.pool.request("POST", self.HTTP_URL, body=io.BytesIO(b"foobar")) - metrics = self.get_sorted_metrics() + metrics = self.get_sorted_metrics(SCOPE) (_, client_request_size, _) = metrics self.assertEqual(client_request_size.name, "http.client.request.size") @@ -625,7 +630,7 @@ def test_generator_request_body_size_metrics(self): "POST", self.HTTP_URL, body=(b for b in (b"foo", b"bar")) ) - metrics = self.get_sorted_metrics() + metrics = self.get_sorted_metrics(SCOPE) self.assertEqual(len(metrics), 2) self.assertNotIn("http.client.request.size", [m.name for m in metrics]) @@ -634,7 +639,7 @@ def test_metric_uninstrument(self): URLLib3Instrumentor().uninstrument() self.pool.request("GET", self.HTTP_URL) - metrics = self.get_sorted_metrics() + metrics = self.get_sorted_metrics(SCOPE) self.assertEqual(len(metrics), 3) for metric in metrics: diff --git a/instrumentation/opentelemetry-instrumentation-wsgi/tests/test_wsgi_middleware.py b/instrumentation/opentelemetry-instrumentation-wsgi/tests/test_wsgi_middleware.py index bb6c3aca2..474c74f36 100644 --- a/instrumentation/opentelemetry-instrumentation-wsgi/tests/test_wsgi_middleware.py +++ b/instrumentation/opentelemetry-instrumentation-wsgi/tests/test_wsgi_middleware.py @@ -204,6 +204,8 @@ def wsgi_with_repeat_custom_response_headers(environ, start_response): "http.server.request.duration": _server_duration_attrs_new, } +SCOPE = "opentelemetry.instrumentation.wsgi" + class TestWsgiApplication(WsgiTestBase): def setUp(self): @@ -396,26 +398,23 @@ def test_wsgi_metrics(self): number_data_point_seen = False histogram_data_point_seen = False - self.assertTrue(len(metrics_list.resource_metrics) != 0) - for resource_metric in metrics_list.resource_metrics: - self.assertTrue(len(resource_metric.scope_metrics) != 0) - for scope_metric in resource_metric.scope_metrics: - self.assertTrue(len(scope_metric.metrics) != 0) - for metric in scope_metric.metrics: - self.assertIn(metric.name, _expected_metric_names_old) - data_points = list(metric.data.data_points) - self.assertEqual(len(data_points), 1) - for point in data_points: - if isinstance(point, HistogramDataPoint): - self.assertEqual(point.count, 3) - histogram_data_point_seen = True - if isinstance(point, NumberDataPoint): - number_data_point_seen = True - for attr in point.attributes: - self.assertIn( - attr, - _recommended_metrics_attrs_old[metric.name], - ) + metrics = self.get_sorted_metrics(SCOPE) + self.assertTrue(len(metrics) > 0) + for metric in metrics: + self.assertIn(metric.name, _expected_metric_names_old) + data_points = list(metric.data.data_points) + self.assertEqual(len(data_points), 1) + for point in data_points: + if isinstance(point, HistogramDataPoint): + self.assertEqual(point.count, 3) + histogram_data_point_seen = True + if isinstance(point, NumberDataPoint): + number_data_point_seen = True + for attr in point.attributes: + self.assertIn( + attr, + _recommended_metrics_attrs_old[metric.name], + ) self.assertTrue(number_data_point_seen and histogram_data_point_seen) def test_wsgi_metrics_new_semconv(self): @@ -428,31 +427,28 @@ def test_wsgi_metrics_new_semconv(self): number_data_point_seen = False histogram_data_point_seen = False - self.assertTrue(len(metrics_list.resource_metrics) != 0) - for resource_metric in metrics_list.resource_metrics: - self.assertTrue(len(resource_metric.scope_metrics) != 0) - for scope_metric in resource_metric.scope_metrics: - self.assertTrue(len(scope_metric.metrics) != 0) - for metric in scope_metric.metrics: - self.assertIn(metric.name, _expected_metric_names_new) - data_points = list(metric.data.data_points) - self.assertEqual(len(data_points), 1) - for point in data_points: - if isinstance(point, HistogramDataPoint): - self.assertEqual(point.count, 3) - if metric.name == "http.server.request.duration": - self.assertEqual( - point.explicit_bounds, - HTTP_DURATION_HISTOGRAM_BUCKETS_NEW, - ) - histogram_data_point_seen = True - if isinstance(point, NumberDataPoint): - number_data_point_seen = True - for attr in point.attributes: - self.assertIn( - attr, - _recommended_metrics_attrs_new[metric.name], - ) + metrics = self.get_sorted_metrics(SCOPE) + self.assertTrue(len(metrics) != 0) + for metric in metrics: + self.assertIn(metric.name, _expected_metric_names_new) + data_points = list(metric.data.data_points) + self.assertEqual(len(data_points), 1) + for point in data_points: + if isinstance(point, HistogramDataPoint): + self.assertEqual(point.count, 3) + if metric.name == "http.server.request.duration": + self.assertEqual( + point.explicit_bounds, + HTTP_DURATION_HISTOGRAM_BUCKETS_NEW, + ) + histogram_data_point_seen = True + if isinstance(point, NumberDataPoint): + number_data_point_seen = True + for attr in point.attributes: + self.assertIn( + attr, + _recommended_metrics_attrs_new[metric.name], + ) self.assertTrue(number_data_point_seen and histogram_data_point_seen) def test_wsgi_metrics_both_semconv(self): @@ -463,15 +459,21 @@ def test_wsgi_metrics_both_semconv(self): number_data_point_seen = False histogram_data_point_seen = False - self.assertTrue(len(metrics_list.resource_metrics) != 0) - for resource_metric in metrics_list.resource_metrics: - self.assertTrue(len(resource_metric.scope_metrics) != 0) - for scope_metric in resource_metric.scope_metrics: - self.assertTrue(len(scope_metric.metrics) != 0) - for metric in scope_metric.metrics: - if metric.unit == "ms": - self.assertEqual(metric.name, "http.server.duration") - elif metric.unit == "s": + metrics = self.get_sorted_metrics(SCOPE) + self.assertTrue(len(metrics) != 0) + for metric in metrics: + if metric.unit == "ms": + self.assertEqual(metric.name, "http.server.duration") + elif metric.unit == "s": + self.assertEqual(metric.name, "http.server.request.duration") + else: + self.assertEqual(metric.name, "http.server.active_requests") + data_points = list(metric.data.data_points) + self.assertEqual(len(data_points), 1) + for point in data_points: + if isinstance(point, HistogramDataPoint): + self.assertEqual(point.count, 1) + if metric.name == "http.server.request.duration": self.assertEqual( metric.name, "http.server.request.duration" )