Enterprise Java

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

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
<!-- 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

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
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

01
02
03
04
05
06
07
08
09
10
# 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

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
# otel-config.yaml
exporters:
  jaeger:
    endpoint: "jaeger:14250"
    tls:
      insecure: true
  zipkin:
 
service:
  pipelines:
    traces:
      receivers: [otlp]
      processors: [batch]
      exporters: [jaeger, zipkin]

3. Advanced Distributed Tracing Techniques

Custom Spans for Business Logic

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
@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)

1
2
3
4
5
6
7
8
9
// 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

1
2
3
4
# application.yml
opentelemetry:
  traces:
    sampler: parentbased_always_on # or "dynamic" for adaptive sampling

4. Visualizing Traces: Jaeger vs. Zipkin vs. Grafana

ToolStrengthsBest For
JaegerPowerful querying, dependency graphsDebugging complex microservices
ZipkinSimple UI, lightweightBasic tracing needs
Grafana TempoDeep integration with Prometheus metricsFull observability (logs + metrics + traces)

5. Best Practices for Production

  1. Use OTLP (OpenTelemetry Protocol) instead of vendor-specific exporters.
  2. Enable Auto-Instrumentation for Spring Web, JDBC, Kafka, etc.
  3. Implement Sampling to reduce storage costs.
  4. Correlate Logs & Traces using trace_id.
  5. 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

Further Reading

Eleftheria Drosopoulou

Eleftheria is an Experienced Business Analyst with a robust background in the computer software industry. Proficient in Computer Software Training, Digital Marketing, HTML Scripting, and Microsoft Office, they bring a wealth of technical skills to the table. Additionally, she has a love for writing articles on various tech subjects, showcasing a talent for translating complex concepts into accessible content.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Back to top button