Observability Reference/Distributed Tracing

Context Propagation & Sampling

W3C traceparent/tracestate, baggage for cross-cutting metadata, head vs tail sampling, and keeping traces coherent across gRPC, HTTP, and Kafka.

4/5Overview: 35m

Why propagation matters

A trace is only useful if every hop participates. Context propagation passes trace_id and span_id across process boundaries so child spans link to the parent.

Break propagation → orphan spans → useless fragmented traces.

W3C Trace Context

Standard HTTP headers:

traceparent: 00-{trace-id}-{parent-span-id}-{flags} tracestate: vendor-specific baggage
  • trace-id — 128-bit, same for entire request
  • parent-span-id — caller's span becomes parent
  • flags — sampling bit (01 = sampled)

All modern tracers (OTel, Datadog, Jaeger) support W3C. Legacy: B3 (Zipkin), Jaeger headers — use OTel propagators to translate.

Inject and extract

Client span start → inject context into HTTP headers → Server middleware extract → child span with parent link

Framework middleware (gRPC interceptors, HTTP filters) must call OTel propagator — don't hand-roll header parsing.

Baggage

Baggage carries arbitrary key-value metadata across services (e.g., tenant_id, experiment_flag).

Unlike span attributes, baggage propagates to all downstream spans automatically.

Caution: baggage rides on every request — keep it small; never put secrets or large blobs.

Async and messaging

Harder cases:

PatternApproach
Kafka consumerExtract trace context from message headers; consumer span links to producer
Thread poolContext must be attached to runnable (OTel Context.attach())
Batch jobsLink span to triggering trace via Link (not parent)

Lost context in thread hops is the #1 Java/Python tracing bug.

Sampling deep dive

Head sampling (decision at trace start):

  • Probabilistic: 1% of traces
  • Parent-based: respect upstream decision
  • Rate limiting: max N traces/sec

Tail sampling (decision after trace completes — OTel Collector):

  • Keep if: error, latency > threshold, specific attribute
  • Requires buffering complete traces — memory cost
Always sample: status=ERROR OR duration>2s Otherwise: 1% probabilistic

Tail sampling catches the slow checkout you'd miss with pure head sampling.

Coherent sampling

If parent is sampled, children must be sampled (parent-based). Otherwise traces fragment — you see a payment span with no gateway parent.

Interview scenarios

"Trace breaks at the message queue" — producer must inject headers into Kafka record headers; consumer extracts.

"Too many traces, high cost" — tail sample: 100% errors, 1% success; head sample at edge for volume cap.

Cross-reference

  • Distributed Systems — retries create duplicate spans; idempotency keys help correlate
  • Cardinality topic — span attributes are high-cardinality safe (unlike metric labels)

Link forward

SLOs use trace-derived latency SLIs; Production Debugging uses exemplars to jump from metrics to these traces.

Further Reading