How to Set Up Distributed Tracing with Jaeger
Deploy Jaeger, export OpenTelemetry spans to it, and propagate W3C trace context so requests are traced across services. Covers reading the waterfall view and configuring ratio sampling.
What and why
Distributed tracing follows a single request as it crosses service boundaries, recording each span and its timing. Jaeger is a popular open-source tracing backend. With OpenTelemetry generating spans and Jaeger storing and visualizing them, you can pinpoint which downstream call makes a request slow.
Prerequisites
- Two or more services that call each other over HTTP or gRPC.
- OpenTelemetry instrumentation already added to each service.
- Docker installed.
Steps
1. Run the Jaeger all-in-one image
docker run -d --name jaeger \
-p 16686:16686 \
-p 4317:4317 -p 4318:4318 \
jaegertracing/all-in-one:latest
Port 16686 serves the UI; 4317/4318 accept OTLP spans.
2. Point exporters at Jaeger
Configure each service to export OTLP to Jaeger:
export OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318
export OTEL_TRACES_EXPORTER=otlp
3. Propagate context across services
Context propagation is what links spans into one trace. OpenTelemetry SDKs inject the W3C traceparent header automatically when you use instrumented HTTP clients. If you build requests manually, inject context yourself:
const { propagation, context } = require('@opentelemetry/api');
const headers = {};
propagation.inject(context.active(), headers);
// pass headers on the outbound request
4. Generate cross-service traffic
Call the entry service so it fans out to the downstream service. Each hop should create child spans under the same trace ID.
5. Analyze traces in the UI
Open http://localhost:16686, select the entry service, and click Find Traces. Open a trace to see the waterfall. The widest bar in the timeline is your bottleneck; expand spans to read attributes and errors.
6. Configure sampling
Full tracing is expensive at scale. Use parent-based, ratio sampling:
export OTEL_TRACES_SAMPLER=parentbased_traceidratio
export OTEL_TRACES_SAMPLER_ARG=0.1
This keeps 10 percent of traces while preserving complete traces (a sampled parent keeps its children).
Verification
- The Jaeger UI lists your services.
- A single request appears as one trace spanning multiple services.
- Span attributes and any errors are visible in the waterfall.
Next Steps
Swap the all-in-one image for a production deployment with persistent storage (Elasticsearch or Cassandra), add tail-based sampling at the Collector, and correlate traces with logs and metrics by exemplars.
Prerequisites
- Two or more services that call each other
- OpenTelemetry instrumentation in place
- Docker installed
Steps
- 1Run the Jaeger all-in-one image
- 2Point exporters at Jaeger
- 3Propagate context across services
- 4Generate cross-service traffic
- 5Analyze traces in the UI
- 6Configure sampling