How does Micrometer translate a meter name and tags for Prometheus versus OTLP? Walk through what happens to a timer named http.server.requests.
answer
- dot.case internal → registry NamingConvention
- Prometheus: snake_case + _seconds/_total/_count/_sum/_bucket
- OTLP: keeps dotted name (OTel semconv)
- tags → Prometheus labels / OTLP attributes
- don't hand-code unit suffixes
basics
~10 sMicrometer uses dot-separated names internally. The Prometheus registry rewrites them to snake_case and adds unit/_total suffixes (http_server_requests_seconds...), turning tags into labels. The OTLP registry keeps the dotted name (http.server.requests) per OpenTelemetry conventions.
solid answer
~40 sMicrometer records meters with a naming convention that is dot-delimited and lowercase — e.g. `http.server.requests` with tags `method`, `status`, `uri`. Each registry applies its own `NamingConvention` at export. `PrometheusMeterRegistry` sanitizes the name to Prometheus rules: dots and other illegal characters become underscores (`http_server_requests`), the meter's base unit is appended as a suffix (a timer in seconds → `_seconds`), and a monotonically increasing counter gets `_total`; timers/summaries also emit `_count`, `_sum`, and `_bucket` series. Tags become Prometheus **labels**. `OtlpMeterRegistry` follows OpenTelemetry semantic conventions and preserves the dotted name `http.server.requests`, exporting tags as OTLP attributes. So the identical in-code meter surfaces as `http_server_requests_seconds_count{uri="/api"}` in Prometheus but `http.server.requests` with attributes in OTLP — a real gotcha when the same metric flows through both pipelines.
code
java · 21 linesimport io.micrometer.core.instrument.*;
Timer timer = Timer.builder("http.server.requests") // canonical dotted name
.tag("uri", "/api")
.tag("status", "200")
.register(registry);
timer.record(() -> handle());
// Exported identities of the SAME meter:
//
// Prometheus (/actuator/prometheus):
// http_server_requests_seconds_count{uri="/api",status="200"} 5
// http_server_requests_seconds_sum{uri="/api",status="200"} 0.42
//
// OTLP (pushed to collector):
// name = "http.server.requests" (attributes: uri=/api, status=200)
// Counter example:
Counter c = registry.counter("orders.placed");
// Prometheus: orders_placed_total | OTLP: orders.placedgo deeper
Know that dots become underscores in Prometheus and tags become labels.
Recall the _total, _count, _sum, _seconds suffix rules and that OTLP keeps dotted names.
Explain per-registry NamingConvention, unit suffixing from base units, and cross-pipeline naming pitfalls.
Govern naming/cardinality across the estate (collector-side normalization, MeterFilter policy) rather than per-service hacks.
**Micrometer's internal naming:** You name meters using a lowercase, dot-separated convention (`orders.placed`, `http.server.requests`) and attach dimensions as **tags** (key/value pairs). Micrometer deliberately keeps names backend-agnostic; each registry owns a `NamingConvention` that maps this canonical form to what its backend requires. This lets one instrumentation call render correctly everywhere. **Prometheus mapping (`PrometheusMeterRegistry`):** Prometheus has strict name rules — names match `[a-zA-Z_:][a-zA-Z0-9_:]*` and by convention include a unit suffix. Micrometer's Prometheus naming convention therefore: - Replaces illegal characters (dots, hyphens) with underscores: `http.server.requests` → `http_server_requests`. - Appends the **base unit**: a `Timer` measures time, published in **seconds**, so you get the `_seconds` family. A `DistributionSummary` in bytes → `_bytes`. - Appends `_total` to monotonic counters: `orders.placed` → `orders_placed_total`. - Expands composite meters into multiple series: a timer emits `_count` (number of events), `_sum` (total time), and, if percentile histograms are enabled, `_bucket` series plus `+Inf`. - Tags become **labels**: `Timer.builder(...).tag("uri","/api")` → `{uri="/api"}`. Result example: `http_server_requests_seconds_count{method="GET",status="200",uri="/api"}`. **OTLP mapping (`OtlpMeterRegistry`):** OpenTelemetry's semantic conventions use dotted, namespaced names, so the OTLP registry **preserves** `http.server.requests` and carries tags as OTLP **attributes** rather than reformatting to snake_case. It also encodes the OTel instrument type and unit metadata in the protobuf payload. This means downstream OTel-native backends see the canonical dotted name. (When such data is later re-exported to Prometheus by a collector, the collector applies its own dot→underscore translation — so the naming difference reappears at that boundary.) **Why it matters / gotchas:** - **Dashboards and alerts are name-specific.** A PromQL query for `http_server_requests_seconds_count` won't match OTLP-native `http.server.requests`; teams running both pipelines must account for the two spellings. - **Unit suffixes are automatic, not free-form.** Don't bake units into the meter name yourself (`request.time.seconds`) — you'll get double suffixes or violate conventions. Let the registry add them from the meter's base unit. - **Counters and `_total`.** If you look for `orders_placed` and it's missing, remember Prometheus appended `_total`. - **Label/tag cardinality.** High-cardinality tags (raw user ids, unbounded URIs) explode the number of series/attributes in both systems; this is the dominant scaling risk, independent of naming. - **Custom conventions.** You can override behavior with a custom `NamingConvention` or `MeterFilter`, but doing so per-registry to force matching names across backends is fragile — prefer aligning at the collector. Overall: same meter in code, two different external identities. Know that Prometheus = snake_case + unit/`_total` + `_count`/`_sum`/`_bucket`, OTLP = dotted OTel-convention names + attributes.
- Why does a counter named orders.placed show up as orders_placed_total in Prometheus?Prometheus convention marks monotonically increasing counters with a _total suffix, and Micrometer's Prometheus NamingConvention appends it automatically (plus converting the dot to an underscore).
- Should you include the unit in the meter name yourself?No. The registry appends the base unit (e.g. _seconds for a Timer) from the meter's declared unit. Hard-coding it risks double suffixes and breaks the OTLP dotted-name convention.
saying these in an interview costs you the question
- Assuming the meter name is byte-identical across Prometheus and OTLP.
- Manually appending _seconds/_total into the meter name.
- Forgetting Prometheus adds _count/_sum/_bucket series for a single timer.