Skip to content
Open
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: 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
********************************************************************************
Copy link
Contributor

@tammy-baylis-swi tammy-baylis-swi Nov 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for this helpful update!

Would it make sense to have this and the other new sections be subsections under Request and Response hooks? so e.g.

Suggested change
********************************************************************************
################################################################################

Copy link
Contributor

@xrmx xrmx Nov 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be as long as the title, also the other titles have the same issue

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