From fd0dd6161692d0c42f597f6234185e31d921cd00 Mon Sep 17 00:00:00 2001 From: Owais Lone Date: Thu, 24 Sep 2020 18:38:44 +0530 Subject: [PATCH] Added ability to extract span attributes from django request objects. OTEL_PYTHON_DJANGO_TRACED_REQUEST_ATTRS env var can be set to a command separated list of attributes names that will be extracted from Django's request object and set as attributes on spans. --- .../CHANGELOG.md | 1 + .../README.rst | 13 +++++++++ .../instrumentation/django/middleware.py | 12 ++++++++ .../tests/test_middleware.py | 28 +++++++++++++++++++ 4 files changed, 54 insertions(+) diff --git a/instrumentation/opentelemetry-instrumentation-django/CHANGELOG.md b/instrumentation/opentelemetry-instrumentation-django/CHANGELOG.md index d3de446730a..30b13c4a223 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 5cb570c7e9f..80ef3aa9f09 100644 --- a/instrumentation/opentelemetry-instrumentation-django/README.rst +++ b/instrumentation/opentelemetry-instrumentation-django/README.rst @@ -30,6 +30,19 @@ 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. + 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 59f7e6e6229..81614da1f5d 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,11 @@ def process_request(self, request): tracer = get_tracer(__name__, __version__) attributes = collect_request_attributes(environ) + for attr in self._traced_request_attrs: + if hasattr(request, attr): + value = str(getattr(request, attr)) + if value: + attributes[attr] = 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 ee82c5d7d9f..7dbb4fc2677 100644 --- a/instrumentation/opentelemetry-instrumentation-django/tests/test_middleware.py +++ b/instrumentation/opentelemetry-instrumentation-django/tests/test_middleware.py @@ -14,6 +14,7 @@ from sys import modules from unittest.mock import patch +from test.support import EnvironmentVarGuard from django import VERSION from django.conf import settings @@ -174,3 +175,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)