Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Django trace response headers #395

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ on:
- 'release/*'
pull_request:
env:
CORE_REPO_SHA: cad261e5dae1fe986c87e6965664b45cc9ab73c3
CORE_REPO_SHA: 7b11971c504387341df0c38f5a34d7d1293c7e4f

jobs:
build:
Expand Down
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
([#299](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/299))
- `opentelemetry-instrumenation-django` now supports request and response hooks.
([#407](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/407))
- Add trace response header support for Django.
([#395](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/395))

### Removed
- Remove `http.status_text` from span attributes
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@

from opentelemetry.context import attach, detach
from opentelemetry.instrumentation.django.version import __version__
from opentelemetry.instrumentation.propagators import (
get_global_back_propagator,
)
from opentelemetry.instrumentation.utils import extract_attributes_from_object
from opentelemetry.instrumentation.wsgi import (
add_response_attributes,
Expand Down Expand Up @@ -179,6 +182,11 @@ def process_response(self, request, response):
response,
)

propagator = get_global_back_propagator()
if propagator:
propagator.inject(response)

# record any exceptions raised while processing the request
exception = request.META.pop(self._environ_exception_key, None)
if _DjangoMiddleware._otel_response_hook:
_DjangoMiddleware._otel_response_hook( # pylint: disable=not-callable
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,19 @@
DjangoInstrumentor,
_DjangoMiddleware,
)
from opentelemetry.instrumentation.propagators import (
TraceResponsePropagator,
set_global_back_propagator,
)
from opentelemetry.sdk.trace import Span
from opentelemetry.test.test_base import TestBase
from opentelemetry.test.wsgitestutil import WsgiTestBase
from opentelemetry.trace import SpanKind, StatusCode
from opentelemetry.trace import (
SpanKind,
StatusCode,
format_span_id,
format_trace_id,
)
from opentelemetry.util.http import get_excluded_urls, get_traced_request_attrs

# pylint: disable=import-error
Expand All @@ -41,6 +50,7 @@
route_span_name,
traced,
traced_template,
with_response_header,
)

DJANGO_2_2 = VERSION >= (2, 2)
Expand All @@ -52,6 +62,7 @@
url(r"^excluded_arg/", excluded),
url(r"^excluded_noarg/", excluded_noarg),
url(r"^excluded_noarg2/", excluded_noarg2),
url(r"^response_header/", with_response_header),
url(r"^span_name/([0-9]{4})/$", route_span_name),
]
_django_instrumentor = DjangoInstrumentor()
Expand Down Expand Up @@ -309,3 +320,55 @@ def response_hook(span, request, response):
self.assertIsInstance(response_hook_args[1], HttpRequest)
self.assertIsInstance(response_hook_args[2], HttpResponse)
self.assertEqual(response_hook_args[2], response)

def test_trace_response_headers(self):
response = Client().get("/span_name/1234/")
self.assertNotIn("Server-Timing", response._headers)
self.memory_exporter.clear()

set_global_back_propagator(TraceResponsePropagator())

response = Client().get("/span_name/1234/")
span = self.memory_exporter.get_finished_spans()[0]

self.assertIn("traceresponse", response._headers)
self.assertEqual(
response._headers["access-control-expose-headers"][0],
"Access-Control-Expose-Headers",
)
self.assertEqual(
response._headers["access-control-expose-headers"][1],
"traceresponse",
)
self.assertEqual(
response._headers["traceresponse"][0], "traceresponse"
)
self.assertEqual(
response._headers["traceresponse"][1],
"00-{0}-{1}-01".format(
format_trace_id(span.get_span_context().trace_id),
format_span_id(span.get_span_context().span_id),
),
)
self.memory_exporter.clear()

def test_trace_response_header_pre_existing_header(self):
set_global_back_propagator(TraceResponsePropagator())

response = Client().get("/response_header/")
span = self.memory_exporter.get_finished_spans()[0]
self.assertIn("traceresponse", response._headers)
self.assertEqual(
response._headers["access-control-expose-headers"][1],
"X-Test-Header, traceresponse",
)
self.assertEqual(
response._headers["traceresponse"][1],
"abc; val=1, "
+ "00-{0}-{1}-01".format(
format_trace_id(span.get_span_context().trace_id),
format_span_id(span.get_span_context().span_id),
),
)

self.memory_exporter.clear()
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
from django.http import HttpResponse

from opentelemetry.instrumentation.propagators import (
_HTTP_HEADER_ACCESS_CONTROL_EXPOSE_HEADERS,
)


def traced(request): # pylint: disable=unused-argument
return HttpResponse()
Expand Down Expand Up @@ -29,3 +33,10 @@ def route_span_name(
request, *args, **kwargs
): # pylint: disable=unused-argument
return HttpResponse()


def with_response_header(request): # pylint: disable=unused-argument
response = HttpResponse()
response["traceresponse"] = "abc; val=1"
response[_HTTP_HEADER_ACCESS_CONTROL_EXPOSE_HEADERS] = "X-Test-Header"
return response