Observability & Operations
Logging & Distributed Tracing
Logs capture discrete events with full detail; traces stitch a single request's path across every service it touched into one timeline — together they answer "what happened" and "where did the time go" that metrics alone cannot.
- Structured logging (key-value/JSON, not free-form text) is what makes logs queryable at scale — grep does not scale past one machine
- A trace is a tree of spans; each span records one unit of work with a start time, duration, and parent span — the whole tree reconstructs the request's path
- Context propagation — a trace ID and span ID passed through every hop, in headers or message metadata — is what lets independently-deployed services contribute spans to the same trace
- Log correlation IDs, often the trace ID itself, let you jump from "this span was slow" to the exact log lines from that service during that span
- Sampling is unavoidable at scale — tracing every request is often too expensive, so systems sample a percentage, or trace every erroring/slow request unconditionally ("tail-based sampling")
- Metrics tell you something is wrong; traces tell you where; logs tell you why — they are complementary, not substitutes for each other
| Logs | Metrics | Traces | |
|---|---|---|---|
| Question answered | what exactly happened | is something wrong, in aggregate | where did the time go |
| Granularity | single event | aggregated over time | single request across services |
| Storage cost | high — full detail | low — pre-aggregated | high, mitigated by sampling |
Span span = tracer.spanBuilder("chargeCard").setParent(Context.current()).startSpan();
try (Scope scope = span.makeCurrent()) {
httpClient.send(request.header("traceparent", span.getSpanContext().toTraceparent()));
} finally {
span.end();
}