---
title: "Kafka-Client-Metriken über OTLP — ein weiteres Puzzleteil"
date: 2026-07-07T00:00:00.000Z
author: "grzegorz"
excerpt: "KIP-714 ermöglicht es Kafka-Clients, ihre eigenen Metriken über OTLP an die Broker zu übermitteln. monedula-metrics-reporter leitet diese Metriken nun ebenfalls weiter, neben den Broker-SPI- und Yammer-Metriken."
---
Im [vorherigen Artikel](/blog/kafka-metrics-opentelemetry-otlp-monedula-metrics-reporter/) habe ich über Kafka-Broker-Metriken geschrieben und darüber, warum ihre Erfassung schwieriger ist, als sie sein sollte.

Die Kurzfassung lautete: Kafka hat nicht ein einziges Metriksystem.

Es hat die neuere Kafka Metrics SPI, konfiguriert über `metric.reporters`, und die ältere Yammer/Coda-Hale-Registry, konfiguriert über `kafka.metrics.reporters`. Einige wichtige Broker-Metriken liegen im einen System, andere im anderen, und die meisten realen Monitoring-Setups laufen nach wie vor über JMX, reguläre Ausdrücke, Prometheus-Mappings und Dashboards, die zu den exportierten Namen passen können oder auch nicht.

Das war der Grund für die Entwicklung von `monedula-metrics-reporter`: ein Kafka-Plugin, das Metriken direkt über OTLP an einen OpenTelemetry Collector exportiert und beide Kafka-Metrik-Registries mit einer einzigen Konfiguration abdeckt.

Doch ein Teil fehlte noch immer.

Broker-Metriken sagen uns viel über den Cluster, aber sie erklären nicht vollständig, was innerhalb der Client-Anwendungen geschieht.

Ein Broker kann uns sagen, dass Produce-Requests langsam sind. Er kann uns sagen, dass Fetch-Requests langsam sind. Er kann uns Auskunft über Request-Queues, Network-Processors, unterreplizierte Partitionen, fehlgeschlagene Requests und vieles mehr geben.

Aber manchmal ist die eigentliche Frage nicht nur:

> Was passiert auf dem Broker?

Sondern auch:

> Was passiert innerhalb des Producers oder Consumers?

Batcht der Producer effizient?
Wird er gedrosselt?
Pollt der Consumer regelmäßig?
Verbringt er Zeit in Rebalances?
Verhält sich eine Anwendung anders als eine andere?

Traditionell bedeutete die Beantwortung dieser Fragen, jede Client-Anwendung einzeln zu instrumentieren. In Java hieß das oft wieder JMX. In anderen Sprachen hing es von der Client-Bibliothek, vom Framework und vom jeweiligen Observability-Setup des Teams ab.

Und in größeren Organisationen wird daraus ein sehr vertrautes Problem: Das Kafka-Team betreibt den Cluster, aber die Client-Anwendungen gehören vielen unterschiedlichen Teams. Wenn also etwas schiefläuft, hat der Cluster-Betreiber oft nicht die Client-seitigen Metriken, die er braucht, und um sie zu bekommen, sind Koordination, Codeänderungen, Redeployments oder zumindest Neustarts erforderlich.

Genau dieses Problem versucht [KIP-714](https://cwiki.apache.org/confluence/display/KAFKA/KIP-714%3A+Client+metrics+and+observability) zu lösen.

## Die Idee hinter KIP-714

KIP-714 fügt eine Broker-gesteuerte Möglichkeit hinzu, Metriken von Kafka-Clients zu erfassen.

Anstatt dass jedes Anwendungsteam seinen eigenen Metrik-Exporter konfiguriert, kann der Kafka-Cluster Client-Metrik-Subscriptions definieren. Eine Subscription legt fest, welche Client-Metriken erfasst werden sollen und wie oft die Clients sie übermitteln sollen.

Die Clients übermitteln ihre Metriken dann über das Kafka-Protokoll an die Broker. Die Payload selbst ist OTLP, sodass das Broker-seitige Plugin sie an einen OpenTelemetry Collector, eine Zeitreihendatenbank oder ein beliebiges anderes Observability-Backend weiterleiten kann.

Der entscheidende Punkt ist die Richtung der Steuerung.

Der Cluster-Betreiber entscheidet, was erfasst wird.

Clients, die KIP-714 unterstützen, können die Subscription empfangen und die angeforderten Metriken übermitteln. Die Anwendung muss kein JMX bereitstellen. Sie braucht keinen Sidecar-Exporter. Sie braucht keinen eigenen Prometheus-Endpunkt.

Es gibt dennoch zwei Voraussetzungen:

1. Auf dem Broker muss ein Client-Telemetry-Plugin konfiguriert sein.
2. Es muss mindestens eine Client-Metrik-Subscription existieren.

Ohne eine Subscription können Clients das Protokoll zwar unterstützen, sie werden aber keine Metriken übermitteln.

Das ist ein gutes Design. Client-Metriken werden nicht versehentlich erfasst. Der Betreiber muss die Erfassung auf der Cluster-Seite ausdrücklich aktivieren.

## Ein wichtiges Detail: Nicht jeder Client sendet dieselben Metriken

KIP-714 schafft einen gemeinsamen Mechanismus zur Erfassung von Client-Metriken, aber es sorgt nicht auf magische Weise dafür, dass jeder Kafka-Client denselben Metriksatz bereitstellt.

Welche Metriken man erhält, hängt von der Client-Bibliothek ab.

Der Java-Client sendet derzeit den umfangreichsten Metriksatz. Das ist nicht verwunderlich: Der Java-Client ist die Referenzimplementierung, er verfügt über eine sehr ausgereifte interne Metrik-Registry, und viele Kafka-Metriken wurden zuerst dort entworfen.

Andere Clients stellen möglicherweise einen kleineren Satz bereit. librdkafka-basierte Clients, etwa die von Python-, Go-, .NET- und Node-Wrappern verwendeten Clients, können ebenfalls an KIP-714 teilnehmen, aber die verfügbaren Metriken sind nicht zwangsläufig identisch mit denen, die der Java-Client sendet.

Das mentale Modell sollte also lauten:

> KIP-714 standardisiert, wie Client-Metriken angefordert und transportiert werden.
> Der tatsächliche Metriksatz hängt weiterhin von der Client-Implementierung ab.

Das ist beim Bau von Dashboards von Bedeutung.

Einige Panels können auf gemeinsamen Metriken basieren und sollten über verschiedene Client-Bibliotheken hinweg funktionieren. Andere Panels sind nur dann aussagekräftig, wenn der Client tatsächlich den umfangreicheren Metriksatz sendet. In der Praxis liefert der Java-Client heute die vollständigste Observability-Geschichte.

## Warum das zu monedula-metrics-reporter hinzufügen?

Ursprünglich ging es bei `monedula-metrics-reporter` um Broker-Metriken.

Es verarbeitete die Kafka-SPI-Registry.
Es verarbeitete die Yammer-Registry.
Es exportierte beide über OTLP.
Es fügte die Broker-Identität als Resource-Attribute hinzu.
Es wurde mit Grafana-Dashboards ausgeliefert, die zu den emittierten Metriken passen.

Doch sobald KIP-714 existiert, ist der nächste Schritt ganz naheliegend:

> Wenn das Plugin ohnehin schon innerhalb des Brokers läuft und ohnehin OTLP an den Collector exportiert, sollte es auch die Client-übermittelten Metriken verarbeiten.

Andernfalls bliebe das Kafka-Monitoring weiterhin fragmentiert.

Man hätte einen Pfad für Broker-SPI-Metriken, einen weiteren für Yammer-Metriken und noch ein weiteres Plugin oder eine weitere Komponente für KIP-714-Client-Telemetrie.

Das würde funktionieren, aber es würde die gesamte Idee, den Kafka-Metrik-Stack zu vereinfachen, zunichtemachen.

Die neue Version von `monedula-metrics-reporter` verarbeitet nun also drei Metrikquellen:

1. Kafka Metrics SPI
2. Yammer / Coda Hale Broker-Metriken
3. KIP-714-Client-übermittelte Telemetrie

Das Ziel ist einfach: ein Plugin, eine Collector-Pipeline, ein Satz von Dashboards.

Client-Metriken werden über denselben OpenTelemetry-Pfad wie die Broker-Metriken weitergeleitet. Sie können dieselben Broker-Labels tragen, etwa `kafka_cluster_id` und `kafka_node_id`, und sie können zusätzlich mit der `client_id` angereichert werden, sodass Metriken verschiedener Anwendungen nicht zu einem einzigen anonymen Strom verschmelzen.

Dieser letzte Punkt ist sehr wichtig.

Wenn zwei Producer denselben Metriknamen übermitteln, müssen wir wissen, welcher Producer welcher ist. Standardmäßig fügt der Reporter die Client-ID als Label hinzu. Sensiblere oder Labels mit hoher Kardinalität, wie Client-Instance-ID, Principal oder Client-Adresse, sind optional zuschaltbar (opt-in).

## Wie es funktioniert

Der Broker-seitige Teil ist über Kafkas `ClientTelemetry`-Interface implementiert.

Wenn ein KIP-714-fähiger Client Telemetrie an einen Broker übermittelt, empfängt das Plugin die OTLP-Payload. Der Request-Handler-Thread leistet dabei nur minimale Arbeit: Er kopiert die Payload in eine beschränkte In-Memory-Queue. Anschließend liest ein Daemon-Thread aus dieser Queue, wandelt die Payload in OpenTelemetry-SDK-Metrikdaten um, reichert sie mit Labels an und exportiert sie über einen dedizierten Metrik-Exporter.

Das ist dasselbe Designprinzip wie beim Rest des Reporters:

> Kafka darf niemals von der Observability-Pipeline blockiert werden.

Wenn der Collector nicht verfügbar, langsam oder fehlkonfiguriert ist, sollte Kafka den Datenverkehr weiter bedienen. Der Telemetrie-Batch kann verworfen werden. Die Kafka-Latenz sollte nicht vom Monitoring-Backend abhängen.

Der Client-Telemetry-Receiver hat ebenfalls seine eigenen Selbstüberwachungs-Metriken:

```text
monedula_reporter_clienttelemetry_received_total
monedula_reporter_clienttelemetry_forwarded_total
monedula_reporter_clienttelemetry_dropped_total
monedula_reporter_clienttelemetry_unsupported_metrics_dropped_total
monedula_reporter_clienttelemetry_queue_depth
````

Wenn also die Client-Telemetrie nicht mehr fließt, gibt es etwas, das man sich ansehen kann, bevor man ins Raten gerät.

## Installation

Das Artefakt wird in Maven Central veröffentlicht, sodass kein manueller Build erforderlich ist.

Für Version `0.10.0` lädst du das JAR aus Maven Central herunter:

```bash
curl -L \
  -o monedula-metrics-reporter-0.10.0.jar \
  https://repo1.maven.org/maven2/dev/monedula/monedula-metrics-reporter/0.10.0/monedula-metrics-reporter-0.10.0.jar
```

Dann kopierst du es in Kafkas Classpath:

```bash
cp monedula-metrics-reporter-0.10.0.jar $KAFKA_HOME/libs/
```

Danach konfigurierst du den Broker:

```properties
metric.reporters=dev.monedula.metricsreporter.OtlpMetricReporter

otlp.metric.reporter.endpoint=http://otel-collector:4317
otlp.metric.reporter.transport=grpc
```

Damit wird der Reporter für Kafka-SPI-Metriken, Yammer-Broker-Metriken und Client-Telemetrie aktiviert.

Client-Telemetrie ist auf Brokern standardmäßig aktiviert:

```properties
otlp.metric.reporter.client.telemetry.enabled=true
```

Du kannst sie explizit deaktivieren:

```properties
otlp.metric.reporter.client.telemetry.enabled=false
```

Wenn sie deaktiviert ist, wird der Broker die Telemetrie-Fähigkeit nicht an Clients ankündigen.

## Collector-Konfiguration

Eine minimale OpenTelemetry-Collector-Konfiguration kann OTLP vom Reporter empfangen und Metriken für Prometheus bereitstellen:

```yaml
receivers:
  otlp:
    protocols:
      grpc:
        endpoint: "0.0.0.0:4317"
      http:
        endpoint: "0.0.0.0:4318"

exporters:
  prometheus:
    endpoint: "0.0.0.0:8889"
    resource_to_telemetry_conversion:
      enabled: true

service:
  pipelines:
    metrics:
      receivers: [otlp]
      exporters: [prometheus]
```

Die Einstellung `resource_to_telemetry_conversion` ist bei der Verwendung von Prometheus wichtig. Sie überführt OpenTelemetry-Resource-Attribute in Prometheus-Labels.

Ohne sie erscheinen Labels wie `kafka_cluster_id`, `kafka_node_id` und `client_id` möglicherweise nicht dort, wo du sie erwartest.

Prometheus kann den Collector dann scrapen:

```yaml
scrape_configs:
  - job_name: otel-collector
    static_configs:
      - targets: ["otel-collector:8889"]
```

## Der wichtige Teil: Client-Metrik-Subscriptions

Das Installieren des Plugins reicht nicht aus.

Das ist der Teil, den man bei KIP-714 leicht übersieht.

Clients übermitteln Metriken nur dann, wenn auf dem Cluster eine Client-Metrik-Subscription konfiguriert ist.

Zum Beispiel:

```bash
kafka-client-metrics.sh \
  --bootstrap-server broker:9092 \
  --alter \
  --name all-clients \
  --metrics "*" \
  --interval 5000
```

Damit wird eine Subscription namens `all-clients` erstellt oder aktualisiert, alle verfügbaren Client-Metriken angefordert und das Push-Intervall auf 5 Sekunden gesetzt.

Für den Produktivbetrieb ist es möglicherweise zu viel, alles zu erfassen. Du kannst dich auch nur auf ausgewählte Metrik-Präfixe abonnieren.

Zum Beispiel könntest du mit den Standard-Kafka-Client-Metriken beginnen:

```bash
kafka-client-metrics.sh \
  --bootstrap-server broker:9092 \
  --alter \
  --name standard-client-metrics \
  --metrics "org.apache.kafka." \
  --interval 10000
```

Die genaue Subscription-Strategie hängt davon ab, was du beobachten möchtest und wie viel Metrikvolumen für dich vertretbar ist.

Wichtig ist, dass die Subscription auf der Broker-Seite lebt. Dadurch kann der Kafka-Betreiber entscheiden, was erfasst werden soll, ohne jedes Anwendungsteam bitten zu müssen, sein Monitoring-Setup zu ändern.

## Was ist mit der Client-Konfiguration?

Bei Clients, die KIP-714 unterstützen, ist das Übermitteln von Metriken üblicherweise standardmäßig aktiviert.

Dennoch kannst du es explizit angeben:

```properties
enable.metrics.push=true
```

Bei Java-Clients ist dies Teil der Kafka-Client-Konfiguration.

Bei librdkafka-basierten Clients, etwa `confluent-kafka-python`, gilt dieselbe Idee. Zum Beispiel:

```python
Producer({
    "bootstrap.servers": "broker:9092",
    "client.id": "orders-producer",
    "enable.metrics.push": True,
})
```

Und für einen Consumer:

```python
Consumer({
    "bootstrap.servers": "broker:9092",
    "group.id": "orders-consumer-group",
    "client.id": "orders-consumer",
    "enable.metrics.push": True,
})
```

Es lohnt sich, die `client.id` bewusst zu setzen.

Der Reporter fügt sie standardmäßig zu den weitergeleiteten Client-Metriken hinzu, sodass Dashboards die Metriken nach Anwendung trennen können. Ohne eine aussagekräftige Client-ID erhältst du zwar trotzdem Metriken, aber sie sind bei der Fehlersuche deutlich schwerer zu verwenden.

## Anreicherungsoptionen

Standardmäßig werden weitergeleitete Client-Metriken mit der Broker-Identität und der Client-ID angereichert:

```properties
otlp.metric.reporter.client.telemetry.enrich.broker=true
otlp.metric.reporter.client.telemetry.enrich.client.id=true
```

Das bedeutet, dass Client-Metriken nach Cluster, Broker und Anwendung gruppiert werden können.

Es gibt außerdem optionale Anreicherungen:

```properties
otlp.metric.reporter.client.telemetry.enrich.client.instance.id=true
otlp.metric.reporter.client.telemetry.enrich.client.identity=true
```

Die Client-Instance-ID ist nützlich, wenn du einzelne laufende Client-Instanzen unterscheiden möchtest, sie hat jedoch eine hohe Kardinalität.

Die Client-Identität fügt Principal und Client-Adresse hinzu. Das kann in kontrollierten Umgebungen sehr nützlich sein, aber es kann auch sensible Informationen enthalten und ist daher standardmäßig deaktiviert.

Kurz gesagt: Beginne mit Broker-Labels und Client-ID. Aktiviere den Rest nur dann, wenn du ihn wirklich brauchst.

## Quickstart

Das Repository enthält einen Quickstart-Stack mit Kafka, dem OpenTelemetry Collector, Prometheus und Grafana.

Starte den Quickstart:

```bash
cd quickstart
docker compose up -d
```

Der Demo-Service erstellt eine KIP-714-Client-Metrik-Subscription und startet Producer- und Consumer-Verkehr. Sobald der Stack läuft, sollten also sowohl die Broker-Dashboards als auch die Client-Dashboards beginnen, Daten zu empfangen.

Grafana ist erreichbar unter:

```text
http://localhost:3000
```

Prometheus ist erreichbar unter:

```text
http://localhost:9090
```

Der Quickstart erstellt die Subscription mit:

```bash
kafka-client-metrics.sh \
  --bootstrap-server kafka1:9092 \
  --alter \
  --name quickstart-clients \
  --metrics "*" \
  --interval 5000
```

Das ist bewusst einfach gehalten: alles erfassen, alle 5 Sekunden übermitteln und die Dashboards schnell zum Leben erwecken.

Für reale Umgebungen würde ich üblicherweise vorsichtiger beginnen. Zuerst die Standard-Metriken erfassen, das Volumen validieren und bei Bedarf weitere Metriken hinzufügen.

## Java-Clients im Vergleich zu anderen Clients

Eine interessante Sache an KIP-714 ist, dass es einen gemeinsamen Mechanismus schafft, aber nicht jede Client-Bibliothek dieselbe Metriktiefe bereitstellt.

Der Java-Client sendet den umfangreichsten Metriksatz. Er kann viele Metriken aus seiner internen Registry übermitteln, darunter detailliertere Producer- und Consumer-Informationen: Batching, Buffer-Nutzung, Request-Timing, Aufschlüsselungen auf Topic-Ebene, Consumer-Lag und -Lead sowie weitere Java-Client-spezifische Signale.

Andere Client-Bibliotheken senden möglicherweise einen kleineren Satz.

Das bedeutet nicht, dass KIP-714 nur für Java-Clients nützlich ist. Es ist nach wie vor sehr wertvoll, einen gemeinsamen, Broker-gesteuerten Erfassungspfad für alle Clients zu haben, die es unterstützen. Aber wenn du dir Dashboards ansiehst, solltest du daran denken, dass fehlende Panels nicht immer bedeuten, dass etwas kaputt ist.

Manchmal existiert die Metrik in dieser Client-Bibliothek schlicht nicht.

Aus diesem Grund sind die neuen Grafana-Dashboards in zwei Teile aufgeteilt:

1. Standard-Panels
2. Erweiterte Panels

Die Standard-Panels sollten für jeden KIP-714-konformen Client funktionieren, der die relevanten gemeinsamen Metriken bereitstellt.

Die erweiterten Panels sind hauptsächlich für den Java-Client nützlich, da der Java-Client derzeit den umfangreichsten Metriksatz bereitstellt.

Diese Aufteilung ist beabsichtigt. Ich möchte kein Dashboard, das nur deshalb kaputt aussieht, weil ein Nicht-Java-Client keine Java-spezifischen Metriken bereitstellt. Es ist besser, die Grenze sichtbar zu machen.

## Neue Dashboards

Die neue Version fügt zwei Client-fokussierte Dashboards hinzu:

* Kafka Producers (client)
* Kafka Consumers (client)

Das Producer-Dashboard konzentriert sich auf die Client-seitige Sicht des Produzierens von Records: Request-Latenz, Throttling, Queueing, Batching, Fehler und Durchsatz.

Das Consumer-Dashboard konzentriert sich auf die Client-seitige Sicht des Konsumierens von Records: Fetch-Latenz, Commit-Rate, Poll-Verhalten, Rebalance-bezogene Signale, Zuordnungsänderungen und, wo verfügbar, Consumer-seitiges Lag/Lead.

Beide Dashboards enthalten Filter für Cluster und Client-ID. Wo der Metriksatz es unterstützt, enthalten sie zudem Drill-downs auf Topic- oder Group-Ebene.

<figure>
  <img src="/blog/monedula-metrics-reporter-kip-714/producer-dashboard.png" alt="Das Producer-Dashboard - Client-Seite." />
  <figcaption>Das Producer-Dashboard - Client-Seite</figcaption>
</figure>

<figure>
  <img src="/blog/monedula-metrics-reporter-kip-714/consumer-dashboard.png" alt="Das Consumer-Dashboard - Client-Seite." />
  <figcaption>Das Consumer-Dashboard - Client-Seite</figcaption>
</figure>

Eines gefällt mir an diesem Setup besonders gut: Derselbe Grafana-Ordner kann nun beide Seiten der Geschichte zeigen.

Broker-Dashboards beantworten:

> Ist der Cluster gesund?

Client-Dashboards beantworten:

> Wie erleben die Anwendungen den Cluster?

Und weil beide aus derselben OTLP-Pipeline mit konsistenten Labels stammen, wird es viel einfacher, sie zu korrelieren.

## Zusammenfassung

KIP-714 ist ein sehr wichtiger Schritt für die Kafka-Observability.

Es verwandelt Client-Metriken von etwas, das jedes Anwendungsteam eigenständig bereitstellen muss, in etwas, das der Kafka-Cluster zentral anfordern kann.

`monedula-metrics-reporter` unterstützt dieses Modell nun.

Es exportiert weiterhin Broker-Metriken aus beiden Kafka-Metrik-Registries. Aber jetzt empfängt es auch Client-übermittelte OTLP-Telemetrie von KIP-714-Clients und leitet sie an denselben OpenTelemetry Collector weiter.

Das Plugin deckt somit nun drei Quellen ab:

* Kafka-SPI-Metriken
* Yammer-Broker-Metriken
* KIP-714-Client-Telemetrie

Das Ergebnis ist eine einfachere Kafka-Monitoring-Architektur: ein Plugin auf den Brokern, eine OTLP-Pipeline und Dashboards, die sowohl das Broker-seitige als auch das Client-seitige Verhalten abdecken.

Der Code ist Open Source und [auf GitHub verfügbar](https://github.com/monedula-dev/monedula-metrics-reporter)