skip to content

MeterRegistry & Registry-per-Backend

A MeterRegistry is the facade for one monitoring backend, with a composite registry to publish to several and a global registry for static access. Understanding the model explains why swapping Prometheus for OTLP is a dependency change, not a code change.

part ofSpring Frameworkoverview, primer and where to startread it →
on this pageshow

questions

5

What is a Micrometer MeterRegistry, and what role does it play in a Spring Boot application?

level: juniorimportance: must knowfreq 70%

answer

  1. SLF4J for metrics
  2. factory + container of meters
  3. inject the interface, not the impl
  4. keyed by name + tags
  5. Boot auto-configures the bean

basics

~10 s

MeterRegistry is Micrometer's central object that creates and holds meters (counters, gauges, timers). Spring Boot auto-configures one and injects it, so your code records metrics through it without knowing which monitoring backend receives them.

solid answer

~40 s

MeterRegistry is the core interface in Micrometer — the metrics facade Spring Boot uses. It is a factory and a container: you call methods like registry.counter(...), Timer.builder(...).register(registry), or Gauge.builder(...) to create meters, and the registry tracks them. Each concrete registry is bound to one monitoring system (Prometheus, etc.) and knows how to publish there. Spring Boot auto-configures the appropriate registry from classpath starters and exposes it as a bean, so you just inject MeterRegistry and record data. Because your instrumentation talks only to the interface, the same code works regardless of which backend is configured — Micrometer is deliberately 'SLF4J for metrics'. Meters are cached by name plus tags, so repeated lookups return the same instrument rather than creating duplicates.

code

java · 24 lines
java
import io.micrometer.core.instrument.Counter;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.Timer;
import org.springframework.stereotype.Service;

@Service
public class OrderService {

    private final Counter ordersPlaced;
    private final Timer checkoutTimer;

    // Spring Boot injects the auto-configured MeterRegistry bean
    public OrderService(MeterRegistry registry) {
        this.ordersPlaced = registry.counter("orders.placed", "channel", "web");
        this.checkoutTimer = Timer.builder("orders.checkout")
                .description("time to complete checkout")
                .register(registry);
    }

    public void checkout(Runnable work) {
        checkoutTimer.record(work); // times the block
        ordersPlaced.increment();
    }
}

go deeper

for a junior

Know it's the object you inject to create counters/timers, and that Boot auto-configures it.

for a middle

Explain the factory + container roles, name+tag caching, and the facade/SLF4J-for-metrics analogy.

for a senior

Discuss the interface-vs-implementation split, the SimpleMeterRegistry fallback, and auto-instrumentation binding to the same registry.

for a principal

Frame it as the seam that decouples instrumentation from backend, enabling backend swaps and multi-backend fan-out without touching call sites.

**Micrometer** is a vendor-neutral metrics instrumentation library — its tagline is 'SLF4J for application metrics.' Just as SLF4J lets you log without hard-coding Logback vs Log4j, Micrometer lets you record metrics without hard-coding Prometheus vs Datadog vs CloudWatch. **MeterRegistry** is the central interface of that library. It has two jobs: 1. **Factory** — it creates *meters*. A **meter** is the general term for a metric instrument. The common types are: **Counter** (a value that only increases, e.g. number of requests), **Gauge** (a value that goes up and down and is sampled on demand, e.g. queue size), **Timer** (records how long events take *and* how many there were), **DistributionSummary** (records the distribution of a value, e.g. payload sizes), **LongTaskTimer** (measures tasks still in progress), and **FunctionCounter/FunctionTimer** (track state held in another object). You create them via the registry: `registry.counter("orders.placed")`, or with builders like `Timer.builder("http.server.requests").register(registry)`. 2. **Container/registry** — it stores every meter created against it, keyed by the combination of **name + tags** (tags are key/value dimensions like `method=GET`). Asking for a meter with the same name and tags returns the *existing* one instead of creating a duplicate — so repeated calls in a hot path are cheap and consistent. **In Spring Boot**, the `spring-boot-starter-actuator` dependency plus a registry implementation (e.g. `micrometer-registry-prometheus`) triggers auto-configuration (`MetricsAutoConfiguration` and the per-system auto-configs) that creates a `MeterRegistry` **bean**. You obtain it by constructor injection: ```java @Service class OrderService { private final Counter placed; OrderService(MeterRegistry registry) { this.placed = registry.counter("orders.placed"); } } ``` Spring also auto-instruments a lot for you (HTTP server timings, JVM memory, GC, datasource pools) and binds them to that same registry. **Key facade point:** your code depends only on the `MeterRegistry` *interface*. The concrete class — `PrometheusMeterRegistry`, `SimpleMeterRegistry`, etc. — decides the naming conventions and how/when data is shipped to the backend. Swapping backends is a dependency change, not a code change. **Gotchas:** - If **no** registry implementation is on the classpath, Boot falls back to a `SimpleMeterRegistry` that only holds the latest values in memory (used by the `/actuator/metrics` endpoint) — nothing is exported anywhere. - The registry is thread-safe; you can safely cache a `Counter`/`Timer` reference in a field. - Meter creation is idempotent per name+tags, but *different* tag sets create *different* time series — watch cardinality.

  • What happens if you inject MeterRegistry but have no registry implementation (like micrometer-registry-prometheus) on the classpath?
    Spring Boot supplies a SimpleMeterRegistry — an in-memory registry that keeps only the latest values and backs the /actuator/metrics endpoint. Nothing is exported to any external system; it is effectively a no-op for shipping data out.
  • Why is calling registry.counter("orders.placed") twice safe?
    Meters are cached by name plus tag set. The second call returns the same Counter instance rather than creating a duplicate time series, so it is safe to look up in a hot path (though caching the reference is still slightly cheaper).

saying these in an interview costs you the question

  • Thinking MeterRegistry itself talks to Prometheus/Datadog — it's the interface; the concrete registry does the exporting.
  • Believing each registry.counter(...) call creates a new metric every time.
  • Assuming metrics are exported even with no registry implementation on the classpath.

context

open as a page

Explain Micrometer's 'registry per monitoring system' model and the role of CompositeMeterRegistry.

level: middleimportance: must knowfreq 60%

basics

~20 s

Each monitoring backend has its own MeterRegistry implementation (one per system). To send the same metrics to several backends at once, Micrometer uses a CompositeMeterRegistry that holds child registries and forwards every meter operation to all of them.

open as a page

How do MeterRegistryCustomizer and MeterFilter interact with a CompositeMeterRegistry — where do common tags and filters actually apply?

level: seniorimportance: should knowfreq 35%

basics

~20 s

In Spring Boot you customize registries with MeterRegistryCustomizer beans (e.g. add common tags) and shape/filter meters with MeterFilter. Boot applies customizers to each registry including the composite; a filter on the composite affects all children fan-out.

open as a page

What is Metrics.globalRegistry, when would you use it, and what are the pitfalls versus injecting a MeterRegistry?

level: seniorimportance: should knowfreq 40%

basics

~20 s

Metrics.globalRegistry is a static, application-wide CompositeMeterRegistry. It lets code without dependency injection record metrics. The pitfall in Spring: unless you wire the Boot registry into it, metrics recorded there aren't exported by Boot's own registry.

open as a page

You must ship metrics to Prometheus (local scrape) and a hosted APM simultaneously, with per-backend cost/cardinality controls and clean shutdown. How do you design this with Micrometer registries?

level: principalimportance: should knowfreq 25%

basics

~20 s

Add both registry starters so Boot fans out via one CompositeMeterRegistry. Instrument once against the injected MeterRegistry. Apply per-child MeterFilters for cost/cardinality, tune each backend's step, and ensure push registries close on shutdown to flush.

open as a page