OpenTelemetry with Spring Boot: Modern Distributed Tracing
In modern microservices architectures, tracking requests as they flow across service boundaries is critical for debugging performance bottlenecks, diagnosing failures, and maintaining system reliability. While Zipkin and Jaeger have been popular distributed tracing tools, OpenTelemetry has emerged as the new standard, offering vendor-neutral instrumentation, logs, metrics, and traces in a unified framework.
This article explores:
- Why OpenTelemetry (OTel) is replacing traditional tracing tools
- How to integrate OpenTelemetry with Spring Boot
- Best practices for distributed tracing in microservices
- Visualizing traces with Jaeger, Zipkin, and Grafana
1. Why OpenTelemetry? The Evolution Beyond Zipkin
The Limitations of Traditional Tracing (Zipkin/Jaeger)
- Vendor lock-in: Zipkin/Jaeger require specific SDKs.
- Limited correlation: Metrics, logs, and traces were separate.
- Manual instrumentation: Required explicit code changes.
How OpenTelemetry Solves These Problems
- Standardized APIs: Works across multiple backends (Jaeger, Zipkin, Prometheus, etc.).
- Auto-instrumentation: Captures traces without code changes.
- Unified Observability: Combines traces, metrics, and logs.
2. Setting Up OpenTelemetry in Spring Boot
Step 1: Add Dependencies
<!-- OpenTelemetry SDK & Auto-Instrumentation -->
<dependency>
<groupId>io.opentelemetry</groupId>
<artifactId>opentelemetry-api</artifactId>
<version>1.30.0</version>
</dependency>
<dependency>
<groupId>io.opentelemetry</groupId>
<artifactId>opentelemetry-sdk</artifactId>
<version>1.30.0</version>
</dependency>
<dependency>
<groupId>io.opentelemetry.instrumentation</groupId>
<artifactId>opentelemetry-spring-boot-starter</artifactId>
<version>2.1.0</version>
</dependency>
Step 2: Configure application.yml
opentelemetry:
service:
name: order-service
traces:
exporter:
otlp:
endpoint: http://otel-collector:4317 # OTLP Collector
logs:
exporter:
otlp:
enabled: true
metrics:
exporter:
otlp:
enabled: true
Step 3: Deploy an OpenTelemetry Collector
# docker-compose.yml
services:
otel-collector:
image: otel/opentelemetry-collector
ports:
- "4317:4317" # OTLP gRPC
- "4318:4318" # OTLP HTTP
volumes:
- ./otel-config.yaml:/etc/otel-config.yaml
command: ["--config=/etc/otel-config.yaml"]
Step 4: Export Traces to Jaeger/Zipkin
# otel-config.yaml
exporters:
jaeger:
endpoint: "jaeger:14250"
tls:
insecure: true
zipkin:
endpoint: "http://zipkin:9411/api/v2/spans"
service:
pipelines:
traces:
receivers: [otlp]
processors: [batch]
exporters: [jaeger, zipkin]
3. Advanced Distributed Tracing Techniques
Custom Spans for Business Logic
@GetMapping("/process")
public String processOrder() {
Span span = Span.current();
span.setAttribute("order.id", "12345"); // Custom attributes
try (Scope scope = span.makeCurrent()) {
// Business logic
inventoryService.reserveItems();
paymentService.chargeCustomer();
return "Order processed!";
} catch (Exception e) {
span.recordException(e); // Error tracking
throw e;
}
}
Baggage Propagation (Cross-Service Context)
// Set baggage in Service A
Baggage.current()
.toBuilder()
.put("user.id", "user-123")
.build()
.makeCurrent();
// Retrieve in Service B
String userId = Baggage.current().getEntryValue("user.id");
Sampling Strategies
# application.yml
opentelemetry:
traces:
sampler: parentbased_always_on # or "dynamic" for adaptive sampling
4. Visualizing Traces: Jaeger vs. Zipkin vs. Grafana
| Tool | Strengths | Best For |
|---|---|---|
| Jaeger | Powerful querying, dependency graphs | Debugging complex microservices |
| Zipkin | Simple UI, lightweight | Basic tracing needs |
| Grafana Tempo | Deep integration with Prometheus metrics | Full observability (logs + metrics + traces) |
5. Best Practices for Production
- Use OTLP (OpenTelemetry Protocol) instead of vendor-specific exporters.
- Enable Auto-Instrumentation for Spring Web, JDBC, Kafka, etc.
- Implement Sampling to reduce storage costs.
- Correlate Logs & Traces using
trace_id. - Monitor Collector Performance to avoid bottlenecks.
Conclusion
OpenTelemetry provides a future-proof, vendor-neutral way to implement distributed tracing in Spring Boot microservices. By replacing Zipkin/Jaeger with OTel, teams gain:
- Auto-instrumentation (no manual code changes)
- Unified observability (traces + metrics + logs)
- Better debugging with correlated telemetry




