Distributed tracing
In a mesh of a dozen services, "checkout is slow" names a symptom with no address. A log gives you one service's account; a trace gives you the whole conversation with timing attached. Profile each service alone and you polish the fast ones while the time drains into a call three hops down you never saw.
Method
- Propagate context or the trace snaps at hop one. Verify every service
forwards the
traceparentheader (W3C Trace Context) or B3. A dropped header spawns a fresh root, and the child spans orphan into a tree you can never rejoin to the parent. - Name spans for the operation, not the method.
handleRequestsays nothing;GET /orders/:idandSELECT orders by usersay what ran. Setspan.kindto server, client, or producer so the waterfall nests parent and child correctly. - Follow the critical path, not the sum. In the waterfall, trace the chain of spans a later span actually blocked on. A 400ms span running beside a 900ms sibling is free; the 900ms one owns the budget. Attack the longest bar on the blocking path.
- Ride baggage for cross-cutting facts. Put
user.tierorfeature.flaginto OpenTelemetry baggage so it propagates to every downstream span. Later you query "traces where tier=free and duration>2s" without threading that value through every signature by hand. - Hunt the gap between spans, not only the long ones. Dead time with no child span open is queueing, pool wait, or GC that your instrumentation missed. A 300ms hole before the database span is the connection pool starving, not the query executing.
- Sort errored traces by where status flipped. Filter
status=errorand find the deepest span that set it. Parents inherit the failure, so the leaf that first went red is the origin; read its events for the exception.
Signals
- Can you point to the one span on the critical path that owns most of the latency?
- Does a single trace id ride the request from edge to database?
- On a failed request, did you locate the deepest span that first set error?
Boundaries
Tracing shows where time and errors move between services; it will not explain why one function is slow inside its own span. For that, drop to a profiler or add metrics. Sampling means a given one-off trace may not exist at all: tail-based sampling keeps errors, head-based may discard them.