diff --git a/instrumentation/opentelemetry-instrumentation-django/CHANGELOG.md b/instrumentation/opentelemetry-instrumentation-django/CHANGELOG.md index d3de446730..30b13c4a22 100644 --- a/instrumentation/opentelemetry-instrumentation-django/CHANGELOG.md +++ b/instrumentation/opentelemetry-instrumentation-django/CHANGELOG.md @@ -3,6 +3,7 @@ ## Unreleased - Changed span name extraction from request to comply semantic convention ([#992](https://github.com/open-telemetry/opentelemetry-python/pull/992)) +- Added support for `OTEL_PYTHON_DJANGO_TRACED_REQUEST_ATTRS` ([#1154](https://github.com/open-telemetry/opentelemetry-python/pull/1154)) ## Version 0.13b0 diff --git a/instrumentation/opentelemetry-instrumentation-django/README.rst b/instrumentation/opentelemetry-instrumentation-django/README.rst index 5cb570c7e9..a2b98cabf4 100644 --- a/instrumentation/opentelemetry-instrumentation-django/README.rst +++ b/instrumentation/opentelemetry-instrumentation-django/README.rst @@ -30,6 +30,21 @@ For example, will exclude requests such as ``https://site/client/123/info`` and ``https://site/xyz/healthcheck``. +Request attributes +******************** +To extract certain attributes from Django's request object and use them as span attributes, set the environment variable ``OTEL_PYTHON_DJANGO_TRACED_REQUEST_ATTRS`` to a comma +delimited list of request attribute names. + +For example, + +:: + + export OTEL_PYTHON_DJANGO_TRACED_REQUEST_ATTRS='path_info,content_type' + +will extract path_info and content_type attributes from every traced request and add them as span attritbues. + +Django Request object reference: https://docs.djangoproject.com/en/3.1/ref/request-response/#attributes + References ---------- 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 59f7e6e622..f468053168 100644 --- a/instrumentation/opentelemetry-instrumentation-django/src/opentelemetry/instrumentation/django/middleware.py +++ b/instrumentation/opentelemetry-instrumentation-django/src/opentelemetry/instrumentation/django/middleware.py @@ -58,6 +58,13 @@ class _DjangoMiddleware(MiddlewareMixin): else: _excluded_urls = ExcludeList(_excluded_urls) + _traced_request_attrs = [ + attr.strip() + for attr in (Configuration().DJANGO_TRACED_REQUEST_ATTRS or "").split( + "," + ) + ] + @staticmethod def _get_span_name(request): try: @@ -95,6 +102,10 @@ def process_request(self, request): tracer = get_tracer(__name__, __version__) attributes = collect_request_attributes(environ) + for attr in self._traced_request_attrs: + value = getattr(request, attr, None) + if value is not None: + attributes[attr] = str(value) span = tracer.start_span( self._get_span_name(request), diff --git a/instrumentation/opentelemetry-instrumentation-django/tests/test_middleware.py b/instrumentation/opentelemetry-instrumentation-django/tests/test_middleware.py index ee82c5d7d9..378139d1c5 100644 --- a/instrumentation/opentelemetry-instrumentation-django/tests/test_middleware.py +++ b/instrumentation/opentelemetry-instrumentation-django/tests/test_middleware.py @@ -174,3 +174,30 @@ def test_span_name_404(self): span = span_list[0] self.assertEqual(span.name, "HTTP GET") + + def test_traced_request_attrs(self): + with patch( + "opentelemetry.instrumentation.django.middleware._DjangoMiddleware._traced_request_attrs", + [], + ): + Client().get("/span_name/1234/", CONTENT_TYPE="test/ct") + span_list = self.memory_exporter.get_finished_spans() + self.assertEqual(len(span_list), 1) + + span = span_list[0] + self.assertNotIn("path_info", span.attributes) + self.assertNotIn("content_type", span.attributes) + self.memory_exporter.clear() + + with patch( + "opentelemetry.instrumentation.django.middleware._DjangoMiddleware._traced_request_attrs", + ["path_info", "content_type", "non_existing_variable"], + ): + Client().get("/span_name/1234/", CONTENT_TYPE="test/ct") + span_list = self.memory_exporter.get_finished_spans() + self.assertEqual(len(span_list), 1) + + span = span_list[0] + self.assertEqual(span.attributes["path_info"], "/span_name/1234/") + self.assertEqual(span.attributes["content_type"], "test/ct") + self.assertNotIn("non_existing_variable", span.attributes)