The console producer turns input lines into records; the console consumer prints them. Kafka 4 129 renamed their options: --reader-property controls input parsing, --formatter-property output, and --command-property passes client settings such as acks.
# Produce one event with a key and two headers, then read exactly that record back
K="docker exec -i l3-kafka /opt/kafka/bin"
B="--bootstrap-server l3-kafka:9092"
T="--topic booknest.order-events"
printf 'source:web,trace:a1\t1001\t{"type":"order_placed","order_id":1001}\n' |
$K/kafka-console-producer.sh $B $T --reader-property parse.headers=true \
--reader-property parse.key=true # headers TAB key TAB value
$K/kafka-console-consumer.sh $B $T --partition 0 --offset 454 --max-messages 1 \
--formatter-property print.timestamp=true --formatter-property print.headers=true \
--formatter-property print.key=true 2>/dev/null | tr '\t' '\n'Output
CreateTime:1790907459929
source:web,trace:a1
1001
{"type":"order_placed","order_id":1001}--partition with --offset fetches one exact record (key 1001 hashed to partition 0, next offset 454), the way to inspect a message that breaks a consumer. In scripts add --max-messages or --timeout-ms, or the consumer waits forever.