Micrometer

Micrometer

Micrometer is the application observability facade for the JVM ecosystem. It provides a vendor-neutral API for metrics collection and — as of Micrometer 1.10 / Spring Boot 3.x — also for distributed tracing. It is to metrics what SLF4J is to logging: write instrumentation once, bind to any backend at configuration time.

Two Distinct Sub-Projects

Micrometer Metrics (Original)

The original Micrometer (io.micrometer:micrometer-core) provides:

  • MeterRegistry — the central registry, accepts meter registrations
  • Meter types: Counter, Gauge, Timer, DistributionSummary, LongTaskTimer, FunctionCounter, FunctionTimer
  • Dimensional (tagged) metrics: registry.counter("http.requests", "method", "GET", "status", "200")
  • Registry implementations for Prometheus, Datadog, InfluxDB, CloudWatch, Graphite, and many more

Micrometer Tracing (New in 1.10 / Boot 3)

io.micrometer:micrometer-tracing provides a tracing API facade. Bridges connect it to concrete implementations:

  • io.micrometer:micrometer-tracing-bridge-otel — OpenTelemetry SDK backend
  • io.micrometer:micrometer-tracing-bridge-brave — Brave/Zipkin backend (legacy)

Micrometer Tracing replaces Spring Cloud Sleuth for Spring Boot 3.x. Sleuth is incompatible with Boot 3 and is no longer maintained.

Micrometer Observation (Unified API)

ObservationRegistry and @Observed provide a single API that produces both metrics and traces simultaneously from one instrumentation point. This is the preferred approach for new instrumentation in Boot 3.

@Service
public class OrderService {
    private final ObservationRegistry observationRegistry;
 
    public Order getOrder(String orderId) {
        return Observation.createNotStarted("order.fetch", observationRegistry)
            .lowCardinalityKeyValue("service", "order-service")
            .observe(() -> repository.findById(orderId));
    }
}

Micrometer Tracing Architecture

Application Code
      │
      ▼
Micrometer Tracing API (io.micrometer:micrometer-tracing)
      │
      ├──▶ Bridge: OTel  (micrometer-tracing-bridge-otel)
      │         │
      │         ▼
      │    OpenTelemetry SDK
      │         │
      │         ├──▶ Zipkin Exporter  → Zipkin UI
      │         ├──▶ OTLP Exporter   → Jaeger / Grafana Tempo
      │         └──▶ Console Exporter (dev)
      │
      └──▶ Bridge: Brave (micrometer-tracing-bridge-brave)
                │
                ▼
           Brave / Zipkin Reporter → Zipkin UI

Use only one bridge at runtime. The OTel bridge is recommended for new projects.

Spring Boot 3 Auto-Configuration

When spring-boot-starter-actuator is present alongside a tracing bridge, Spring Boot 3 auto-configures:

BeanPurpose
ObservationRegistryCentral observation registry
TracerMicrometer Tracing facade (backed by OTel or Brave)
HttpServerObservationFilterInstruments all incoming HTTP requests
ObservationWebClientCustomizerInstruments all WebClient requests
MeterRegistry (composite)Collects metrics, ships to configured backends
PrometheusRegistryExposes /actuator/prometheus (if dependency present)

Core Meter Types

Counter

Monotonically increasing value. Use for: requests handled, errors thrown, events published.

Counter counter = Counter
    .builder("bff.aggregation.calls")
    .description("Number of dashboard aggregations performed")
    .tag("type", "dashboard")
    .register(registry);
 
counter.increment();           // +1
counter.increment(5.0);        // +5

Timer

Records latency and throughput. Automatically exposes count, sum, max, and percentile buckets.

Timer timer = Timer
    .builder("bff.aggregation.latency")
    .description("End-to-end dashboard aggregation latency")
    .tag("type", "dashboard")
    .publishPercentiles(0.50, 0.95, 0.99)    // publish as gauges
    .publishPercentileHistogram()             // publish as histogram buckets (for Prometheus)
    .register(registry);
 
timer.record(() -> doAggregation());          // synchronous
timer.record(Duration.ofMillis(42));          // manual

Gauge

Point-in-time value. Use for: queue depth, active connections, thread count.

// Gauge backed by a collection size
registry.gauge("bff.pending.requests", pendingRequestQueue, Queue::size);
 
// Gauge backed by an AtomicInteger
AtomicInteger activeConnections = registry.gauge(
    "bff.active.connections",
    new AtomicInteger(0)
);

DistributionSummary

Like a Timer but for non-time values (bytes, queue depths, batch sizes).

DistributionSummary summary = DistributionSummary
    .builder("bff.response.payload.bytes")
    .baseUnit("bytes")
    .register(registry);
 
summary.record(responseBody.length());

Prometheus Integration

Add io.micrometer:micrometer-registry-prometheus to the classpath. Spring Boot auto-registers it and exposes /actuator/prometheus.

management:
  endpoints:
    web:
      exposure:
        include: health, prometheus, metrics
  metrics:
    export:
      prometheus:
        enabled: true
    tags:
      # Common tags applied to all meters
      application: ${spring.application.name}
      environment: ${spring.profiles.active:default}

Prometheus scrapes this endpoint and stores time-series data for Grafana queries.

Tracing Configuration

Full application.yml for OTel + Zipkin

management:
  tracing:
    sampling:
      probability: 1.0      # 1.0 = 100% (dev); 0.1 = 10% (prod)
  zipkin:
    tracing:
      endpoint: http://localhost:9411/api/v2/spans
  otlp:
    tracing:
      # For Jaeger or Grafana Tempo via OTLP
      endpoint: http://localhost:4318/v1/traces

Full build.gradle Dependencies

dependencies {
    // Actuator (required for auto-configuration)
    implementation 'org.springframework.boot:spring-boot-starter-actuator'
 
    // Micrometer Tracing — OTel bridge
    implementation 'io.micrometer:micrometer-tracing-bridge-otel'
 
    // OTel exporter — choose one (or both)
    implementation 'io.opentelemetry:opentelemetry-exporter-zipkin'
    // implementation 'io.opentelemetry:opentelemetry-exporter-otlp'
 
    // Metrics — Prometheus
    implementation 'io.micrometer:micrometer-registry-prometheus'
 
    // Reactor instrumentation (for WebFlux reactive chain tracing)
    implementation 'io.micrometer:micrometer-tracing'
}

Manual Span Creation

Inject Tracer (Micrometer facade, not OTel's directly) for manual spans:

@Autowired
private Tracer tracer;
 
public Mono<Result> doWork(String input) {
    Span span = tracer.nextSpan()
        .name("custom.operation")
        .tag("input.type", getType(input))
        .start();
 
    return performWork(input)
        .doOnError(e -> span.error(e))
        .doFinally(signal -> span.end());
}

Reactive Pipeline Considerations

In WebFlux (Project Reactor), ThreadLocal-based MDC does not propagate across operator boundaries. Micrometer Tracing stores the active span in the Reactor Context, which does travel across operators.

For structured logging to include trace IDs in WebFlux, use the MdcContextLifter + Hooks.onEachOperator pattern described in P5-BFF-Observability-Testing — OBS-04.

Micrometer vs. Spring Cloud Sleuth

AspectSpring Cloud SleuthMicrometer Tracing
Spring Boot compatibilityBoot 2.x onlyBoot 3.x (required)
Maintenance statusDiscontinuedActive
Tracing APIBrave-firstVendor-neutral facade
OTel supportLimited, bolt-onNative bridge
Auto-configSpring Cloud BOMSpring Boot BOM

Do not use Spring Cloud Sleuth with Spring Boot 3.x. The dependency will not resolve from current Spring Cloud 2024.0.x BOMs.

BFF-Specific Metrics

In a BFF-Pattern context, these metrics are most valuable:

http.server.requests          — per-route latency + throughput (auto)
spring.cloud.gateway.requests — SCG route-level metrics (auto with SCG)
resilience4j.circuitbreaker.* — circuit breaker state and calls (auto with R4J)
bff.aggregation.calls         — custom: aggregation invocation rate
bff.aggregation.latency       — custom: end-to-end aggregation latency
bff.downstream.errors         — custom: downstream error rate by service
  • Metrics-and-Dashboards — Micrometer is the implementation facade; Metrics-and-Dashboards covers the pattern-level measurement frameworks (RED, USE, Four Golden Signals) that Micrometer metrics feed into
  • SLO-SLI-SLA — Micrometer metrics (request duration histograms, error counters) are the raw data source for computing SLI ratios (good events / valid events) that define SLOs
  • Structured-Logging — Micrometer's Observation API bridges metrics and trace context; Structured-Logging defines the JSON field schema that includes traceId for metrics-to-log correlation