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 097331d88d37 CAMEL-25431: camel-core - backlog tracer stamps a called 
route with the time it was called (#27518)
097331d88d37 is described below

commit 097331d88d37436f26c61dc462965d9630ea2d07
Author: Claus Ibsen <[email protected]>
AuthorDate: Thu Oct 8 00:41:20 2026 +0200

    CAMEL-25431: camel-core - backlog tracer stamps a called route with the 
time it was called (#27518)
    
    * CAMEL-25431: camel-core - the backlog tracer stamps a called route with 
the time it was called
    
    The tracer stamped the events of entering and leaving a route with the time
    the exchange was created. For a route called later by the same exchange
    (direct, after a delay or redeliveries) the history went back in time: its
    steps showed before the call that reached them. A route is now entered at
    the time it is called; the route the exchange started in keeps the time it
    was created. The event of leaving a route carries the time it was entered,
    as before for the first route, so a reader adds the elapsed time to get the
    time it was left, as the camel-jbang monitor does.
    
    Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
    Claude-Session: https://claude.ai/code/session_01STT6whBgK1AqsSsUKrnE8m
    
    * CAMEL-25431: camel-core-model - throwException is labelled by its 
exception type and message
    
    A throwException declared with exceptionType and message, as the YAML and
    XML DSLs declare it, was labelled throwException[] in traces and the route
    history. It is now labelled with the type and the message, e.g.
    throwException[java.lang.IllegalStateException: card declined].
    
    Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
    Claude-Session: https://claude.ai/code/session_01STT6whBgK1AqsSsUKrnE8m
    
    ---------
    
    Co-authored-by: Claude Opus 5.5 (1M context) <[email protected]>
---
 .../camel/impl/engine/CamelInternalProcessor.java  | 17 +++--
 .../camel/model/ThrowExceptionDefinition.java      |  3 +
 .../BacklogTracerCalledRouteTimestampTest.java     | 82 ++++++++++++++++++++++
 3 files changed, 97 insertions(+), 5 deletions(-)

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 fb4088bad17f..ae37ef23e671 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
@@ -676,9 +676,12 @@ public class CamelInternalProcessor extends 
DelegateAsyncProcessor implements In
                         true, backlogTracer.isBodyIncludeStreams(), 
backlogTracer.isBodyIncludeFiles(),
                         backlogTracer.getBodyMaxChars());
 
+                // the time the exchange entered this route: when it was 
created in the route it started in, now in
+                // a route it is sent to later (direct, seda), so the history 
does not go back in time
+                long entered = routeId != null && !routeId.equals(fromRouteId) 
? System.currentTimeMillis() : created;
                 DefaultBacklogTracerEventMessage first = new 
DefaultBacklogTracerEventMessage(
                         camelContext,
-                        true, false, backlogTracer.incrementTraceCounter(), 
created, source, fromRouteId, routeId,
+                        true, false, backlogTracer.incrementTraceCounter(), 
entered, source, fromRouteId, routeId,
                         input.getId(),
                         null, null, null,
                         input.getShortName(), input.getLabel(),
@@ -698,7 +701,6 @@ public class CamelInternalProcessor extends 
DelegateAsyncProcessor implements In
         public void after(Exchange exchange, DefaultBacklogTracerEventMessage 
first) throws Exception {
             if (first != null) {
 
-                final long created = exchange.getClock().getCreated();
                 NamedNode input = routeDefinition.getInput();
                 String source = LoggerHelper.getLineNumberLoggerName(input);
                 String exchangeId = exchange.getExchangeId();
@@ -715,9 +717,12 @@ public class CamelInternalProcessor extends 
DelegateAsyncProcessor implements In
                         true, backlogTracer.isBodyIncludeStreams(), 
backlogTracer.isBodyIncludeFiles(),
                         backlogTracer.getBodyMaxChars());
 
+                // stamped with the time the route was entered, as the first 
event: the elapsed time set below makes
+                // the time it was left (a reader adds the two), also for a 
route called later by the same exchange
                 DefaultBacklogTracerEventMessage last = new 
DefaultBacklogTracerEventMessage(
                         camelContext,
-                        false, true, backlogTracer.incrementTraceCounter(), 
created, source, fromRouteId, routeId,
+                        false, true, backlogTracer.incrementTraceCounter(), 
first.getTimestamp(), source,
+                        fromRouteId, routeId,
                         input.getId(),
                         null, null, null,
                         input.getShortName(), input.getLabel(),
@@ -829,7 +834,6 @@ public class CamelInternalProcessor extends 
DelegateAsyncProcessor implements In
         @Override
         public void after(Exchange exchange, DefaultBacklogTracerEventMessage 
first) throws Exception {
             if (first != null) {
-                final long created = exchange.getClock().getCreated();
                 String source = 
LoggerHelper.getLineNumberLoggerName(processorDefinition);
                 String exchangeId = exchange.getExchangeId();
                 String correlationExchangeId = 
exchange.getProperty(ExchangePropertyKey.CORRELATION_ID, String.class);
@@ -845,9 +849,12 @@ public class CamelInternalProcessor extends 
DelegateAsyncProcessor implements In
                         true, backlogTracer.isBodyIncludeStreams(), 
backlogTracer.isBodyIncludeFiles(),
                         backlogTracer.getBodyMaxChars());
 
+                // stamped with the time the route was entered, as the first 
event: the elapsed time set below makes
+                // the time it was left (a reader adds the two), also for a 
route called later by the same exchange
                 DefaultBacklogTracerEventMessage last = new 
DefaultBacklogTracerEventMessage(
                         camelContext,
-                        false, true, backlogTracer.incrementTraceCounter(), 
created, source, fromRouteId, routeId,
+                        false, true, backlogTracer.incrementTraceCounter(), 
first.getTimestamp(), source,
+                        fromRouteId, routeId,
                         processorDefinition.getId(),
                         null, null, null,
                         processorDefinition.getShortName(), 
processorDefinition.getLabel(),
diff --git 
a/core/camel-core-model/src/main/java/org/apache/camel/model/ThrowExceptionDefinition.java
 
b/core/camel-core-model/src/main/java/org/apache/camel/model/ThrowExceptionDefinition.java
index 4e30b199fe24..48aee5f0cb97 100644
--- 
a/core/camel-core-model/src/main/java/org/apache/camel/model/ThrowExceptionDefinition.java
+++ 
b/core/camel-core-model/src/main/java/org/apache/camel/model/ThrowExceptionDefinition.java
@@ -79,6 +79,9 @@ public class ThrowExceptionDefinition extends 
NoOutputDefinition<ThrowExceptionD
             return exception.getClass().getCanonicalName();
         } else if (ref != null) {
             return "ref:" + ref;
+        } else if (exceptionType != null) {
+            // as the DSLs declare it: the type, and the message when there is 
one
+            return message != null ? exceptionType + ": " + message : 
exceptionType;
         } else {
             return "";
         }
diff --git 
a/core/camel-core/src/test/java/org/apache/camel/processor/BacklogTracerCalledRouteTimestampTest.java
 
b/core/camel-core/src/test/java/org/apache/camel/processor/BacklogTracerCalledRouteTimestampTest.java
new file mode 100644
index 000000000000..20b537a82875
--- /dev/null
+++ 
b/core/camel-core/src/test/java/org/apache/camel/processor/BacklogTracerCalledRouteTimestampTest.java
@@ -0,0 +1,82 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.processor;
+
+import java.util.List;
+
+import org.apache.camel.ContextTestSupport;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.spi.BacklogTracer;
+import org.apache.camel.spi.BacklogTracerEventMessage;
+import org.junit.jupiter.api.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * The trace of an exchange goes forward in time: a route called later by the 
same exchange (direct) is entered and left
+ * at the time it happens, not at the time the exchange was created, which put 
its steps before the call that reached
+ * them.
+ */
+class BacklogTracerCalledRouteTimestampTest extends ContextTestSupport {
+
+    @Test
+    void theCalledRouteIsEnteredWhenItIsCalled() {
+        BacklogTracer tracer = 
context.getCamelContextExtension().getContextPlugin(BacklogTracer.class);
+        tracer.setEnabled(true);
+
+        template.sendBody("direct:start", "Hello");
+
+        List<BacklogTracerEventMessage> events = 
tracer.dumpAllTracedMessages();
+        BacklogTracerEventMessage created = events.stream()
+                .filter(e -> e.isFirst() && 
"start".equals(e.getRouteId())).findFirst().orElseThrow();
+        BacklogTracerEventMessage entered = events.stream()
+                .filter(e -> e.isFirst() && 
"called".equals(e.getRouteId())).findFirst().orElseThrow();
+        BacklogTracerEventMessage left = events.stream()
+                .filter(e -> e.isLast() && 
"called".equals(e.getRouteId())).findFirst().orElseThrow();
+
+        // the delay of 200 ms comes before the call
+        assertThat(entered.getTimestamp() - 
created.getTimestamp()).isGreaterThanOrEqualTo(150);
+        // the last event of a route carries the time it was entered, and its 
elapsed time the time it was left
+        assertThat(left.getTimestamp()).isEqualTo(entered.getTimestamp());
+        // in order, as a reader sees them: no event before the one before it 
(the elapsed time is measured from a
+        // moment after the stamp, so a few ms of slack; the history went back 
by the whole delay before)
+        for (int i = 1; i < events.size(); i++) {
+            
assertThat(seen(events.get(i))).isGreaterThanOrEqualTo(seen(events.get(i - 1)) 
- 20);
+        }
+    }
+
+    private static long seen(BacklogTracerEventMessage event) {
+        return event.isLast() ? event.getTimestamp() + event.getElapsed() : 
event.getTimestamp();
+    }
+
+    @Override
+    protected RouteBuilder createRouteBuilder() {
+        return new RouteBuilder() {
+            @Override
+            public void configure() {
+                context.setBacklogTracing(true);
+
+                from("direct:start").routeId("start")
+                        .delay(200)
+                        .to("direct:called");
+
+                from("direct:called").routeId("called")
+                        .log("called");
+            }
+        };
+    }
+}

Reply via email to