feat(middleware): add W3C Trace Context correlation middleware - #4970
feat(middleware): add W3C Trace Context correlation middleware#4970cofin wants to merge 5 commits into
Conversation
| return value | ||
|
|
||
|
|
||
| class CorrelationContext: |
There was a problem hiding this comment.
Is this necessary? It diverges from our usual design quite a bit, and context vars are easy to break e.g. by not copying context properly (which is quite common, and I'm not even sure we're always doing it correctly everywhere).
IMO we should put this in ScopeState, since it only exists within a request context, and whenever you're in a request context, you'll also have access to the scope, and therefore the ScopeState. You could still add a convenience wrapper, just one that stores it in scope instead of a context var.
There was a problem hiding this comment.
yep - we can definitely go this route. Give me some time to rework this, but I'd much prefer to keep it simple if there's a better way.
| """ | ||
| for name in self.header_names: | ||
| name_bytes = name.encode("latin-1") | ||
| for raw_name, raw_value in scope.get("headers", ()): |
There was a problem hiding this comment.
Why not use our built in headers utils for this?
| "x-cloud-trace-context", | ||
| "grpc-trace-bin", | ||
| "x-amzn-trace-id", | ||
| "x-b3-traceid", |
There was a problem hiding this comment.
I think these proprietary ones should not be enabled by default? Could lead to confusing results otherwise
There was a problem hiding this comment.
To me, I think auto-handling the cloud provider's injection would be the least confusing right? If someone is using this on a cloud console, it would "just work". Otherwise, they'd have a different correlation ID than the one pushed in by the vendor.
I think most of the cloud vendors honor one of these, so I don't think it's a massive list we have to maintain, but I can double check this.
Would this be a configuration item we'd need to add otherwise?
There was a problem hiding this comment.
I thought about this since we last discussed. I do agree with taking them out. I'll remove the cloud specific ones and provide a way to override/append additional correlation headers.
| async def _await_middleware(awaitable: Awaitable[None]) -> None: | ||
| await awaitable |
There was a problem hiding this comment.
What's the purpose of this?
Co-authored-by: Janek Nouvertné <provinzkraut@posteo.de>
… context var - drop the ContextVar-based CorrelationContext in favor of storing the ID on ScopeState, making it available to handlers and other middlewares via the scope; add a get_correlation_id() accessor - use Headers.from_scope() for header extraction instead of iterating raw scope headers - base the middleware on ASGIMiddleware - fix inverted hex validation that rejected all valid traceparent values
…nd improve documentation
3c150ea to
b5767d3
Compare
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #4970 +/- ##
==========================================
+ Coverage 67.32% 67.44% +0.11%
==========================================
Files 293 294 +1
Lines 15246 15377 +131
Branches 1728 1751 +23
==========================================
+ Hits 10265 10371 +106
- Misses 4834 4857 +23
- Partials 147 149 +2 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
Description
Applications need a consistent correlation identifier across handlers, logging, and other request-scoped work without requiring an observability integration. This adds standalone correlation middleware for HTTP and WebSocket connections.
CorrelationMiddlewareselects the first safe value from a configurable list of request headers and generates a UUID when no suitable value is present. W3Ctraceparentvalues are validated before extracting the trace ID. The selected identifier is available throughCorrelationContextandscope["state"]["correlation_id"], and can be written to a configurable response header.Header names are normalized and deduplicated in priority order. Empty values and values containing control characters are skipped. Existing response correlation headers are replaced, and request context and scope state are restored when the middleware unwinds.
The middleware reads directly from the ASGI scope without additional dependencies or blocking work. It uses
__slots__, performs bounded header processing, and does not add application-level state.Refs #4719
📚 Documentation preview 📚: https://litestar-org.github.io/litestar-docs-preview/4970