skip to content

How do you integrate Kafka client metrics into a Micrometer/OpenTelemetry-based observability stack (e.g. a Spring Boot service exporting to Prometheus)?

level: middleimportance: should knowfreq 40%

answer

  1. Micrometer binders: KafkaClientMetrics / KafkaStreamsMetrics
  2. reads client.metrics() -> registers kafka.* gauges
  3. spring-kafka: micrometerEnabled (default true) on factories
  4. OTel via Micrometer bridge / OTel agent / KIP-714 push
  5. watch cardinality + re-bind on client recreation

basics

~20 s

Bind Kafka's client metrics into Micrometer using KafkaClientMetrics (or Spring Boot's auto-binding for KafkaTemplate/listener containers). Micrometer then exports them to your backend (Prometheus, OTel). It reads the client's metrics() map and registers them as Micrometer gauges.

solid answer

~40 s

Micrometer ships binders — `KafkaClientMetrics`, `KafkaConsumerMetrics`, `KafkaStreamsMetrics` — that wrap a KafkaProducer/Consumer/Streams instance and expose its native Kafka metrics as Micrometer meters (prefixed `kafka.*`). They work by reading the client's `metrics()` map and the underlying KafkaMetric values, so you get request-latency-avg, records-lag-max, buffer-available-bytes, etc., in whatever backend your MeterRegistry targets (Prometheus, OTLP, Datadog). In Spring Boot with `spring-kafka`, this is largely automatic: setting `micrometerEnabled` on the producer/consumer factories (default true) binds metrics, and observation support adds tracing. For OpenTelemetry specifically you either route Micrometer through the OTel registry/bridge, or use KIP-714 to push OTLP straight to the broker, or run the OTel Java agent. Key caveat: binders that bind a Kafka client must be re-bound when the client is recreated, and high-cardinality per-partition tags can blow up your TSDB.

go deeper

for a junior

Know that a Micrometer Kafka binder exposes Kafka client metrics to Prometheus, and Spring Boot wires it for you.

for a middle

Use KafkaClientMetrics.bindTo(registry), know spring-kafka's micrometerEnabled, and the OTel options.

for a senior

Reason about re-binding, cardinality control via MeterFilters, recording level, and which OTel path fits.

for a principal

Set fleet conventions: metric naming, cardinality budgets, and choose Micrometer export vs OTel agent vs KIP-714 push per ownership model.

**The building blocks.** - *Micrometer* is a vendor-neutral metrics facade for the JVM: you record meters (counters, gauges, timers) once and a `MeterRegistry` exports them to a backend (Prometheus, OTLP, Datadog, etc.). - *Kafka clients* already expose metrics internally via `producer.metrics()` / `consumer.metrics()` returning a `Map<MetricName, ? extends Metric>`. - The job of integration is to *bridge* Kafka's metric set into Micrometer (or directly into OpenTelemetry) so it lands in your existing dashboards. **Micrometer binders.** Micrometer's `micrometer-core` provides Kafka binders in `io.micrometer.core.instrument.binder.kafka`: - `KafkaClientMetrics(KafkaProducer/KafkaConsumer/AdminClient)` — generic binder for any client; registers all `kafka.*` meters. - `KafkaStreamsMetrics(KafkaStreams)` — Streams metrics. - (`KafkaConsumerMetrics` is the older JMX-scraping binder; the newer `KafkaClientMetrics` reads the client directly.) You construct the binder with the client and call `bindTo(meterRegistry)`. Internally it enumerates the client's metrics and registers a Micrometer gauge per Kafka metric, copying tags (client-id, topic, node-id) as Micrometer tags. Because Kafka metrics are created lazily, the binder also schedules periodic re-checks so newly appearing metrics get registered. **Spring Boot / spring-kafka.** `spring-kafka` integrates this for you: `DefaultKafkaProducerFactory` and `DefaultKafkaConsumerFactory` have a `micrometerEnabled` flag (true by default) that binds `KafkaClientMetrics` to the application's `MeterRegistry`. Listener containers add their own metrics (e.g. `spring.kafka.listener` timers) and, with observation enabled (`observationEnabled`/`@KafkaListener` observation), produce spans/traces. With `spring-boot-starter-actuator` + `micrometer-registry-prometheus`, these surface at `/actuator/prometheus` automatically. **OpenTelemetry paths.** Three common routes: 1. **Micrometer → OTel bridge**: use the OpenTelemetry MeterRegistry (or the Micrometer-to-OTel bridge) so the same Kafka meters export over OTLP. 2. **OTel Java agent**: the agent auto-instruments Kafka clients for *tracing* (producer/consumer spans with context propagation) and can also collect metrics; minimal code changes. 3. **KIP-714 push**: skip per-client export entirely and let the broker collect OTLP metrics (see the KIP-714 question). Good when operators, not app teams, own observability. **Edge cases / gotchas:** - **Re-binding on client recreation.** A binder holds a reference to a specific client; if you close and recreate the producer/consumer, you must bind the new instance or metrics go stale. spring-kafka handles this in its factories. - **Cardinality.** Per-topic and (in DEBUG recording level) per-partition tags multiply series; filter with MeterFilters or aggregate. - **Naming.** Micrometer renames Kafka's `request-latency-avg` to dotted `kafka.request.latency.avg` style and Prometheus further munges it to underscores — account for that in dashboards/alerts. - **Recording level.** `metrics.recording.level=DEBUG` on the client unlocks extra per-partition metrics but increases overhead and cardinality. - **Double counting.** Don't simultaneously scrape JMX *and* bind Micrometer for the same client unless you intend two copies.

  • You recreate the KafkaConsumer at runtime and your Kafka Micrometer metrics freeze. Why?
    A binder (KafkaClientMetrics) wraps a specific client instance. The old instance is gone, so its gauges go stale/zero. You must bind the new consumer instance. spring-kafka's factories handle re-binding automatically; manual binders don't.
  • What's the difference between using the Micrometer binder versus the OpenTelemetry Java agent for Kafka clients?
    The Micrometer binder exports Kafka's native client metrics as meters into your MeterRegistry (metrics-focused). The OTel agent auto-instruments for distributed tracing (producer/consumer spans, context propagation) and can also collect metrics with zero code changes. They serve different primary purposes and can be combined.

saying these in an interview costs you the question

  • Thinking Micrometer invents new Kafka metrics — it just bridges the client's existing metrics() map.
  • Forgetting to re-bind the binder when the client is recreated.
  • Ignoring tag cardinality (per-topic/per-partition) and blowing up the TSDB.
  • Assuming the OTel agent gives the same per-metric gauges as the Micrometer binder — the agent is primarily tracing.
  • Hardcoding metric names without accounting for Micrometer/Prometheus name munging.

context