Observability for the Kafka Rust Client
The Rust client for Apache Kafka® includes built-in logging and a metrics framework for monitoring client behavior and performance. Use these logs and metrics for troubleshooting.
Log levels
The log crate defines the following levels, from least to most verbose:
Level |
When the client uses it |
|---|---|
|
Unrecoverable conditions that prevent normal operation. |
|
Recoverable problems, unexpected configuration values, and deprecated behavior. |
|
High-level lifecycle events such as client creation and shutdown. |
|
Per-request activity, broker connections, and internal state changes useful for diagnosing problems. |
|
Fine-grained detail including network I/O and individual record handling. Produces a large volume of output. |
Set up logging
To set up logging, add the log crate,
log = "0.4", to your project. Set up a concrete logging backend to
control how and where the output appears.
env_logger is a common choice for command-line applications and examples.
Add it to your project:
cargo add env_logger
Initialize
env_loggerat the start of your application, before creating your client:fn main() { env_logger::init(); // create your producer, consumer, or admin client here }
Before running your application, set the
RUST_LOGenvironment variable to control which log records appear:RUST_LOG=confluent_kafka=debug cargo run
Alternatively, set the level for only the client crate:
RUST_LOG=confluent_kafka=info cargo run
You can also combine the log level with your application’s own logging:
RUST_LOG=my_app=debug,confluent_kafka=warn cargo run
For the full syntax, see the env_logger documentation.
Metrics
The client tracks performance and operational data in an internal metrics framework that uses the same design as the Kafka Java Client. The framework uses these terms:
- Metric
A single named measurement, such as an average fetch size. Each metric is identified by a MetricName that carries a name, a logical group, a description, and a map of key-value tags.
- Sensor
A component that records a continuous sequence of numerical values and updates a set of associated metrics with statistics such as averages, maximums, and minimums.
- Recording level
A per-sensor setting that controls whether the sensor’s recordings are collected, based on the configured
metrics.recording.level. All built-in sensors useINFO-level recording.- Registry
The client’s internal collection of all currently registered metrics. You read the registry through
metrics(), as described in Accessing metrics.
Accessing metrics
Call metrics() on a KafkaConsumer or KafkaProducer to get a
live view of all currently registered metrics:
use confluent_kafka::common::Metric;
use confluent_kafka::consumer::Consumer;
let view = consumer.metrics();
for (name, metric) in view.snapshot().iter() {
println!("{}: {:?}", name, metric.metric_value());
}
metrics() returns a MetricsView, which is a live, read-only view of
the client’s metrics registry. Every lookup reads the registry as it is at the
time of the call. To iterate over all metrics or take a point-in-time copy, call
MetricsView::snapshot(). This returns an
Arc<HashMap<MetricName, Arc<KafkaMetric>>> reflecting the registry at that
moment. Metrics added or removed afterward appear in MetricsView, but
not in MetricsView::snapshot().
MetricName
Each metric is identified by a MetricName with these fields:
Field |
Description |
|---|---|
|
The metric’s short name. For example |
|
The logical group the metric belongs to. For example,
|
|
A human-readable description of what the metric measures. |
|
Additional key-value attributes, such as |
Two MetricName values are equal when their name, group, and
tags match. Equality ignores description, matching the behavior of the
Java Client.
MetricValue
metric_value() on a KafkaMetric returns a MetricValue enum:
Variant |
Description |
|---|---|
|
A measurable value such as a rate, average, or maximum. |
|
A string-valued gauge. |
|
A long-valued gauge. |
|
An integer-valued gauge. |
Consumer metric groups
The consumer registers metrics in several groups. KafkaAdminClient
doesn’t expose a metrics() method. All metrics listed here use
INFO-level recording and are always active regardless of the
metrics.recording.level setting.
Consumer metrics
The consumer-metrics cover poll timing, commit timing, and idle ratios.
Metric name |
Description |
|---|---|
|
The number of seconds since the last |
|
The average delay between |
|
The maximum delay between |
|
The average fraction of time that |
|
The total time spent in |
|
The total time spent in |
Fetch manager metrics
The consumer-fetch-manager-metrics track fetch request activity
and record consumption at the client, topic, and partition level.
Metric name |
Description |
|---|---|
|
The average number of bytes fetched per request. |
|
The maximum number of bytes fetched per request. |
|
The average number of bytes consumed per second. |
|
The total number of bytes consumed. |
|
The average number of records per fetch request. |
|
The average number of records consumed per second. |
|
The total number of records consumed. |
|
The average time taken for a fetch request, in milliseconds. |
|
The maximum time taken for a fetch request, in milliseconds. |
|
The number of fetch requests per second. |
|
The total number of fetch requests. |
|
The maximum lag, in number of records, for any partition in the current window. Lag is based on the current position, not the committed offset. |
|
The minimum lead, in number of records, for any partition in the current window. |
|
The average throttle time imposed by the broker, in milliseconds. |
|
The maximum throttle time imposed by the broker, in milliseconds. |
Topic-level fetch metrics use the same names as the client-level metrics
and are distinguished by the topic tag.
Metric name |
Description |
|---|---|
|
The average number of bytes fetched per request for this topic. |
|
The maximum number of bytes fetched per request for this topic. |
|
The average number of bytes consumed per second for this topic. |
|
The total number of bytes consumed for this topic. |
|
The average number of records per fetch request for this topic. |
|
The average number of records consumed per second for this topic. |
|
The total number of records consumed for this topic. |
Partition-level fetch metrics are tagged with topic and partition.
Metric name |
Description |
|---|---|
|
The latest lag of the partition. |
|
The maximum lag of the partition. |
|
The average lag of the partition. |
|
The latest lead of the partition. |
|
The minimum lead of the partition. |
|
The average lead of the partition. |
|
The current read replica for the partition, or |
Rebalance metrics
The consumer-coordinator-metrics track group rebalance frequency,
latency, and failure rate.
Metric name |
Description |
|---|---|
|
The average time taken for a group rebalance, in milliseconds. |
|
The maximum time taken for a group rebalance, in milliseconds. |
|
The total time spent in group rebalances, in milliseconds. |
|
The total number of rebalance events. |
|
The number of rebalance events per hour. |
|
The total number of failed rebalance events. |
|
The number of failed rebalance events per hour. |
|
The number of seconds since the last rebalance event. |
|
The number of partitions currently assigned to this consumer. |
|
The average time taken for a partition-assigned rebalance listener callback. |
|
The maximum time taken for a partition-assigned rebalance listener callback. |
|
The average time taken for a partition-revoked rebalance listener callback. |
|
The maximum time taken for a partition-revoked rebalance listener callback. |
|
The average time taken for a partition-lost rebalance listener callback. |
|
The maximum time taken for a partition-lost rebalance listener callback. |
Heartbeat metrics
The consumer-coordinator-metrics track the frequency, latency,
and recency of heartbeats sent to the group coordinator.
Metric name |
Description |
|---|---|
|
The maximum time taken to receive a heartbeat response, in milliseconds. |
|
The number of heartbeats sent per second. |
|
The total number of heartbeats sent. |
|
The number of seconds since the last heartbeat. |
Commit metrics
The consumer-coordinator-metrics track commit requests sent
to the group coordinator.
Metric name |
Description |
|---|---|
|
The average time taken for a commit request, in milliseconds. |
|
The maximum time taken for a commit request, in milliseconds. |
|
The number of commit calls per second. |
|
The total number of commit calls. |
Internal consumer thread metrics
The consumer-metrics track the internal async event queues
between the application thread and the consumer’s background network thread.
Metric name |
Description |
|---|---|
|
The average time between network thread poll iterations, in milliseconds. |
|
The maximum time between network thread poll iterations, in milliseconds. |
|
The current number of events waiting in the application event queue. |
|
The average time an event spends in the application event queue, in milliseconds. |
|
The maximum time an event spends in the application event queue, in milliseconds. |
|
The average time to process an event from the application event queue, in milliseconds. |
|
The maximum time to process an event from the application event queue, in milliseconds. |
|
The current number of events waiting in the background event queue. |
|
The average time an event spends in the background event queue, in milliseconds. |
|
The maximum time an event spends in the background event queue, in milliseconds. |
|
The average time to process an event from the background event queue, in milliseconds. |
|
The maximum time to process an event from the background event queue, in milliseconds. |
|
The current number of unsent requests in the queue. |
|
The average time a request spends in the unsent requests queue, in milliseconds. |
|
The maximum time a request spends in the unsent requests queue, in milliseconds. |
Producer metric groups
The producer registers metrics in groups: producer-metrics for
client-level statistics and producer-topic-metrics for per-topic
statistics.
Producer metrics
The producer-metrics cover send throughput, batch characteristics,
request latency, throttling, and cumulative operation timing.
Metric name |
Description |
|---|---|
|
The average number of bytes sent per partition per request. |
|
The maximum number of bytes sent per partition per request. |
|
The average number of batch splits per second. |
|
The total number of batch splits. |
|
The average compression rate of record batches, defined as the average ratio of the compressed batch size over the uncompressed size. |
|
Total time the producer has spent in |
|
The age in seconds of the current producer metadata being used. |
|
Total time the producer has spent waiting on topic metadata, in nanoseconds. |
|
The average time that a broker throttled a request, in milliseconds. |
|
The maximum time that a broker throttled a request, in milliseconds. |
|
The average per-second number of record sends that resulted in errors. |
|
The total number of record sends that resulted in errors. |
|
The average time in milliseconds record batches spent in the send buffer. |
|
The maximum time in milliseconds record batches spent in the send buffer. |
|
The average per-second number of retried record sends. |
|
The total number of retried record sends. |
|
The average number of records sent per second. |
|
The total number of records sent. |
|
The average record size, in bytes. |
|
The maximum record size, in bytes. |
|
The average number of records per request. |
|
The average request latency in milliseconds. |
|
The maximum request latency in milliseconds. |
|
The current number of in-flight requests awaiting a response. |
|
Total time the producer has spent in |
|
Total time the producer has spent in |
|
Total time the producer has spent in |
|
Total time the producer has spent in |
|
Total time the producer has spent in |
Producer topic metrics
The producer-topic-metrics are scoped to individual topics and
carry a topic tag in addition to the client-id tag. They report
the same metric names regardless of topic, distinguished by their tags.
Metric name |
Description |
|---|---|
|
The average number of bytes sent per second for a topic. |
|
The total number of bytes sent for a topic. |
|
The average compression rate of record batches for a topic, defined as the average ratio of the compressed batch size over the uncompressed size. |
|
The average per-second number of record sends that resulted in errors for a topic. |
|
The total number of record sends that resulted in errors for a topic. |
|
The average per-second number of retried record sends for a topic. |
|
The total number of retried record sends for a topic. |
|
The average number of records sent per second for a topic. |
|
The total number of records sent for a topic. |
Configure metrics collection
You can’t attach a metrics reporter to the producer, consumer, or admin
client. metrics() returns a read-only MetricsView, and the client
doesn’t support the metric.reporters property. If you set it, the client
ignores it and logs it at the info level as an unused configuration. To
export client metrics, read them periodically with metrics(), as described
in Accessing metrics.
To control how the registry collects data, use the metrics.recording.level,
metrics.num.samples, and metrics.sample.window.ms configuration
properties. For full definitions and defaults, see
Configuration for Rust Client for Kafka.
The MetricsReporter trait works only with a Metrics registry that you
create yourself, for example to track your application’s own metrics with the
same framework. Implement the trait to receive callbacks when metrics are
registered, updated, or removed, and register it with
Metrics::add_reporter():
use std::sync::Arc;
use confluent_kafka::common::Metric;
use confluent_kafka::common::metrics::{KafkaMetric, Metrics, MetricsReporter};
struct MyReporter;
impl MetricsReporter for MyReporter {
fn init(&self, metrics: &[Arc<KafkaMetric>]) {
println!("Initial metrics: {}", metrics.len());
}
fn metric_change(&self, metric: &Arc<KafkaMetric>) {
println!("Metric updated: {}", metric.metric_name());
}
fn metric_removal(&self, metric: &Arc<KafkaMetric>) {
println!("Metric removed: {}", metric.metric_name());
}
}
let metrics = Metrics::new();
metrics.add_reporter(Arc::new(MyReporter));
add_reporter() calls init() with every metric already in the registry.
The trait also provides a close() method, which is called when the
metrics registry closes. Each method defaults to a no-operation
implementation. Override only the methods that you use.
Debugging
Debug the Rust client by enabling debug-level logging first, then inspect
metrics snapshots to identify the source of the problem.
Start with logs
Enable debug-level logging for the client to see per-request output,
broker connection state, and internal event details not visible at the
info level:
RUST_LOG=confluent_kafka=debug cargo run
If you need even finer detail, including network I/O and individual fetch
handling, switch to trace:
RUST_LOG=confluent_kafka=trace cargo run
To keep client debug output while reducing noise from your own application,
set a separate level for each target. This example limits my_app to
warnings and errors and keeps the client at debug:
RUST_LOG=my_app=warn,confluent_kafka=debug cargo run
Interpreting errors
The client’s Error type exposes a set of classification predicates that
let you decide how to handle an error without pattern-matching on every
variant:
Method |
When to use it |
|---|---|
|
Returns |
|
Returns |
|
Returns |
|
Returns |
Check retriability to decide whether to continue polling or shut down the consumer:
use std::time::Duration;
use confluent_kafka::consumer::Consumer;
loop {
match consumer.poll(Duration::from_millis(500)).await {
Ok(records) => { /* process records */ },
Err(e) if e.is_retriable_error() => {
// transient — wait briefly and retry the poll loop
log::warn!("Retriable error, continuing: {e}");
},
Err(e) => {
// non-retriable — log and shut down
log::error!("Fatal consumer error: {e}");
break;
},
}
}
Troubleshooting
This section covers common issues and their resolutions. If you encounter other issues, gather the diagnostic information in the Debugging checklist and open a support case.
Consumer doesn’t start because of the group protocol
If the broker doesn’t support the consumer group protocol, the consumer hangs.
The Rust client supports only group.protocol=consumer, which must be explicitly
set. If group.protocol=classic, KafkaConsumer::new returns an
UnsupportedVersion error. If the broker doesn’t support KIP-848, the consumer
can stall silently while waiting for group coordinator responses.
If the broker doesn’t support the consumer group protocol, the consumer’s
heartbeat request fails, and the next call to poll returns an error for
which is_unsupported_version_error() returns true, with the message
The cluster does not support the new CONSUMER group protocol. The message
also suggests setting group.protocol=classic, which doesn’t apply to the
Rust client.
At the error level, the log shows a line that starts with
ConsumerGroupHeartbeatRequest failed due to unsupported version.
Fix the group protocol error
Verify that you’re using Kafka 4.0 or later, Confluent Platform 8.0 or later,
or Confluent Cloud. Confirm that group.protocol=consumer is in your configuration.
Rebalances are frequent or failing
Frequent rebalances degrade throughput and can cause repeated reprocessing of records.
Metrics for rebalances
rebalance-rate-per-hourandfailed-rebalance-rate-per-hour: A spike in either indicates instability.time-between-poll-max: A high value means the application is spending too long betweenpoll()calls.last-heartbeat-seconds-ago: A growing value means the consumer isn’t completing heartbeats, either because the event loop is blocked or because the coordinator isn’t responding.
Fix frequent rebalances
Check the following configuration values:
If record processing takes longer than
max.poll.interval.ms, the consumer leaves the group, which triggers a rebalance. Increase themax.poll.interval.msvalue or process fewer records per poll batch usingmax.poll.records.With the KIP-848 protocol, the broker controls the session timeout with
group.consumer.session.timeout.ms. The client-sidesession.timeout.msproperty has no effect. If the consumer doesn’t send a heartbeat within the broker’s session timeout, the broker considers it failed and removes it from the group. Ensure the event loop isn’t blocked.
Consumer lag is growing
Rising consumer lag means producers are writing records faster than the consumer can process them.
Metrics for consumer lag
records-lag-max: The maximum lag across all partitions.records-lagtagged withtopicandpartition: The lag for each partition. Use it to identify hot partitions.
Reduce consumer lag
Increase the number of consumer instances in the group, up to the number of partitions.
If processing is fast and network round-trips are the bottleneck, increase
max.poll.records.Profile the application code that runs between
poll()calls.
Commit failures
Commit failures can cause records to be reprocessed after a restart or a rebalance.
Metrics for commit failures
commit-latency-avgandcommit-latency-max: High values indicate broker pressure or network latency.commit-rate: An unexpected drop might indicate the commit loop is exiting early.
Fix commit failures
commit_sync() returns an error on failure. Check whether
error.is_retriable_error() returns true. If it does, retry the commit.
A CoordinatorNotAvailable error means the group coordinator is unavailable,
and a NotCoordinator error means that the broker contacted is no longer the
group coordinator, for example because the coordinator moved. These errors are
retriable, and the client refreshes coordinator metadata automatically.
High fetch latency
High fetch latency reduces throughput and increases end-to-end processing delay.
Metrics for fetch latency
fetch-latency-avgandfetch-latency-max: High values indicate network delay, broker-side slowness, or fetch configuration issues.fetch-throttle-time-avgandfetch-throttle-time-max: A nonzero value means the broker is rate-limiting the consumer, which is distinct from organic latency. Iffetch-throttle-time-avgaccounts for most offetch-latency-avg, the root cause is quota exhaustion, not a performance problem.bytes-consumed-rate: A low rate combined with high latency points to small, infrequent fetches.
Reduce fetch latency
Check broker-side metrics for disk I/O or CPU pressure.
Review
fetch.max.wait.msandfetch.min.bytes: increasingfetch.min.bytesbatches more data per request at the cost of added latency.Review
fetch-throttle-time-avg: a nonzero value means the broker is rate-limiting the consumer.
Internal event queues backing up
The consumer uses asynchronous event queues between the application thread and the background network thread. Queues that grow without draining indicate a bottleneck inside the client’s internal processing loop.
Metrics for event queue backlogs
application-event-queue-sizeandbackground-event-queue-size: If either is consistently nonzero, the corresponding processing thread is not keeping up.unsent-requests-queue-size: A growing queue means the network thread can’t send requests fast enough. Check for broker connectivity issues or high broker latency.time-between-network-thread-poll-max: A spike here indicates that the network thread was blocked, which can delay heartbeats and trigger a rebalance.
Clear event queue backlogs
Check that application code between poll() calls isn’t
blocking the Tokio runtime. Check broker connectivity. If queues stay backed
up, collect the information in Debugging checklist and open a
support case.
Debugging checklist
When opening a support case or filing a bug report, collect the following information:
The full error message and, if available, the error variant name from
format!("{:?}", error)Log output at
debuglevel from the moment the problem starts, usingRUST_LOG=confluent_kafka=debugA snapshot of the metrics returned by
consumer.metrics()at the time of the problem, printed as(name, value)pairsThe consumer configuration, without credentials:
bootstrap.servers,group.id,group.protocol, timeout settings, and any non-default valuesThe Kafka broker version or Confluent Cloud cluster type
The Rust client version from
Cargo.lock