Appendix Q: Kafka Diagnostics
Collecting broker logs, consumer group state, topic metadata, JMX metrics, and thread dumps for Kafka troubleshooting.
When a Kafka-backed integration route stalls β consumer lag climbs, messages disappear, or throughput drops β you need a systematic approach to collecting diagnostic data. This appendix covers the tools and commands for gathering the information you need to diagnose Kafka issues, from the local Podman stack through production Kubernetes deployments.
Diagnostic data categories
Kafka diagnostics fall into five categories, each answering different questions:
| Category | What it tells you | Key tools |
|---|---|---|
| Broker logs | Errors, warnings, rebalance events, slow requests | Container logs, log4j |
| Topic metadata | Partition layout, replication status, config overrides | kafka-topics.sh |
| Consumer group state | Lag, assignment, coordinator, member status | kafka-consumer-groups.sh |
| JMX metrics | Request latency, throughput, ISR counts, buffer usage | JMX exporter, Kafka UI |
| JVM diagnostics | Thread dumps, heap dumps, GC logs | jcmd, jstack, JFR |
Broker logs
Local Podman stack
# Stream broker logs (follow mode)
podman logs -f eip-kafka
# Last 200 lines
podman logs --tail 200 eip-kafka
# Grep for specific errors
podman logs eip-kafka 2>&1 | grep -i "error\|exception\|warn"
What to look for
| Log pattern | Indicates |
|---|---|
LEADER_NOT_AVAILABLE |
Partition leader election in progress β transient during startup |
NOT_ENOUGH_REPLICAS |
Too few in-sync replicas for acks=all writes |
REBALANCE / JoinGroup |
Consumer group rebalancing β check frequency and duration |
RequestTimedOut |
Broker unable to handle requests within timeout β check load |
OutOfMemoryError |
JVM heap exhaustion β increase broker memory or reduce load |
CorruptRecordException |
Data corruption β investigate disk health |
Adjusting broker log level
For the Podman stack, set the KAFKA_LOG4J_ROOT_LOGLEVEL environment variable:
# compose.yaml
environment:
- KAFKA_LOG4J_ROOT_LOGLEVEL=INFO
- KAFKA_LOG4J_LOGGERS=kafka.controller=DEBUG,kafka.server=DEBUG
For specific debugging scenarios:
- Rebalance issues:
kafka.coordinator.group=DEBUG - Replication:
kafka.server.ReplicaManager=DEBUG - Request handling:
kafka.network.RequestChannel=DEBUG
Topic metadata
List topics and their configuration
# List all topics
podman exec eip-kafka /opt/kafka/bin/kafka-topics.sh \
--bootstrap-server localhost:9092 --list
# Describe a specific topic (partitions, replicas, ISR)
podman exec eip-kafka /opt/kafka/bin/kafka-topics.sh \
--bootstrap-server localhost:9092 \
--describe --topic eip.orders.placed
Output shows partition layout, leader assignment, and in-sync replica status:
Topic: eip.orders.placed PartitionCount: 3 ReplicationFactor: 1
Partition: 0 Leader: 1 Replicas: 1 Isr: 1
Partition: 1 Leader: 1 Replicas: 1 Isr: 1
Partition: 2 Leader: 1 Replicas: 1 Isr: 1
Red flags: Isr count less than Replicas count means replicas are out of sync. Leader: -1 means no leader is elected for that partition.
Check topic-level configuration overrides
podman exec eip-kafka /opt/kafka/bin/kafka-configs.sh \
--bootstrap-server localhost:9092 \
--entity-type topics --entity-name eip.orders.placed \
--describe
Check disk usage per topic
podman exec eip-kafka /opt/kafka/bin/kafka-log-dirs.sh \
--bootstrap-server localhost:9092 \
--describe --topic-list eip.orders.placed
This shows the size of each partitionβs log segments on disk β essential for capacity planning and identifying topics that are growing faster than expected.
Consumer group diagnostics
Consumer group state is usually the first thing to check when messages are βstuck.β
List consumer groups
podman exec eip-kafka /opt/kafka/bin/kafka-consumer-groups.sh \
--bootstrap-server localhost:9092 --list
Describe a consumer group (lag, assignment)
podman exec eip-kafka /opt/kafka/bin/kafka-consumer-groups.sh \
--bootstrap-server localhost:9092 \
--describe --group inventory-service
Output:
GROUP TOPIC PARTITION CURRENT-OFFSET LOG-END-OFFSET LAG CONSUMER-ID HOST CLIENT-ID
inventory-service eip.orders.placed 0 1042 1050 8 consumer-1-abc-xyz /10.89.0.5 consumer-1
inventory-service eip.orders.placed 1 987 987 0 consumer-1-abc-xyz /10.89.0.5 consumer-1
inventory-service eip.orders.placed 2 1103 1200 97 consumer-2-def-uvw /10.89.0.6 consumer-2
What each column means:
- CURRENT-OFFSET: last committed offset for this (group, partition)
- LOG-END-OFFSET: latest message offset in the partition
- LAG:
LOG-END-OFFSET - CURRENT-OFFSETβ messages not yet committed - CONSUMER-ID: which consumer instance owns this partition
- HOST: consumerβs IP address
Diagnostic patterns:
| Observation | Likely cause |
|---|---|
| High lag, consumer assigned | Consumer is slow β check processing time, downstream dependencies |
| High lag, no consumer assigned | Consumer crashed or was evicted β check consumer logs |
| All partitions assigned to one consumer | Other consumers failed to join β check connectivity, group coordinator |
| Lag growing steadily | Production rate exceeds consumption rate β add consumers or optimize processing |
| Lag spikes then recovers | Burst traffic or brief consumer pause (GC, rebalance) |
Check consumer group state
podman exec eip-kafka /opt/kafka/bin/kafka-consumer-groups.sh \
--bootstrap-server localhost:9092 \
--describe --group inventory-service --state
Output:
GROUP COORDINATOR (ID) ASSIGNMENT-STRATEGY STATE #MEMBERS
inventory-service kafka:9092 (1) range Stable 2
States: Stable (normal), PreparingRebalance (rebalancing in progress), CompletingRebalance (almost done), Empty (no active consumers), Dead (group metadata expired).
Reset consumer group offsets
When you need to reprocess messages (after a bug fix, for example):
# Dry run first β see what would change
podman exec eip-kafka /opt/kafka/bin/kafka-consumer-groups.sh \
--bootstrap-server localhost:9092 \
--group inventory-service \
--topic eip.orders.placed \
--reset-offsets --to-earliest --dry-run
# Execute the reset (group must have no active consumers)
podman exec eip-kafka /opt/kafka/bin/kafka-consumer-groups.sh \
--bootstrap-server localhost:9092 \
--group inventory-service \
--topic eip.orders.placed \
--reset-offsets --to-earliest --execute
Other reset targets: --to-latest, --to-offset <n>, --to-datetime <ISO-8601>, --shift-by <n>.
JMX metrics
Kafka exposes hundreds of metrics via JMX. For integration route debugging, focus on these:
Broker-side metrics
| Metric | What it tells you |
|---|---|
kafka.server:type=BrokerTopicMetrics,name=MessagesInPerSec |
Ingestion rate β is the broker keeping up? |
kafka.server:type=BrokerTopicMetrics,name=BytesInPerSec |
Byte-level ingestion rate |
kafka.server:type=ReplicaManager,name=UnderReplicatedPartitions |
Replicas falling behind β disk or network issue |
kafka.server:type=ReplicaManager,name=IsrShrinksPerSec |
ISR shrink rate β replicas going out of sync |
kafka.network:type=RequestMetrics,name=TotalTimeMs,request=Produce |
Produce request latency β broker overload indicator |
kafka.network:type=RequestMetrics,name=TotalTimeMs,request=Fetch |
Fetch request latency |
Client-side metrics (Camel/Kafka producer)
| Metric | What it tells you |
|---|---|
kafka.producer:type=producer-metrics,client-id=*,record-send-rate |
Records sent per second |
kafka.producer:type=producer-metrics,client-id=*,record-error-rate |
Failed sends per second |
kafka.producer:type=producer-metrics,client-id=*,buffer-available-bytes |
Remaining buffer space |
kafka.producer:type=producer-metrics,client-id=*,request-latency-avg |
Average round-trip time |
Exposing JMX metrics in the Podman stack
# compose.yaml β add JMX exporter to Kafka
environment:
- KAFKA_JMX_OPTS=-Dcom.sun.management.jmxremote
-Dcom.sun.management.jmxremote.port=9999
-Dcom.sun.management.jmxremote.rmi.port=9999
-Dcom.sun.management.jmxremote.authenticate=false
-Dcom.sun.management.jmxremote.ssl=false
ports:
- "9999:9999"
Our Kafka UI at http://localhost:8090 surfaces many of these metrics in a friendlier format β check the broker and topic dashboards first before diving into raw JMX.
JVM diagnostics
When Kafka issues are JVM-related (GC pauses, thread contention, memory pressure):
Thread dump
# Get the JVM PID inside the container
podman exec eip-kafka bash -c 'jcmd 1 Thread.print'
Look for:
- Threads stuck in
BLOCKEDstate β contention on a lock - Many threads in
WAITINGon the same monitor β resource starvation GCthreads consuming excessive CPU
Heap dump
# Trigger a heap dump (large file β ensure disk space)
podman exec eip-kafka bash -c 'jcmd 1 GC.heap_dump /tmp/kafka-heap.hprof'
# Copy it out of the container
podman cp eip-kafka:/tmp/kafka-heap.hprof ./kafka-heap.hprof
Analyze with Eclipse MAT, VisualVM, or jhat to find memory leaks.
GC logging
Add GC logging to the brokerβs JVM options:
environment:
- KAFKA_OPTS=-Xlog:gc*:file=/var/log/kafka/gc.log:time,tags:filecount=5,filesize=20M
A diagnostic workflow
When something goes wrong, work through this checklist:
- Check consumer group lag β is the consumer falling behind? (
kafka-consumer-groups.sh --describe) - Check consumer group state β is the group stable or rebalancing? (
--state) - Check broker logs β any errors, warnings, or rebalance entries? (
podman logs) - Check topic metadata β are all partitions healthy with leaders and ISR? (
kafka-topics.sh --describe) - Check Camel route logs β is the route processing messages or stuck?
- Check JMX metrics β request latency, under-replicated partitions, buffer usage
- Thread dump β if the JVM appears hung, capture thread state
Most Kafka issues in integration routes fall into three categories: consumer lag (consumer too slow), rebalance storms (consumers joining/leaving frequently), and producer failures (broker unavailable, buffer exhaustion). The diagnostic workflow above covers all three.
Verification status: verified β conceptual reference chapter, no runnable example.