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");
+ }
+ };
+ }
+}