OpenTelemetry for Spring Boot: Guide with Examples
Monitoring distributed Spring Boot applications requires robust observability tools. This guide shows you how to implement OpenTelemetry for Spring Boot integration, covering automatic instrumentation, OpenTelemetry manual instrumentation Java examples, and production deployment strategies.
Components of Spring OpenTelemetry
Spring OpenTelemetry provides comprehensive observability through three key data types:
- Distributed Traces: Complete request flows across microservices
- Application Metrics: Performance indicators and business KPIs
- Structured Logs: Contextual events with trace correlation
OpenTelemetry vs Micrometer in Spring
| Feature | OpenTelemetry | Micrometer |
|---|---|---|
| Scope | Traces, Metrics, Logs | Primarily Metrics |
| Distributed Tracing | Full W3C standard support | Limited capabilities |
| Backend Support | Any OTLP-compatible system | Vendor-specific |
| Spring Integration | Auto + Manual instrumentation | Native Spring Boot |
Recommendation: Use OpenTelemetry Spring Boot for distributed systems requiring comprehensive observability. For APM capabilities, OpenTelemetry provides full application performance monitoring with traces, metrics, and logs. For a detailed comparison, see OpenTelemetry vs Micrometer.
Business Benefits of Spring
Implementing OpenTelemetry in Spring Boot applications delivers measurable improvements:
- Reduced MTTR: Faster root cause identification with distributed traces
- Proactive Monitoring: Detect issues before user impact
- End-to-End Visibility: Track requests across entire microservice architecture
- Performance Optimization: Data-driven optimization decisions
Organizations typically see 40-60% reduction in incident resolution time after proper implementation.
Step-by-Step Spring Implementation
Approach 1: Java Agent (Recommended)
Best for: Quick setup with zero code changes
For a complete guide to the Java Agent with Spring Boot, see OpenTelemetry Java Agent for Spring Boot. For other Java instrumentation options, see the OpenTelemetry Java guide.
# Download OpenTelemetry Java Agent
wget https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/latest/download/opentelemetry-javaagent.jar
# Configure environment
export OTEL_SERVICE_NAME=spring-app
export OTEL_SERVICE_VERSION=1.0.0
export OTEL_TRACES_EXPORTER=otlp
export OTEL_EXPORTER_OTLP_ENDPOINT=https://api.uptrace.dev:4317
export OTEL_EXPORTER_OTLP_HEADERS=uptrace-dsn=<your_uptrace_dsn>
# Run with instrumentation
java -javaagent:opentelemetry-javaagent.jar -jar your-spring-app.jar
Approach 2: Spring Boot Starter
Best for: Custom instrumentation and fine-grained control
Dependencies (Gradle):
dependencies {
implementation 'io.opentelemetry.instrumentation:opentelemetry-spring-boot-starter:1.32.0-alpha'
implementation 'io.opentelemetry:opentelemetry-exporter-otlp:1.32.0'
}
Configuration (application.yml):
Add these properties to configure OpenTelemetry through Spring Boot's configuration system:
otel:
service:
name: ${spring.application.name}
traces:
exporter: otlp
sampler: parentbased_traceidratio
sampler.arg: 1.0
exporter:
otlp:
endpoint: https://api.uptrace.dev:4317
headers: uptrace-dsn=<your_uptrace_dsn>
Spring Boot Example
Let's implement a complete working example:
# Clone demo application
git clone https://github.com/gothinkster/spring-boot-realworld-example-app.git
cd spring-boot-realworld-example-app
# Build application
./gradlew bootJar
# Run with OpenTelemetry
java -javaagent:opentelemetry-javaagent.jar \
-jar build/libs/spring-boot-realworld-example-app-0.0.1-SNAPSHOT.jar
# Test API
curl http://localhost:8080/tags
Custom Instrumentation for Business Telemetry
For comprehensive manual instrumentation patterns with complete working examples, see OpenTelemetry Java Manual Instrumentation.
Custom Trace Implementation
This example shows how to create custom spans that capture business-specific context and operations:
@Service
public class OrderProcessingService {
private final Tracer tracer;
public OrderProcessingService(OpenTelemetry openTelemetry) {
this.tracer = openTelemetry.getTracer("com.example.orders", "1.0.0");
}
public OrderResult processOrder(OrderRequest request) {
Span orderSpan = tracer.spanBuilder("process-order")
.setAttribute("order.id", request.getOrderId())
.setAttribute("customer.id", request.getCustomerId())
.setAttribute("order.amount", request.getTotalAmount())
.startSpan();
try (Scope scope = orderSpan.makeCurrent()) {
// Business logic with automatic context propagation
validateOrder(request);
orderSpan.addEvent("order.validated");
PaymentResult payment = processPayment(request);
orderSpan.setAttribute("payment.status", payment.getStatus());
OrderResult result = finalizeOrder(request);
orderSpan.setStatus(StatusCode.OK);
return result;
} catch (Exception e) {
orderSpan.setStatus(StatusCode.ERROR, e.getMessage());
orderSpan.recordException(e);
throw e;
} finally {
orderSpan.end();
}
}
}
Business Metrics Collection
Use this pattern to capture custom business metrics that provide insights into your application's performance and user behavior:
@Component
public class BusinessMetricsCollector {
private final LongCounter orderCounter;
private final DoubleHistogram orderValueHistogram;
public BusinessMetricsCollector(OpenTelemetry openTelemetry) {
Meter meter = openTelemetry.getMeter("com.example.business", "1.0.0");
orderCounter = meter.counterBuilder("orders.processed.total")
.setDescription("Total orders processed")
.build();
orderValueHistogram = meter.histogramBuilder("order.value")
.setDescription("Order value distribution")
.setUnit("USD")
.build();
}
public void recordOrder(String type, String status, double value) {
orderCounter.add(1, Attributes.of(
AttributeKey.stringKey("order.type"), type,
AttributeKey.stringKey("order.status"), status
));
orderValueHistogram.record(value, Attributes.of(
AttributeKey.stringKey("order.type"), type
));
}
}
Enhanced Controller Instrumentation
Here's how to enrich your REST controllers with additional telemetry context and business metrics:
@RestController
@RequestMapping("/api/orders")
public class OrderController {
private final OrderProcessingService orderService;
private final BusinessMetricsCollector metricsCollector;
@PostMapping
public ResponseEntity<OrderResponse> createOrder(@RequestBody OrderRequest request) {
// Current span enrichment
Span currentSpan = Span.current();
currentSpan.setAttribute("order.items.count", request.getItems().size());
currentSpan.setAttribute("customer.region", request.getCustomerRegion());
try {
OrderResult result = orderService.processOrder(request);
// Record business metrics
metricsCollector.recordOrder(
request.getOrderType(),
result.getStatus(),
request.getTotalAmount()
);
currentSpan.setStatus(StatusCode.OK);
return ResponseEntity.ok(new OrderResponse(result.getOrderId()));
} catch (OrderValidationException e) {
currentSpan.setStatus(StatusCode.ERROR, "Validation failed");
return ResponseEntity.badRequest()
.body(new ErrorResponse("VALIDATION_ERROR", e.getMessage()));
}
}
}
Otel Collector Setup
Basic Configuration
Here's a minimal collector configuration that receives telemetry from your Spring Boot app and forwards it to your observability backend:
# otel-collector-config.yaml
receivers:
otlp:
protocols:
grpc:
endpoint: 0.0.0.0:4317
processors:
batch:
timeout: 5s
send_batch_size: 1000
memory_limiter:
limit_mib: 1000
exporters:
otlp:
endpoint: https://your-backend:4317
headers:
authorization: Bearer ${API_TOKEN}
service:
pipelines:
traces:
receivers: [otlp]
processors: [memory_limiter, batch]
exporters: [otlp]
metrics:
receivers: [otlp]
processors: [memory_limiter, batch]
exporters: [otlp]
Production Deployment
This Docker Compose setup demonstrates how to deploy your Spring Boot app with the collector in a production-like environment:
# docker-compose.yml
version: '3.8'
services:
spring-app:
image: your-spring-app:latest
environment:
- OTEL_SERVICE_NAME=spring-app
- OTEL_EXPORTER_OTLP_ENDPOINT=http://otel-collector:4317
- OTEL_TRACES_SAMPLER=parentbased_traceidratio
- OTEL_TRACES_SAMPLER_ARG=0.1
depends_on:
- otel-collector
otel-collector:
image: otel/opentelemetry-collector-contrib:latest
command: ["--config=/etc/otel-collector-config.yaml"]
volumes:
- ./otel-collector-config.yaml:/etc/otel-collector-config.yaml
ports:
- "4317:4317"
Troubleshooting
No Telemetry Data
Symptoms: Application runs but no traces appear in backend
Diagnosis:
# Enable debug logging
export OTEL_JAVAAGENT_DEBUG=true
# Check connectivity
curl -v http://your-collector:4317/v1/traces
Solution:
otel:
exporter:
otlp:
endpoint: http://collector:4317 # Correct endpoint
protocol: grpc # Match collector config
Broken Trace Propagation
Symptoms: Disconnected spans across services
Diagnosis:
@GetMapping("/debug/trace")
public Map<String, String> debugTrace(HttpServletRequest request) {
Map<String, String> headers = new HashMap<>();
headers.put("traceparent", request.getHeader("traceparent"));
headers.put("current-trace-id", Span.current().getSpanContext().getTraceId());
return headers;
}
Solution:
@Bean
public TextMapPropagator textMapPropagator() {
return TextMapPropagator.composite(
W3CTraceContextPropagator.getInstance(),
W3CBaggagePropagator.getInstance()
);
}
High Memory Usage
Symptoms: Increasing heap usage, frequent GC
Solutions:
# Optimize batch processing
otel:
bsp:
max.queue.size: 1024
max.export.batch.size: 256
schedule.delay: 2s
# Implement sampling
otel:
traces:
sampler: parentbased_traceidratio
sampler.arg: 0.1 # 10% sampling
Performance and Resource Impact
Performance Impact Analysis
| Component | Overhead | Mitigation |
|---|---|---|
| CPU Usage | 3-8% | Optimize sampling rates |
| Memory | 10-15% | Configure buffer limits |
| Network | 5-10KB/request | Enable compression |
| Latency | 1-5ms/request | Use async exporters |
Production Optimization
These environment variables help optimize OpenTelemetry performance for high-traffic production environments:
# JVM optimization
export JAVA_OPTS="-XX:+UseG1GC -Xms2g -Xmx4g"
# OpenTelemetry performance tuning
export OTEL_BSP_SCHEDULE_DELAY=5000
export OTEL_BSP_MAX_EXPORT_BATCH_SIZE=512
export OTEL_TRACES_SAMPLER_ARG=0.1
# Disable unnecessary instrumentation
export OTEL_INSTRUMENTATION_LOGBACK_APPENDER_ENABLED=false
Best Practices
Environment-Specific Configurations
Development:
otel:
traces:
sampler: parentbased_always_on # Full sampling
exporter:
otlp:
endpoint: http://localhost:4317
Production:
otel:
traces:
sampler: parentbased_traceidratio
sampler.arg: 0.1 # Controlled sampling
exporter:
otlp:
endpoint: https://prod-collector:4317
compression: gzip
Monitoring Strategy
- Start Simple: Begin with Java Agent for immediate results
- Add Context: Implement custom instrumentation for business insights
- Optimize Gradually: Fine-tune sampling and performance settings
- Monitor Impact: Track OpenTelemetry's resource usage
- Scale Appropriately: Adjust configuration based on traffic patterns
Conclusion
OpenTelemetry in Spring Boot implementation provides comprehensive observability with manageable complexity. Key success factors:
- Quick Start: Java Agent enables immediate instrumentation
- Business Context: Custom metrics provide actionable insights
- Production Ready: Proper sampling and resource management
- Incremental Adoption: Gradual rollout across services
With proper Spring Boot OpenTelemetry setup, you achieve end-to-end visibility while maintaining application performance.
For servlet container monitoring, see Tomcat instrumentation to monitor the underlying server infrastructure.
Ready to start? Sign up for Uptrace and begin monitoring your Spring Boot applications with comprehensive observability today! Compare with other top APM tools or explore open-source APM deployment options.