diff --git a/instrumentation/opentelemetry-instrumentation-django/setup.cfg b/instrumentation/opentelemetry-instrumentation-django/setup.cfg index 40dbc5f109..a67b610115 100644 --- a/instrumentation/opentelemetry-instrumentation-django/setup.cfg +++ b/instrumentation/opentelemetry-instrumentation-django/setup.cfg @@ -45,6 +45,8 @@ install_requires = opentelemetry-semantic-conventions == 0.22.dev0 [options.extras_require] +asgi = + opentelemetry-instrumentation-asgi == 0.22.dev0 test = opentelemetry-test == 0.22.dev0 diff --git a/instrumentation/opentelemetry-instrumentation-django/src/opentelemetry/instrumentation/django/middleware.py b/instrumentation/opentelemetry-instrumentation-django/src/opentelemetry/instrumentation/django/middleware.py index f37372d721..1cba945c5d 100644 --- a/instrumentation/opentelemetry-instrumentation-django/src/opentelemetry/instrumentation/django/middleware.py +++ b/instrumentation/opentelemetry-instrumentation-django/src/opentelemetry/instrumentation/django/middleware.py @@ -26,7 +26,7 @@ from opentelemetry.instrumentation.utils import extract_attributes_from_object from opentelemetry.instrumentation.wsgi import ( add_response_attributes, - collect_request_attributes, + collect_request_attributes as wsgi_collect_request_attributes, wsgi_getter, ) from opentelemetry.propagate import extract @@ -47,6 +47,25 @@ except ImportError: MiddlewareMixin = object +try: + from django.core.handlers.asgi import ASGIRequest +except ImportError: + ASGIRequest = None + +try: + from opentelemetry.instrumentation.asgi import ( + asgi_getter, + collect_request_attributes as asgi_collect_request_attributes, + set_status_code, + ) + _is_asgi_supported = True +except ImportError: + asgi_getter = None + asgi_collect_request_attributes = None + set_status_code = None + _is_asgi_supported = False + + _logger = getLogger(__name__) _attributes_by_preference = [ [ @@ -111,6 +130,9 @@ def _get_span_name(request): except Resolver404: return "HTTP {}".format(request.method) + def _is_asgi_request(self, request): + return ASGIRequest and isinstance(request, ASGIRequest) + def process_request(self, request): # request.META is a dictionary containing all available HTTP headers # Read more about request.META here: @@ -119,12 +141,23 @@ def process_request(self, request): if self._excluded_urls.url_disabled(request.build_absolute_uri("?")): return + is_asgi_request = self._is_asgi_request(request) + if is_asgi_request and not _is_asgi_supported: + return + # pylint:disable=W0212 request._otel_start_time = time() request_meta = request.META - token = attach(extract(request_meta, getter=wsgi_getter)) + if is_asgi_request: + carrier_getter = asgi_getter + collect_request_attributes = asgi_collect_request_attributes + else: + carrier_getter = wsgi_getter + collect_request_attributes = wsgi_collect_request_attributes + + token = attach(extract(request_meta, getter=carrier_getter)) span = self._tracer.start_span( self._get_span_name(request), @@ -186,15 +219,22 @@ def process_response(self, request, response): if self._excluded_urls.url_disabled(request.build_absolute_uri("?")): return response + is_asgi_request = self._is_asgi_request(request) + if is_asgi_request and not _is_asgi_supported: + return + activation = request.META.pop(self._environ_activation_key, None) span = request.META.pop(self._environ_span_key, None) if activation and span: - add_response_attributes( - span, - "{} {}".format(response.status_code, response.reason_phrase), - response, - ) + if is_asgi_request: + set_status_code(request.META[self._environ_span_key], response.status_code) + else: + add_response_attributes( + span, + "{} {}".format(response.status_code, response.reason_phrase), + response, + ) propagator = get_global_response_propagator() if propagator: @@ -217,7 +257,10 @@ def process_response(self, request, response): activation.__exit__(None, None, None) if self._environ_token in request.META.keys(): - detach(request.environ.get(self._environ_token)) + if is_asgi_request: + detach(request.META.get(self._environ_token)) + else: + detach(request.environ.get(self._environ_token)) request.META.pop(self._environ_token) return response diff --git a/tox.ini b/tox.ini index f3bd555015..8bff7ef520 100644 --- a/tox.ini +++ b/tox.ini @@ -233,7 +233,7 @@ commands_pre = falcon,flask,django,pyramid,tornado,starlette,fastapi: pip install {toxinidir}/util/opentelemetry-util-http[test] wsgi,falcon,flask,django,pyramid: pip install {toxinidir}/instrumentation/opentelemetry-instrumentation-wsgi[test] - asgi,starlette,fastapi: pip install {toxinidir}/instrumentation/opentelemetry-instrumentation-asgi[test] + asgi,django,starlette,fastapi: pip install {toxinidir}/instrumentation/opentelemetry-instrumentation-asgi[test] asyncpg: pip install {toxinidir}/instrumentation/opentelemetry-instrumentation-asyncpg[test] @@ -328,7 +328,7 @@ commands = [testenv:lint] basepython: python3.8 -recreate = False +recreate = False deps = -c dev-requirements.txt flaky