fix(server,grpc): continue inbound W3C traces in HTTP middleware - #60
Merged
Conversation
Both HTTP tracing middlewares started their server span from the raw request context, so an inbound traceparent header was discarded and every span became a new root. A caller instrumented with this library's own rest client injects traceparent on the way out (rest/otel_middleware.go), and the grpc interceptors extract it on the way in — but the HTTP side of server and the gRPC gateway did not, so a rest -> server hop rendered as two disconnected traces. Extract W3C trace context and baggage from the request headers before starting the span, using the same composite propagator the gRPC interceptors already use. With no inbound headers this is a no-op and the span is still a root. Covers both packages together because they share the defect and the fix, and a consumer hitting one usually hits the other.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Both HTTP tracing middlewares started their server span from the raw request context:
No
Extract, so an inboundtraceparentheader was discarded and every server span became a new root.This is inconsistent within the library itself:
restclient outboundtraceparent✅grpcunary/stream interceptorsserverHTTP middlewaregrpcgateway HTTP middlewareSo a
rest→servercall made with this library on both ends rendered as two disconnected traces.Change
Extract W3C trace context + baggage from request headers before starting the span, reusing the composite propagator the gRPC interceptors already use. With no inbound headers this is a no-op and the span is still a root.
Tests (written first, observed failing)
New tests assert the span joins the caller's trace. Before the fix:
Added for both packages, plus a companion test pinning the no-header case to a root span so the fix can't regress into always-remote-parent.
Verification
task ci:check— all packages pass, golangci-lint 0 issues.go vet -tags='example integration argo' ./...clean.Note for the migration guide
Existing users gain correctly-parented server spans. Traces that previously appeared as separate roots will now join their caller — a visible change in any tracing backend, in the correct direction. Closes the "Shared gap (server + grpc)" item in
docs/plans/2026-07-22-v3-audit-backlog.md.