Monitoring Spring Boot Microservices with Actuator, Micrometer, and OpenTelemetry

Alexandr Bandurchin
October 02, 2025
7 min read

Spring Boot microservices generate metrics through Spring Boot Actuator, a production-ready feature set that exposes operational information about running applications. When you add Actuator to your Spring Boot service, it automatically exposes endpoints for health checks, metrics, environment properties, and application configuration.

The challenge with microservices is that metrics from one service only tell part of the story. A slow API response might originate from a downstream dependency three services away. Effective monitoring requires collecting metrics from all services and correlating them with logs and traces to understand what's actually happening in your distributed system.

Spring Boot Actuator

Spring Boot Actuator provides production-ready features for monitoring and managing applications. Add it to your project with a single dependency:

xml
<dependency>  <groupId>org.springframework.boot</groupId>  <artifactId>spring-boot-starter-actuator</artifactId> </dependency> 

Actuator exposes several endpoints. The /actuator/health endpoint reports application health status, checking database connections, disk space, and custom health indicators. The /actuator/metrics endpoint lists all available metrics. The /actuator/info endpoint displays application information from your build configuration.

By default, only health and info endpoints are exposed over HTTP. Enable additional endpoints in application.properties:

properties
management.endpoints.web.exposure.include=health,info,metrics,prometheus management.endpoint.health.show-details=always 

The metrics endpoint exposes JVM metrics (memory usage, garbage collection, thread counts), HTTP request metrics (count, duration, status codes), system metrics (CPU usage, file descriptors), and database connection pool metrics (active connections, idle connections).

Micrometer Integration

Micrometer provides a vendor-neutral facade for metrics collection, similar to how SLF4J works for logging. Spring Boot 2.0+ includes Micrometer by default, allowing you to instrument code once and export to multiple monitoring systems.

Micrometer supports dimensional metrics with tags. Instead of having separate metrics like api.users.get.latency and api.orders.get.latency, you have one metric api.latency with tags endpoint=users and endpoint=orders. This makes querying and aggregating metrics simpler.

Custom Metrics

Create custom metrics by injecting MeterRegistry into your Spring components:

java
@Service public class OrderService {  private final Counter orderCounter;  private final Timer orderProcessingTimer;   public OrderService(MeterRegistry registry) {  this.orderCounter = Counter.builder("orders.created")  .description("Total orders created")  .tag("service", "order-service")  .register(registry);   this.orderProcessingTimer = Timer.builder("orders.processing.time")  .description("Order processing duration")  .register(registry);  }   public Order createOrder(OrderRequest request) {  return orderProcessingTimer.record(() -> {  Order order = processOrder(request);  orderCounter.increment();  return order;  });  } } 

Micrometer provides four core metric types. Counters track incrementing values that never decrease (request counts, error counts). Gauges measure current values that can go up or down (memory usage, queue size). Timers measure latency and frequency of events (request duration). Distribution summaries track distribution of values (payload sizes, batch sizes).

Prometheus Export

Prometheus scrapes metrics by pulling from HTTP endpoints. Add the Prometheus registry to expose metrics in Prometheus format:

xml
<dependency>  <groupId>io.micrometer</groupId>  <artifactId>micrometer-registry-prometheus</artifactId> </dependency> 

This automatically creates a /actuator/prometheus endpoint that returns metrics in Prometheus text format. Configure Prometheus to scrape this endpoint by adding a job to prometheus.yml:

yaml
scrape_configs:  - job_name: 'spring-boot-app'  metrics_path: '/actuator/prometheus'  scrape_interval: 15s  static_configs:  - targets: ['localhost:8080'] 

Prometheus stores metrics as time series data, allowing you to query historical trends and set up alerts. Use PromQL to query metrics. For example, to see the 95th percentile latency for HTTP requests: histogram_quantile(0.95, http_server_requests_seconds_bucket).

OpenTelemetry for Complete Observability

Prometheus handles metrics well, but microservices need more than metrics. When debugging a production issue, you need to see the complete request path through multiple services, correlate logs with traces, and understand which service caused the slowdown.

OpenTelemetry provides unified instrumentation for metrics, logs, and traces. Instead of using separate tools for each signal type, OpenTelemetry collects everything through one API.

OpenTelemetry Setup

Add OpenTelemetry dependencies to your Spring Boot application:

xml
<dependency>  <groupId>io.opentelemetry</groupId>  <artifactId>opentelemetry-api</artifactId> </dependency> <dependency>  <groupId>io.opentelemetry.instrumentation</groupId>  <artifactId>opentelemetry-spring-boot-starter</artifactId> </dependency> 

Configure OpenTelemetry in application.properties:

properties
otel.service.name=order-service otel.traces.exporter=otlp otel.metrics.exporter=otlp otel.logs.exporter=otlp otel.exporter.otlp.endpoint=http://localhost:4317 

OpenTelemetry automatically instruments Spring Boot applications, capturing HTTP requests, database queries, and external API calls without code changes. It propagates trace context across service boundaries, allowing you to follow a single user request through your entire microservices architecture.

Distributed Tracing

Distributed tracing shows how requests flow through services. Each trace contains spans representing operations within services. A span includes operation name, start time, duration, status, and attributes describing the operation.

When Service A calls Service B, OpenTelemetry propagates the trace context in HTTP headers. Service B continues the same trace, creating child spans for its operations. This creates a complete picture of the request path.

Manual instrumentation gives you control over what gets traced:

java
@Service public class OrderService {  private final Tracer tracer;   public OrderService(Tracer tracer) {  this.tracer = tracer;  }   public Order processOrder(OrderRequest request) {  Span span = tracer.spanBuilder("process-order")  .setAttribute("order.id", request.getId())  .setAttribute("customer.id", request.getCustomerId())  .startSpan();   try (Scope scope = span.makeCurrent()) {  // Business logic here  Order order = createOrder(request);  span.addEvent("order.created");  return order;  } catch (Exception e) {  span.recordException(e);  span.setStatus(StatusCode.ERROR);  throw e;  } finally {  span.end();  }  } } 

Monitoring Different Service Types

REST APIs need request rate, error rate, and latency metrics. Track these by endpoint and method. Monitor connection pool usage to detect connection leaks. Watch for timeout errors indicating downstream dependencies are slow.

Background job processors need completion rate, processing time, and failure rate metrics. Track queue depth to ensure jobs aren't backing up. Monitor retry counts to detect problematic jobs. Set up alerts when dead letter queues grow.

Database-heavy services need query performance metrics. Monitor slow query counts and cache hit rates. Track transaction rollback rates to detect data integrity issues. Watch connection pool exhaustion.

Health Checks

Health checks tell orchestration platforms whether a service can accept traffic. Spring Boot Actuator provides health indicators for common dependencies. The database health indicator checks if connections work. The disk space health indicator verifies sufficient disk space. Redis health indicator tests cache connectivity.

Create custom health indicators for external dependencies:

java
@Component public class PaymentGatewayHealthIndicator implements HealthIndicator {  private final PaymentGatewayClient client;   @Override  public Health health() {  try {  client.ping();  return Health.up()  .withDetail("gateway", "responsive")  .build();  } catch (Exception e) {  return Health.down()  .withDetail("error", e.getMessage())  .build();  }  } } 

Kubernetes uses liveness and readiness probes based on health endpoints. Liveness probes determine if the container should restart. Readiness probes determine if the service should receive traffic. Configure separate endpoints for each:

properties
management.endpoint.health.probes.enabled=true management.health.livenessState.enabled=true management.health.readinessState.enabled=true 

Monitoring Best Practices

Tag metrics with service name, environment, and version. This allows filtering and grouping in your monitoring dashboard. Add business-specific tags like customer tier or region when relevant.

Set up alerting on RED metrics: Rate (requests per second), Errors (error rate), Duration (latency percentiles). These three metrics cover most production issues. Alert on p99 latency, not average latency—averages hide outliers that affect real users.

Monitor resource saturation: CPU usage approaching 80%, memory usage climbing steadily, connection pool exhaustion, thread pool saturation. These indicate you need to scale or optimize.

Track business metrics alongside technical metrics. Orders per minute, signup conversion rate, payment success rate. Technical metrics show how the system performs. Business metrics show whether it's delivering value.

Uptrace for Unified Observability

Uptrace as a Free OpenTelemetry APM provides complete OpenTelemetry support for Spring Boot applications. Instead of running separate tools for metrics (Prometheus), traces (Jaeger), and logs (Elasticsearch), Uptrace handles all three.

When you see high latency in metrics, click through to traces showing which downstream service caused the delay. Jump from traces to logs to see error messages in context. Correlation happens automatically because OpenTelemetry links all signals through trace context.

Uptrace's query language works across metrics, logs, and traces. Search for all traces containing errors in the payment service during the last hour. Find logs from a specific trace. Graph metric values for services involved in slow traces.

For microservices running on Kubernetes, check out our guide on Kubernetes microservices monitoring. If you're using event-driven patterns, see monitoring Kafka microservices.

Getting Started

Start with Spring Boot Actuator and expose the Prometheus endpoint. This gives you basic metrics without changing application code. Add custom metrics for business-critical operations using Micrometer.

Once metrics are flowing, add OpenTelemetry for distributed tracing. This shows request paths through your services. Correlate traces with metrics to understand why certain requests are slow.

Set up health checks for all external dependencies. Configure Kubernetes probes to use these health endpoints. This prevents routing traffic to unhealthy instances.

Finally, establish alerting rules for RED metrics and resource saturation. Start with conservative thresholds and adjust based on normal traffic patterns.

Ready to monitor your Spring Boot microservices? Start with Uptrace for unified metrics, logs, and traces.

You may also be interested in: