skip to content

You need your Spring Boot service to push metrics via OTLP to an OpenTelemetry Collector. How do you configure OtlpMeterRegistry, and what key properties matter?

level: seniorimportance: should knowfreq 40%

answer

  1. micrometer-registry-otlp → OtlpMeterRegistry (StepMeterRegistry)
  2. management.otlp.metrics.export.{url,step,headers,aggregation-temporality}
  3. default url localhost:4318/v1/metrics, step 1m
  4. CUMULATIVE default vs DELTA
  5. resource-attributes / service.name identify source

basics

~10 s

Add micrometer-registry-otlp; Spring Boot auto-configures OtlpMeterRegistry. Set management.otlp.metrics.export.url to the collector (default http://localhost:4318/v1/metrics), tune step (interval), add auth via export.headers, and pick the aggregation-temporality. It pushes on each step.

solid answer

~40 s

Add the `io.micrometer:micrometer-registry-otlp` dependency; Spring Boot auto-configures an `OtlpMeterRegistry`, a `StepMeterRegistry` that batches and POSTs metrics over OTLP each step. The essential properties live under `management.otlp.metrics.export`: `url` — the collector endpoint (default `http://localhost:4318/v1/metrics`, OTLP/HTTP protobuf); `step` — publish interval (default 1m); `headers.*` — for auth, e.g. `Authorization`; and `aggregation-temporality` — CUMULATIVE (default, safe for Prometheus-backed pipelines) or DELTA. You also attach **resource attributes** (service.name, environment) so the backend can identify the source — Spring maps `management.otlp.metrics.export.resource-attributes` and picks up `spring.application.name`. Because it's push, the collector must be reachable and, if the collector is down during a step, that window can be lost. Keep the step aligned with your latency needs but not so short it floods the collector.

code

java · 22 lines
java
// build.gradle.kts
// runtimeOnly("io.micrometer:micrometer-registry-otlp")

/* application.yml
spring:
  application:
    name: order-service        # becomes OTLP resource attribute service.name
management:
  otlp:
    metrics:
      export:
        url: http://otel-collector:4318/v1/metrics
        step: 30s
        aggregation-temporality: cumulative
        headers:
          Authorization: "Bearer ${OTLP_TOKEN}"
        resource-attributes:
          deployment.environment: production
*/

// No custom code needed: Boot auto-configures OtlpMeterRegistry and it
// pushes every registered meter to the collector on each 30s step.

go deeper

for a junior

Know it's push, needs a url, and uses the micrometer-registry-otlp dependency.

for a middle

Configure url/step/headers and know the 4318/v1/metrics default and 1m step.

for a senior

Reason about temporality, resource attributes, and collector-down data loss.

for a principal

Architect the collector topology (sidecar vs gateway), auth/multitenancy via headers, and cost/latency of step tuning across services.

**Setup:** Adding `io.micrometer:micrometer-registry-otlp` triggers Spring Boot's OTLP metrics auto-configuration, which builds an `OtlpMeterRegistry`. It is a `StepMeterRegistry`: measurements are aggregated over a fixed **step** window and published (pushed) at the end of each window over the **OTLP** protocol — OpenTelemetry's wire protocol — as HTTP/protobuf by default. **Key properties (prefix `management.otlp.metrics.export`):** - **`url`** — destination, default `http://localhost:4318/v1/metrics`. Point it at your **OpenTelemetry Collector** (or a vendor OTLP endpoint). Port 4318 is OTLP/HTTP; the `/v1/metrics` path is the metrics signal. - **`step`** — how often to push; default `1m`. Smaller = fresher data but more load; larger = coarser and more data lost if a crash happens mid-window. - **`headers.<name>`** — arbitrary request headers, primarily for auth (`headers.Authorization=Bearer ...`) or tenant routing (`headers.X-Scope-OrgID=...`). - **`aggregation-temporality`** — `cumulative` (default) reports running totals since start; `delta` reports the change since the previous push. Must match what the backend expects. Cumulative pairs naturally with Prometheus-style storage; some vendors/pipelines prefer delta. - **`resource-attributes.<key>`** — OTel **resource attributes** describing the emitting service (e.g. `service.name`, `deployment.environment`). Spring also derives `service.name` from `spring.application.name`. These let the backend distinguish sources and are how OTLP replaces Prometheus's target-based identity. - **`enabled`** — toggle export on/off. **How it differs operationally from Prometheus pull:** with OTLP you own the target, cadence, and auth *in the app*; the collector centralizes fan-out to one or many backends (metrics + traces + logs in one pipeline). There's no `up` liveness signal for free — if the app stops pushing, the backend just sees a gap; detecting 'app down' needs its own logic. **Naming reminder:** OTLP keeps Micrometer's dotted names (`http.server.requests`) per OTel semantic conventions, unlike the Prometheus registry's snake_case + `_total`/`_seconds`. If the collector re-exports to Prometheus, it applies its own name translation. **Gotchas:** - **Collector reachability & buffering:** a push registry loses the current step's data if the collector is unreachable at publish time; there's limited in-app buffering. Run a local/sidecar collector for resilience. - **Temporality mismatch:** sending cumulative to a delta-expecting backend (or vice versa) yields wrong rates — align it deliberately. - **Step too short:** floods the collector and inflates cost; too long delays alerting. - **Secrets in headers:** inject auth tokens via env/config server, not committed properties. - **Both registries at once:** you can keep Prometheus scrape *and* OTLP push simultaneously via the composite registry; useful during migration. **When to choose OTLP push:** ephemeral/serverless workloads, apps the monitor can't scrape (NAT/firewall), or when standardizing on one OpenTelemetry pipeline for all telemetry signals.

  • What happens to the current step's metrics if the collector is unreachable when it's time to push?
    That window is typically lost — OtlpMeterRegistry has only limited buffering and no durable queue. A common mitigation is a local/sidecar collector that the app pushes to reliably and which forwards onward.
  • Why choose aggregation-temporality DELTA over the CUMULATIVE default?
    DELTA reports only the change per interval, which some backends (and stateless/serverless sources) prefer because they don't need to track a long-running baseline. CUMULATIVE suits Prometheus-style storage that computes rates from running totals.

saying these in an interview costs you the question

  • Thinking you configure a Prometheus URL for OTLP (it targets a collector on 4318).
  • Assuming OTLP guarantees delivery / durable buffering across collector outages.
  • Not setting service.name/resource attributes, leaving the backend unable to identify the source.

context