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 <[email protected]> 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);
