Practical Guide to Micrometer: JVM Metrics Framework with Spring Boot, Prometheus, and Grafana
This article introduces the Micrometer metrics library for JVM applications, explains its core Meter types and registries, demonstrates how to instrument Spring Boot services with counters, timers, gauges, and other meters, and shows end‑to‑end integration with Prometheus and Grafana for monitoring.
Micrometer is a vendor‑agnostic metrics library for JVM applications. It defines the Meter abstraction (Timer, Counter, Gauge, DistributionSummary, LongTaskTimer, FunctionCounter, FunctionTimer, TimeGauge) and a MeterRegistry that creates and stores meters. Common registry implementations include SimpleMeterRegistry (in‑memory only), CompositeMeterRegistry (aggregates multiple registries), and the global static registry accessed via Metrics .
MeterRegistry registry = new SimpleMeterRegistry();
Counter counter = registry.counter("counter");
counter.increment();Composite registries start empty; meters added before a concrete registry are ineffective until a real registry is added.
CompositeMeterRegistry composite = new CompositeMeterRegistry();
Counter compositeCounter = composite.counter("counter");
compositeCounter.increment(); // no effect
SimpleMeterRegistry simple = new SimpleMeterRegistry();
composite.add(simple);
compositeCounter.increment(); // now worksThe global registry simplifies usage via static methods:
Metrics.addRegistry(new SimpleMeterRegistry());
Counter counter = Metrics.counter("counter", "tag-1", "tag-2");
counter.increment();Micrometer uses Tag objects to add dimensions to a meter. Tags are key‑value pairs and must appear in pairs (Key=Value). Naming conventions can be customized with a NamingConvention implementation.
registry.config().namingConvention(myCustomNamingConvention);Examples of naming conversion for Prometheus, Atlas, Graphite, and InfluxDB are shown, illustrating how a dot‑separated name like http.server.requests is transformed for each backend.
Key meter types and typical usage:
Counter
A monotonically increasing integer used for counting events such as orders or HTTP requests. Example with tags for channel and timestamp:
Metrics.counter("order.create", "channel", order.getChannel(), "createTime", FORMATTER.format(order.getCreateTime())).increment();FunctionCounter
Wraps a ToDoubleFunction so the counter value is derived from an external object (e.g., an AtomicInteger ).
FunctionCounter.builder("functionCounter", n, AtomicInteger::get)
.baseUnit("function")
.description("functionCounter")
.tag("createOrder", "CHANNEL-A")
.register(registry);Timer
Measures the duration of short‑lived events. It can record via a Runnable , Callable , or a supplier, and also provides a Sample API for manual start/stop.
Timer timer = Metrics.timer("method.cost.time", "method.name", method.getName());
timer.record(() -> createOrder(order)); Timer.Sample sample = Timer.start(registry);
// business logic
sample.stop(registry.timer("my.timer", "response", response.status()));FunctionTimer
Similar to Timer but obtains count and total time from functions, useful for monitoring libraries such as caches.
FunctionTimer.builder("functionTimer", holder, p -> totalCount.get(), p -> totalTimeNanos.get(), TimeUnit.NANOSECONDS)
.register(new SimpleMeterRegistry());LongTaskTimer
Tracks long‑running tasks that may span many seconds or minutes. It can be used directly or via Spring’s @Timed and @Scheduled annotations.
LongTaskTimer longTaskTimer = meterRegistry.more().longTaskTimer("longTaskTimer");
longTaskTimer.record(() -> { /* task */ });Gauge & TimeGauge
Provides a snapshot of a value that can go up and down, such as collection size, memory usage, or a custom Number . Gauges can be created from collections, maps, or atomic numbers.
Gauge gauge = registry.gauge("listGauge", Collections.emptyList(), new ArrayList<>(), List::size);
AtomicInteger n = registry.gauge("numberGauge", new AtomicInteger(0));
TimeGauge timeGauge = TimeGauge.builder("timeGauge", count, TimeUnit.SECONDS, AtomicInteger::get).register(registry);DistributionSummary
Records the distribution of non‑time‑based values (e.g., payload size). It can be built with optional description, base unit, tags, and scaling.
DistributionSummary summary = DistributionSummary.builder("response.size")
.description("size of HTTP responses")
.baseUnit("bytes")
.tags("region", "test")
.register(registry);Integration with Spring Boot is straightforward because spring-boot-starter-actuator already pulls in Micrometer. Adding the micrometer-registry-prometheus dependency enables a /actuator/prometheus endpoint that Prometheus can scrape.
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
<version>1.1.0</version>
</dependency>Sample Spring components (entity, controller, service, AOP aspect) illustrate how to record counters and timers for order processing and message sending.
@RestController
public class OrderController {
@Autowired private OrderService orderService;
@PostMapping("/order")
public ResponseEntity
createOrder(@RequestBody Order order) {
return ResponseEntity.ok(orderService.createOrder(order));
}
}Application YAML config enables all actuator endpoints on a separate management port:
server:
port: 9091
management:
server:
port: 10091
endpoints:
web:
exposure:
include: '*'
base-path: /managementPrometheus is installed manually, configured to scrape the Spring Boot /management/prometheus endpoint, and started with its default storage path.
wget https://github.com/prometheus/prometheus/releases/download/v2.5.0/prometheus-2.5.0.linux-amd64.tar.gz
tar xvfz prometheus-*.tar.gz
./prometheus --config.file=prometheus.yml scrape_configs:
- job_name: 'prometheus'
metrics_path: /management/prometheus
static_configs:
- targets: ['localhost:10091']Grafana is installed via RPM, started on port 3000, and configured with a Prometheus data source. Dashboards can then visualize counters, timers, and other meters using PromQL queries.
wget https://s3-us-west-2.amazonaws.com/grafana-releases/release/grafana-5.3.4-1.x86_64.rpm
sudo yum localinstall grafana-5.3.4-1.x86_64.rpm
service grafana-server startThe article also contains promotional sections encouraging readers to follow a public account for free resources, but the technical content remains a comprehensive tutorial on using Micrometer for JVM monitoring.
Code Ape Tech Column
Former Ant Group P8 engineer, pure technologist, sharing full‑stack Java, job interview and career advice through a column. Site: java-family.cn
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.