Updated Branches:
  refs/heads/master 09ceee600 -> ea3d7dc8e

CAMEL-6407: Include message history in stacktraces from error handler, making 
it easier to know where the problem was. Work in progress.


Project: http://git-wip-us.apache.org/repos/asf/camel/repo
Commit: http://git-wip-us.apache.org/repos/asf/camel/commit/ca007a55
Tree: http://git-wip-us.apache.org/repos/asf/camel/tree/ca007a55
Diff: http://git-wip-us.apache.org/repos/asf/camel/diff/ca007a55

Branch: refs/heads/master
Commit: ca007a55f5274ade7ee517ba69f7dea8b01a2a7c
Parents: 09ceee6
Author: Claus Ibsen <davscl...@apache.org>
Authored: Fri May 31 08:31:48 2013 +0200
Committer: Claus Ibsen <davscl...@apache.org>
Committed: Fri May 31 08:31:59 2013 +0200

----------------------------------------------------------------------
 .../org/apache/camel/RuntimeConfiguration.java     |   14 +++++
 .../org/apache/camel/impl/DefaultCamelContext.java |    9 +++
 .../org/apache/camel/impl/DefaultRouteContext.java |   14 +++++
 .../apache/camel/model/OnExceptionDefinition.java  |   16 ++++++
 .../camel/model/ProcessorDefinitionHelper.java     |   35 +++++++++++++
 .../camel/model/RedeliveryPolicyDefinition.java    |   37 +++++++++++++
 .../org/apache/camel/model/RouteDefinition.java    |   40 +++++++++++++++
 .../camel/processor/CamelInternalProcessor.java    |   27 ++++++++++
 .../camel/processor/RedeliveryErrorHandler.java    |   27 +++++++++-
 .../apache/camel/processor/RedeliveryPolicy.java   |   22 ++++++++
 .../processor/interceptor/DefaultChannel.java      |    5 ++
 11 files changed, 243 insertions(+), 3 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/camel/blob/ca007a55/camel-core/src/main/java/org/apache/camel/RuntimeConfiguration.java
----------------------------------------------------------------------
diff --git 
a/camel-core/src/main/java/org/apache/camel/RuntimeConfiguration.java 
b/camel-core/src/main/java/org/apache/camel/RuntimeConfiguration.java
index 96afdc0..7da882a 100644
--- a/camel-core/src/main/java/org/apache/camel/RuntimeConfiguration.java
+++ b/camel-core/src/main/java/org/apache/camel/RuntimeConfiguration.java
@@ -53,6 +53,20 @@ public interface RuntimeConfiguration {
     Boolean isTracing();
 
     /**
+     * Sets whether message history is enabled or not (default is disabled).
+     *
+     * @param messageHistory whether message history is enabled
+     */
+    void setMessageHistory(Boolean messageHistory);
+
+    /**
+     * Returns whether stream cache is enabled
+     *
+     * @return <tt>true</tt> if stream cache is enabled
+     */
+    Boolean isMessageHistory();
+
+    /**
      * Sets whether fault handling is enabled or not (default is disabled).
      *
      * @param handleFault whether to enable fault handling.

http://git-wip-us.apache.org/repos/asf/camel/blob/ca007a55/camel-core/src/main/java/org/apache/camel/impl/DefaultCamelContext.java
----------------------------------------------------------------------
diff --git 
a/camel-core/src/main/java/org/apache/camel/impl/DefaultCamelContext.java 
b/camel-core/src/main/java/org/apache/camel/impl/DefaultCamelContext.java
index 4067efb..33f45c5 100644
--- a/camel-core/src/main/java/org/apache/camel/impl/DefaultCamelContext.java
+++ b/camel-core/src/main/java/org/apache/camel/impl/DefaultCamelContext.java
@@ -173,6 +173,7 @@ public class DefaultCamelContext extends ServiceSupport 
implements ModelCamelCon
     private final ThreadLocal<Boolean> isStartingRoutes = new 
ThreadLocal<Boolean>();
     private Boolean autoStartup = Boolean.TRUE;
     private Boolean trace = Boolean.FALSE;
+    private Boolean messageHistory = Boolean.FALSE;
     private Boolean streamCache = Boolean.FALSE;
     private Boolean handleFault = Boolean.FALSE;
     private Boolean disableJMX = Boolean.FALSE;
@@ -1290,6 +1291,14 @@ public class DefaultCamelContext extends ServiceSupport 
implements ModelCamelCon
         return trace;
     }
 
+    public Boolean isMessageHistory() {
+        return messageHistory;
+    }
+
+    public void setMessageHistory(Boolean messageHistory) {
+        this.messageHistory = messageHistory;
+    }
+
     public Boolean isHandleFault() {
         return handleFault;
     }

http://git-wip-us.apache.org/repos/asf/camel/blob/ca007a55/camel-core/src/main/java/org/apache/camel/impl/DefaultRouteContext.java
----------------------------------------------------------------------
diff --git 
a/camel-core/src/main/java/org/apache/camel/impl/DefaultRouteContext.java 
b/camel-core/src/main/java/org/apache/camel/impl/DefaultRouteContext.java
index 40b5415..2680e84 100644
--- a/camel-core/src/main/java/org/apache/camel/impl/DefaultRouteContext.java
+++ b/camel-core/src/main/java/org/apache/camel/impl/DefaultRouteContext.java
@@ -58,6 +58,7 @@ public class DefaultRouteContext implements RouteContext {
     private InterceptStrategy managedInterceptStrategy;
     private boolean routeAdded;
     private Boolean trace;
+    private Boolean messageHistory;
     private Boolean streamCache;
     private Boolean handleFault;
     private Long delay;
@@ -251,6 +252,19 @@ public class DefaultRouteContext implements RouteContext {
         }
     }
 
+    public void setMessageHistory(Boolean messageHistory) {
+        this.messageHistory = messageHistory;
+    }
+
+    public Boolean isMessageHistory() {
+        if (messageHistory != null) {
+            return messageHistory;
+        } else {
+            // fallback to the option from camel context
+            return getCamelContext().isMessageHistory();
+        }
+    }
+
     public void setStreamCaching(Boolean cache) {
         this.streamCache = cache;
     }

http://git-wip-us.apache.org/repos/asf/camel/blob/ca007a55/camel-core/src/main/java/org/apache/camel/model/OnExceptionDefinition.java
----------------------------------------------------------------------
diff --git 
a/camel-core/src/main/java/org/apache/camel/model/OnExceptionDefinition.java 
b/camel-core/src/main/java/org/apache/camel/model/OnExceptionDefinition.java
index 622f856..bff027f 100644
--- a/camel-core/src/main/java/org/apache/camel/model/OnExceptionDefinition.java
+++ b/camel-core/src/main/java/org/apache/camel/model/OnExceptionDefinition.java
@@ -575,6 +575,22 @@ public class OnExceptionDefinition extends 
ProcessorDefinition<OnExceptionDefini
     }
 
     /**
+     * Sets whether to log exhausted exceptions with message history
+     */
+    public OnExceptionDefinition logExhaustedMessageHistory(boolean 
logExhaustedMessageHistory) {
+        
getOrCreateRedeliveryPolicy().logExhaustedMessageHistory(logExhaustedMessageHistory);
+        return this;
+    }
+
+    /**
+     * Sets whether to log exhausted exceptions with message history
+     */
+    public OnExceptionDefinition logExhaustedMessageHistory(String 
logExhaustedMessageHistory) {
+        
getOrCreateRedeliveryPolicy().logExhaustedMessageHistory(logExhaustedMessageHistory);
+        return this;
+    }
+
+    /**
      * Sets the maximum redeliveries
      * <ul>
      * <li>5 = default value</li>

http://git-wip-us.apache.org/repos/asf/camel/blob/ca007a55/camel-core/src/main/java/org/apache/camel/model/ProcessorDefinitionHelper.java
----------------------------------------------------------------------
diff --git 
a/camel-core/src/main/java/org/apache/camel/model/ProcessorDefinitionHelper.java
 
b/camel-core/src/main/java/org/apache/camel/model/ProcessorDefinitionHelper.java
index 6395ec6..4e1d153 100644
--- 
a/camel-core/src/main/java/org/apache/camel/model/ProcessorDefinitionHelper.java
+++ 
b/camel-core/src/main/java/org/apache/camel/model/ProcessorDefinitionHelper.java
@@ -24,6 +24,7 @@ import java.util.Set;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.ScheduledExecutorService;
 
+import org.apache.camel.Exchange;
 import org.apache.camel.spi.ExecutorServiceManager;
 import org.apache.camel.spi.RouteContext;
 import org.apache.camel.util.ObjectHelper;
@@ -466,4 +467,38 @@ public final class ProcessorDefinitionHelper {
         return null;
     }
 
+    @SuppressWarnings("unchecked")
+    public static String dumpMessageHistoryStacktrace(Exchange exchange, 
boolean logStackTrace) {
+        List<ProcessorDefinition<?>> list = 
exchange.getProperty("CamelMessageHistory", List.class);
+        if (list == null || list.isEmpty()) {
+            return null;
+        }
+
+        final String HEADER_FORMAT = "%-20s %-20s %-80s";
+        final String OUTPUT_FORMAT = "[%-18.18s] [%-18.18s] [%-78.78s]";
+
+        StringBuilder sb = new StringBuilder();
+        sb.append("\n");
+        sb.append("Message History\n");
+        
sb.append("--------------------------------------------------------------------------------------------------------------------------\n");
+        sb.append(String.format(HEADER_FORMAT, "RouteId", "ProcessorId", 
"Processor"));
+        sb.append("\n");
+
+        for (ProcessorDefinition<?> node : list) {
+
+            String routeId = getRouteId(node);
+            String id = node.getId();
+            String label = node.getLabel();
+
+            sb.append(String.format(OUTPUT_FORMAT, routeId, id, label));
+            sb.append("\n");
+        }
+
+        if (logStackTrace) {
+            sb.append("\nStacktrace\n");
+            
sb.append("--------------------------------------------------------------------------------------------------------------------------");
+        }
+        return sb.toString();
+    }
+
 }

http://git-wip-us.apache.org/repos/asf/camel/blob/ca007a55/camel-core/src/main/java/org/apache/camel/model/RedeliveryPolicyDefinition.java
----------------------------------------------------------------------
diff --git 
a/camel-core/src/main/java/org/apache/camel/model/RedeliveryPolicyDefinition.java
 
b/camel-core/src/main/java/org/apache/camel/model/RedeliveryPolicyDefinition.java
index 475c629..1a2f878 100644
--- 
a/camel-core/src/main/java/org/apache/camel/model/RedeliveryPolicyDefinition.java
+++ 
b/camel-core/src/main/java/org/apache/camel/model/RedeliveryPolicyDefinition.java
@@ -68,6 +68,8 @@ public class RedeliveryPolicyDefinition {
     @XmlAttribute
     private String logExhausted;
     @XmlAttribute
+    private String logExhaustedMessageHistory;
+    @XmlAttribute
     private String disableRedelivery;
     @XmlAttribute
     private String delayPattern;
@@ -136,6 +138,9 @@ public class RedeliveryPolicyDefinition {
             if (logExhausted != null) {
                 
answer.setLogExhausted(CamelContextHelper.parseBoolean(context, logExhausted));
             }
+            if (logExhaustedMessageHistory != null) {
+                
answer.setLogExhaustedMessageHistory(CamelContextHelper.parseBoolean(context, 
logExhaustedMessageHistory));
+            }
             if (disableRedelivery != null) {
                 if (CamelContextHelper.parseBoolean(context, 
disableRedelivery)) {
                     answer.setMaximumRedeliveries(0);
@@ -428,6 +433,30 @@ public class RedeliveryPolicyDefinition {
     }
 
     /**
+     * Sets whether exhausted exceptions should be logged including message 
history or not (supports property placeholders).
+     * Can be used to include or reduce verbose.
+     *
+     * @param logExhaustedMessageHistory  whether exhausted exceptions should 
be logged with message history
+     * @return the builder
+     */
+    public RedeliveryPolicyDefinition logExhaustedMessageHistory(boolean 
logExhaustedMessageHistory) {
+        
setLogExhaustedMessageHistory(Boolean.toString(logExhaustedMessageHistory));
+        return this;
+    }
+
+    /**
+     * Sets whether exhausted exceptions should be logged including message 
history or not (supports property placeholders).
+     * Can be used to include or reduce verbose.
+     *
+     * @param logExhaustedMessageHistory  whether exhausted exceptions should 
be logged with message history
+     * @return the builder
+     */
+    public RedeliveryPolicyDefinition logExhaustedMessageHistory(String 
logExhaustedMessageHistory) {
+        setLogExhaustedMessageHistory(logExhaustedMessageHistory);
+        return this;
+    }
+
+    /**
      * Sets the maximum redeliveries
      * <ul>
      *   <li>x = redeliver at most x times</li>
@@ -653,6 +682,14 @@ public class RedeliveryPolicyDefinition {
         this.logExhausted = logExhausted;
     }
 
+    public String getLogExhaustedMessageHistory() {
+        return logExhaustedMessageHistory;
+    }
+
+    public void setLogExhaustedMessageHistory(String 
logExhaustedMessageHistory) {
+        this.logExhaustedMessageHistory = logExhaustedMessageHistory;
+    }
+
     public String getDisableRedelivery() {
         return disableRedelivery;
     }

http://git-wip-us.apache.org/repos/asf/camel/blob/ca007a55/camel-core/src/main/java/org/apache/camel/model/RouteDefinition.java
----------------------------------------------------------------------
diff --git 
a/camel-core/src/main/java/org/apache/camel/model/RouteDefinition.java 
b/camel-core/src/main/java/org/apache/camel/model/RouteDefinition.java
index 6e86431..d4af9b3 100644
--- a/camel-core/src/main/java/org/apache/camel/model/RouteDefinition.java
+++ b/camel-core/src/main/java/org/apache/camel/model/RouteDefinition.java
@@ -68,6 +68,7 @@ public class RouteDefinition extends 
ProcessorDefinition<RouteDefinition> {
     private String group;
     private String streamCache;
     private String trace;
+    private String messageHistory;
     private String handleFault;
     private String delayer;
     private String autoStartup;
@@ -402,6 +403,26 @@ public class RouteDefinition extends 
ProcessorDefinition<RouteDefinition> {
     }
 
     /**
+     * Enable message history for this route.
+     *
+     * @return the builder
+     */
+    public RouteDefinition messageHistory() {
+        setMessageHistory("true");
+        return this;
+    }
+
+    /**
+     * Disable message history for this route.
+     *
+     * @return the builder
+     */
+    public RouteDefinition noMessageHistory() {
+        setMessageHistory("false");
+        return this;
+    }
+
+    /**
      * Disable handle fault for this route.
      *
      * @return the builder
@@ -615,6 +636,14 @@ public class RouteDefinition extends 
ProcessorDefinition<RouteDefinition> {
         this.trace = trace;
     }
 
+    public String getMessageHistory() {
+        return messageHistory;
+    }
+
+    public void setMessageHistory(String messageHistory) {
+        this.messageHistory = messageHistory;
+    }
+
     public String getHandleFault() {
         return handleFault;
     }
@@ -777,6 +806,17 @@ public class RouteDefinition extends 
ProcessorDefinition<RouteDefinition> {
             }
         }
 
+        // configure message history
+        if (messageHistory != null) {
+            Boolean isMessageHistory = 
CamelContextHelper.parseBoolean(camelContext, getMessageHistory());
+            if (isMessageHistory != null) {
+                routeContext.setMessageHistory(isMessageHistory);
+                if (isMessageHistory) {
+                    log.debug("Message history is enabled on route: {}", 
getId());
+                }
+            }
+        }
+
         // configure stream caching
         if (streamCache != null) {
             Boolean isStreamCache = 
CamelContextHelper.parseBoolean(camelContext, getStreamCache());

http://git-wip-us.apache.org/repos/asf/camel/blob/ca007a55/camel-core/src/main/java/org/apache/camel/processor/CamelInternalProcessor.java
----------------------------------------------------------------------
diff --git 
a/camel-core/src/main/java/org/apache/camel/processor/CamelInternalProcessor.java
 
b/camel-core/src/main/java/org/apache/camel/processor/CamelInternalProcessor.java
index 217c8d7..6424099 100644
--- 
a/camel-core/src/main/java/org/apache/camel/processor/CamelInternalProcessor.java
+++ 
b/camel-core/src/main/java/org/apache/camel/processor/CamelInternalProcessor.java
@@ -681,4 +681,31 @@ public class CamelInternalProcessor extends 
DelegateAsyncProcessor {
         }
     }
 
+    public static class MessageHistoryAdvice implements 
CamelInternalProcessorAdvice {
+
+        private final ProcessorDefinition<?> definition;
+
+        public MessageHistoryAdvice(ProcessorDefinition<?> definition) {
+            this.definition = definition;
+        }
+
+        @Override
+        @SuppressWarnings("unchecked")
+        public Object before(Exchange exchange) throws Exception {
+            List<ProcessorDefinition<?>> history = 
exchange.getProperty("CamelMessageHistory", List.class);
+            if (history == null) {
+                history = new ArrayList<ProcessorDefinition<?>>();
+                exchange.setProperty("CamelMessageHistory", history);
+            }
+            history.add(definition);
+            return null;
+        }
+
+        @Override
+        public void after(Exchange exchange, Object data) throws Exception {
+            // noop
+        }
+
+    }
+
 }

http://git-wip-us.apache.org/repos/asf/camel/blob/ca007a55/camel-core/src/main/java/org/apache/camel/processor/RedeliveryErrorHandler.java
----------------------------------------------------------------------
diff --git 
a/camel-core/src/main/java/org/apache/camel/processor/RedeliveryErrorHandler.java
 
b/camel-core/src/main/java/org/apache/camel/processor/RedeliveryErrorHandler.java
index 19acacb..e038c66 100644
--- 
a/camel-core/src/main/java/org/apache/camel/processor/RedeliveryErrorHandler.java
+++ 
b/camel-core/src/main/java/org/apache/camel/processor/RedeliveryErrorHandler.java
@@ -30,6 +30,7 @@ import org.apache.camel.Message;
 import org.apache.camel.Predicate;
 import org.apache.camel.Processor;
 import org.apache.camel.model.OnExceptionDefinition;
+import org.apache.camel.model.ProcessorDefinitionHelper;
 import org.apache.camel.spi.ShutdownPrepared;
 import org.apache.camel.spi.SubUnitOfWorkCallback;
 import org.apache.camel.spi.UnitOfWork;
@@ -955,6 +956,15 @@ public abstract class RedeliveryErrorHandler extends 
ErrorHandlerSupport impleme
             if (cause != null) {
                 msg = msg + " due: " + cause.getMessage();
             }
+
+            // should we include message history
+            if (!shouldRedeliver && 
data.currentRedeliveryPolicy.isLogExhaustedMessageHistory()) {
+                String routeStackTrace = 
ProcessorDefinitionHelper.dumpMessageHistoryStacktrace(exchange, false);
+                if (routeStackTrace != null) {
+                    msg = msg + "\n" + routeStackTrace;
+                }
+            }
+
             if (newLogLevel == LoggingLevel.ERROR) {
                 // log intended rollback on maximum WARN level (no ERROR)
                 logger.log(msg, LoggingLevel.WARN);
@@ -962,10 +972,21 @@ public abstract class RedeliveryErrorHandler extends 
ErrorHandlerSupport impleme
                 // otherwise use the desired logging level
                 logger.log(msg, newLogLevel);
             }
-        } else if (e != null && logStackTrace) {
-            logger.log(message, e, newLogLevel);
         } else {
-            logger.log(message, newLogLevel);
+            String msg = message;
+            // should we include message history
+            if (!shouldRedeliver && 
data.currentRedeliveryPolicy.isLogExhaustedMessageHistory()) {
+                String routeStackTrace = 
ProcessorDefinitionHelper.dumpMessageHistoryStacktrace(exchange, e != null && 
logStackTrace);
+                if (routeStackTrace != null) {
+                    msg = msg + "\n" + routeStackTrace;
+                }
+            }
+
+            if (e != null && logStackTrace) {
+                logger.log(msg, e, newLogLevel);
+            } else {
+                logger.log(msg, newLogLevel);
+            }
         }
     }
 

http://git-wip-us.apache.org/repos/asf/camel/blob/ca007a55/camel-core/src/main/java/org/apache/camel/processor/RedeliveryPolicy.java
----------------------------------------------------------------------
diff --git 
a/camel-core/src/main/java/org/apache/camel/processor/RedeliveryPolicy.java 
b/camel-core/src/main/java/org/apache/camel/processor/RedeliveryPolicy.java
index 279524a..1242168 100644
--- a/camel-core/src/main/java/org/apache/camel/processor/RedeliveryPolicy.java
+++ b/camel-core/src/main/java/org/apache/camel/processor/RedeliveryPolicy.java
@@ -49,6 +49,7 @@ import org.slf4j.LoggerFactory;
  *   <li>logStackTrace = true</li>
  *   <li>logHandled = false</li>
  *   <li>logExhausted = true</li>
+ *   <li>logExhaustedMessageHistory = true</li>
  * </ul>
  * <p/>
  * Setting the maximumRedeliveries to a negative value such as -1 will then 
always redeliver (unlimited).
@@ -93,6 +94,7 @@ public class RedeliveryPolicy implements Cloneable, 
Serializable {
     protected boolean logHandled;
     protected boolean logContinued;
     protected boolean logExhausted = true;
+    protected boolean logExhaustedMessageHistory = true;
     protected boolean logRetryAttempted = true;
     protected String delayPattern;
     protected boolean asyncDelayedRedelivery;
@@ -116,6 +118,7 @@ public class RedeliveryPolicy implements Cloneable, 
Serializable {
             + ", logHandled=" + logHandled
             + ", logContinued=" + logContinued
             + ", logExhausted=" + logExhausted
+            + ", logExhaustedMessageHistory=" + logExhaustedMessageHistory
             + ", useExponentialBackOff="  + useExponentialBackOff
             + ", backOffMultiplier=" + backOffMultiplier
             + ", useCollisionAvoidance=" + useCollisionAvoidance
@@ -380,6 +383,14 @@ public class RedeliveryPolicy implements Cloneable, 
Serializable {
     }
 
     /**
+     * Sets whether to log exhausted errors including message history
+     */
+    public RedeliveryPolicy logExhaustedMessageHistory(boolean 
logExhaustedMessageHistory) {
+        setLogExhaustedMessageHistory(logExhaustedMessageHistory);
+        return this;
+    }
+
+    /**
      * Sets the delay pattern with delay intervals.
      */
     public RedeliveryPolicy delayPattern(String delayPattern) {
@@ -639,6 +650,17 @@ public class RedeliveryPolicy implements Cloneable, 
Serializable {
         this.logExhausted = logExhausted;
     }
 
+    public boolean isLogExhaustedMessageHistory() {
+        return logExhaustedMessageHistory;
+    }
+
+    /**
+     * Sets whether exhausted exceptions should be logged with message history 
included.
+     */
+    public void setLogExhaustedMessageHistory(boolean 
logExhaustedMessageHistory) {
+        this.logExhaustedMessageHistory = logExhaustedMessageHistory;
+    }
+
     public boolean isAsyncDelayedRedelivery() {
         return asyncDelayedRedelivery;
     }

http://git-wip-us.apache.org/repos/asf/camel/blob/ca007a55/camel-core/src/main/java/org/apache/camel/processor/interceptor/DefaultChannel.java
----------------------------------------------------------------------
diff --git 
a/camel-core/src/main/java/org/apache/camel/processor/interceptor/DefaultChannel.java
 
b/camel-core/src/main/java/org/apache/camel/processor/interceptor/DefaultChannel.java
index 725a3bd..f9d8c10 100644
--- 
a/camel-core/src/main/java/org/apache/camel/processor/interceptor/DefaultChannel.java
+++ 
b/camel-core/src/main/java/org/apache/camel/processor/interceptor/DefaultChannel.java
@@ -220,6 +220,11 @@ public class DefaultChannel extends CamelInternalProcessor 
implements ModelChann
             }
         }
 
+        if (routeContext.isMessageHistory()) {
+            // add message history advice
+            addAdvice(new MessageHistoryAdvice(targetOutputDef));
+        }
+
         // the regular tracer is not a task on internalProcessor as this is 
not really needed
         // end users have to explicit enable the tracer to use it, and then 
its okay if we wrap
         // the processors (but by default tracer is disabled, and therefore we 
do not wrap processors)

Reply via email to