Tracing & APM
Rewind any request through every hop
Distributed tracing follows one request across services using propagated trace-ids and spans. APM layers on metrics/dashboards: service maps, error rates, and latency breakdowns that turn 'the checkout is slow' into 'the payments call is slow'.
What you will learn
- Propagate trace context across services and queues
- Interpret span trees with latency attribution
- Use APM service maps for shotgun diagnosis
A trace = one request's tree of spans (client → LB → service → DB). Every hop adds a span carrying parent-id; the collector stitches by trace-id. APM visualizes as service maps + error/latency dashboards on top of trace data.
What is this?
Tools: OpenTelemetry (SDK + collectors, vendor-neutral), AWS X-Ray, Azure Application Insights, GCP Cloud Trace, Datadog/New Relic APM. Protocol: W3C trace-context headers (traceparent), baggage. Span parts: name, start/end, status, attributes, links. Sampling: head (probabilistic at entry), tail (retain by result), rate-limited; OpenTelemetry parent-based sampler. Auto-instrumentation: language agents hook HTTP/DB frameworks (zero-code for common paths). APM surface: service map (topology + latency), golden signals per service, DB call breakdowns, error traces, slow-trace search. Correlation: attach trace-id to every log line for log↔trace join.
Why do you need it?
- Latency is almost never 'that box is slow' — it's a specific call in a chain.
- Correlated logs + traces answer 80% of production bugs in minutes.
- Sampling makes full-fidelity trace data affordable at traffic scale.
- APM dashboards put the whole estate's shape on one reel.
Mental model
A courier's package timeline: pickup, sorting, each leg, with its own tracking number
Tracing
Every facility stamps its leg (span) attached to the same tracking number (trace-id). When a parcel is late, you see exactly which facility delayed it — not 'the courier is slow'.
How it works
- Client creates trace-id + root span; sends W3C headers downstream.
- Each service starts its own span with parent from the header; adds attributes.
- Collector receives spans, groups by trace-id into complete tree.
- Backend computes latency per span, percentile distributions, and service-linked graphs.
- Dashboards + alerts hang off trace-derived series (APM).
Trace tree.
Examples
OpenTelemetry-ish manual propagation
// server middleware reads + activates contextconst ctx = propagation.extract(global.active, req.headers);const tracer = trace.getTracer('checkout');const span = tracer.startSpan('handleOrder', { kind: 1 /*server*/ }, ctx);// DB call gets child span; span ends on response// log line: { trace_id: span.spanContext().traceId }Explicit extraction + child spans; the collector needs nothing but the emitted spans.
In production
- Auto-instrument the HTTP/DB layer fleet-wide via OTel agents.
- Tail-sample errors at full fidelity + head-sample the rest to 5%.
- APM alert: p99 trace root exceeded for the checkout path → page with trace links.
Best practices
- Propagate trace context through EVERY hop — a dropped header kills the trace.
- Keep span counts sane: don't over-instrument; one span per logical operation.
- Attach trace-id in logs for the log↔trace join.
- Sample deliberately (head + tail); never lose the errors.
- Use semantic conventions so attributes stay consistent across services.
Common mistakes
- No propagation middleware → traces start and stop per service (orphan spans).
- Sampling at 100% everywhere → bill melts for a lovely waterfall chart.
- Logs without trace-id → you have the nice trace, but can't connect it to the event.
- Instrumenting only 'interesting' endpoints → blind spots in the path you're trying to debug.
Debugging tips
- A trace ends early → the backend service dropped the context (missing extract).
- Big gap between consecutive spans → queue/network wait; check queue depth + message lag.
- Trace shows one mega-service span → code didn't create child spans for DB/HTTP calls.
- p99 spike traces can't reproduce → sample retains, so bias filtering by status/resource.
Interview checkpoint
Hands-on lab
- Run OTel SDK with manual + automatic spans on two local services
- Propagate context so the trace becomes a single tree
- Inspect the tree in a local collector export
Trace One Request Across Two Handlers
35 min · intermediate
Quick quiz
A span is:
The trace's most fragile step is: