Implementing Custom Interceptors
1. Understanding Interceptor Concept
| Aspect | Description | Detail |
|---|---|---|
| Interceptor | Hook in send/consume path | Cross-cutting |
| Use | Metrics, tracing, audit | Non-invasive |
| Chain | Multiple in order | Composable |
2. Creating Producer Interceptor
| Method | Description | Detail |
|---|---|---|
| onSend() | Before record sent | Modify/inspect |
| onAcknowledgement() | On ack/error | Metrics |
| close() | Cleanup | Optional |
Example: Producer interceptor
public class AuditInterceptor implements ProducerInterceptor<String,String> {
public ProducerRecord<String,String> onSend(ProducerRecord<String,String> r){
return r; }
public void onAcknowledgement(RecordMetadata m, Exception e){}
public void close(){}
public void configure(Map<String,?> c){}
}
3. Creating Consumer Interceptor
| Method | Description | Detail |
|---|---|---|
| onConsume() | Before poll returns | Inspect/modify |
| onCommit() | After offset commit | Tracking |
| close() | Cleanup | Optional |
Example: Consumer interceptor
public ConsumerRecords<String,String> onConsume(
ConsumerRecords<String,String> records){
return records; // inspect/transform
}
4. Implementing onSend() Method
| Aspect | Description | Detail |
|---|---|---|
| Input | ProducerRecord | Before serialize |
| Return | Possibly modified record | Same/new |
| No Throw | Catch exceptions | Don't break send |
Example: Add header in onSend
public ProducerRecord<String,String> onSend(ProducerRecord<String,String> r){
r.headers().add("trace-id", traceId().getBytes());
return r;
}
5. Implementing onConsume() Method
| Aspect | Description | Detail |
|---|---|---|
| Input | ConsumerRecords batch | All partitions |
| Return | Filtered/modified batch | Same type |
| Use | Decrypt, filter, count | Pre-process |
Example: Count in onConsume
public ConsumerRecords<String,String> onConsume(
ConsumerRecords<String,String> recs){
meter.mark(recs.count());
return recs;
}
6. Configuring Interceptor Chain
| Config | Description | Detail |
|---|---|---|
| interceptor.classes | Ordered FQN list | Comma-separated |
| Producer | ProducerInterceptor impls | onSend order |
| Consumer | ConsumerInterceptor impls | onConsume order |
Example: Register chain
props.put(ProducerConfig.INTERCEPTOR_CLASSES_CONFIG,
"com.acme.AuditInterceptor,com.acme.MetricsInterceptor");
7. Adding Monitoring Metrics
| Metric | Where | Detail |
|---|---|---|
| Send Count | onSend | Throughput |
| Error Rate | onAcknowledgement | Failures |
| Lag | onConsume timestamps | Latency |
Example: Track acks
public void onAcknowledgement(RecordMetadata m, Exception e){
if (e != null) errors.inc(); else sent.inc();
}
8. Implementing Tracing
| Aspect | Description | Detail |
|---|---|---|
| Inject | Trace context to headers | onSend |
| Extract | Continue span on consume | onConsume |
| Standard | W3C traceparent | OpenTelemetry |
Example: Inject traceparent
r.headers().add("traceparent",
span.context().toTraceparent().getBytes());
9. Adding Security Checks
| Aspect | Description | Detail |
|---|---|---|
| PII Redaction | Strip sensitive fields | onSend |
| Validation | Reject bad payloads | Early |
| Audit | Log principal/action | Compliance |
Example: Redact in onSend
String redacted = r.value().replaceAll("\\d{16}", "****");
return new ProducerRecord<>(r.topic(), r.key(), redacted);
10. Transforming Messages
| Aspect | Description | Detail |
|---|---|---|
| Enrich | Add fields/metadata | onSend |
| Compress/Encrypt | Transform payload | Reversible |
| Caution | Keep lightweight | Hot path |