Apache Camel's Dead Letter Channel (DLC) is an Enterprise Integration Pattern that gracefully handles failed messages by routing them to a designated endpoint, preventing route blockages. This article explores DLC configuration using a lightweight log endpoint—no JMS broker required—ideal for development, testing, or simple logging setups. You'll get a complete, runnable example to see it in action.
What is Dead Letter Channel?
The DLC activates when a message processing fails after exhausting retries. Unlike Camel's default error handler, which logs exceptions, DLC preserves the original input message and forwards it to a "dead letter" endpoint like a log, file, or queue. Key benefits include message isolation for later analysis and non-blocking route behavior.
It supports redelivery policies for automatic retries before final routing. This pattern shines in enterprise integration where failures (e.g., network issues, data validation errors) must not halt overall flow.
Why Use Log Endpoint?
The log component offers zero-setup error handling: it captures failures at ERROR level with full exchange details (body, headers, exception stack trace). Perfect for:
- Quick debugging without external dependencies.
- Console output in standalone apps or containers.
- Custom formatting via options like
showAll=trueormultiline=true.
No database, file system, or broker needed—run it anywhere Camel supports logging (SLF4J/Logback by default).
Complete Working Example
This standalone Java app uses Camel Main to simulate failures via a timer-triggered route. It retries twice, then logs the failure.
Java DSL Code
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.main.Main;
public class DeadLetterLogDemo extends RouteBuilder {
@Override
public void configure() {
// Configure DLC with log endpoint
errorHandler(deadLetterChannel("log:dead?level=ERROR&showAll=true&multiline=true")
.useOriginalMessage() // Preserves original input
.maximumRedeliveries(2)
.redeliveryDelay(1000)
.retryAttemptedLogLevel(org.apache.camel.LoggingLevel.WARN));
// Route that simulates failure
from("timer:fail?period=5000&repeatCount=1")
.log("🔥 Starting processing: ${body}")
.process(exchange -> {
// Simulate a runtime failure (e.g., bad data parsing)
throw new RuntimeException("💥 Simulated processing error!");
})
.log("✅ Success log (won't reach here)");
}
public static void main(String[] args) throws Exception {
Main main = new Main();
main.configure().addRoutesBuilder(new DeadLetterLogDemo());
main.run(args); // Runs until Ctrl+C
}
}
Expected Console Output
🔥 Starting processing:
Failed delivery for (MessageId: 25B7DFF9FB26DD8-0000000000000000 on ExchangeId: 25B7DFF9FB26DD8-0000000000000000). On delivery attempt: 0 caught: java.lang.RuntimeException: 💥 Simulated processing error!
Failed delivery for (MessageId: 25B7DFF9FB26DD8-0000000000000000 on ExchangeId: 25B7DFF9FB26DD8-0000000000000000). On delivery attempt: 1 caught: java.lang.RuntimeException: 💥 Simulated processing error!
Failed delivery for (MessageId: 25B7DFF9FB26DD8-0000000000000000 on ExchangeId: 25B7DFF9FB26DD8-0000000000000000). On delivery attempt: 2 caught: java.lang.RuntimeException: 💥 Simulated processing error!
Exchange[
Id: 25B7DFF9FB26DD8-0000000000000000
RouteGroup: null
RouteId: route1
ExchangePattern: InOnly
Properties: {CamelExceptionCaught=java.lang.RuntimeException: 💥 Simulated processing error!, CamelFailureRouteId=route1, CamelFatalFallbackErrorHandler=[route1], CamelToEndpoint=log://dead?level=ERROR&multiline=true&showAll=true}
Advanced Customizations
Enhance with processors for richer logging:
errorHandler(deadLetterChannel("log:dead?level=ERROR")
.onExceptionOccurred(exchange -> {
exchange.getIn().setHeader("failureTime", System.currentTimeMillis());
exchange.getIn().setHeader("errorDetails", exchange.getException().getMessage());
}));
Best Practices
- Always pair with
useOriginalMessage()to avoid losing input data. - Set
maximumRedeliveries=0for immediate DLC without retries in tests. - Monitor log volume in production; consider rotating to file or alerting.
- Test edge cases: network timeouts, serialization errors, poison messages.
- Global DLC via
camelContext.setErrorHandlerFactory()for app-wide coverage.
This setup provides robust, observable error handling. Experiment by changing the failure to a real processor (e.g., invalid JSON parsing) for your use cases.
Leave a Reply