diff --git a/CHANGELOG.md b/CHANGELOG.md index e8391ab527..1fad0c2fc7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased](https://github.com/open-telemetry/opentelemetry-python/compare/v1.9.1-0.28b1...HEAD) +- `opentelemetry-instrumentation-wsgi` WSGI: Conditionally create SERVER spans + ([#903](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/903)) + ### Fixed - `opentelemetry-instrumentation-logging` retrieves service name defensively. @@ -21,7 +24,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `opentelemetry-instrumentation-pika` requires `packaging` dependency - `opentelemetry-instrumentation-tornado` Tornado: Conditionally create SERVER spans - ([#867](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/889)) + ([#889](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/889)) ## [1.9.0-0.28b0](https://github.com/open-telemetry/opentelemetry-python/releases/tag/v1.9.0-0.28b0) - 2022-01-26 diff --git a/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py b/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py index 54c4705f8e..fdb2076ccd 100644 --- a/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py @@ -109,9 +109,11 @@ def response_hook(span: Span, environ: WSGIEnvironment, status: str, response_he import wsgiref.util as wsgiref_util from opentelemetry import context, trace -from opentelemetry.instrumentation.utils import http_status_to_status_code +from opentelemetry.instrumentation.utils import ( + _start_internal_or_server_span, + http_status_to_status_code, +) from opentelemetry.instrumentation.wsgi.version import __version__ -from opentelemetry.propagate import extract from opentelemetry.propagators.textmap import Getter from opentelemetry.semconv.trace import SpanAttributes from opentelemetry.trace.status import Status, StatusCode @@ -279,12 +281,12 @@ def __call__(self, environ, start_response): environ: A WSGI environment. start_response: The WSGI start_response callable. """ - - token = context.attach(extract(environ, getter=wsgi_getter)) - - span = self.tracer.start_span( - get_default_span_name(environ), - kind=trace.SpanKind.SERVER, + span, token = _start_internal_or_server_span( + tracer=self.tracer, + span_name=get_default_span_name(environ), + start_time=None, + context_carrier=environ, + context_getter=wsgi_getter, attributes=collect_request_attributes(environ), ) @@ -308,7 +310,8 @@ def __call__(self, environ, start_response): if span.is_recording(): span.set_status(Status(StatusCode.ERROR, str(ex))) span.end() - context.detach(token) + if token is not None: + context.detach(token) raise @@ -324,7 +327,8 @@ def _end_span_after_iterating(iterable, span, tracer, token): if close: close() span.end() - context.detach(token) + if token is not None: + context.detach(token) # TODO: inherit from opentelemetry.instrumentation.propagators.Setter diff --git a/instrumentation/opentelemetry-instrumentation-wsgi/tests/test_wsgi_middleware.py b/instrumentation/opentelemetry-instrumentation-wsgi/tests/test_wsgi_middleware.py index daaa519ba8..13b39c9937 100644 --- a/instrumentation/opentelemetry-instrumentation-wsgi/tests/test_wsgi_middleware.py +++ b/instrumentation/opentelemetry-instrumentation-wsgi/tests/test_wsgi_middleware.py @@ -414,5 +414,35 @@ def test_basic_wsgi_call(self): self.validate_response(response, exporter) +class TestWsgiMiddlewareWrappedWithAnotherFramework(WsgiTestBase): + 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__) + + with tracer.start_as_current_span( + "test", kind=trace_api.SpanKind.SERVER + ) as parent_span: + app = otel_wsgi.OpenTelemetryMiddleware( + simple_wsgi, tracer_provider=tracer_provider + ) + response = app(self.environ, self.start_response) + while True: + try: + value = next(response) + self.assertEqual(value, b"*") + except StopIteration: + break + + span_list = exporter.get_finished_spans() + + self.assertEqual(trace_api.SpanKind.INTERNAL, span_list[0].kind) + self.assertEqual(trace_api.SpanKind.SERVER, parent_span.kind) + + # internal span should be child of the parent span we have provided + self.assertEqual( + parent_span.context.span_id, span_list[0].parent.span_id + ) + + if __name__ == "__main__": unittest.main() diff --git a/opentelemetry-instrumentation/src/opentelemetry/instrumentation/utils.py b/opentelemetry-instrumentation/src/opentelemetry/instrumentation/utils.py index 3d6ae39405..e4b0b03ba6 100644 --- a/opentelemetry-instrumentation/src/opentelemetry/instrumentation/utils.py +++ b/opentelemetry-instrumentation/src/opentelemetry/instrumentation/utils.py @@ -76,7 +76,12 @@ def unwrap(obj, attr: str): def _start_internal_or_server_span( - tracer, span_name, start_time, context_carrier, context_getter + tracer, + span_name, + start_time, + context_carrier, + context_getter, + attributes=None, ): """Returns internal or server span along with the token which can be used by caller to reset context @@ -107,5 +112,6 @@ def _start_internal_or_server_span( context=ctx, kind=span_kind, start_time=start_time, + attributes=attributes, ) return span, token