Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -142,12 +142,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 " + environ.get("REQUEST_METHOD")


class OpenTelemetryMiddleware:
Expand All @@ -158,11 +154,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=None):
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can just use get_default_span_name as default here:

def function(argument):
    return "prefix_0_" + argument


class Middleware:

    def __init__(self, callback=function):
        self.callback = callback

print(Middleware().callback("argument"))
print(Middleware(lambda x: "prefix_1_" + x).callback("argument"))
prefix_0_argument
prefix_1_argument

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The middleware is initialized only once per process right? Is it worth it adding a dedicated callback to init for this? What if the get_default_span_name() is a method on the middleware class? Users can then just override the class and change the default logic. If we don't want users to go through overriding classes, may be we can provide option constructors in addition? Something like, OpenTelemetryMiddleware.with_span_name_setter(name_setter_func).

I don't think this is a big deal but it feels we'll end up adding more and more args to init to allow customizing some behaviour. Might be worth it prescribing sub-classing instead.

Either that or perhaps the callback could be a generic span callback that gets the whole span and can modify the span in any way, setting the name being one.

self.wsgi = wsgi
self.tracer = trace.get_tracer(__name__, __version__)
self.name_callback = name_callback or get_default_span_name

@staticmethod
def _create_start_response(span, start_response):
Expand All @@ -182,7 +182,7 @@ def __call__(self, environ, start_response):
"""

parent_span = 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,
Expand Down
15 changes: 13 additions & 2 deletions ext/opentelemetry-ext-wsgi/tests/test_wsgi_middleware.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ 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"):
while True:
try:
value = next(response)
Expand All @@ -95,7 +95,7 @@ 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,
Expand Down Expand Up @@ -147,6 +147,17 @@ 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):
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)


class TestWsgiAttributes(unittest.TestCase):
def setUp(self):
Expand Down