You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by da...@apache.org on 2023/02/13 13:02:07 UTC

[camel] branch main updated (4b96afdadb6 -> 6346f886bad)

This is an automated email from the ASF dual-hosted git repository.

davsclaus pushed a change to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git


    from 4b96afdadb6 Sync deps
     new 5329417c50e CAMEL-19040: Backlog tracer to capture exchange properties.
     new 9025fe851b2 CAMEL-19043: camel-core - Add Exchange.RECEIVED_TIMESTAMP as internal exchange property
     new d98b3d32d6e Fixed test after xml-io model dumper (order attributes changed)
     new af015087139 CAMEL-19040: Backlog tracer - Capture exception and also first/last to know better what is input and output from Camel.
     new d950a13d59f CAMEL-19040: Backlog tracer - Capture exception and also first/last to know better what is input and output from Camel.
     new 6346f886bad CAMEL-19040: Backlog tracer - Capture exception and also first/last to know better what is input and output from Camel.

The 6 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .../java/org/apache/camel/ExchangePropertyKey.java |   3 +
 .../java/org/apache/camel/spi/BacklogTracer.java   |  20 ++
 .../camel/spi/BacklogTracerEventMessage.java       |  40 ++++
 .../camel/impl/debugger/BacklogDebugger.java       |   6 +-
 .../apache/camel/impl/debugger/BacklogTracer.java  |  22 +++
 .../debugger/DefaultBacklogTracerEventMessage.java |  94 +++++++++-
 .../camel/impl/engine/CamelInternalProcessor.java  |  68 +++++--
 .../mbean/ManagedBacklogTracerMBean.java           |   6 +
 .../management/mbean/ManagedBacklogTracer.java     |  10 +
 .../camel/management/BacklogTracerFilterTest.java  |   2 +-
 .../apache/camel/management/BacklogTracerTest.java |  42 +++--
 .../camel/management/ManagedFromRestGetTest.java   |  10 +-
 .../management/ManagedFromRestPlaceholderTest.java |  11 +-
 .../org/apache/camel/support/MessageHelper.java    | 202 ++++++++++++++++++---
 .../modules/ROOT/pages/backlog-tracer.adoc         |  22 ++-
 .../ROOT/pages/camel-4-migration-guide.adoc        |   4 +
 16 files changed, 487 insertions(+), 75 deletions(-)


[camel] 01/06: CAMEL-19040: Backlog tracer to capture exchange properties.

Posted by da...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

davsclaus pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git

commit 5329417c50ed4f971bfce6983b9d125068659043
Author: Claus Ibsen <cl...@gmail.com>
AuthorDate: Mon Feb 13 08:46:43 2023 +0100

    CAMEL-19040: Backlog tracer to capture exchange properties.
---
 .../java/org/apache/camel/spi/BacklogTracer.java   |  10 ++
 .../apache/camel/impl/debugger/BacklogTracer.java  |  11 ++
 .../camel/impl/engine/CamelInternalProcessor.java  |   5 +-
 .../mbean/ManagedBacklogTracerMBean.java           |   6 ++
 .../management/mbean/ManagedBacklogTracer.java     |  10 ++
 .../org/apache/camel/support/MessageHelper.java    | 120 ++++++++++++++++-----
 6 files changed, 134 insertions(+), 28 deletions(-)

diff --git a/core/camel-api/src/main/java/org/apache/camel/spi/BacklogTracer.java b/core/camel-api/src/main/java/org/apache/camel/spi/BacklogTracer.java
index 044ac2d9066..580955018b0 100644
--- a/core/camel-api/src/main/java/org/apache/camel/spi/BacklogTracer.java
+++ b/core/camel-api/src/main/java/org/apache/camel/spi/BacklogTracer.java
@@ -99,6 +99,16 @@ public interface BacklogTracer {
      */
     void setBodyIncludeFiles(boolean bodyIncludeFiles);
 
+    /**
+     * Trace messages to include exchange properties
+     */
+    boolean isIncludeExchangeProperties();
+
+    /**
+     * Trace messages to include exchange properties
+     */
+    void setIncludeExchangeProperties(boolean includeExchangeProperties);
+
     /**
      * Filter for tracing by route or node id
      */
diff --git a/core/camel-base-engine/src/main/java/org/apache/camel/impl/debugger/BacklogTracer.java b/core/camel-base-engine/src/main/java/org/apache/camel/impl/debugger/BacklogTracer.java
index 6d509e36e4a..a89131c84c9 100644
--- a/core/camel-base-engine/src/main/java/org/apache/camel/impl/debugger/BacklogTracer.java
+++ b/core/camel-base-engine/src/main/java/org/apache/camel/impl/debugger/BacklogTracer.java
@@ -60,6 +60,7 @@ public final class BacklogTracer extends ServiceSupport implements org.apache.ca
     private int bodyMaxChars = 128 * 1024;
     private boolean bodyIncludeStreams;
     private boolean bodyIncludeFiles = true;
+    private boolean includeExchangeProperties = true;
     // a pattern to filter tracing nodes
     private String tracePattern;
     private String[] patterns;
@@ -222,6 +223,16 @@ public final class BacklogTracer extends ServiceSupport implements org.apache.ca
         this.bodyIncludeFiles = bodyIncludeFiles;
     }
 
+    @Override
+    public boolean isIncludeExchangeProperties() {
+        return includeExchangeProperties;
+    }
+
+    @Override
+    public void setIncludeExchangeProperties(boolean includeExchangeProperties) {
+        this.includeExchangeProperties = includeExchangeProperties;
+    }
+
     @Override
     public String getTracePattern() {
         return tracePattern;
diff --git a/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/CamelInternalProcessor.java b/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/CamelInternalProcessor.java
index 0ab81f951f0..d95c803f1d1 100644
--- a/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/CamelInternalProcessor.java
+++ b/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/CamelInternalProcessor.java
@@ -584,10 +584,11 @@ public class CamelInternalProcessor extends DelegateAsyncProcessor implements In
                 long timestamp = System.currentTimeMillis();
                 String toNode = processorDefinition.getId();
                 String exchangeId = exchange.getExchangeId();
-                String messageAsXml = MessageHelper.dumpAsXml(exchange.getIn(), true, 4,
+                boolean includeExchangeProperties = backlogTracer.isIncludeExchangeProperties();
+                String messageAsXml = MessageHelper.dumpAsXml(exchange.getIn(), includeExchangeProperties, true, 4,
                         true, backlogTracer.isBodyIncludeStreams(), backlogTracer.isBodyIncludeFiles(),
                         backlogTracer.getBodyMaxChars());
-                String messageAsJSon = MessageHelper.dumpAsJSon(exchange.getIn(), true, 4,
+                String messageAsJSon = MessageHelper.dumpAsJSon(exchange.getIn(), includeExchangeProperties, true, 4,
                         true, backlogTracer.isBodyIncludeStreams(), backlogTracer.isBodyIncludeFiles(),
                         backlogTracer.getBodyMaxChars(), true);
 
diff --git a/core/camel-management-api/src/main/java/org/apache/camel/api/management/mbean/ManagedBacklogTracerMBean.java b/core/camel-management-api/src/main/java/org/apache/camel/api/management/mbean/ManagedBacklogTracerMBean.java
index 61f7c1b9b96..cad1bdbbe9a 100644
--- a/core/camel-management-api/src/main/java/org/apache/camel/api/management/mbean/ManagedBacklogTracerMBean.java
+++ b/core/camel-management-api/src/main/java/org/apache/camel/api/management/mbean/ManagedBacklogTracerMBean.java
@@ -90,6 +90,12 @@ public interface ManagedBacklogTracerMBean {
     @ManagedAttribute(description = "Whether to include file based message body in the trace message.")
     void setBodyIncludeFiles(boolean bodyIncludeFiles);
 
+    @ManagedAttribute(description = "Whether to include exchange properties in the trace message.")
+    boolean isIncludeExchangeProperties();
+
+    @ManagedAttribute(description = "Whether to include exchange properties in the trace message.")
+    void setIncludeExchangeProperties(boolean includeExchangeProperties);
+
     @ManagedOperation(description = "Dumps the traced messages for the given node or route")
     List<BacklogTracerEventMessage> dumpTracedMessages(String nodeOrRouteId);
 
diff --git a/core/camel-management/src/main/java/org/apache/camel/management/mbean/ManagedBacklogTracer.java b/core/camel-management/src/main/java/org/apache/camel/management/mbean/ManagedBacklogTracer.java
index 7a4c82cd332..19de3926c2d 100644
--- a/core/camel-management/src/main/java/org/apache/camel/management/mbean/ManagedBacklogTracer.java
+++ b/core/camel-management/src/main/java/org/apache/camel/management/mbean/ManagedBacklogTracer.java
@@ -161,6 +161,16 @@ public class ManagedBacklogTracer implements ManagedBacklogTracerMBean {
         backlogTracer.setBodyIncludeFiles(bodyIncludeFiles);
     }
 
+    @Override
+    public boolean isIncludeExchangeProperties() {
+        return backlogTracer.isIncludeExchangeProperties();
+    }
+
+    @Override
+    public void setIncludeExchangeProperties(boolean includeExchangeProperties) {
+        backlogTracer.setIncludeExchangeProperties(includeExchangeProperties);
+    }
+
     @Override
     public List<BacklogTracerEventMessage> dumpTracedMessages(String nodeOrRouteId) {
         return backlogTracer.dumpTracedMessages(nodeOrRouteId);
diff --git a/core/camel-support/src/main/java/org/apache/camel/support/MessageHelper.java b/core/camel-support/src/main/java/org/apache/camel/support/MessageHelper.java
index 917692421a2..7f69612a3af 100644
--- a/core/camel-support/src/main/java/org/apache/camel/support/MessageHelper.java
+++ b/core/camel-support/src/main/java/org/apache/camel/support/MessageHelper.java
@@ -471,25 +471,26 @@ public final class MessageHelper {
      */
     public static String dumpAsXml(
             Message message, boolean includeBody, int indent, boolean allowStreams, boolean allowFiles, int maxChars) {
-        return dumpAsXml(message, includeBody, indent, allowStreams, allowStreams, allowFiles, maxChars);
+        return dumpAsXml(message, false, includeBody, indent, allowStreams, allowStreams, allowFiles, maxChars);
     }
 
     /**
      * Dumps the message as a generic XML structure.
      *
-     * @param  message            the message
-     * @param  includeBody        whether or not to include the message body
-     * @param  indent             number of spaces to indent
-     * @param  allowCachedStreams whether to include message body if they are stream cache based
-     * @param  allowStreams       whether to include message body if they are stream based
-     * @param  allowFiles         whether to include message body if they are file based
-     * @param  maxChars           clip body after maximum chars (to avoid very big messages). Use 0 or negative value to
-     *                            not limit at all.
-     * @return                    the XML
+     * @param  message                   the message
+     * @param  includeBody               whether or not to include the message body
+     * @param  includeExchangeProperties whether or not to include exchange properties
+     * @param  indent                    number of spaces to indent
+     * @param  allowCachedStreams        whether to include message body if they are stream cache based
+     * @param  allowStreams              whether to include message body if they are stream based
+     * @param  allowFiles                whether to include message body if they are file based
+     * @param  maxChars                  clip body after maximum chars (to avoid very big messages). Use 0 or negative
+     *                                   value to not limit at all.
+     * @return                           the XML
      */
     public static String dumpAsXml(
-            Message message, boolean includeBody, int indent, boolean allowCachedStreams, boolean allowStreams,
-            boolean allowFiles, int maxChars) {
+            Message message, boolean includeExchangeProperties, boolean includeBody, int indent,
+            boolean allowCachedStreams, boolean allowStreams, boolean allowFiles, int maxChars) {
         StringBuilder sb = new StringBuilder();
 
         StringBuilder prefix = new StringBuilder();
@@ -501,6 +502,42 @@ public final class MessageHelper {
         sb.append(prefix);
         sb.append("<message exchangeId=\"").append(message.getExchange().getExchangeId()).append("\">\n");
 
+        // exchange properties
+        if (includeExchangeProperties && message.getExchange().hasProperties()) {
+            sb.append(prefix);
+            sb.append("  <exchangeProperties>\n");
+            // sort the exchange properties so they are listed A..Z
+            Map<String, Object> properties = new TreeMap<>(message.getExchange().getProperties());
+            for (Map.Entry<String, Object> entry : properties.entrySet()) {
+                Object value = entry.getValue();
+                String type = ObjectHelper.classCanonicalName(value);
+                sb.append(prefix);
+                sb.append("    <exchangeProperty key=\"").append(entry.getKey()).append("\"");
+                if (type != null) {
+                    sb.append(" type=\"").append(type).append("\"");
+                }
+                sb.append(">");
+
+                // dump header value as XML, use Camel type converter to convert
+                // to String
+                if (value != null) {
+                    try {
+                        String xml = message.getExchange().getContext().getTypeConverter().tryConvertTo(String.class,
+                                message.getExchange(), value);
+                        if (xml != null) {
+                            // must always xml encode
+                            sb.append(StringHelper.xmlEncode(xml));
+                        }
+                    } catch (Throwable e) {
+                        // ignore as the body is for logging purpose
+                    }
+                }
+
+                sb.append("</exchangeProperty>\n");
+            }
+            sb.append(prefix);
+            sb.append("  </exchangeProperties>\n");
+        }
         // headers
         if (message.hasHeaders()) {
             sb.append(prefix);
@@ -537,7 +574,6 @@ public final class MessageHelper {
             sb.append(prefix);
             sb.append("  </headers>\n");
         }
-
         if (includeBody) {
             sb.append(prefix);
             sb.append("  <body");
@@ -803,31 +839,63 @@ public final class MessageHelper {
     public static String dumpAsJSon(
             Message message, boolean includeBody, int indent, boolean allowStreams, boolean allowFiles, int maxChars,
             boolean pretty) {
-        return dumpAsJSon(message, includeBody, indent, false, allowStreams, allowFiles, maxChars, pretty);
+        return dumpAsJSon(message, false, includeBody, indent, false, allowStreams, allowFiles, maxChars, pretty);
     }
 
     /**
      * Dumps the message as a generic JSon structure.
      *
-     * @param  message            the message
-     * @param  includeBody        whether or not to include the message body
-     * @param  indent             number of spaces to indent
-     * @param  allowCachedStreams whether to include message body if they are stream cached based
-     * @param  allowStreams       whether to include message body if they are stream based
-     * @param  allowFiles         whether to include message body if they are file based
-     * @param  maxChars           clip body after maximum chars (to avoid very big messages). Use 0 or negative value to
-     *                            not limit at all.
-     * @return                    the JSon
+     * @param  message                   the message
+     * @param  includeExchangeProperties whether or not to include exchange properties
+     * @param  includeBody               whether or not to include the message body
+     * @param  indent                    number of spaces to indent
+     * @param  allowCachedStreams        whether to include message body if they are stream cached based
+     * @param  allowStreams              whether to include message body if they are stream based
+     * @param  allowFiles                whether to include message body if they are file based
+     * @param  maxChars                  clip body after maximum chars (to avoid very big messages). Use 0 or negative
+     *                                   value to not limit at all.
+     * @return                           the JSon
      */
     public static String dumpAsJSon(
-            Message message, boolean includeBody, int indent, boolean allowCachedStreams, boolean allowStreams,
-            boolean allowFiles, int maxChars, boolean pretty) {
+            Message message, boolean includeExchangeProperties, boolean includeBody, int indent,
+            boolean allowCachedStreams, boolean allowStreams, boolean allowFiles, int maxChars, boolean pretty) {
 
         JsonObject root = new JsonObject();
         JsonObject jo = new JsonObject();
         root.put("message", jo);
         jo.put("exchangeId", message.getExchange().getExchangeId());
 
+        // exchange properties
+        if (includeExchangeProperties && message.getExchange().hasProperties()) {
+            JsonArray arr = new JsonArray();
+            // sort the exchange properties so they are listed A..Z
+            Map<String, Object> properties = new TreeMap<>(message.getExchange().getProperties());
+            for (Map.Entry<String, Object> entry : properties.entrySet()) {
+                Object value = entry.getValue();
+                String type = ObjectHelper.classCanonicalName(value);
+                JsonObject jh = new JsonObject();
+                jh.put("key", entry.getKey());
+                if (type != null) {
+                    jh.put("type", type);
+                }
+                // dump property value as JSon, use Camel type converter to convert to String
+                if (value != null) {
+                    try {
+                        String data = message.getExchange().getContext().getTypeConverter().tryConvertTo(String.class,
+                                message.getExchange(), value);
+                        if (data != null) {
+                            jh.put("value", Jsoner.unescape(data));
+                        }
+                    } catch (Throwable e) {
+                        // ignore as the body is for logging purpose
+                    }
+                }
+                arr.add(jh);
+            }
+            if (!arr.isEmpty()) {
+                jo.put("exchangeProperties", arr);
+            }
+        }
         // headers
         if (message.hasHeaders()) {
             JsonArray arr = new JsonArray();
@@ -853,12 +921,12 @@ public final class MessageHelper {
                         // ignore as the body is for logging purpose
                     }
                 }
+                arr.add(jh);
             }
             if (!arr.isEmpty()) {
                 jo.put("headers", arr);
             }
         }
-
         if (includeBody) {
             JsonObject jb = new JsonObject();
             jo.put("body", jb);


[camel] 03/06: Fixed test after xml-io model dumper (order attributes changed)

Posted by da...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

davsclaus pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git

commit d98b3d32d6e30e4100a96efdb5ee91e5008b1f70
Author: Claus Ibsen <cl...@gmail.com>
AuthorDate: Mon Feb 13 09:18:32 2023 +0100

    Fixed test after xml-io model dumper (order attributes changed)
---
 .../org/apache/camel/management/ManagedFromRestGetTest.java   | 10 +++++-----
 .../camel/management/ManagedFromRestPlaceholderTest.java      | 11 +++++------
 2 files changed, 10 insertions(+), 11 deletions(-)

diff --git a/core/camel-management/src/test/java/org/apache/camel/management/ManagedFromRestGetTest.java b/core/camel-management/src/test/java/org/apache/camel/management/ManagedFromRestGetTest.java
index 8321c74485f..842ee64ab8b 100644
--- a/core/camel-management/src/test/java/org/apache/camel/management/ManagedFromRestGetTest.java
+++ b/core/camel-management/src/test/java/org/apache/camel/management/ManagedFromRestGetTest.java
@@ -64,14 +64,14 @@ public class ManagedFromRestGetTest extends ManagementTestSupport {
         assertTrue(xml.contains("application/json"));
         assertTrue(xml.contains("</rests>"));
 
-        assertTrue(xml.contains("<param collectionFormat=\"multi\" dataType=\"string\" defaultValue=\"b\" "
-                                + "description=\"header param description2\" name=\"header_letter\" required=\"false\" type=\"query\">"));
-        assertTrue(xml.contains("<param dataType=\"integer\" defaultValue=\"1\" "
-                                + "description=\"header param description1\" name=\"header_count\" required=\"true\" type=\"header\">"));
+        assertTrue(xml.contains("<param defaultValue=\"1\" dataType=\"integer\" name=\"header_count\""
+                + " description=\"header param description1\" type=\"header\" required=\"true\">"));
+        assertTrue(xml.contains("<param defaultValue=\"b\" dataType=\"string\" name=\"header_letter\""
+                + " description=\"header param description2\" type=\"query\" collectionFormat=\"multi\" required=\"false\">"));
         assertTrue(xml.contains("<value>1</value>"));
         assertTrue(xml.contains("<value>a</value>"));
 
-        assertTrue(xml.contains("<responseMessage code=\"300\" message=\"test msg\" responseModel=\"java.lang.Integer\"/>"));
+        assertTrue(xml.contains("<responseMessage code=\"300\" responseModel=\"java.lang.Integer\" message=\"test msg\"/>"));
 
         String xml2 = (String) mbeanServer.invoke(on, "dumpRoutesAsXml", null, null);
         log.info(xml2);
diff --git a/core/camel-management/src/test/java/org/apache/camel/management/ManagedFromRestPlaceholderTest.java b/core/camel-management/src/test/java/org/apache/camel/management/ManagedFromRestPlaceholderTest.java
index a21759d67fa..2f979809d0a 100644
--- a/core/camel-management/src/test/java/org/apache/camel/management/ManagedFromRestPlaceholderTest.java
+++ b/core/camel-management/src/test/java/org/apache/camel/management/ManagedFromRestPlaceholderTest.java
@@ -65,15 +65,14 @@ public class ManagedFromRestPlaceholderTest extends ManagementTestSupport {
         assertTrue(xml.contains("application/json"));
         assertTrue(xml.contains("</rests>"));
 
-        assertTrue(xml.contains(
-                "<param collectionFormat=\"multi\" dataType=\"string\" defaultValue=\"b\" description=\"header param description2\" "
-                                + "name=\"header_letter\" required=\"false\" type=\"query\">"));
-        assertTrue(xml.contains("<param dataType=\"integer\" defaultValue=\"1\" description=\"header param description1\" "
-                                + "name=\"header_count\" required=\"true\" type=\"header\">"));
+        assertTrue(xml.contains("<param defaultValue=\"1\" dataType=\"integer\" name=\"header_count\""
+                + " description=\"header param description1\" type=\"header\" required=\"true\">"));
+        assertTrue(xml.contains("<param defaultValue=\"b\" dataType=\"string\" name=\"header_letter\""
+                + " description=\"header param description2\" type=\"query\" collectionFormat=\"multi\" required=\"false\">"));
         assertTrue(xml.contains("<value>1</value>"));
         assertTrue(xml.contains("<value>a</value>"));
 
-        assertTrue(xml.contains("<responseMessage code=\"300\" message=\"test msg\" responseModel=\"java.lang.Integer\"/>"));
+        assertTrue(xml.contains("<responseMessage code=\"300\" responseModel=\"java.lang.Integer\" message=\"test msg\"/>"));
 
         String xml2 = (String) mbeanServer.invoke(on, "dumpRoutesAsXml", null, null);
         log.info(xml2);


[camel] 04/06: CAMEL-19040: Backlog tracer - Capture exception and also first/last to know better what is input and output from Camel.

Posted by da...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

davsclaus pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git

commit af01508713924648345a4a4198debae3db9898df
Author: Claus Ibsen <cl...@gmail.com>
AuthorDate: Mon Feb 13 13:54:14 2023 +0100

    CAMEL-19040: Backlog tracer - Capture exception and also first/last to know better what is input and output from Camel.
---
 .../java/org/apache/camel/spi/BacklogTracer.java   | 10 +++
 .../camel/spi/BacklogTracerEventMessage.java       | 40 +++++++++
 .../camel/impl/debugger/BacklogDebugger.java       |  6 +-
 .../apache/camel/impl/debugger/BacklogTracer.java  | 11 +++
 .../debugger/DefaultBacklogTracerEventMessage.java | 94 +++++++++++++++++++++-
 .../camel/impl/engine/CamelInternalProcessor.java  | 63 ++++++++++++---
 .../camel/management/BacklogTracerFilterTest.java  |  2 +-
 .../apache/camel/management/BacklogTracerTest.java | 42 +++++-----
 .../camel/management/ManagedFromRestGetTest.java   |  4 +-
 .../management/ManagedFromRestPlaceholderTest.java |  4 +-
 .../org/apache/camel/support/MessageHelper.java    | 82 +++++++++++++++++++
 11 files changed, 320 insertions(+), 38 deletions(-)

diff --git a/core/camel-api/src/main/java/org/apache/camel/spi/BacklogTracer.java b/core/camel-api/src/main/java/org/apache/camel/spi/BacklogTracer.java
index 580955018b0..9ff19a3aa21 100644
--- a/core/camel-api/src/main/java/org/apache/camel/spi/BacklogTracer.java
+++ b/core/camel-api/src/main/java/org/apache/camel/spi/BacklogTracer.java
@@ -109,6 +109,16 @@ public interface BacklogTracer {
      */
     void setIncludeExchangeProperties(boolean includeExchangeProperties);
 
+    /**
+     * Trace messages to include exception if the message failed
+     */
+    boolean isIncludeException();
+
+    /**
+     * Trace messages to include exception if the message failed
+     */
+    void setIncludeException(boolean includeException);
+
     /**
      * Filter for tracing by route or node id
      */
diff --git a/core/camel-api/src/main/java/org/apache/camel/spi/BacklogTracerEventMessage.java b/core/camel-api/src/main/java/org/apache/camel/spi/BacklogTracerEventMessage.java
index 6b2d66e8a07..95e7da612c7 100644
--- a/core/camel-api/src/main/java/org/apache/camel/spi/BacklogTracerEventMessage.java
+++ b/core/camel-api/src/main/java/org/apache/camel/spi/BacklogTracerEventMessage.java
@@ -31,6 +31,16 @@ public interface BacklogTracerEventMessage {
      */
     long getUid();
 
+    /**
+     * Whether this is a new incoming message and this is the first trace.
+     */
+    boolean isFirst();
+
+    /**
+     * Whether this is the last trace of the message (its complete).
+     */
+    boolean isLast();
+
     /**
      * Timestamp of the traced event
      */
@@ -61,6 +71,36 @@ public interface BacklogTracerEventMessage {
      */
     String getMessageAsJSon();
 
+    /**
+     * Time elapsed for processing the given node (in millis).
+     */
+    long getElapsed();
+
+    /**
+     * Whether the message is done processing the given node
+     */
+    boolean isDone();
+
+    /**
+     * Did the message fail during processing (i.e. was an exception thrown)
+     */
+    boolean isFailed();
+
+    /**
+     * Was there an exception thrown during processing
+     */
+    boolean hasException();
+
+    /**
+     * The exception as XML (exception type, message and stacktrace)
+     */
+    String getExceptionAsXml();
+
+    /**
+     * The exception as JSon (exception type, message and stacktrace)
+     */
+    String getExceptionAsJSon();
+
     /**
      * Dumps the event message as XML using the {@link #ROOT_TAG} as root tag.
      * <p/>
diff --git a/core/camel-base-engine/src/main/java/org/apache/camel/impl/debugger/BacklogDebugger.java b/core/camel-base-engine/src/main/java/org/apache/camel/impl/debugger/BacklogDebugger.java
index 18c92cf43cd..cb1e41fc048 100644
--- a/core/camel-base-engine/src/main/java/org/apache/camel/impl/debugger/BacklogDebugger.java
+++ b/core/camel-base-engine/src/main/java/org/apache/camel/impl/debugger/BacklogDebugger.java
@@ -608,7 +608,7 @@ public final class BacklogDebugger extends ServiceSupport {
         suspendedBreakpointMessages.computeIfPresent(
                 nodeId,
                 (nId, message) -> new DefaultBacklogTracerEventMessage(
-                        message.getUid(), message.getTimestamp(), message.getRouteId(), message.getToNode(),
+                        false, false, message.getUid(), message.getTimestamp(), message.getRouteId(), message.getToNode(),
                         message.getExchangeId(),
                         dumpAsXml(suspendedExchange.getExchange()),
                         dumpAsJSon(suspendedExchange.getExchange())));
@@ -670,7 +670,7 @@ public final class BacklogDebugger extends ServiceSupport {
 
             BacklogTracerEventMessage msg
                     = new DefaultBacklogTracerEventMessage(
-                            uid, timestamp, routeId, toNode, exchangeId, messageAsXml, messageAsJSon);
+                            false, false, uid, timestamp, routeId, toNode, exchangeId, messageAsXml, messageAsJSon);
             suspendedBreakpointMessages.put(nodeId, msg);
 
             // suspend at this breakpoint
@@ -738,7 +738,7 @@ public final class BacklogDebugger extends ServiceSupport {
 
             BacklogTracerEventMessage msg
                     = new DefaultBacklogTracerEventMessage(
-                            uid, timestamp, routeId, toNode, exchangeId, messageAsXml, messageAsJSon);
+                            false, false, uid, timestamp, routeId, toNode, exchangeId, messageAsXml, messageAsJSon);
             suspendedBreakpointMessages.put(toNode, msg);
 
             // suspend at this breakpoint
diff --git a/core/camel-base-engine/src/main/java/org/apache/camel/impl/debugger/BacklogTracer.java b/core/camel-base-engine/src/main/java/org/apache/camel/impl/debugger/BacklogTracer.java
index a89131c84c9..f03e661c748 100644
--- a/core/camel-base-engine/src/main/java/org/apache/camel/impl/debugger/BacklogTracer.java
+++ b/core/camel-base-engine/src/main/java/org/apache/camel/impl/debugger/BacklogTracer.java
@@ -61,6 +61,7 @@ public final class BacklogTracer extends ServiceSupport implements org.apache.ca
     private boolean bodyIncludeStreams;
     private boolean bodyIncludeFiles = true;
     private boolean includeExchangeProperties = true;
+    private boolean includeException = true;
     // a pattern to filter tracing nodes
     private String tracePattern;
     private String[] patterns;
@@ -233,6 +234,16 @@ public final class BacklogTracer extends ServiceSupport implements org.apache.ca
         this.includeExchangeProperties = includeExchangeProperties;
     }
 
+    @Override
+    public boolean isIncludeException() {
+        return includeException;
+    }
+
+    @Override
+    public void setIncludeException(boolean includeException) {
+        this.includeException = includeException;
+    }
+
     @Override
     public String getTracePattern() {
         return tracePattern;
diff --git a/core/camel-base-engine/src/main/java/org/apache/camel/impl/debugger/DefaultBacklogTracerEventMessage.java b/core/camel-base-engine/src/main/java/org/apache/camel/impl/debugger/DefaultBacklogTracerEventMessage.java
index 2b1a7009eb7..7f1bff48b3d 100644
--- a/core/camel-base-engine/src/main/java/org/apache/camel/impl/debugger/DefaultBacklogTracerEventMessage.java
+++ b/core/camel-base-engine/src/main/java/org/apache/camel/impl/debugger/DefaultBacklogTracerEventMessage.java
@@ -20,6 +20,7 @@ import java.text.SimpleDateFormat;
 import java.util.Map;
 
 import org.apache.camel.spi.BacklogTracerEventMessage;
+import org.apache.camel.util.StopWatch;
 import org.apache.camel.util.json.JsonObject;
 import org.apache.camel.util.json.Jsonable;
 import org.apache.camel.util.json.Jsoner;
@@ -29,6 +30,9 @@ import org.apache.camel.util.json.Jsoner;
  */
 public final class DefaultBacklogTracerEventMessage implements BacklogTracerEventMessage {
 
+    private final StopWatch watch;
+    private final boolean first;
+    private final boolean last;
     private final long uid;
     private final long timestamp;
     private final String routeId;
@@ -36,9 +40,18 @@ public final class DefaultBacklogTracerEventMessage implements BacklogTracerEven
     private final String exchangeId;
     private final String messageAsXml;
     private final String messageAsJSon;
-
-    public DefaultBacklogTracerEventMessage(long uid, long timestamp, String routeId, String toNode, String exchangeId,
+    private String exceptionAsXml;
+    private String exceptionAsJSon;
+    private long duration;
+    private boolean done;
+    private boolean failed;
+
+    public DefaultBacklogTracerEventMessage(boolean first, boolean last, long uid, long timestamp,
+                                            String routeId, String toNode, String exchangeId,
                                             String messageAsXml, String messageAsJSon) {
+        this.watch = new StopWatch();
+        this.first = first;
+        this.last = last;
         this.uid = uid;
         this.timestamp = timestamp;
         this.routeId = routeId;
@@ -48,11 +61,29 @@ public final class DefaultBacklogTracerEventMessage implements BacklogTracerEven
         this.messageAsJSon = messageAsJSon;
     }
 
+    /**
+     * Callback when the message has been processed at the given node
+     */
+    public void doneProcessing() {
+        done = true;
+        duration = watch.taken();
+    }
+
     @Override
     public long getUid() {
         return uid;
     }
 
+    @Override
+    public boolean isFirst() {
+        return first;
+    }
+
+    @Override
+    public boolean isLast() {
+        return last;
+    }
+
     @Override
     public long getTimestamp() {
         return timestamp;
@@ -83,6 +114,43 @@ public final class DefaultBacklogTracerEventMessage implements BacklogTracerEven
         return messageAsJSon;
     }
 
+    @Override
+    public long getElapsed() {
+        return done ? duration : watch.taken();
+    }
+
+    @Override
+    public boolean isDone() {
+        return done;
+    }
+
+    public boolean isFailed() {
+        return hasException();
+    }
+
+    @Override
+    public boolean hasException() {
+        return exceptionAsXml != null || exceptionAsJSon != null;
+    }
+
+    @Override
+    public String getExceptionAsXml() {
+        return exceptionAsXml;
+    }
+
+    public void setExceptionAsXml(String exceptionAsXml) {
+        this.exceptionAsXml = exceptionAsXml;
+    }
+
+    @Override
+    public String getExceptionAsJSon() {
+        return exceptionAsJSon;
+    }
+
+    public void setExceptionAsJSon(String exceptionAsJSon) {
+        this.exceptionAsJSon = exceptionAsJSon;
+    }
+
     @Override
     public String toString() {
         return "DefaultBacklogTracerEventMessage[" + exchangeId + " at " + toNode + "]";
@@ -105,8 +173,13 @@ public final class DefaultBacklogTracerEventMessage implements BacklogTracerEven
         StringBuilder sb = new StringBuilder();
         sb.append(prefix).append("<").append(ROOT_TAG).append(">\n");
         sb.append(prefix).append("  <uid>").append(uid).append("</uid>\n");
+        sb.append(prefix).append("  <first>").append(first).append("</first>\n");
+        sb.append(prefix).append("  <last>").append(last).append("</last>\n");
         String ts = new SimpleDateFormat(TIMESTAMP_FORMAT).format(timestamp);
         sb.append(prefix).append("  <timestamp>").append(ts).append("</timestamp>\n");
+        sb.append(prefix).append("  <elapsed>").append(getElapsed()).append("</elapsed>\n");
+        sb.append(prefix).append("  <done>").append(isDone()).append("</done>\n");
+        sb.append(prefix).append("  <failed>").append(isFailed()).append("</failed>\n");
         // route id is optional and we then use an empty value for no route id
         sb.append(prefix).append("  <routeId>").append(routeId != null ? routeId : "").append("</routeId>\n");
         if (toNode != null) {
@@ -117,6 +190,9 @@ public final class DefaultBacklogTracerEventMessage implements BacklogTracerEven
         }
         sb.append(prefix).append("  <exchangeId>").append(exchangeId).append("</exchangeId>\n");
         sb.append(prefix).append(messageAsXml).append("\n");
+        if (exceptionAsXml != null) {
+            sb.append(prefix).append(exceptionAsXml).append("\n");
+        }
         sb.append(prefix).append("</").append(ROOT_TAG).append(">");
         return sb.toString();
     }
@@ -135,6 +211,8 @@ public final class DefaultBacklogTracerEventMessage implements BacklogTracerEven
     public Map<String, Object> asJSon() {
         JsonObject jo = new JsonObject();
         jo.put("uid", uid);
+        jo.put("first", first);
+        jo.put("last", last);
         if (routeId != null) {
             jo.put("routeId", routeId);
         }
@@ -144,6 +222,9 @@ public final class DefaultBacklogTracerEventMessage implements BacklogTracerEven
         if (timestamp > 0) {
             jo.put("timestamp", timestamp);
         }
+        jo.put("elapsed", getElapsed());
+        jo.put("done", isDone());
+        jo.put("failed", isFailed());
         try {
             // parse back to json object and avoid double message root
             JsonObject msg = (JsonObject) Jsoner.deserialize(messageAsJSon);
@@ -151,6 +232,15 @@ public final class DefaultBacklogTracerEventMessage implements BacklogTracerEven
         } catch (Exception e) {
             // ignore
         }
+        if (exceptionAsJSon != null) {
+            try {
+                // parse back to json object and avoid double message root
+                JsonObject msg = (JsonObject) Jsoner.deserialize(exceptionAsJSon);
+                jo.put("exception", msg.get("exception"));
+            } catch (Exception e) {
+                // ignore
+            }
+        }
         return jo;
     }
 }
diff --git a/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/CamelInternalProcessor.java b/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/CamelInternalProcessor.java
index d95c803f1d1..715afafd99f 100644
--- a/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/CamelInternalProcessor.java
+++ b/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/CamelInternalProcessor.java
@@ -563,7 +563,8 @@ public class CamelInternalProcessor extends DelegateAsyncProcessor implements In
     /**
      * Advice to execute the {@link BacklogTracer} if enabled.
      */
-    public static final class BacklogTracerAdvice implements CamelInternalProcessorAdvice, Ordered {
+    public static final class BacklogTracerAdvice
+            implements CamelInternalProcessorAdvice<DefaultBacklogTracerEventMessage>, Ordered {
 
         private final BacklogTracer backlogTracer;
         private final NamedNode processorDefinition;
@@ -579,7 +580,7 @@ public class CamelInternalProcessor extends DelegateAsyncProcessor implements In
         }
 
         @Override
-        public Object before(Exchange exchange) throws Exception {
+        public DefaultBacklogTracerEventMessage before(Exchange exchange) throws Exception {
             if (backlogTracer.shouldTrace(processorDefinition, exchange)) {
                 long timestamp = System.currentTimeMillis();
                 String toNode = processorDefinition.getId();
@@ -596,28 +597,72 @@ public class CamelInternalProcessor extends DelegateAsyncProcessor implements In
                 String routeId = routeDefinition != null ? routeDefinition.getRouteId() : null;
                 if (first) {
                     long created = exchange.getCreated();
-                    DefaultBacklogTracerEventMessage pseudo = new DefaultBacklogTracerEventMessage(
-                            backlogTracer.incrementTraceCounter(), created, routeId, null, exchangeId, messageAsXml,
+                    DefaultBacklogTracerEventMessage pseudoFirst = new DefaultBacklogTracerEventMessage(
+                            true, false, backlogTracer.incrementTraceCounter(), created, routeId, null, exchangeId,
+                            messageAsXml,
                             messageAsJSon);
-                    backlogTracer.traceEvent(pseudo);
+                    backlogTracer.traceEvent(pseudoFirst);
+                    exchange.adapt(ExtendedExchange.class).addOnCompletion(new SynchronizationAdapter() {
+                        @Override
+                        public void onDone(Exchange exchange) {
+                            // create pseudo last
+                            String routeId = routeDefinition != null ? routeDefinition.getRouteId() : null;
+                            String exchangeId = exchange.getExchangeId();
+                            boolean includeExchangeProperties = backlogTracer.isIncludeExchangeProperties();
+                            long created = exchange.getCreated();
+                            String messageAsXml = MessageHelper.dumpAsXml(exchange.getIn(), includeExchangeProperties, true, 4,
+                                    true, backlogTracer.isBodyIncludeStreams(), backlogTracer.isBodyIncludeFiles(),
+                                    backlogTracer.getBodyMaxChars());
+                            String messageAsJSon
+                                    = MessageHelper.dumpAsJSon(exchange.getIn(), includeExchangeProperties, true, 4,
+                                            true, backlogTracer.isBodyIncludeStreams(), backlogTracer.isBodyIncludeFiles(),
+                                            backlogTracer.getBodyMaxChars(), true);
+                            DefaultBacklogTracerEventMessage pseudoLast = new DefaultBacklogTracerEventMessage(
+                                    false, true, backlogTracer.incrementTraceCounter(), created, routeId, null, exchangeId,
+                                    messageAsXml,
+                                    messageAsJSon);
+                            backlogTracer.traceEvent(pseudoLast);
+                            doneProcessing(exchange, pseudoLast);
+                            doneProcessing(exchange, pseudoFirst);
+                        }
+                    });
                 }
                 DefaultBacklogTracerEventMessage event = new DefaultBacklogTracerEventMessage(
-                        backlogTracer.incrementTraceCounter(), timestamp, routeId, toNode, exchangeId, messageAsXml,
+                        false, false, backlogTracer.incrementTraceCounter(), timestamp, routeId, toNode, exchangeId,
+                        messageAsXml,
                         messageAsJSon);
                 backlogTracer.traceEvent(event);
+
+                return event;
             }
 
             return null;
         }
 
         @Override
-        public void after(Exchange exchange, Object data) throws Exception {
-            // noop
+        public void after(Exchange exchange, DefaultBacklogTracerEventMessage data) throws Exception {
+            doneProcessing(exchange, data);
+        }
+
+        private void doneProcessing(Exchange exchange, DefaultBacklogTracerEventMessage data) {
+            if (data != null) {
+                data.doneProcessing();
+                if (!data.isFirst()) {
+                    // we want to capture if there was an exception
+                    Throwable e = exchange.getException();
+                    if (e != null) {
+                        String xml = MessageHelper.dumpExceptionAsXML(e, 4);
+                        data.setExceptionAsXml(xml);
+                        String json = MessageHelper.dumpExceptionAsJSon(e, 4, true);
+                        data.setExceptionAsJSon(json);
+                    }
+                }
+            }
         }
 
         @Override
         public boolean hasState() {
-            return false;
+            return true;
         }
 
         @Override
diff --git a/core/camel-management/src/test/java/org/apache/camel/management/BacklogTracerFilterTest.java b/core/camel-management/src/test/java/org/apache/camel/management/BacklogTracerFilterTest.java
index dc7d6d034bd..d4c1766b217 100644
--- a/core/camel-management/src/test/java/org/apache/camel/management/BacklogTracerFilterTest.java
+++ b/core/camel-management/src/test/java/org/apache/camel/management/BacklogTracerFilterTest.java
@@ -70,7 +70,7 @@ public class BacklogTracerFilterTest extends ManagementTestSupport {
                 = (List<BacklogTracerEventMessage>) mbeanServer.invoke(on, "dumpAllTracedMessages", null, null);
 
         assertNotNull(events);
-        assertEquals(3, events.size());
+        assertEquals(4, events.size());
 
         BacklogTracerEventMessage event = events.get(0);
         assertEquals(null, event.getToNode());
diff --git a/core/camel-management/src/test/java/org/apache/camel/management/BacklogTracerTest.java b/core/camel-management/src/test/java/org/apache/camel/management/BacklogTracerTest.java
index 68a68a0ee74..9d0ee97df6f 100644
--- a/core/camel-management/src/test/java/org/apache/camel/management/BacklogTracerTest.java
+++ b/core/camel-management/src/test/java/org/apache/camel/management/BacklogTracerTest.java
@@ -151,7 +151,11 @@ public class BacklogTracerTest extends ManagementTestSupport {
                 = (List<BacklogTracerEventMessage>) mbeanServer.invoke(on, "dumpAllTracedMessages", null, null);
 
         assertNotNull(events);
-        assertEquals(6, events.size());
+        assertEquals(8, events.size());
+
+        // first and last events
+        assertTrue(events.get(0).isFirst());
+        assertTrue(events.get(7).isLast());
 
         BacklogTracerEventMessage event0 = events.get(0);
         assertEquals("route1", event0.getRouteId());
@@ -177,7 +181,7 @@ public class BacklogTracerTest extends ManagementTestSupport {
                      + "    </message>",
                 event2.getMessageAsXml());
 
-        BacklogTracerEventMessage event3 = events.get(3);
+        BacklogTracerEventMessage event3 = events.get(4);
         assertEquals("route1", event3.getRouteId());
         assertEquals(null, event3.getToNode());
         assertEquals("    <message exchangeId=\"" + fooExchanges.get(1).getExchangeId() + "\">\n"
@@ -185,7 +189,7 @@ public class BacklogTracerTest extends ManagementTestSupport {
                      + "    </message>",
                 event3.getMessageAsXml());
 
-        BacklogTracerEventMessage event4 = events.get(4);
+        BacklogTracerEventMessage event4 = events.get(5);
         assertEquals("route1", event4.getRouteId());
         assertEquals("foo", event4.getToNode());
         assertEquals("    <message exchangeId=\"" + fooExchanges.get(1).getExchangeId() + "\">\n"
@@ -193,7 +197,7 @@ public class BacklogTracerTest extends ManagementTestSupport {
                      + "    </message>",
                 event3.getMessageAsXml());
 
-        BacklogTracerEventMessage event5 = events.get(5);
+        BacklogTracerEventMessage event5 = events.get(6);
         assertEquals("route1", event5.getRouteId());
         assertEquals("bar", event5.getToNode());
         assertEquals("    <message exchangeId=\"" + barExchanges.get(1).getExchangeId() + "\">\n"
@@ -231,7 +235,7 @@ public class BacklogTracerTest extends ManagementTestSupport {
         assertNotNull(dom);
 
         NodeList list = dom.getElementsByTagName("backlogTracerEventMessage");
-        assertEquals(6, list.getLength());
+        assertEquals(8, list.getLength());
     }
 
     @SuppressWarnings("unchecked")
@@ -263,12 +267,12 @@ public class BacklogTracerTest extends ManagementTestSupport {
                 = (List<BacklogTracerEventMessage>) mbeanServer.invoke(on, "dumpAllTracedMessages", null, null);
 
         assertNotNull(events);
-        assertEquals(6, events.size());
+        assertEquals(8, events.size());
 
         // and if we get again they are still there
         events = (List<BacklogTracerEventMessage>) mbeanServer.invoke(on, "dumpAllTracedMessages", null, null);
         assertNotNull(events);
-        assertEquals(6, events.size());
+        assertEquals(8, events.size());
 
         // send in another message
         resetMocks();
@@ -280,10 +284,10 @@ public class BacklogTracerTest extends ManagementTestSupport {
 
         assertMockEndpointsSatisfied();
 
-        // and now we should have 3 more messages
+        // and now we should have 4 more messages
         events = (List<BacklogTracerEventMessage>) mbeanServer.invoke(on, "dumpAllTracedMessages", null, null);
         assertNotNull(events);
-        assertEquals(9, events.size());
+        assertEquals(12, events.size());
     }
 
     @SuppressWarnings("unchecked")
@@ -380,12 +384,12 @@ public class BacklogTracerTest extends ManagementTestSupport {
 
         List<BacklogTracerEventMessage> events = (List<BacklogTracerEventMessage>) mbeanServer.invoke(on, "dumpTracedMessages",
                 new Object[] { "foo" }, new String[] { "java.lang.String" });
-        assertEquals(10, events.size());
+        assertEquals(7, events.size());
 
-        // the first should be 0 and the last 9
+        // the first should be 3 and the last 9
         String xml = events.get(0).getMessageAsXml();
-        assertTrue(xml.contains("###0###"));
-        xml = events.get(9).getMessageAsXml();
+        assertTrue(xml.contains("###3###"));
+        xml = events.get(6).getMessageAsXml();
         assertTrue(xml.contains("###9###"));
 
         // send in another message
@@ -393,12 +397,12 @@ public class BacklogTracerTest extends ManagementTestSupport {
 
         events = (List<BacklogTracerEventMessage>) mbeanServer.invoke(on, "dumpTracedMessages",
                 new Object[] { "foo" }, new String[] { "java.lang.String" });
-        assertEquals(10, events.size());
+        assertEquals(7, events.size());
 
         // and we are shifted one now
         xml = events.get(0).getMessageAsXml();
-        assertTrue(xml.contains("###1###"));
-        xml = events.get(9).getMessageAsXml();
+        assertTrue(xml.contains("###4###"));
+        xml = events.get(6).getMessageAsXml();
         assertTrue(xml.contains("###10###"));
 
         // send in 4 messages
@@ -409,12 +413,12 @@ public class BacklogTracerTest extends ManagementTestSupport {
 
         events = (List<BacklogTracerEventMessage>) mbeanServer.invoke(on, "dumpTracedMessages",
                 new Object[] { "foo" }, new String[] { "java.lang.String" });
-        assertEquals(10, events.size());
+        assertEquals(7, events.size());
 
         // and we are shifted +4 now
         xml = events.get(0).getMessageAsXml();
-        assertTrue(xml.contains("###5###"));
-        xml = events.get(9).getMessageAsXml();
+        assertTrue(xml.contains("###8###"));
+        xml = events.get(6).getMessageAsXml();
         assertTrue(xml.contains("###14###"));
     }
 
diff --git a/core/camel-management/src/test/java/org/apache/camel/management/ManagedFromRestGetTest.java b/core/camel-management/src/test/java/org/apache/camel/management/ManagedFromRestGetTest.java
index 842ee64ab8b..086719e7c02 100644
--- a/core/camel-management/src/test/java/org/apache/camel/management/ManagedFromRestGetTest.java
+++ b/core/camel-management/src/test/java/org/apache/camel/management/ManagedFromRestGetTest.java
@@ -65,9 +65,9 @@ public class ManagedFromRestGetTest extends ManagementTestSupport {
         assertTrue(xml.contains("</rests>"));
 
         assertTrue(xml.contains("<param defaultValue=\"1\" dataType=\"integer\" name=\"header_count\""
-                + " description=\"header param description1\" type=\"header\" required=\"true\">"));
+                                + " description=\"header param description1\" type=\"header\" required=\"true\">"));
         assertTrue(xml.contains("<param defaultValue=\"b\" dataType=\"string\" name=\"header_letter\""
-                + " description=\"header param description2\" type=\"query\" collectionFormat=\"multi\" required=\"false\">"));
+                                + " description=\"header param description2\" type=\"query\" collectionFormat=\"multi\" required=\"false\">"));
         assertTrue(xml.contains("<value>1</value>"));
         assertTrue(xml.contains("<value>a</value>"));
 
diff --git a/core/camel-management/src/test/java/org/apache/camel/management/ManagedFromRestPlaceholderTest.java b/core/camel-management/src/test/java/org/apache/camel/management/ManagedFromRestPlaceholderTest.java
index 2f979809d0a..7984b258693 100644
--- a/core/camel-management/src/test/java/org/apache/camel/management/ManagedFromRestPlaceholderTest.java
+++ b/core/camel-management/src/test/java/org/apache/camel/management/ManagedFromRestPlaceholderTest.java
@@ -66,9 +66,9 @@ public class ManagedFromRestPlaceholderTest extends ManagementTestSupport {
         assertTrue(xml.contains("</rests>"));
 
         assertTrue(xml.contains("<param defaultValue=\"1\" dataType=\"integer\" name=\"header_count\""
-                + " description=\"header param description1\" type=\"header\" required=\"true\">"));
+                                + " description=\"header param description1\" type=\"header\" required=\"true\">"));
         assertTrue(xml.contains("<param defaultValue=\"b\" dataType=\"string\" name=\"header_letter\""
-                + " description=\"header param description2\" type=\"query\" collectionFormat=\"multi\" required=\"false\">"));
+                                + " description=\"header param description2\" type=\"query\" collectionFormat=\"multi\" required=\"false\">"));
         assertTrue(xml.contains("<value>1</value>"));
         assertTrue(xml.contains("<value>a</value>"));
 
diff --git a/core/camel-support/src/main/java/org/apache/camel/support/MessageHelper.java b/core/camel-support/src/main/java/org/apache/camel/support/MessageHelper.java
index 7f69612a3af..ee85d1603b4 100644
--- a/core/camel-support/src/main/java/org/apache/camel/support/MessageHelper.java
+++ b/core/camel-support/src/main/java/org/apache/camel/support/MessageHelper.java
@@ -20,7 +20,9 @@ import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
+import java.io.PrintWriter;
 import java.io.Reader;
+import java.io.StringWriter;
 import java.io.Writer;
 import java.util.List;
 import java.util.Map;
@@ -854,6 +856,7 @@ public final class MessageHelper {
      * @param  allowFiles                whether to include message body if they are file based
      * @param  maxChars                  clip body after maximum chars (to avoid very big messages). Use 0 or negative
      *                                   value to not limit at all.
+     * @param  pretty                    whether to pretty print JSon
      * @return                           the JSon
      */
     public static String dumpAsJSon(
@@ -952,4 +955,83 @@ public final class MessageHelper {
         return answer;
     }
 
+    /**
+     * Dumps the exception as a generic XML structure.
+     *
+     * @param  indent number of spaces to indent
+     * @return        the XML
+     */
+    public static String dumpExceptionAsXML(Throwable exception, int indent) {
+        StringBuilder prefix = new StringBuilder();
+        for (int i = 0; i < indent; i++) {
+            prefix.append(" ");
+        }
+
+        StringBuilder sb = new StringBuilder();
+        try {
+            sb.append(prefix).append("<exception");
+            String type = ObjectHelper.classCanonicalName(exception);
+            if (type != null) {
+                sb.append(" type=\"").append(type).append("\"");
+            }
+            String msg = exception.getMessage();
+            if (msg != null) {
+                msg = StringHelper.xmlEncode(msg);
+                sb.append(" message=\"").append(msg).append("\"");
+            }
+            sb.append(">\n");
+            StringWriter sw = new StringWriter();
+            exception.printStackTrace(new PrintWriter(sw));
+            String trace = sw.toString();
+            // must always xml encode
+            sb.append(StringHelper.xmlEncode(trace));
+            sb.append(prefix).append("</exception>");
+        } catch (Throwable e) {
+            // ignore
+        }
+
+        return sb.toString();
+    }
+
+    /**
+     * Dumps the exception as a generic JSon structure.
+     *
+     * @param  indent number of spaces to indent
+     * @param  pretty whether to pretty print JSon
+     * @return        the JSon
+     */
+    public static String dumpExceptionAsJSon(Throwable exception, int indent, boolean pretty) {
+        JsonObject root = new JsonObject();
+        JsonObject jo = new JsonObject();
+        root.put("exception", jo);
+
+        String type = ObjectHelper.classCanonicalName(exception);
+        if (type != null) {
+            jo.put("type", type);
+        }
+        String msg = exception.getMessage();
+        if (msg != null) {
+            jo.put("message", type);
+        } else {
+            jo.put("message", "[null]");
+        }
+        StringWriter sw = new StringWriter();
+        exception.printStackTrace(new PrintWriter(sw));
+        String trace = sw.toString();
+        try {
+            jo.put("stackTrace", Jsoner.unescape(trace));
+        } catch (Throwable e) {
+            // ignore as the body is for logging purpose
+        }
+        String answer = root.toJson();
+        if (pretty) {
+            if (indent > 0) {
+                answer = Jsoner.prettyPrint(answer, indent);
+            } else {
+                answer = Jsoner.prettyPrint(answer);
+            }
+        }
+        return answer;
+    }
+
 }


[camel] 02/06: CAMEL-19043: camel-core - Add Exchange.RECEIVED_TIMESTAMP as internal exchange property

Posted by da...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

davsclaus pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git

commit 9025fe851b205252f47801c9aa1b2d513c8d88ba
Author: Claus Ibsen <cl...@gmail.com>
AuthorDate: Mon Feb 13 08:50:17 2023 +0100

    CAMEL-19043: camel-core - Add Exchange.RECEIVED_TIMESTAMP as internal exchange property
---
 core/camel-api/src/main/java/org/apache/camel/ExchangePropertyKey.java | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/core/camel-api/src/main/java/org/apache/camel/ExchangePropertyKey.java b/core/camel-api/src/main/java/org/apache/camel/ExchangePropertyKey.java
index bbad9689eb4..f254840d4f2 100644
--- a/core/camel-api/src/main/java/org/apache/camel/ExchangePropertyKey.java
+++ b/core/camel-api/src/main/java/org/apache/camel/ExchangePropertyKey.java
@@ -61,6 +61,7 @@ public enum ExchangePropertyKey {
     ON_COMPLETION(Exchange.ON_COMPLETION),
     ON_COMPLETION_ROUTE_IDS(Exchange.ON_COMPLETION_ROUTE_IDS),
     PARENT_UNIT_OF_WORK(Exchange.PARENT_UNIT_OF_WORK),
+    RECEIVED_TIMESTAMP(Exchange.RECEIVED_TIMESTAMP),
     RECIPIENT_LIST_ENDPOINT(Exchange.RECIPIENT_LIST_ENDPOINT),
     SLIP_ENDPOINT(Exchange.SLIP_ENDPOINT),
     SLIP_PRODUCER(Exchange.SLIP_PRODUCER),
@@ -162,6 +163,8 @@ public enum ExchangePropertyKey {
                 return ON_COMPLETION_ROUTE_IDS;
             case Exchange.PARENT_UNIT_OF_WORK:
                 return PARENT_UNIT_OF_WORK;
+            case Exchange.RECEIVED_TIMESTAMP:
+                return RECEIVED_TIMESTAMP;
             case Exchange.RECIPIENT_LIST_ENDPOINT:
                 return RECIPIENT_LIST_ENDPOINT;
             case Exchange.SLIP_ENDPOINT:


[camel] 06/06: CAMEL-19040: Backlog tracer - Capture exception and also first/last to know better what is input and output from Camel.

Posted by da...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

davsclaus pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git

commit 6346f886bad1368bf6bc9e67758747d996a2f4c3
Author: Claus Ibsen <cl...@gmail.com>
AuthorDate: Mon Feb 13 14:01:20 2023 +0100

    CAMEL-19040: Backlog tracer - Capture exception and also first/last to know better what is input and output from Camel.
---
 .../modules/ROOT/pages/backlog-tracer.adoc         | 22 ++++++++++++++++++++--
 1 file changed, 20 insertions(+), 2 deletions(-)

diff --git a/docs/user-manual/modules/ROOT/pages/backlog-tracer.adoc b/docs/user-manual/modules/ROOT/pages/backlog-tracer.adoc
index 5fb3d8e5ed7..7175c7a9def 100644
--- a/docs/user-manual/modules/ROOT/pages/backlog-tracer.adoc
+++ b/docs/user-manual/modules/ROOT/pages/backlog-tracer.adoc
@@ -29,6 +29,7 @@ messages in either a POJO or XML format.
 
 |Option |Default |Description
 
+|standby | `false` |Whether the tracer is standby. If a tracer is in standby then the tracer is activated during startup and are ready to be enabled manually via JMX or calling the enabled method.
 |enabled |`false` |Flag to enable or disable this tracer
 
 |backlogSize |`1000` |Maximum number of total traced messages to keep in the backlog (FIFO
@@ -58,6 +59,11 @@ xref:stream-caching.adoc[Stream Caching].
 
 |bodyIncludeFiles |`true` |Whether to include the message body of file based messages. The overhead
 is that the file content has to be read from the file.
+
+|includeExchangeProperties |`true` |Trace messages to include exchange properties.
+
+|includeException |`true` |Trace messages to include exception if the message failed.
+
 |===
 
 [[BacklogTracer-Operations]]
@@ -70,17 +76,22 @@ is that the file content has to be read from the file.
 
 |getTraceCounter |`long` |Gets the total number of traced messages.
 
+|getQueueSize |`long` |Number of traced messages in the backlog.
+
 |resetTraceCounter |`void` |To reset the trace counter.
 
 |dumpTracedMessages(nodeOrRouteId) |`List<BacklogTracerEventMessage>` |To dump the traced messages from the give node or route id.
 
-|dumpTracedMessagesAsXml(nodeOrRouteId) |`String` |To dump the traced messages from the give node or route id in XML
-format.
+|dumpTracedMessagesAsXml(nodeOrRouteId) |`String` |To dump the traced messages from the give node or route id in XML format.
+
+|dumpTracedMessagesAsJSon(nodeOrRouteId) |`String` |To dump the traced messages from the give node or route id in JSon format.
 
 |dumpAllTracedMessages |`List<BacklogTracerEventMessage>` |To dump all the traced messages
 
 |dumpAllTracedMessagesAsXml |`String` |To dump all the traced messages in XML format.
 
+|dumpAllTracedMessagesAsJSon |`String` |To dump all the traced messages in JSon format.
+
 |===
 
 == Enabling
@@ -101,6 +112,13 @@ And in Spring XML
 </camelContext>
 ----
 
+And in Camel Main you can enable this  in the `application.properties` file:
+
+[source,properties]
+----
+camel.main.backlog-tracing = true
+----
+
 And in Spring Boot you can enable this in the `application.properties` file:
 
 [source,properties]


[camel] 05/06: CAMEL-19040: Backlog tracer - Capture exception and also first/last to know better what is input and output from Camel.

Posted by da...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

davsclaus pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git

commit d950a13d59f7374d099e7f11f26b45e14b17a504
Author: Claus Ibsen <cl...@gmail.com>
AuthorDate: Mon Feb 13 13:56:26 2023 +0100

    CAMEL-19040: Backlog tracer - Capture exception and also first/last to know better what is input and output from Camel.
---
 docs/user-manual/modules/ROOT/pages/camel-4-migration-guide.adoc | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/docs/user-manual/modules/ROOT/pages/camel-4-migration-guide.adoc b/docs/user-manual/modules/ROOT/pages/camel-4-migration-guide.adoc
index 1d50a58c2e5..db0a442ba88 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-4-migration-guide.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-4-migration-guide.adoc
@@ -63,6 +63,10 @@ The old behavior can be archived by setting `backlogTracingStandby=true`.
 Move the following class from `org.apache.camel.api.management.mbean.BacklogTracerEventMessage` in `camel-management-api` JAR
 to `org.apache.camel.spi.BacklogTracerEventMessage` in `camel-api` JAR.
 
+The `org.apache.camel.impl.debugger.DefaultBacklogTracerEventMessage` has been refactored into an interface `org.apache.camel.spi.BacklogTracerEventMessage`
+with some additional details about traced messages. For example Camel now captures a _first_ and _last_ trace
+that contains the input and outgoing (if `InOut`) messages.
+
 == XML serialization
 
 The default xml serialization using `ModelToXMLDumper` has been improved and now uses a generated xml