コード例 #1
0
    def test_hooks(self):
        def server_request_hook(span, scope):
            span.update_name("name from server hook")

        def client_request_hook(recieve_span, request):
            recieve_span.update_name("name from client request hook")

        def client_response_hook(send_span, response):
            send_span.set_attribute("attr-from-hook", "value")

        def update_expected_hook_results(expected):
            for entry in expected:
                if entry["kind"] == trace_api.SpanKind.SERVER:
                    entry["name"] = "name from server hook"
                elif entry["name"] == "/ http receive":
                    entry["name"] = "name from client request hook"
                elif entry["name"] == "/ http send":
                    entry["attributes"].update({"attr-from-hook": "value"})
            return expected

        app = otel_asgi.OpenTelemetryMiddleware(
            simple_asgi,
            server_request_hook=server_request_hook,
            client_request_hook=client_request_hook,
            client_response_hook=client_response_hook,
        )
        self.seed_app(app)
        self.send_default_request()
        outputs = self.get_all_output()
        self.validate_outputs(outputs,
                              modifiers=[update_expected_hook_results])
コード例 #2
0
 def test_asgi_metrics(self):
     app = otel_asgi.OpenTelemetryMiddleware(simple_asgi)
     self.seed_app(app)
     self.send_default_request()
     self.seed_app(app)
     self.send_default_request()
     self.seed_app(app)
     self.send_default_request()
     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)
             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])
     self.assertTrue(number_data_point_seen and histogram_data_point_seen)
コード例 #3
0
 def test_asgi_exc_info(self):
     """Test that exception information is emitted as expected."""
     app = otel_asgi.OpenTelemetryMiddleware(error_asgi)
     self.seed_app(app)
     self.send_default_request()
     outputs = self.get_all_output()
     self.validate_outputs(outputs, error=ValueError)
コード例 #4
0
 def test_basic_asgi_call(self):
     """Test that spans are emitted as expected."""
     app = otel_asgi.OpenTelemetryMiddleware(simple_asgi)
     self.seed_app(app)
     self.send_default_request()
     outputs = self.get_all_output()
     self.validate_outputs(outputs)
コード例 #5
0
 def test_websocket_custom_response_headers_not_in_span_attributes(self):
     self.scope = {
         "type": "websocket",
         "http_version": "1.1",
         "scheme": "ws",
         "path": "/",
         "query_string": b"",
         "headers": [],
         "client": ("127.0.0.1", 32767),
         "server": ("127.0.0.1", 80),
     }
     self.app = otel_asgi.OpenTelemetryMiddleware(
         websocket_app_with_custom_headers,
         tracer_provider=self.tracer_provider,
     )
     self.seed_app(self.app)
     self.send_input({"type": "websocket.connect"})
     self.send_input({"type": "websocket.receive", "text": "ping"})
     self.send_input({"type": "websocket.disconnect"})
     self.get_all_output()
     span_list = self.exporter.get_finished_spans()
     not_expected = {
         "http.response.header.custom_test_header_3":
         ("test-header-value-3", ),
     }
     for span in span_list:
         if span.kind == SpanKind.SERVER:
             for key, _ in not_expected.items():
                 self.assertNotIn(key, span.attributes)
コード例 #6
0
    def test_mark_span_internal_in_presence_of_span_from_other_framework(self):
        tracer_provider, exporter = TestBase.create_tracer_provider()
        tracer = tracer_provider.get_tracer(__name__)
        app = otel_asgi.OpenTelemetryMiddleware(
            simple_asgi, tracer_provider=tracer_provider)

        # Wrapping the otel intercepted app with server span
        async def wrapped_app(scope, receive, send):
            with tracer.start_as_current_span("test",
                                              kind=SpanKind.SERVER) as _:
                await app(scope, receive, send)

        self.seed_app(wrapped_app)
        self.send_default_request()
        span_list = exporter.get_finished_spans()

        self.assertEqual(SpanKind.INTERNAL, span_list[0].kind)
        self.assertEqual(SpanKind.INTERNAL, span_list[1].kind)
        self.assertEqual(SpanKind.INTERNAL, span_list[2].kind)
        self.assertEqual(trace_api.SpanKind.INTERNAL, span_list[3].kind)

        # SERVER "test"
        self.assertEqual(SpanKind.SERVER, span_list[4].kind)

        # internal span should be child of the test span we have provided
        self.assertEqual(span_list[4].context.span_id,
                         span_list[3].parent.span_id)
    def test_traceresponse_header(self):
        """Test a traceresponse header is sent when a global propagator is set."""

        orig = get_global_response_propagator()
        set_global_response_propagator(TraceResponsePropagator())

        app = otel_asgi.OpenTelemetryMiddleware(simple_asgi)
        self.seed_app(app)
        self.send_default_request()

        span = self.memory_exporter.get_finished_spans()[-1]
        self.assertEqual(trace_api.SpanKind.SERVER, span.kind)

        response_start, response_body, *_ = self.get_all_output()
        self.assertEqual(response_body["body"], b"*")
        self.assertEqual(response_start["status"], 200)

        traceresponse = "00-{0}-{1}-01".format(
            format_trace_id(span.get_span_context().trace_id),
            format_span_id(span.get_span_context().span_id),
        )
        self.assertListEqual(
            response_start["headers"],
            [
                [b"Content-Type", b"text/plain"],
                [b"traceresponse", f"{traceresponse}".encode()],
                [b"access-control-expose-headers", b"traceresponse"],
            ],
        )

        set_global_response_propagator(orig)
コード例 #8
0
 def test_lifespan(self):
     self.scope["type"] = "lifespan"
     app = otel_asgi.OpenTelemetryMiddleware(simple_asgi)
     self.seed_app(app)
     self.send_default_request()
     span_list = self.memory_exporter.get_finished_spans()
     self.assertEqual(len(span_list), 0)
コード例 #9
0
 def test_websocket(self):
     self.scope = {
         "type": "websocket",
         "http_version": "1.1",
         "scheme": "ws",
         "path": "/",
         "query_string": b"",
         "headers": [],
         "client": ("127.0.0.1", 32767),
         "server": ("127.0.0.1", 80),
     }
     app = otel_asgi.OpenTelemetryMiddleware(simple_asgi)
     self.seed_app(app)
     self.send_input({"type": "websocket.connect"})
     self.send_input({"type": "websocket.receive", "text": "ping"})
     self.send_input({"type": "websocket.disconnect"})
     self.get_all_output()
     span_list = self.memory_exporter.get_finished_spans()
     self.assertEqual(len(span_list), 6)
     expected = [
         "/ asgi.websocket.receive",
         "/ asgi.websocket.send",
         "/ asgi.websocket.receive",
         "/ asgi.websocket.send",
         "/ asgi.websocket.receive",
         "/ asgi",
     ]
     actual = [span.name for span in span_list]
     self.assertListEqual(actual, expected)
コード例 #10
0
    def test_custom_tracer_provider_otel_asgi(self):
        resource = resources.Resource.create({"service-test-key": "value"})
        result = TestBase.create_tracer_provider(resource=resource)
        tracer_provider, exporter = result

        app = otel_asgi.OpenTelemetryMiddleware(
            simple_asgi, tracer_provider=tracer_provider)
        self.seed_app(app)
        self.send_default_request()
        span_list = exporter.get_finished_spans()
        for span in span_list:
            self.assertEqual(span.resource.attributes["service-test-key"],
                             "value")
コード例 #11
0
    def test_host_header(self):
        """Test that host header is converted to http.server_name."""
        hostname = b"server_name_1"

        def update_expected_server(expected):
            expected[3]["attributes"].update(
                {SpanAttributes.HTTP_SERVER_NAME: hostname.decode("utf8")})
            return expected

        self.scope["headers"].append([b"host", hostname])
        app = otel_asgi.OpenTelemetryMiddleware(simple_asgi)
        self.seed_app(app)
        self.send_default_request()
        outputs = self.get_all_output()
        self.validate_outputs(outputs, modifiers=[update_expected_server])
コード例 #12
0
    def test_user_agent(self):
        """Test that host header is converted to http.server_name."""
        user_agent = b"test-agent"

        def update_expected_user_agent(expected):
            expected[3]["attributes"].update(
                {SpanAttributes.HTTP_USER_AGENT: user_agent.decode("utf8")})
            return expected

        self.scope["headers"].append([b"user-agent", user_agent])
        app = otel_asgi.OpenTelemetryMiddleware(simple_asgi)
        self.seed_app(app)
        self.send_default_request()
        outputs = self.get_all_output()
        self.validate_outputs(outputs, modifiers=[update_expected_user_agent])
コード例 #13
0
 def test_http_custom_response_headers_not_in_span_attributes(self):
     self.app = otel_asgi.OpenTelemetryMiddleware(
         http_app_with_custom_headers, tracer_provider=self.tracer_provider)
     self.seed_app(self.app)
     self.send_default_request()
     self.get_all_output()
     span_list = self.exporter.get_finished_spans()
     not_expected = {
         "http.response.header.custom_test_header_3":
         ("test-header-value-3", ),
     }
     for span in span_list:
         if span.kind == SpanKind.SERVER:
             for key, _ in not_expected.items():
                 self.assertNotIn(key, span.attributes)
コード例 #14
0
 def test_asgi_not_recording(self):
     mock_tracer = mock.Mock()
     mock_span = mock.Mock()
     mock_span.is_recording.return_value = False
     mock_tracer.start_as_current_span.return_value = mock_span
     mock_tracer.start_as_current_span.return_value.__enter__ = mock_span
     mock_tracer.start_as_current_span.return_value.__exit__ = mock_span
     with mock.patch("opentelemetry.trace.get_tracer") as tracer:
         tracer.return_value = mock_tracer
         app = otel_asgi.OpenTelemetryMiddleware(simple_asgi)
         self.seed_app(app)
         self.send_default_request()
         self.assertFalse(mock_span.is_recording())
         self.assertTrue(mock_span.is_recording.called)
         self.assertFalse(mock_span.set_attribute.called)
         self.assertFalse(mock_span.set_status.called)
コード例 #15
0
    def test_behavior_with_scope_server_as_none(self):
        """Test that middleware is ok when server is none in scope."""
        def update_expected_server(expected):
            expected[3]["attributes"].update({
                "http.host": "0.0.0.0",
                "host.port": 80,
                "http.url": "http://0.0.0.0/",
            })
            return expected

        self.scope["server"] = None
        app = otel_asgi.OpenTelemetryMiddleware(simple_asgi)
        self.seed_app(app)
        self.send_default_request()
        outputs = self.get_all_output()
        self.validate_outputs(outputs, modifiers=[update_expected_server])
コード例 #16
0
 def test_http_custom_response_headers_in_span_attributes(self):
     self.app = otel_asgi.OpenTelemetryMiddleware(
         http_app_with_custom_headers, tracer_provider=self.tracer_provider)
     self.seed_app(self.app)
     self.send_default_request()
     self.get_all_output()
     span_list = self.exporter.get_finished_spans()
     expected = {
         "http.response.header.custom_test_header_1":
         ("test-header-value-1", ),
         "http.response.header.custom_test_header_2":
         ("test-header-value-2", ),
     }
     for span in span_list:
         if span.kind == SpanKind.SERVER:
             self.assertSpanHasAttributes(span, expected)
コード例 #17
0
    def test_override_span_name(self):
        """Test that span_names can be overwritten by our callback function."""
        span_name = "Dymaxion"

        def get_predefined_span_details(_):
            return span_name, {}

        def update_expected_span_name(expected):
            for entry in expected:
                entry["name"] = " ".join([span_name] +
                                         entry["name"].split(" ")[-1:])
            return expected

        app = otel_asgi.OpenTelemetryMiddleware(
            simple_asgi, span_details_callback=get_predefined_span_details)
        self.seed_app(app)
        self.send_default_request()
        outputs = self.get_all_output()
        self.validate_outputs(outputs, modifiers=[update_expected_span_name])
コード例 #18
0
 def test_no_metric_for_websockets(self):
     self.scope = {
         "type": "websocket",
         "http_version": "1.1",
         "scheme": "ws",
         "path": "/",
         "query_string": b"",
         "headers": [],
         "client": ("127.0.0.1", 32767),
         "server": ("127.0.0.1", 80),
     }
     app = otel_asgi.OpenTelemetryMiddleware(simple_asgi)
     self.seed_app(app)
     self.send_input({"type": "websocket.connect"})
     self.send_input({"type": "websocket.receive", "text": "ping"})
     self.send_input({"type": "websocket.disconnect"})
     self.get_all_output()
     metrics_list = self.memory_metrics_reader.get_metrics_data()
     self.assertEqual(len(metrics_list.resource_metrics[0].scope_metrics),
                      0)
コード例 #19
0
 def test_basic_metric_success(self):
     app = otel_asgi.OpenTelemetryMiddleware(simple_asgi)
     self.seed_app(app)
     start = default_timer()
     self.send_default_request()
     duration = max(round((default_timer() - start) * 1000), 0)
     expected_duration_attributes = {
         "http.method": "GET",
         "http.host": "127.0.0.1",
         "http.scheme": "http",
         "http.flavor": "1.0",
         "net.host.port": 80,
         "http.status_code": 200,
     }
     expected_requests_count_attributes = {
         "http.method": "GET",
         "http.host": "127.0.0.1",
         "http.scheme": "http",
         "http.flavor": "1.0",
     }
     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),
                         )
                         self.assertEqual(point.count, 1)
                         self.assertAlmostEqual(duration,
                                                point.sum,
                                                delta=5)
                     elif isinstance(point, NumberDataPoint):
                         self.assertDictEqual(
                             expected_requests_count_attributes,
                             dict(point.attributes),
                         )
                         self.assertEqual(point.value, 0)
コード例 #20
0
    def test_websocket_traceresponse_header(self):
        """Test a traceresponse header is set for websocket messages"""

        orig = get_global_response_propagator()
        set_global_response_propagator(TraceResponsePropagator())

        self.scope = {
            "type": "websocket",
            "http_version": "1.1",
            "scheme": "ws",
            "path": "/",
            "query_string": b"",
            "headers": [],
            "client": ("127.0.0.1", 32767),
            "server": ("127.0.0.1", 80),
        }
        app = otel_asgi.OpenTelemetryMiddleware(simple_asgi)
        self.seed_app(app)
        self.send_input({"type": "websocket.connect"})
        self.send_input({"type": "websocket.receive", "text": "ping"})
        self.send_input({"type": "websocket.disconnect"})
        _, socket_send, *_ = self.get_all_output()

        span = self.memory_exporter.get_finished_spans()[-1]
        self.assertEqual(trace_api.SpanKind.SERVER, span.kind)

        traceresponse = "00-{0}-{1}-01".format(
            format_trace_id(span.get_span_context().trace_id),
            format_span_id(span.get_span_context().span_id),
        )
        self.assertListEqual(
            socket_send["headers"],
            [
                [b"traceresponse", f"{traceresponse}".encode()],
                [b"access-control-expose-headers", b"traceresponse"],
            ],
        )

        set_global_response_propagator(orig)
コード例 #21
0
 def setUp(self):
     super().setUp()
     self.tracer_provider, self.exporter = TestBase.create_tracer_provider()
     self.tracer = self.tracer_provider.get_tracer(__name__)
     self.app = otel_asgi.OpenTelemetryMiddleware(
         simple_asgi, tracer_provider=self.tracer_provider)
コード例 #22
0
 def test_websocket(self):
     self.scope = {
         "type": "websocket",
         "http_version": "1.1",
         "scheme": "ws",
         "path": "/",
         "query_string": b"",
         "headers": [],
         "client": ("127.0.0.1", 32767),
         "server": ("127.0.0.1", 80),
     }
     app = otel_asgi.OpenTelemetryMiddleware(simple_asgi)
     self.seed_app(app)
     self.send_input({"type": "websocket.connect"})
     self.send_input({"type": "websocket.receive", "text": "ping"})
     self.send_input({"type": "websocket.disconnect"})
     self.get_all_output()
     span_list = self.memory_exporter.get_finished_spans()
     self.assertEqual(len(span_list), 6)
     expected = [
         {
             "name": "/ websocket receive",
             "kind": trace_api.SpanKind.INTERNAL,
             "attributes": {
                 "type": "websocket.connect"
             },
         },
         {
             "name": "/ websocket send",
             "kind": trace_api.SpanKind.INTERNAL,
             "attributes": {
                 "type": "websocket.accept"
             },
         },
         {
             "name": "/ websocket receive",
             "kind": trace_api.SpanKind.INTERNAL,
             "attributes": {
                 "type": "websocket.receive",
                 SpanAttributes.HTTP_STATUS_CODE: 200,
             },
         },
         {
             "name": "/ websocket send",
             "kind": trace_api.SpanKind.INTERNAL,
             "attributes": {
                 "type": "websocket.send",
                 SpanAttributes.HTTP_STATUS_CODE: 200,
             },
         },
         {
             "name": "/ websocket receive",
             "kind": trace_api.SpanKind.INTERNAL,
             "attributes": {
                 "type": "websocket.disconnect"
             },
         },
         {
             "name": "/",
             "kind": trace_api.SpanKind.SERVER,
             "attributes": {
                 SpanAttributes.HTTP_SCHEME: self.scope["scheme"],
                 SpanAttributes.NET_HOST_PORT: self.scope["server"][1],
                 SpanAttributes.HTTP_HOST: self.scope["server"][0],
                 SpanAttributes.HTTP_FLAVOR: self.scope["http_version"],
                 SpanAttributes.HTTP_TARGET: self.scope["path"],
                 SpanAttributes.HTTP_URL:
                 f'{self.scope["scheme"]}://{self.scope["server"][0]}{self.scope["path"]}',
                 SpanAttributes.NET_PEER_IP: self.scope["client"][0],
                 SpanAttributes.NET_PEER_PORT: self.scope["client"][1],
                 SpanAttributes.HTTP_STATUS_CODE: 200,
             },
         },
     ]
     for span, expected in zip(span_list, expected):
         self.assertEqual(span.name, expected["name"])
         self.assertEqual(span.kind, expected["kind"])
         self.assertDictEqual(dict(span.attributes), expected["attributes"])