Table of contents
Part of nurago, a collection of independent Go packages for backend services.
import "github.com/tecnickcom/nurago/pkg/traceid"
Package traceid captures a request-scoped trace ID at the service boundary: it
reads the ID from an inbound HTTP header, propagates it through the
context.Context for the lifetime of the request, and writes it back into
outbound HTTP headers when calling downstream services, without coupling
business logic to any particular tracing framework.
How It Works
The package uses an unexported struct type (ctxKey) as the context key, guaranteeing no collision with any other package’s context values.
NewContextstores the trace ID in the context. If an ID is already present the context is returned unchanged, making it safe to call at every layer without overwriting an upstream-supplied ID.ForceContextstores the trace ID unconditionally, overwriting any ID already present; use it when the authoritative ID has just been determined and the context must agree with what is propagated downstream.FromContextretrieves the stored ID, falling back to a caller-supplied default (typicallyDefaultValue) when none is set.FromHTTPRequestHeaderextracts the trace ID from an incoming HTTP request header and validates it withValid(1 toMaxIDLencharacters from the set [0-9A-Za-z._-]). Any value that does not match (including an empty or absent header) is replaced with the caller-supplied default, preventing header-injection.SetHTTPRequestHeaderFromContextreads the ID from the context and writes it onto an outgoing [*http.Request] header, completing the propagation loop to downstream services.
The context stored under the key is not validated (see NewContext); callers
that ingest an ID from an untrusted origin should validate it with Valid, or
obtain it through FromHTTPRequestHeader, before storing or logging it.
The package also exports the conventional defaults DefaultHeader (“X-Request-ID”),
DefaultValue (""), and DefaultLogKey (“traceid”) so all services in a system
can share consistent naming without hardcoding strings.
Usage
At the inbound boundary (e.g. an HTTP middleware):
id := traceid.FromHTTPRequestHeader(r, traceid.DefaultHeader, traceid.DefaultValue)
ctx := traceid.NewContext(r.Context(), id)
// pass ctx to all downstream handlers
Anywhere in the call chain (logging, metrics, business logic):
id := traceid.FromContext(ctx, traceid.DefaultValue)
logger.With(traceid.DefaultLogKey, id).Info("processing request")
At an outbound boundary (e.g. before calling a downstream service):
traceid.SetHTTPRequestHeaderFromContext(ctx, outboundReq, traceid.DefaultHeader, traceid.DefaultValue)
When To Use
- Log lines across a request must share one correlation ID.
- You want trace propagation without coupling business logic to a tracing SDK.
- Downstream services expect the ID in a specific header.
Example
// a well-formed trace ID (1 to MaxIDLen chars from [0-9A-Za-z._-])
fmt.Println(traceid.Valid("0191b2f1-8f3a-7c2d-9e4b-1a2b3c4d5e6f"))
// an id carrying control characters is rejected, preventing header/log injection
fmt.Println(traceid.Valid("evil\r\nX-Injected: 1"))
// Output:
// true
// false
Full source is in example_traceid_test.go. More runnable examples are on pkg.go.dev.
Dependencies
This package reaches no external module: it uses only the Go standard library.