diff --git a/ext/opentelemetry-ext-flask/tests/test_programmatic.py b/ext/opentelemetry-ext-flask/tests/test_programmatic.py index 95f82373e3d..30fa9c4eb24 100644 --- a/ext/opentelemetry-ext-flask/tests/test_programmatic.py +++ b/ext/opentelemetry-ext-flask/tests/test_programmatic.py @@ -118,7 +118,7 @@ def test_404(self): resp.close() span_list = self.memory_exporter.get_finished_spans() self.assertEqual(len(span_list), 1) - self.assertEqual(span_list[0].name, "/bye") + self.assertEqual(span_list[0].name, "HTTP POST") self.assertEqual(span_list[0].kind, trace.SpanKind.SERVER) self.assertEqual(span_list[0].attributes, expected_attrs) diff --git a/ext/opentelemetry-ext-pyramid/tests/test_programmatic.py b/ext/opentelemetry-ext-pyramid/tests/test_programmatic.py index b1ecbb38bb7..e8937e8f5e6 100644 --- a/ext/opentelemetry-ext-pyramid/tests/test_programmatic.py +++ b/ext/opentelemetry-ext-pyramid/tests/test_programmatic.py @@ -101,7 +101,7 @@ def test_404(self): resp.close() span_list = self.memory_exporter.get_finished_spans() self.assertEqual(len(span_list), 1) - self.assertEqual(span_list[0].name, "/bye") + self.assertEqual(span_list[0].name, "HTTP POST") self.assertEqual(span_list[0].kind, trace.SpanKind.SERVER) self.assertEqual(span_list[0].attributes, expected_attrs) diff --git a/ext/opentelemetry-ext-wsgi/src/opentelemetry/ext/wsgi/__init__.py b/ext/opentelemetry-ext-wsgi/src/opentelemetry/ext/wsgi/__init__.py index c5b0216869c..9968a0c1c83 100644 --- a/ext/opentelemetry-ext-wsgi/src/opentelemetry/ext/wsgi/__init__.py +++ b/ext/opentelemetry-ext-wsgi/src/opentelemetry/ext/wsgi/__init__.py @@ -93,12 +93,15 @@ def collect_request_attributes(environ): result = { "component": "http", - "http.method": environ["REQUEST_METHOD"], - "http.server_name": environ["SERVER_NAME"], - "http.scheme": environ["wsgi.url_scheme"], - "host.port": int(environ["SERVER_PORT"]), + "http.method": environ.get("REQUEST_METHOD"), + "http.server_name": environ.get("SERVER_NAME"), + "http.scheme": environ.get("wsgi.url_scheme"), } + host_port = environ.get("SERVER_PORT") + if host_port is not None: + result.update({"host.port": int(host_port)}) + setifnotnone(result, "http.host", environ.get("HTTP_HOST")) target = environ.get("RAW_URI") if target is None: # Note: `"" or None is None` @@ -149,12 +152,8 @@ def add_response_attributes( def get_default_span_name(environ): - """Calculates a (generic) span name for an incoming HTTP request based on the PEP3333 conforming WSGI environ.""" - - # TODO: Update once - # https://github.com/open-telemetry/opentelemetry-specification/issues/270 - # is resolved - return environ.get("PATH_INFO", "/") + """Default implementation for name_callback, returns HTTP {METHOD_NAME}.""" + return "HTTP {}".format(environ.get("REQUEST_METHOD", "")).strip() class OpenTelemetryMiddleware: @@ -165,11 +164,15 @@ class OpenTelemetryMiddleware: Args: wsgi: The WSGI application callable to forward requests to. + name_callback: Callback which calculates a generic span name for an + incoming HTTP request based on the PEP3333 WSGI environ. + Optional: Defaults to get_default_span_name. """ - def __init__(self, wsgi): + def __init__(self, wsgi, name_callback=get_default_span_name): self.wsgi = wsgi self.tracer = trace.get_tracer(__name__, __version__) + self.name_callback = name_callback @staticmethod def _create_start_response(span, start_response): @@ -191,7 +194,7 @@ def __call__(self, environ, start_response): token = context.attach( propagators.extract(get_header_from_environ, environ) ) - span_name = get_default_span_name(environ) + span_name = self.name_callback(environ) span = self.tracer.start_span( span_name, diff --git a/ext/opentelemetry-ext-wsgi/tests/test_wsgi_middleware.py b/ext/opentelemetry-ext-wsgi/tests/test_wsgi_middleware.py index e56410b6121..0d018b68414 100644 --- a/ext/opentelemetry-ext-wsgi/tests/test_wsgi_middleware.py +++ b/ext/opentelemetry-ext-wsgi/tests/test_wsgi_middleware.py @@ -74,7 +74,9 @@ def error_wsgi(environ, start_response): class TestWsgiApplication(WsgiTestBase): - def validate_response(self, response, error=None): + def validate_response( + self, response, error=None, span_name="HTTP GET", http_method="GET" + ): while True: try: value = next(response) @@ -95,23 +97,22 @@ def validate_response(self, response, error=None): span_list = self.memory_exporter.get_finished_spans() self.assertEqual(len(span_list), 1) - self.assertEqual(span_list[0].name, "/") + self.assertEqual(span_list[0].name, span_name) self.assertEqual(span_list[0].kind, trace_api.SpanKind.SERVER) - self.assertEqual( - span_list[0].attributes, - { - "component": "http", - "http.method": "GET", - "http.server_name": "127.0.0.1", - "http.scheme": "http", - "host.port": 80, - "http.host": "127.0.0.1", - "http.flavor": "1.0", - "http.url": "http://127.0.0.1/", - "http.status_text": "OK", - "http.status_code": 200, - }, - ) + expected_attributes = { + "component": "http", + "http.server_name": "127.0.0.1", + "http.scheme": "http", + "host.port": 80, + "http.host": "127.0.0.1", + "http.flavor": "1.0", + "http.url": "http://127.0.0.1/", + "http.status_text": "OK", + "http.status_code": 200, + } + if http_method is not None: + expected_attributes["http.method"] = http_method + self.assertEqual(span_list[0].attributes, expected_attributes) def test_basic_wsgi_call(self): app = otel_wsgi.OpenTelemetryMiddleware(simple_wsgi) @@ -147,6 +148,27 @@ def test_wsgi_exc_info(self): response = app(self.environ, self.start_response) self.validate_response(response, error=ValueError) + def test_override_span_name(self): + """Test that span_names can be overwritten by our callback function.""" + span_name = "Dymaxion" + + def get_predefined_span_name(scope): + # pylint: disable=unused-argument + return span_name + + app = otel_wsgi.OpenTelemetryMiddleware( + simple_wsgi, name_callback=get_predefined_span_name + ) + response = app(self.environ, self.start_response) + self.validate_response(response, span_name=span_name) + + def test_default_span_name_missing_request_method(self): + """Test that default span_names with missing request method.""" + self.environ.pop("REQUEST_METHOD") + app = otel_wsgi.OpenTelemetryMiddleware(simple_wsgi) + response = app(self.environ, self.start_response) + self.validate_response(response, span_name="HTTP", http_method=None) + class TestWsgiAttributes(unittest.TestCase): def setUp(self):