Spring Actuator: Micrometer Counters, Gauges, Prometheus Scrape
- Last updated
- Prerequisites:
- Spring Boot basics
- Understanding of metrics and monitoring concepts
- spring
- actuator
- micrometer
- metrics
- prometheus
- monitoring
Read at your depth
The practical view
Spring Boot Actuator exposes health, metrics, and info endpoints (/actuator/health, /actuator/metrics). Metrics are collected with Micrometer: counters (increment), gauges (current value), timers (duration + count), distribution summaries. Enable Prometheus with the micrometer-registry-prometheus dependency; then /actuator/prometheus exposes metrics in Prometheus format, scraped by Prometheus. Create your own: Counter.builder("orders.created").register(meterRegistry) then counter.increment(). Use @Timed for timers around methods. Health indicators: HealthIndicator beans contribute to /actuator/health (UP/DOWN).
The same idea in other frameworks
N/A — server-side Spring concern.N/A — Spring concern.N/A — Spring concern.N/A — Spring concern.Legacy vs modern
Log-based metrics vs Micrometer counters
Parsing logs to count events is brittle and lossy; a Micrometer counter gives an exact, queryable metric.
log.info("order created id={}", id); // ❌ OLD: counting requires log parsingprivate final Counter ordersCreated; // ✅ NEW: exact, tagged, scrapableInterview gotchas
Context
This tests the meter type semantics — interviewers want the monotonic-counter vs current-value distinction and the practical implications for Prometheus queries.
The mechanical answer
A Counter is a monotonically increasing value — it only goes up (it tracks the total number of events since start). In Prometheus, counters are queried with rate()/increase() to derive per-second rates, and they reset on restart (Prometheus handles resets). A Gauge is a current value that can go up and down — a snapshot at scrape time (queue depth, in-flight requests, temperature, memory usage). Choosing wrong causes wrong queries: using a gauge for an event total loses the rate history; using a counter for a current level (e.g. active sessions) produces meaningless rates. Micrometer enforces this conceptually: counters expose increment(), gauges wrap a supplier and are read at scrape time. Timers are a third type — they measure durations and expose count + total + histogram.
Trap
The naive answer is 'counter and gauge are interchangeable'. They have opposite semantics (monotonic total vs current level) and different query patterns. Another trap: creating a gauge that calls an expensive method at every scrape — gauges are read on scrape, so the supplier runs on each Prometheus pull. Candidates who map the meter type to the query pattern (rate vs snapshot) show real monitoring understanding.
Context
This probes the metrics export architecture — interviewers want the pull-based scrape contract and how the Micrometer registry renders into the Prometheus format.
The mechanical answer
Prometheus uses a pull model: the Prometheus server periodically requests metrics from each target's /metrics endpoint. In Spring Boot, that's /actuator/prometheus, which the Micrometer PrometheusMeterRegistry renders in the Prometheus text exposition format (metric names like orders_created_total, label sets, and sample values). The scrape interval is configured on the Prometheus side; each scrape reads the CURRENT state of the registry's meters. Because it's pull-based, the app doesn't push anywhere — it just serves the exposition format. This differs from push models (StatsD, Datadog) where the app sends to an agent. The pull model gives Prometheus control over scrape frequency, discovery (via service discovery), and consistency across instances.
Trap
The naive answer is 'Actuator pushes metrics to Prometheus'. It serves them; Prometheus pulls. Another trap: forgetting that the registry holds in-memory state — a gauge's value at scrape time is what's reported, so long-lived gauges must reflect current state (a lambda, not a captured constant). Candidates who describe the scrape contract and the exposition format show real depth.