com.bakdata.kafka.ErrorHeaderTransformer Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of error-handling Show documentation
Show all versions of error-handling Show documentation
A library for error handling in Kafka Streams.
/*
* MIT License
*
* Copyright (c) 2021 bakdata
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.bakdata.kafka;
import java.nio.charset.StandardCharsets;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.apache.kafka.common.header.Headers;
import org.apache.kafka.streams.kstream.ValueTransformer;
import org.apache.kafka.streams.kstream.ValueTransformerSupplier;
import org.apache.kafka.streams.processor.ProcessorContext;
/**
* {@link ValueTransformer} that produces a message with the original value and headers detailing the error that has
* been captured.
*
* Headers added by this ValueTransformer:
*
* - {@code __streams.errors.topic}: original input topic of the erroneous record
* - {@code __streams.errors.partition}: original partition of the input topic of the erroneous record
* - {@code __streams.errors.offset}: original offset in the partition of the input topic of the erroneous
* record
* - {@code __streams.errors.description}: description of the context in which an exception has been thrown
* - {@code __streams.errors.exception.class.name}: class of the exception that was captured
* - {@code __streams.errors.exception.message}: message of the exception that was captured
* - {@code __streams.errors.exception.stack_trace}: stack trace of the exception that was captured
*
*
* @param type of value
*/
@RequiredArgsConstructor
public class ErrorHeaderTransformer implements ValueTransformer, V> {
/**
* Prefix of all headers added by this ValueTransformer
*/
public static final String HEADER_PREFIX = "__streams.errors.";
/**
* Header indicating the original input topic of the erroneous record
*/
public static final String TOPIC = HEADER_PREFIX + "topic";
/**
* Header indicating the original partition in the input topic of the erroneous record
*/
public static final String PARTITION = HEADER_PREFIX + "partition";
/**
* Header indicating the original offset in the partition of the input topic of the erroneous record
*/
public static final String OFFSET = "HEADER_PREFIX + offset";
/**
* Header indicating the description of the context in which an exception has been thrown
*/
public static final String DESCRIPTION = HEADER_PREFIX + "description";
/**
* Prefix of all headers detailing the error message added by this ValueTransformer
*/
public static final String EXCEPTION_PREFIX = HEADER_PREFIX + "exception.";
/**
* Header indicating the class of the exception that was captured
*/
public static final String EXCEPTION_CLASS_NAME = EXCEPTION_PREFIX + "class.name";
/**
* Header indicating the message of the exception that was captured
*/
public static final String EXCEPTION_MESSAGE = EXCEPTION_PREFIX + "message";
/**
* Header indicating the stack trace of the exception that was captured
*/
public static final String EXCEPTION_STACK_TRACE = EXCEPTION_PREFIX + "stack_trace";
private final @NonNull String description;
private ProcessorContext context = null;
/**
* Create a new {@code ErrorHeaderTransformer} with the provided description
*
* @param description description of the context in which an exception has been thrown
* @param type of value
* @return {@code ValueTransformerSupplier} that produces a message with the original value and headers detailing
* the error that has been captured.
*/
public static ValueTransformerSupplier, V> withErrorHeaders(final String description) {
return () -> new ErrorHeaderTransformer<>(description);
}
@Override
public void init(final ProcessorContext context) {
this.context = context;
}
@Override
public V transform(final ProcessingError value) {
this.addHeader(TOPIC, this.context.topic());
this.addHeader(PARTITION, Integer.toString(this.context.partition()));
this.addHeader(OFFSET, Long.toString(this.context.offset()));
this.addHeader(EXCEPTION_CLASS_NAME, value.getThrowable().getClass().getName());
this.addHeader(EXCEPTION_MESSAGE, value.getThrowable().getMessage());
this.addHeader(EXCEPTION_STACK_TRACE, ExceptionUtils.getStackTrace(value.getThrowable()));
this.addHeader(DESCRIPTION, this.description);
return value.getValue();
}
@Override
public void close() {
// do nothing
}
private void addHeader(final String key, final String value) {
final Headers headers = this.context.headers();
headers.remove(key);
headers.add(key, value.getBytes(StandardCharsets.UTF_8));
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy