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