Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
([#3884](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3884))
- `opentelemetry-instrumentation-aiohttp-server`: add support for custom header captures via `OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_REQUEST` and `OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_RESPONSE`
([#3916](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3916))
- `opentelemetry-instrumentation-django`: improve docs for response_hook with examples of providing attributes from middlewares
([#3923](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3923))

### Fixed

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@

will extract the ``path_info`` and ``content_type`` attributes from every traced request and add them as span attributes.

Django Request object reference: https://docs.djangoproject.com/en/3.1/ref/request-response/#attributes
* `Django Request object reference <https://docs.djangoproject.com/en/5.2/ref/request-response/#attributes>`_

Request and Response hooks
***************************
Expand All @@ -77,8 +77,76 @@ def response_hook(span, request, response):

DjangoInstrumentor().instrument(request_hook=request_hook, response_hook=response_hook)

Django Request object: https://docs.djangoproject.com/en/3.1/ref/request-response/#httprequest-objects
Django Response object: https://docs.djangoproject.com/en/3.1/ref/request-response/#httpresponse-objects
* `Django Request object <https://docs.djangoproject.com/en/5.2/ref/request-response/#httprequest-objects>`_
* `Django Response object <https://docs.djangoproject.com/en/5.2/ref/request-response/#httpresponse-objects>`_

Adding attributes from middleware context
********************************************************************************
In many Django applications, certain request attributes become available only *after*
specific middlewares have executed. For example:

- ``django.contrib.auth.middleware.AuthenticationMiddleware`` populates ``request.user``
- ``django.contrib.sites.middleware.CurrentSiteMiddleware`` populates ``request.site``

Because the OpenTelemetry instrumentation creates the span **before** Django middlewares run,
these attributes are **not yet available** in the ``request_hook`` stage.

Therefore, such attributes should be safely attached in the **response_hook**, which executes
after Django finishes processing the request (and after all middlewares have completed).

Example: Attaching the authenticated user and current site to the span:

.. code:: python

def response_hook(span, request, response):
# Attach user information if available
if request.user.is_authenticated:
span.set_attribute("enduser.id", request.user.pk)
span.set_attribute("enduser.username", request.user.get_username())

# Attach current site (if provided by CurrentSiteMiddleware)
if getattr(request, "site", None):
span.set_attribute("site.id", getattr(request.site, "pk", None))
span.set_attribute("site.domain", getattr(request.site, "domain", None))

DjangoInstrumentor().instrument(response_hook=response_hook)

This ensures that middleware-dependent context (like user or site information) is properly
recorded once Django’s middleware stack has finished execution.

Custom Django middleware can also attach arbitrary data to the ``request`` object,
which can later be included as span attributes in the ``response_hook``.

* `Django middleware reference <https://docs.djangoproject.com/en/5.2/topics/http/middleware/>`_

Best practices
***************
- Use **response_hook** (not request_hook) when accessing attributes added by Django middlewares.
- Common middleware-provided attributes include:

- ``request.user`` (AuthenticationMiddleware)
- ``request.site`` (CurrentSiteMiddleware)

- Avoid adding large or sensitive data (e.g., passwords, session tokens, PII) to spans.
- Use **namespaced attribute keys**, e.g., ``enduser.*``, ``site.*``, or ``custom.*``, for clarity.
- Hooks should execute quickly — avoid blocking or long-running operations.
- Hooks can be safely combined with OpenTelemetry **Context propagation** or **Baggage**
for consistent tracing across services.

* `OpenTelemetry semantic conventions <https://opentelemetry.io/docs/specs/semconv/http/http-spans/>`_

Middleware execution order
******************************************
In Django’s request lifecycle, the OpenTelemetry `request_hook` is executed before
the first middleware runs. Therefore:

- At `request_hook` time → only the bare `HttpRequest` object is available.
- After middlewares → `request.user`, `request.site` etc. become available.
- At `response_hook` time → all middlewares (including authentication and site middlewares)
have already run, making it the correct place to attach these attributes.

Developers who need to trace attributes from middlewares should always use `response_hook`
to ensure complete and accurate span data.

Capture HTTP request and response headers
*****************************************
Expand Down