Sova is a Kafka consumer group metrics exporter for Prometheus, written in pure Java.
This repository builds a ZIP distribution, not a container image.
Images are built in the strimzi-kafka-operator repository.
Unpack the ZIP and run the Main class on the classpath, with configuration supplied as environment variables:
unzip sova-*.zip && cd sova-*/
export STRIMZI_KAFKA_ADMIN_CLIENT_CONFIGURATION='bootstrap.servers=kafka-bootstrap:9093
security.protocol=SSL
ssl.truststore.type=PEM
ssl.truststore.certificates=${strimzisecrets:my-ns/my-cluster-cluster-ca-cert:ca.crt}'
java -classpath "libs/*" io.strimzi.sova.MainMetrics are available at http://localhost:9404/metrics.
The liveness probe is at http://localhost:8080/healthy and the readiness probe at http://localhost:8080/ready.
Sova connects to Kafka in plaintext unless told otherwise.
The bootstrap servers, TLS, mutual TLS, SASL and any other Kafka client setting are configured through STRIMZI_KAFKA_ADMIN_CLIENT_CONFIGURATION.
Its value is Kafka client properties, one key=value per line.
bootstrap.servers is required; Sova fails at startup without it.
Every entry is passed to the Kafka Admin client as it is, for example:
bootstrap.servers=my-cluster-kafka-bootstrap:9093
security.protocol=SSL
ssl.truststore.type=PEM
ssl.truststore.certificates=${strimzisecrets:my-ns/my-cluster-cluster-ca-cert:ca.crt}A value that is not valid properties syntax fails Sova at startup.
Note: At startup, Sova logs only the keys of this variable, never the values to keep credentials out of the log.
The /metrics endpoint is served over plain HTTP.
| Metric name | Labels | Description |
|---|---|---|
kafka_brokers |
— | Total number of brokers in the cluster |
kafka_broker_info |
id, address |
Broker metadata (always 1) |
kafka_topic_partitions |
topic |
Number of partitions for the topic |
kafka_topic_partition_current_offset |
topic, partition |
Latest (high-water-mark) offset |
kafka_topic_partition_oldest_offset |
topic, partition |
Earliest available offset |
kafka_topic_partition_leader |
topic, partition |
Leader broker ID |
kafka_topic_partition_replicas |
topic, partition |
Total replica count |
kafka_topic_partition_in_sync_replica |
topic, partition |
In-sync replica count |
kafka_topic_partition_leader_is_preferred |
topic, partition |
1 if preferred leader is elected |
kafka_topic_partition_under_replicated_partition |
topic, partition |
1 if under-replicated |
kafka_consumergroup_members |
consumergroup |
Active member count |
kafka_consumergroup_current_offset |
consumergroup, topic, partition |
Committed offset |
kafka_consumergroup_current_offset_sum |
consumergroup, topic |
Sum of committed offsets |
kafka_consumergroup_lag |
consumergroup, topic, partition |
Lag per partition |
kafka_consumergroup_lag_sum |
consumergroup, topic |
Sum of lags across partitions |
kafka_consumergroup_state |
consumergroup, state |
Presence indicator: emits 1 for the group's current state (the state label), including Empty and Dead. Query a specific state, e.g. kafka_consumergroup_state{state="Stable"} |
sova_build_info |
version, revision, branch, java_info |
Build information |
sova_last_successful_collection_timestamp_seconds |
— | Unix timestamp of the last successful collection cycle, 0 if none has completed |
The process_* and jvm_* families are exported too.
-1means "no commit", and it is a real sample. If a consumer group has committed to a topic but not to every one of its partitions, the uncommitted partitions report-1in bothkafka_consumergroup_current_offsetandkafka_consumergroup_lag. Anyavg(),min(), ortopk()over rawkafka_consumergroup_lagmust filter> -1first, or the uncommitted partitions will skew the result.lag > Nthreshold alerts are unaffected, since-1never trips a positive threshold.- Lag is never clamped and can be negative. A committed offset ahead of the high-water mark is a real anomaly worth seeing, not a glitch, so it is reported as-is. Because the broker, topic, and consumer-group data is collected concurrently within a cycle, a busy partition can also show a small transient negative lag that means nothing — do not alert on
lag < 0without afor:duration. - The
_summetrics are not the sum of the visible per-partition series. Uncommitted partitions (the-1ones above) are excluded from bothkafka_consumergroup_lag_sumandkafka_consumergroup_current_offset_sum, since folding a-1into a sum only drags it down for no useful meaning. A panel showing per-partition series next to the_sumwill not reconcile — this is deliberate, not a bug. kafka_consumergroup_lag_sumcan be absent rather than0if offset fetching fails for a whole topic during a cycle. Preferabsent_over_time(...[5m])over a bareabsent()so a single bad collection cycle does not page on its own.sova_last_successful_collection_timestamp_secondsis the staleness signal, reading0before the first successful cycle.time() - sova_last_successful_collection_timestamp_secondsis the "is this exporter actually working" panel.
| Environment variable | Default | Description |
|---|---|---|
STRIMZI_GROUP_REGEX |
.* |
Consumer group include regex |
STRIMZI_GROUP_EXCLUDE_REGEX |
(none) | Consumer group exclude regex |
STRIMZI_TOPIC_REGEX |
.* |
Topic include regex |
STRIMZI_TOPIC_EXCLUDE_REGEX |
(none) | Topic exclude regex |
STRIMZI_KAFKA_ADMIN_CLIENT_CONFIGURATION |
(required) | Kafka client properties, one key=value per line; must contain bootstrap.servers (plus TLS, SASL, other client config) |
STRIMZI_METRICS_PORT |
9404 |
Port for /metrics endpoint |
STRIMZI_MANAGEMENT_PORT |
8080 |
Port for /healthy and /ready endpoints |
STRIMZI_COLLECT_INTERVAL_MS |
30000 |
Metrics collection interval in milliseconds |
STRIMZI_LOG_LEVEL |
INFO |
Root log level |
STRIMZI_AC_LOG_LEVEL |
WARN |
Log level for the org.apache.kafka logger |
/healthy is always 200 while the JVM can answer: a broken collection must not restart a pod that is still serving its last good snapshot.
/ready is 200 only while the snapshot being served is younger than two collection intervals.
Only a successful collection cycle replaces the snapshot, so failing cycles and cycles that hang - which throw nothing - both turn the probe DOWN once the exported numbers are frozen for that long.
Before the first cycle completes there is no snapshot, so the probe is DOWN.