Distributed tracing tracks a single request as it flows through multiple services — essential for understanding latency and failures in a microservices architecture, where a single user action might touch many separate services.
What Distributed Tracing Solves
In a microservices architecture, a slow or failed request could originate in any of several services — without tracing, diagnosing exactly where time is spent or where a failure occurred requires painstakingly correlating logs across multiple systems manually.
Core Tracing Concepts
Trace — the complete journey of one request across all services it touches.
Span — a single unit of work within a trace (e.g. one service's processing time).
Context propagation — passing a trace identifier between services so their individual spans can be linked into one coherent trace.
Prerequisites
- Docker installed
- An application instrumented to send trace data (see How to Instrument an Application with OpenTelemetry)
Step 1 — Run Jaeger (All-in-One, for Evaluation/Small Scale)
docker run -d \
--name jaeger \
--restart unless-stopped \
-p 16686:16686 \
-p 4317:4317 \
-p 4318:4318 \
jaegertracing/all-in-one:latest
The all-in-one image bundles all Jaeger components for simple deployments; production at scale typically uses separately-scaled components instead.
Step 2 — Access the Jaeger UI
http://YOUR_SERVER_IP:16686
Step 3 — Instrument Your Application to Send Traces
Applications need instrumentation (via OpenTelemetry, the current standard) to generate and send trace data to Jaeger — see How to Instrument an Application with OpenTelemetry for the application-side setup, configured to export to your Jaeger instance's endpoint.
Step 4 — Generate Some Traffic
Make requests to your instrumented application to generate trace data, then check the Jaeger UI to confirm traces are appearing.
Step 5 — Search and Analyze Traces
In the Jaeger UI, search by service name, operation, or duration to find specific traces — each trace shows a timeline breakdown of how long each service/span took, immediately revealing where latency is concentrated.
Understanding a Trace Timeline
A typical trace view shows nested spans as horizontal bars, with width representing duration — a single unusually wide span quickly reveals which specific service call is the bottleneck in an otherwise fast overall request.
Sampling: Not Every Request Needs Full Tracing
For high-traffic services, tracing every single request can be expensive — configure sampling (e.g. tracing 1-10% of requests) to get statistically meaningful visibility without the full overhead, adjustable based on your actual traffic volume and storage capacity.
Correlating Traces with Logs
Include the trace ID in your application's structured logs (see Structured Logging Best Practices for Easier Debugging) — letting you jump directly from a specific log line to its full distributed trace context, and vice versa.
When Tracing Isn't Worth the Complexity
For a genuinely simple, single-service application, distributed tracing adds real complexity for limited benefit — it earns its value specifically when requests span multiple independent services.
Common Errors
No traces appear despite instrumentation — verify the application's OpenTelemetry exporter is correctly configured with Jaeger's actual endpoint and port, and check for connectivity/firewall issues between the application and Jaeger.
Continue Reading
- How to Instrument an Application with OpenTelemetry
- How to Correlate Logs, Metrics, and Traces During an Incident
- What Is Observability? Metrics, Logs, and Traces Explained
Browse more articles in Advanced Observability & Incident Management.