From 95a5b82ff32d2f84f4c3fe0bbf0c3c2998b80f13 Mon Sep 17 00:00:00 2001 From: Owais Lone Date: Mon, 19 Oct 2020 13:50:46 +0530 Subject: [PATCH] Django: Record status, http.status_code and event on exception --- .../instrumentation/django/middleware.py | 29 +++++++++---------- .../tests/test_middleware.py | 11 ++++++- .../CHANGELOG.md | 3 ++ 3 files changed, 26 insertions(+), 17 deletions(-) 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 0503d7bbcb6..41343873d04 100644 --- a/instrumentation/opentelemetry-instrumentation-django/src/opentelemetry/instrumentation/django/middleware.py +++ b/instrumentation/opentelemetry-instrumentation-django/src/opentelemetry/instrumentation/django/middleware.py @@ -60,6 +60,7 @@ class _DjangoMiddleware(MiddlewareMixin): ) _environ_token = "opentelemetry-instrumentor-django.token" _environ_span_key = "opentelemetry-instrumentor-django.span_key" + _environ_exception_key = "opentelemetry-instrumentor-django.exception_key" _excluded_urls = Configuration().DJANGO_EXCLUDED_URLS or [] if _excluded_urls: @@ -177,22 +178,11 @@ def process_view(self, request, view_func, *args, **kwargs): span.set_attribute("http.route", route) def process_exception(self, request, exception): - # Django can call this method and process_response later. In order - # to avoid __exit__ and detach from being called twice then, the - # respective keys are being removed here. if self._excluded_urls.url_disabled(request.build_absolute_uri("?")): return if self._environ_activation_key in request.META.keys(): - request.META[self._environ_activation_key].__exit__( - type(exception), - exception, - getattr(exception, "__traceback__", None), - ) - request.META.pop(self._environ_activation_key) - - detach(request.environ[self._environ_token]) - request.META.pop(self._environ_token, None) + request.META[self._environ_exception_key] = exception def process_response(self, request, response): if self._excluded_urls.url_disabled(request.build_absolute_uri("?")): @@ -213,9 +203,17 @@ def process_response(self, request, response): ) request.META.pop(self._environ_span_key) - request.META[self._environ_activation_key].__exit__( - None, None, None - ) + exception = request.META.pop(self._environ_exception_key, None) + if exception: + request.META[self._environ_activation_key].__exit__( + type(exception), + exception, + getattr(exception, "__traceback__", None), + ) + else: + request.META[self._environ_activation_key].__exit__( + None, None, None + ) request.META.pop(self._environ_activation_key) if self._environ_token in request.META.keys(): @@ -231,5 +229,4 @@ def process_response(self, request, response): ) except Exception as ex: # pylint: disable=W0703 _logger.warning("Error recording duration metrics: %s", ex) - return response diff --git a/instrumentation/opentelemetry-instrumentation-django/tests/test_middleware.py b/instrumentation/opentelemetry-instrumentation-django/tests/test_middleware.py index 5c034a23af2..4aa794f0de2 100644 --- a/instrumentation/opentelemetry-instrumentation-django/tests/test_middleware.py +++ b/instrumentation/opentelemetry-instrumentation-django/tests/test_middleware.py @@ -194,7 +194,7 @@ def test_error(self): ) self.assertEqual(span.kind, SpanKind.SERVER) self.assertEqual( - span.status.canonical_code, StatusCanonicalCode.UNKNOWN + span.status.canonical_code, StatusCanonicalCode.INTERNAL ) self.assertEqual(span.attributes["http.method"], "GET") self.assertEqual( @@ -202,13 +202,22 @@ def test_error(self): ) self.assertEqual(span.attributes["http.route"], "^error/") self.assertEqual(span.attributes["http.scheme"], "http") + self.assertEqual(span.attributes["http.status_code"], 500) self.assertIsNotNone(_django_instrumentor.meter) self.assertEqual(len(_django_instrumentor.meter.metrics), 1) + + self.assertEqual(len(span.events), 1) + event = span.events[0] + self.assertEqual(event.name, "exception") + self.assertEqual(event.attributes["exception.type"], "ValueError") + self.assertEqual(event.attributes["exception.message"], "error") + recorder = _django_instrumentor.meter.metrics.pop() match_key = get_dict_as_key( { "http.flavor": "1.1", "http.method": "GET", + "http.status_code": "500", "http.url": "http://testserver/error/", } ) diff --git a/instrumentation/opentelemetry-instrumentation-flask/CHANGELOG.md b/instrumentation/opentelemetry-instrumentation-flask/CHANGELOG.md index a4641e55b66..bd6ea89cb21 100644 --- a/instrumentation/opentelemetry-instrumentation-flask/CHANGELOG.md +++ b/instrumentation/opentelemetry-instrumentation-flask/CHANGELOG.md @@ -2,6 +2,9 @@ ## Unreleased +- Record span status and http.status_code attribute on exception + ([#1257](https://github.com/open-telemetry/opentelemetry-python/pull/1257)) + ## Version 0.13b0 Released 2020-09-17