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 2022/01/06 19:25:16 UTC
[camel] branch main updated: ManagedDebugger exposes Message History (#6671)
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
The following commit(s) were added to refs/heads/main by this push:
new eb7bb93 ManagedDebugger exposes Message History (#6671)
eb7bb93 is described below
commit eb7bb93a6966b1d0308b46239185b1c8d6ea208a
Author: javaduke <Eu...@modusbox.com>
AuthorDate: Thu Jan 6 12:24:43 2022 -0700
ManagedDebugger exposes Message History (#6671)
* Message History as list implementation
* History as XML
* Need to encode attributes as XML
* Renamed the operation
---
.../mbean/ManagedBacklogDebuggerMBean.java | 3 +
.../management/mbean/ManagedBacklogDebugger.java | 73 ++++++++++++++++++++++
.../camel/management/BacklogDebuggerTest.java | 67 ++++++++++++++++++++
.../modules/ROOT/pages/backlog-debugger.adoc | 1 +
4 files changed, 144 insertions(+)
diff --git a/core/camel-management-api/src/main/java/org/apache/camel/api/management/mbean/ManagedBacklogDebuggerMBean.java b/core/camel-management-api/src/main/java/org/apache/camel/api/management/mbean/ManagedBacklogDebuggerMBean.java
index 7695974..b824a84 100644
--- a/core/camel-management-api/src/main/java/org/apache/camel/api/management/mbean/ManagedBacklogDebuggerMBean.java
+++ b/core/camel-management-api/src/main/java/org/apache/camel/api/management/mbean/ManagedBacklogDebuggerMBean.java
@@ -172,4 +172,7 @@ public interface ManagedBacklogDebuggerMBean {
@ManagedOperation(description = "Updates/adds the exchange property (with a new type) on the suspended breakpoint at the given node id")
void setExchangePropertyOnBreakpoint(String nodeId, String exchangePropertyName, Object value, String type);
+
+ @ManagedOperation(description = "Returns the message history at the given node id as XML")
+ String messageHistoryOnBreakpointAsXml(String nodeId);
}
diff --git a/core/camel-management/src/main/java/org/apache/camel/management/mbean/ManagedBacklogDebugger.java b/core/camel-management/src/main/java/org/apache/camel/management/mbean/ManagedBacklogDebugger.java
index 6188f0f..9db605a 100644
--- a/core/camel-management/src/main/java/org/apache/camel/management/mbean/ManagedBacklogDebugger.java
+++ b/core/camel-management/src/main/java/org/apache/camel/management/mbean/ManagedBacklogDebugger.java
@@ -18,14 +18,18 @@ package org.apache.camel.management.mbean;
import java.io.ByteArrayOutputStream;
import java.io.ObjectOutputStream;
+import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.camel.CamelContext;
import org.apache.camel.Exchange;
+import org.apache.camel.ExchangePropertyKey;
import org.apache.camel.Expression;
+import org.apache.camel.MessageHistory;
import org.apache.camel.NoTypeConversionAvailableException;
import org.apache.camel.Predicate;
+import org.apache.camel.Route;
import org.apache.camel.RuntimeCamelException;
import org.apache.camel.api.management.ManagedResource;
import org.apache.camel.api.management.mbean.BacklogTracerEventMessage;
@@ -33,8 +37,11 @@ import org.apache.camel.api.management.mbean.ManagedBacklogDebuggerMBean;
import org.apache.camel.impl.debugger.BacklogDebugger;
import org.apache.camel.spi.Language;
import org.apache.camel.spi.ManagementStrategy;
+import org.apache.camel.support.LoggerHelper;
import org.apache.camel.util.ObjectHelper;
+import org.apache.camel.util.StopWatch;
import org.apache.camel.util.StringHelper;
+import org.apache.camel.util.URISupport;
@ManagedResource(description = "Managed BacklogDebugger")
public class ManagedBacklogDebugger implements ManagedBacklogDebuggerMBean {
@@ -368,6 +375,72 @@ public class ManagedBacklogDebugger implements ManagedBacklogDebuggerMBean {
return null;
}
+ @Override
+ public String messageHistoryOnBreakpointAsXml(String nodeId) {
+ StringBuffer messageHistoryBuffer = new StringBuffer();
+ messageHistoryBuffer.append("<messageHistory>\n");
+
+ Exchange suspendedExchange = backlogDebugger.getSuspendedExchange(nodeId);
+ if (suspendedExchange != null) {
+ List<MessageHistory> list = suspendedExchange.getProperty(ExchangePropertyKey.MESSAGE_HISTORY, List.class);
+ if (list != null) {
+ // add incoming origin of message on the top
+ String routeId = suspendedExchange.getFromRouteId();
+ Route route = suspendedExchange.getContext().getRoute(routeId);
+ String loc = route != null ? route.getSourceLocation() : "";
+ String id = routeId;
+ String label = "";
+ if (suspendedExchange.getFromEndpoint() != null) {
+ label = "from["
+ + URISupport
+ .sanitizeUri(
+ StringHelper.limitLength(suspendedExchange.getFromEndpoint().getEndpointUri(), 100))
+ + "]";
+ }
+ long elapsed = new StopWatch(suspendedExchange.getCreated()).taken();
+
+ messageHistoryBuffer
+ .append(" <messageHistoryEntry")
+ .append(" location=\"").append(StringHelper.xmlEncode(loc)).append("\"")
+ .append(" routeId=\"").append(StringHelper.xmlEncode(routeId)).append("\"")
+ .append(" processorId=\"").append(StringHelper.xmlEncode(id)).append("\"")
+ .append(" processor=\"").append(StringHelper.xmlEncode(label)).append("\"")
+ .append(" elapsed=\"").append(elapsed).append("\"")
+ .append("/>\n");
+
+ for (MessageHistory history : list) {
+ // and then each history
+ loc = LoggerHelper.getLineNumberLoggerName(history.getNode());
+ if (loc == null) {
+ loc = "";
+ }
+ routeId = history.getRouteId() != null ? history.getRouteId() : "";
+ id = history.getNode().getId();
+ // we need to avoid leak the sensible information here
+ // the sanitizeUri takes a very long time for very long string
+ // and the format cuts this to
+ // 78 characters, anyway. Cut this to 100 characters. This will
+ // give enough space for removing
+ // characters in the sanitizeUri method and will be reasonably
+ // fast
+ label = URISupport.sanitizeUri(StringHelper.limitLength(history.getNode().getLabel(), 100));
+ elapsed = history.getElapsed();
+
+ messageHistoryBuffer
+ .append(" <messageHistoryEntry")
+ .append(" location=\"").append(StringHelper.xmlEncode(loc)).append("\"")
+ .append(" routeId=\"").append(StringHelper.xmlEncode(routeId)).append("\"")
+ .append(" processorId=\"").append(StringHelper.xmlEncode(id)).append("\"")
+ .append(" processor=\"").append(StringHelper.xmlEncode(label)).append("\"")
+ .append(" elapsed=\"").append(elapsed).append("\"")
+ .append("/>\n");
+ }
+ }
+ }
+ messageHistoryBuffer.append("</messageHistory>\n");
+ return messageHistoryBuffer.toString();
+ }
+
private String dumpExchangePropertiesAsXml(String id) {
StringBuilder sb = new StringBuilder();
sb.append(" <exchangeProperties>\n");
diff --git a/core/camel-management/src/test/java/org/apache/camel/management/BacklogDebuggerTest.java b/core/camel-management/src/test/java/org/apache/camel/management/BacklogDebuggerTest.java
index c368f52..01b8702 100644
--- a/core/camel-management/src/test/java/org/apache/camel/management/BacklogDebuggerTest.java
+++ b/core/camel-management/src/test/java/org/apache/camel/management/BacklogDebuggerTest.java
@@ -824,6 +824,72 @@ public class BacklogDebuggerTest extends ManagementTestSupport {
assertEquals(0, nodes.size());
}
+ @SuppressWarnings("unchecked")
+ @Test
+ public void testBacklogDebuggerMessageHistory() throws Exception {
+ MBeanServer mbeanServer = getMBeanServer();
+ ObjectName on = new ObjectName(
+ "org.apache.camel:context=" + context.getManagementName() + ",type=tracer,name=BacklogDebugger");
+ assertNotNull(on);
+ mbeanServer.isRegistered(on);
+
+ Boolean enabled = (Boolean) mbeanServer.getAttribute(on, "Enabled");
+ assertEquals(Boolean.FALSE, enabled, "Should not be enabled");
+
+ // enable debugger
+ mbeanServer.invoke(on, "enableDebugger", null, null);
+
+ enabled = (Boolean) mbeanServer.getAttribute(on, "Enabled");
+ assertEquals(Boolean.TRUE, enabled, "Should be enabled");
+
+ // add breakpoint at bar
+ mbeanServer.invoke(on, "addBreakpoint", new Object[] { "bar" }, new String[] { "java.lang.String" });
+
+ MockEndpoint mock = getMockEndpoint("mock:result");
+ mock.expectedMessageCount(0);
+ mock.setSleepForEmptyTest(100);
+
+ template.sendBody("seda:start", "Hello World");
+
+ assertMockEndpointsSatisfied();
+
+ // wait for breakpoint at bar
+ await().atMost(1, TimeUnit.SECONDS).untilAsserted(() -> {
+ Set<String> suspended = (Set<String>) mbeanServer.invoke(on, "getSuspendedBreakpointNodeIds", null, null);
+ assertNotNull(suspended);
+ assertEquals(1, suspended.size());
+ assertEquals("bar", suspended.iterator().next());
+ });
+
+ Object response = mbeanServer.invoke(on, "messageHistoryOnBreakpointAsXml",
+ new Object[] { "bar" },
+ new String[] { "java.lang.String" });
+
+ assertNotNull(response);
+ assertTrue(response.getClass().isAssignableFrom(String.class));
+ String history = (String) response;
+ int count = (history.split("messageHistoryEntry", -1).length) - 1;
+ assertEquals(4, count);
+ assertTrue(history.contains("processor=\"from[seda://start?concurrentConsumers=2]\""));
+ assertTrue(history.contains("routeId=\"route1\""));
+ assertTrue(history.contains("processorId=\"route1\""));
+ assertTrue(history.contains("location=\""));
+ assertTrue(history.contains("elapsed=\""));
+
+ resetMocks();
+ mock.expectedMessageCount(1);
+
+ // resume breakpoint
+ mbeanServer.invoke(on, "resumeBreakpoint", new Object[] { "bar" }, new String[] { "java.lang.String" });
+
+ assertMockEndpointsSatisfied();
+
+ // and no suspended anymore
+ Set<String> nodes = (Set<String>) mbeanServer.invoke(on, "getSuspendedBreakpointNodeIds", null, null);
+ assertNotNull(nodes);
+ assertEquals(0, nodes.size());
+ }
+
@Override
protected RouteBuilder createRouteBuilder() throws Exception {
return new RouteBuilder() {
@@ -831,6 +897,7 @@ public class BacklogDebuggerTest extends ManagementTestSupport {
public void configure() throws Exception {
context.setUseBreadcrumb(false);
context.setDebugging(true);
+ context.setMessageHistory(true);
from("seda:start?concurrentConsumers=2")
.setProperty("myProperty", constant("myValue")).id("setProp")
diff --git a/docs/user-manual/modules/ROOT/pages/backlog-debugger.adoc b/docs/user-manual/modules/ROOT/pages/backlog-debugger.adoc
index 47fcb00..d1d8e5a 100644
--- a/docs/user-manual/modules/ROOT/pages/backlog-debugger.adoc
+++ b/docs/user-manual/modules/ROOT/pages/backlog-debugger.adoc
@@ -55,6 +55,7 @@ NOTE: This requires to enabled JMX by including `camel-management` JAR in the cl
|`stepBreakpoint(nodeId)` |`void` |To start single step mode from a suspended breakpoint at the given node. Then invoke `step` to step to next node in the route.
|`step` |`void` |To step to next node when in single step mode.
|`validateConditionalBreakpoint` |`String` |Used for validating if a given predicate is valid or not. Returns null if valid, otherwise a string with the error message.
+|`messageHistoryOnBreakpointAsXml(nodeId)` |`String` |Returns message history at the given node Id in XML format
|=======================================================================
== Enabling