diff --git a/instrumentation/opentelemetry-instrumentation-asgi/tests/test_asgi_middleware.py b/instrumentation/opentelemetry-instrumentation-asgi/tests/test_asgi_middleware.py index 61d53e8ba2..f716c9f2f1 100644 --- a/instrumentation/opentelemetry-instrumentation-asgi/tests/test_asgi_middleware.py +++ b/instrumentation/opentelemetry-instrumentation-asgi/tests/test_asgi_middleware.py @@ -306,6 +306,9 @@ def hook(*_): return hook +SCOPE = "opentelemetry.instrumentation.asgi" + + # pylint: disable=too-many-public-methods class TestAsgiApplication(AsyncAsgiTestBase): def setUp(self): @@ -340,16 +343,7 @@ def subTest(self, msg=..., **params): def _assert_exemplars_present( self, metric_names: set[str], context: str = "" ): - metrics_list = self.memory_metrics_reader.get_metrics_data() - print(metrics_list) - metrics = [] - for resource_metric in ( - getattr(metrics_list, "resource_metrics", []) or [] - ): - for scope_metric in ( - getattr(resource_metric, "scope_metrics", []) or [] - ): - metrics.extend(getattr(scope_metric, "metrics", []) or []) + metrics = self.get_sorted_metrics(SCOPE) found = {name: 0 for name in metric_names} for metric in metrics: @@ -421,7 +415,7 @@ def validate_outputs( self.assertIsNone(exc_info) # Check spans - span_list = self.memory_exporter.get_finished_spans() + span_list = self.get_finished_spans() expected_old = [ { "name": "GET / http receive", @@ -557,7 +551,7 @@ def validate_outputs( self.assertDictEqual(dict(span.attributes), expected["attributes"]) self.assertEqual( span.instrumentation_scope.name, - "opentelemetry.instrumentation.asgi", + SCOPE, ) if "events" in expected: self.assertEqual(len(span.events), len(expected["events"])) @@ -647,7 +641,7 @@ async def test_background_execution(self): await self.send_default_request() outputs = await self.get_all_output() self.validate_outputs(outputs) - span_list = self.memory_exporter.get_finished_spans() + span_list = self.get_finished_spans() server_span = span_list[-1] assert server_span.kind == SpanKind.SERVER span_duration_nanos = server_span.end_time - server_span.start_time @@ -674,7 +668,7 @@ async def test_exclude_internal_spans(self): self.seed_app(app) await self.send_default_request() await self.get_all_output() - span_list = self.memory_exporter.get_finished_spans() + span_list = self.get_finished_spans() self.assertTrue(span_list) for span in span_list: for excluded_span in excluded_spans: @@ -706,7 +700,7 @@ def add_body_and_trailer_span(expected: list): return expected self.validate_outputs(outputs, modifiers=[add_body_and_trailer_span]) - span_list = self.memory_exporter.get_finished_spans() + span_list = self.get_finished_spans() server_span = span_list[-1] assert server_span.kind == SpanKind.SERVER span_duration_nanos = server_span.end_time - server_span.start_time @@ -767,7 +761,7 @@ async def test_no_op_tracer_provider_otel_asgi(self): self.assertEqual(response_body["body"], b"*") self.assertEqual(response_start["status"], 200) - span_list = self.memory_exporter.get_finished_spans() + span_list = self.get_finished_spans() self.assertEqual(len(span_list), 0) async def test_behavior_with_scope_server_as_none(self): @@ -1091,7 +1085,7 @@ async def test_traceresponse_header(self): await self.send_default_request() response_start, response_body, *_ = await self.get_all_output() - span = self.memory_exporter.get_finished_spans()[-1] + span = self.get_finished_spans()[-1] self.assertEqual(trace_api.SpanKind.SERVER, span.kind) self.assertEqual(response_body["body"], b"*") @@ -1131,7 +1125,7 @@ async def test_websocket(self): await self.send_input({"type": "websocket.receive", "text": "ping"}) await self.send_input({"type": "websocket.disconnect"}) await self.get_all_output() - span_list = self.memory_exporter.get_finished_spans() + span_list = self.get_finished_spans() self.assertEqual(len(span_list), 6) expected = [ { @@ -1205,7 +1199,7 @@ async def test_websocket_new_semconv(self): await self.send_input({"type": "websocket.receive", "text": "ping"}) await self.send_input({"type": "websocket.disconnect"}) await self.get_all_output() - span_list = self.memory_exporter.get_finished_spans() + span_list = self.get_finished_spans() self.assertEqual(len(span_list), 6) expected = [ { @@ -1278,7 +1272,7 @@ async def test_websocket_both_semconv(self): await self.send_input({"type": "websocket.receive", "text": "ping"}) await self.send_input({"type": "websocket.disconnect"}) await self.get_all_output() - span_list = self.memory_exporter.get_finished_spans() + span_list = self.get_finished_spans() self.assertEqual(len(span_list), 6) expected = [ { @@ -1368,7 +1362,7 @@ async def test_websocket_traceresponse_header(self): await self.send_input({"type": "websocket.disconnect"}) _, socket_send, *_ = await self.get_all_output() - span = self.memory_exporter.get_finished_spans()[-1] + span = self.get_finished_spans()[-1] self.assertEqual(trace_api.SpanKind.SERVER, span.kind) trace_id = format_trace_id(span.get_span_context().trace_id) @@ -1390,7 +1384,7 @@ async def test_lifespan(self): app = otel_asgi.OpenTelemetryMiddleware(simple_asgi) self.seed_app(app) await self.send_default_request() - span_list = self.memory_exporter.get_finished_spans() + span_list = self.get_finished_spans() self.assertEqual(len(span_list), 0) async def test_hooks(self): @@ -1471,32 +1465,22 @@ async def test_asgi_metrics(self): self.seed_app(app) await self.send_default_request() await self.get_all_output() - 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) != 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) - self.assertEqual( - scope_metric.scope.name, - "opentelemetry.instrumentation.asgi", - ) - 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_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_attrs_old[metric.name]) self.assertTrue(number_data_point_seen and histogram_data_point_seen) async def test_asgi_metrics_new_semconv(self): @@ -1511,37 +1495,27 @@ async def test_asgi_metrics_new_semconv(self): self.seed_app(app) await self.send_default_request() await self.get_all_output() - 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) != 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) - self.assertEqual( - scope_metric.scope.name, - "opentelemetry.instrumentation.asgi", - ) - 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_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_attrs_new[metric.name]) self.assertTrue(number_data_point_seen and histogram_data_point_seen) async def test_asgi_metrics_both_semconv(self): @@ -1556,37 +1530,27 @@ async def test_asgi_metrics_both_semconv(self): self.seed_app(app) await self.send_default_request() await self.get_all_output() - 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) != 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) - self.assertEqual( - scope_metric.scope.name, - "opentelemetry.instrumentation.asgi", - ) - for metric in scope_metric.metrics: - self.assertIn(metric.name, _expected_metric_names_both) - 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_attrs_both[metric.name] - ) + metrics = self.get_sorted_metrics(SCOPE) + self.assertTrue(len(metrics) != 0) + for metric in metrics: + self.assertIn(metric.name, _expected_metric_names_both) + 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_attrs_both[metric.name]) self.assertTrue(number_data_point_seen and histogram_data_point_seen) async def test_asgi_metrics_exemplars_expected_old_semconv(self): @@ -1644,32 +1608,28 @@ async def test_basic_metric_success(self): "http.scheme": "http", "http.flavor": "1.0", } - 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: - 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), - ) - self.assertEqual(point.count, 1) - if metric.name == "http.server.duration": - self.assertAlmostEqual( - duration, point.sum, delta=30 - ) - elif metric.name == "http.server.response.size": - self.assertEqual(1024, point.sum) - elif metric.name == "http.server.request.size": - self.assertEqual(128, point.sum) - elif isinstance(point, NumberDataPoint): - self.assertDictEqual( - expected_requests_count_attributes, - dict(point.attributes), - ) - self.assertEqual(point.value, 0) + for metric in metrics: + for point in list(metric.data.data_points): + if isinstance(point, HistogramDataPoint): + self.assertDictEqual( + expected_duration_attributes, + dict(point.attributes), + ) + self.assertEqual(point.count, 1) + if metric.name == "http.server.duration": + self.assertAlmostEqual(duration, point.sum, delta=30) + elif metric.name == "http.server.response.size": + self.assertEqual(1024, point.sum) + elif metric.name == "http.server.request.size": + self.assertEqual(128, point.sum) + elif isinstance(point, NumberDataPoint): + self.assertDictEqual( + expected_requests_count_attributes, + dict(point.attributes), + ) + self.assertEqual(point.value, 0) async def test_basic_metric_success_nonrecording_span(self): mock_tracer = mock.Mock() @@ -1702,34 +1662,30 @@ async def test_basic_metric_success_nonrecording_span(self): "http.scheme": "http", "http.flavor": "1.0", } - 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: - 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), - ) - self.assertEqual(point.count, 1) - if metric.name == "http.server.duration": - self.assertAlmostEqual( - duration, point.sum, delta=15 - ) - elif ( - metric.name == "http.server.response.size" - ): - self.assertEqual(1024, point.sum) - elif metric.name == "http.server.request.size": - self.assertEqual(128, point.sum) - elif isinstance(point, NumberDataPoint): - self.assertDictEqual( - expected_requests_count_attributes, - dict(point.attributes), - ) - self.assertEqual(point.value, 0) + for metric in metrics: + for point in list(metric.data.data_points): + if isinstance(point, HistogramDataPoint): + self.assertDictEqual( + expected_duration_attributes, + dict(point.attributes), + ) + self.assertEqual(point.count, 1) + if metric.name == "http.server.duration": + self.assertAlmostEqual( + duration, point.sum, delta=15 + ) + elif metric.name == "http.server.response.size": + self.assertEqual(1024, point.sum) + elif metric.name == "http.server.request.size": + self.assertEqual(128, point.sum) + elif isinstance(point, NumberDataPoint): + self.assertDictEqual( + expected_requests_count_attributes, + dict(point.attributes), + ) + self.assertEqual(point.value, 0) async def test_basic_metric_success_new_semconv(self): app = otel_asgi.OpenTelemetryMiddleware(simple_asgi) @@ -1748,36 +1704,28 @@ async def test_basic_metric_success_new_semconv(self): "http.request.method": "GET", "url.scheme": "http", } - 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: - 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), - ) - self.assertEqual(point.count, 1) - if metric.name == "http.server.request.duration": - self.assertAlmostEqual( - duration_s, point.sum, places=2 - ) - elif ( - metric.name == "http.server.response.body.size" - ): - self.assertEqual(1024, point.sum) - elif ( - metric.name == "http.server.request.body.size" - ): - self.assertEqual(128, point.sum) - elif isinstance(point, NumberDataPoint): - self.assertDictEqual( - expected_requests_count_attributes, - dict(point.attributes), - ) - self.assertEqual(point.value, 0) + for metric in metrics: + for point in list(metric.data.data_points): + if isinstance(point, HistogramDataPoint): + self.assertDictEqual( + expected_duration_attributes, + dict(point.attributes), + ) + self.assertEqual(point.count, 1) + if metric.name == "http.server.request.duration": + self.assertAlmostEqual(duration_s, point.sum, places=2) + elif metric.name == "http.server.response.body.size": + self.assertEqual(1024, point.sum) + elif metric.name == "http.server.request.body.size": + self.assertEqual(128, point.sum) + elif isinstance(point, NumberDataPoint): + self.assertDictEqual( + expected_requests_count_attributes, + dict(point.attributes), + ) + self.assertEqual(point.value, 0) async def test_basic_metric_success_both_semconv(self): app = otel_asgi.OpenTelemetryMiddleware(simple_asgi) @@ -1809,64 +1757,54 @@ async def test_basic_metric_success_both_semconv(self): "network.protocol.version": "1.0", "http.response.status_code": 200, } - 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: - 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.assertEqual(point.count, 1) - if metric.name == "http.server.request.duration": - self.assertAlmostEqual( - duration_s, point.sum, places=2 - ) - self.assertDictEqual( - expected_duration_attributes_new, - dict(point.attributes), - ) - elif ( - metric.name == "http.server.response.body.size" - ): - self.assertEqual(1024, point.sum) - self.assertDictEqual( - expected_duration_attributes_new, - dict(point.attributes), - ) - elif ( - metric.name == "http.server.request.body.size" - ): - self.assertEqual(128, point.sum) - self.assertDictEqual( - expected_duration_attributes_new, - dict(point.attributes), - ) - elif metric.name == "http.server.duration": - self.assertAlmostEqual( - duration, point.sum, delta=30 - ) - self.assertDictEqual( - expected_duration_attributes_old, - dict(point.attributes), - ) - elif metric.name == "http.server.response.size": - self.assertEqual(1024, point.sum) - self.assertDictEqual( - expected_duration_attributes_old, - dict(point.attributes), - ) - elif metric.name == "http.server.request.size": - self.assertEqual(128, point.sum) - self.assertDictEqual( - expected_duration_attributes_old, - dict(point.attributes), - ) - elif isinstance(point, NumberDataPoint): - self.assertDictEqual( - expected_requests_count_attributes, - dict(point.attributes), - ) - self.assertEqual(point.value, 0) + for metric in metrics: + for point in list(metric.data.data_points): + if isinstance(point, HistogramDataPoint): + self.assertEqual(point.count, 1) + if metric.name == "http.server.request.duration": + self.assertAlmostEqual(duration_s, point.sum, places=2) + self.assertDictEqual( + expected_duration_attributes_new, + dict(point.attributes), + ) + elif metric.name == "http.server.response.body.size": + self.assertEqual(1024, point.sum) + self.assertDictEqual( + expected_duration_attributes_new, + dict(point.attributes), + ) + elif metric.name == "http.server.request.body.size": + self.assertEqual(128, point.sum) + self.assertDictEqual( + expected_duration_attributes_new, + dict(point.attributes), + ) + elif metric.name == "http.server.duration": + self.assertAlmostEqual(duration, point.sum, delta=30) + self.assertDictEqual( + expected_duration_attributes_old, + dict(point.attributes), + ) + elif metric.name == "http.server.response.size": + self.assertEqual(1024, point.sum) + self.assertDictEqual( + expected_duration_attributes_old, + dict(point.attributes), + ) + elif metric.name == "http.server.request.size": + self.assertEqual(128, point.sum) + self.assertDictEqual( + expected_duration_attributes_old, + dict(point.attributes), + ) + elif isinstance(point, NumberDataPoint): + self.assertDictEqual( + expected_requests_count_attributes, + dict(point.attributes), + ) + self.assertEqual(point.value, 0) async def test_metric_target_attribute(self): expected_target = "/api/user/{id}" @@ -1886,20 +1824,18 @@ async def target_asgi(scope, receive, send): self.seed_app(app) await self.send_default_request() await self.get_all_output() - metrics_list = self.memory_metrics_reader.get_metrics_data() + metrics = self.get_sorted_metrics(SCOPE) assertions = 0 - for resource_metric in metrics_list.resource_metrics: - for scope_metrics in resource_metric.scope_metrics: - for metric in scope_metrics.metrics: - if metric.name == "http.server.active_requests": - continue - for point in metric.data.data_points: - if isinstance(point, HistogramDataPoint): - self.assertEqual( - point.attributes["http.target"], - expected_target, - ) - assertions += 1 + for metric in metrics: + if metric.name == "http.server.active_requests": + continue + for point in metric.data.data_points: + if isinstance(point, HistogramDataPoint): + self.assertEqual( + point.attributes["http.target"], + expected_target, + ) + assertions += 1 self.assertEqual(assertions, 3) async def test_no_metric_for_websockets(self): @@ -1919,7 +1855,7 @@ async def test_no_metric_for_websockets(self): await self.send_input({"type": "websocket.receive", "text": "ping"}) await self.send_input({"type": "websocket.disconnect"}) await self.get_all_output() - self.assertIsNone(self.memory_metrics_reader.get_metrics_data()) + self.assertEqual(len(self.get_sorted_metrics(SCOPE)), 0) async def test_excluded_urls(self): self.scope["path"] = "/test_excluded_urls" @@ -1929,7 +1865,7 @@ async def test_excluded_urls(self): self.seed_app(app) await self.send_default_request() await self.get_all_output() - spans = self.memory_exporter.get_finished_spans() + spans = self.get_finished_spans() self.assertEqual(len(spans), 0) async def test_no_excluded_urls(self): @@ -1941,7 +1877,7 @@ async def test_no_excluded_urls(self): self.scope["path"] = "/test_no_excluded_urls" await self.send_default_request() await self.get_all_output() - spans = self.memory_exporter.get_finished_spans() + spans = self.get_finished_spans() self.assertGreater(len(spans), 0)