Distributed Tracing with Jaeger & OpenTelemetry

This is the current tracing implementation. The project was migrated from Zipkin + Brave to Jaeger + OpenTelemetry as part of the Spring Boot 3 modernisation. See the legacy Sleuth & Zipkin guide for the old approach.
Why Jaeger over Zipkin?
Both Zipkin and Jaeger are distributed tracing backends, but the ecosystem has been moving toward Jaeger (a CNCF graduated project) for its richer feature set, native Grafana integration, and alignment with the OpenTelemetry standard.
Comparison: Zipkin vs Jaeger
Origin
Twitter (2012)
Uber (2015)
CNCF Status
Not a CNCF project
✅ CNCF Graduated
UI
Simple, basic
Rich, feature-complete
Trace comparison
❌ Not supported
✅ Side-by-side comparison
Service dependency graph
Basic
✅ Rich, auto-generated
Search & filtering
Limited
✅ Advanced (tags, duration, errors)
Grafana integration
Plugin only
✅ Native datasource
OpenTelemetry (OTLP)
Not natively
✅ Native OTLP support
Spring Boot 3 client
micrometer-tracing-bridge-brave + zipkin-reporter-brave
micrometer-tracing-bridge-otel + opentelemetry-exporter-otlp
Storage backends
In-memory, MySQL, Cassandra, ES
Cassandra, ES, Kafka, Badger
Default UI port
9411
16686
How Jaeger Works
Jaeger follows the OpenTelemetry data model. Every request in the system produces a Trace, which is composed of Spans. Each span represents a unit of work done by one service.

Key Concepts
Trace — The full journey of a single request across all services. Identified by a unique
traceIdshared by every service that handles the request.Span — A single unit of work within one service (e.g. an HTTP handler, a DB query). Has its own
spanId, start time, and duration.Parent-Child relationship — Spans form a tree. When
ordercallsproductvia Feign, theproductspan is a child of theorderspan — this is what produces the indented waterfall view.Tags & Logs — Key-value metadata attached to spans (HTTP status code, error messages, user IDs) that make debugging possible.
Architecture: How Traces Flow to Jaeger

Trace ID in Logs
With OTel, each log line includes the traceId and spanId automatically. In our project you will see:
d275c2bdbf332de2bbe5166ca341c58d→traceId(same across all services for one request)b100f35cf830195a→spanId(unique per service hop)
Copy the traceId and paste it into Jaeger's search bar to instantly find all spans for that request.
Migration from Zipkin to Jaeger
What Changed
The migration involved three layers: the Maven dependencies, the application configuration, and the infrastructure.
1. Maven Dependencies (per service)
2. Application Configuration
The config key moved from management.zipkin.tracing.endpoint (Brave/Zipkin protocol) to management.otlp.tracing.endpoint (OTLP protocol). The sampling and enabled flags remain unchanged:
3. Infrastructure — Docker Compose
4. Infrastructure — Kubernetes
The k8s/minikube/bootstrap/zipkin/ and k8s/aws-eks/bootstrap/zipkin/ directories were replaced with jaeger/ directories containing a deployment.yml and service.yml for the Jaeger all-in-one container.
Zero Java code changes were needed.
Implementation in Our Project
Jaeger Ports Reference
16686
HTTP
Jaeger UI — open in browser
4318
HTTP
OTLP HTTP — services send traces here
4317
gRPC
OTLP gRPC — alternative transport
Accessing the Jaeger UI
Jaeger UI — Key Features
Search Traces
Select a Service, an optional Operation, set a time range, and click Find Traces. Each result shows a horizontal bar representing the total duration, and the number of spans inside.
Trace Detail View
Clicking a trace opens the waterfall view — each row is one span, color-coded per service, indented to show the parent-child tree. Clicking a span reveals its tags (HTTP method, status code, URL) and logs (error messages, events).
System Architecture Tab
Jaeger auto-generates a service dependency graph from the trace data. Every Feign call, RabbitMQ publish, or DB query is mapped. No manual configuration is needed.
Compare Traces
Select two traces side-by-side to compare their execution trees. Useful for spotting why the same operation is slow on one request but not another.
Last updated