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 f18de4210adf CAMEL-23615: Add getLastExchangeFailureHandledTimestamp()
f18de4210adf is described below
commit f18de4210adfccf66a8d7e2b1492e0294ab97e1d
Author: Claus Ibsen <[email protected]>
AuthorDate: Wed May 27 08:05:19 2026 +0200
CAMEL-23615: Add getLastExchangeFailureHandledTimestamp()
Adds lastExchangeFailureHandledTimestamp to ManagedPerformanceCounterMBean
to track when the last handled failure occurred (e.g. routed to dead letter
channel). Exposed in dumpStatsAsXml/statsAsJSon and all dev consoles
(Route, RouteGroup, Processor, Consumer, Context).
Closes #23526
---
.../camel/impl/console/ConsumerDevConsole.java | 4 ++
.../camel/impl/console/ContextDevConsole.java | 9 +++
.../camel/impl/console/ProcessorDevConsole.java | 9 +++
.../apache/camel/impl/console/RouteDevConsole.java | 9 +++
.../camel/impl/console/RouteGroupDevConsole.java | 9 +++
.../mbean/ManagedPerformanceCounterMBean.java | 3 +
.../mbean/ManagedPerformanceCounter.java | 13 ++++
.../ManagedFailureHandledTimestampTest.java | 81 ++++++++++++++++++++++
8 files changed, 137 insertions(+)
diff --git
a/core/camel-console/src/main/java/org/apache/camel/impl/console/ConsumerDevConsole.java
b/core/camel-console/src/main/java/org/apache/camel/impl/console/ConsumerDevConsole.java
index bdba7ca057b1..7c8680a23df2 100644
---
a/core/camel-console/src/main/java/org/apache/camel/impl/console/ConsumerDevConsole.java
+++
b/core/camel-console/src/main/java/org/apache/camel/impl/console/ConsumerDevConsole.java
@@ -245,6 +245,10 @@ public class ConsumerDevConsole extends AbstractDevConsole
{
if (last != null) {
stats.put("lastCompletedExchangeTimestamp", last.getTime());
}
+ last = mr.getLastExchangeFailureHandledTimestamp();
+ if (last != null) {
+ stats.put("lastFailureHandledExchangeTimestamp", last.getTime());
+ }
last = mr.getLastExchangeFailureTimestamp();
if (last != null) {
stats.put("lastFailedExchangeTimestamp", last.getTime());
diff --git
a/core/camel-console/src/main/java/org/apache/camel/impl/console/ContextDevConsole.java
b/core/camel-console/src/main/java/org/apache/camel/impl/console/ContextDevConsole.java
index 84b1834e2834..01aeb464a22a 100644
---
a/core/camel-console/src/main/java/org/apache/camel/impl/console/ContextDevConsole.java
+++
b/core/camel-console/src/main/java/org/apache/camel/impl/console/ContextDevConsole.java
@@ -117,6 +117,11 @@ public class ContextDevConsole extends AbstractDevConsole {
String ago = TimeUtils.printSince(last.getTime());
sb.append(String.format("%n Since Last Completed: %s",
ago));
}
+ last = mb.getLastExchangeFailureHandledTimestamp();
+ if (last != null) {
+ String ago = TimeUtils.printSince(last.getTime());
+ sb.append(String.format("%n Since Last Failure Handled:
%s", ago));
+ }
last = mb.getLastExchangeFailureTimestamp();
if (last != null) {
String ago = TimeUtils.printSince(last.getTime());
@@ -194,6 +199,10 @@ public class ContextDevConsole extends AbstractDevConsole {
if (last != null) {
stats.put("lastCompletedExchangeTimestamp",
last.getTime());
}
+ last = mb.getLastExchangeFailureHandledTimestamp();
+ if (last != null) {
+ stats.put("lastFailureHandledExchangeTimestamp",
last.getTime());
+ }
last = mb.getLastExchangeFailureTimestamp();
if (last != null) {
stats.put("lastFailedExchangeTimestamp", last.getTime());
diff --git
a/core/camel-console/src/main/java/org/apache/camel/impl/console/ProcessorDevConsole.java
b/core/camel-console/src/main/java/org/apache/camel/impl/console/ProcessorDevConsole.java
index a3306453fad7..b19974090332 100644
---
a/core/camel-console/src/main/java/org/apache/camel/impl/console/ProcessorDevConsole.java
+++
b/core/camel-console/src/main/java/org/apache/camel/impl/console/ProcessorDevConsole.java
@@ -175,6 +175,11 @@ public class ProcessorDevConsole extends
AbstractDevConsole {
String ago = TimeUtils.printSince(last.getTime());
sb.append(String.format("%n Since Last Completed: %s",
ago));
}
+ last = mp.getLastExchangeFailureHandledTimestamp();
+ if (last != null) {
+ String ago = TimeUtils.printSince(last.getTime());
+ sb.append(String.format("%n Since Last Failure Handled:
%s", ago));
+ }
last = mp.getLastExchangeFailureTimestamp();
if (last != null) {
String ago = TimeUtils.printSince(last.getTime());
@@ -346,6 +351,10 @@ public class ProcessorDevConsole extends
AbstractDevConsole {
if (last != null) {
stats.put("lastCompletedExchangeTimestamp", last.getTime());
}
+ last = mp.getLastExchangeFailureHandledTimestamp();
+ if (last != null) {
+ stats.put("lastFailureHandledExchangeTimestamp", last.getTime());
+ }
last = mp.getLastExchangeFailureTimestamp();
if (last != null) {
stats.put("lastFailedExchangeTimestamp", last.getTime());
diff --git
a/core/camel-console/src/main/java/org/apache/camel/impl/console/RouteDevConsole.java
b/core/camel-console/src/main/java/org/apache/camel/impl/console/RouteDevConsole.java
index f5df4eb4e91f..88df80b03187 100644
---
a/core/camel-console/src/main/java/org/apache/camel/impl/console/RouteDevConsole.java
+++
b/core/camel-console/src/main/java/org/apache/camel/impl/console/RouteDevConsole.java
@@ -170,6 +170,11 @@ public class RouteDevConsole extends AbstractDevConsole {
String ago = TimeUtils.printSince(last.getTime());
sb.append(String.format("%n Since Last Completed: %s",
ago));
}
+ last = mrb.getLastExchangeFailureHandledTimestamp();
+ if (last != null) {
+ String ago = TimeUtils.printSince(last.getTime());
+ sb.append(String.format("%n Since Last Failure Handled:
%s", ago));
+ }
last = mrb.getLastExchangeFailureTimestamp();
if (last != null) {
String ago = TimeUtils.printSince(last.getTime());
@@ -495,6 +500,10 @@ public class RouteDevConsole extends AbstractDevConsole {
if (last != null) {
stats.put("lastCompletedExchangeTimestamp", last.getTime());
}
+ last = mrb.getLastExchangeFailureHandledTimestamp();
+ if (last != null) {
+ stats.put("lastFailureHandledExchangeTimestamp", last.getTime());
+ }
last = mrb.getLastExchangeFailureTimestamp();
if (last != null) {
stats.put("lastFailedExchangeTimestamp", last.getTime());
diff --git
a/core/camel-console/src/main/java/org/apache/camel/impl/console/RouteGroupDevConsole.java
b/core/camel-console/src/main/java/org/apache/camel/impl/console/RouteGroupDevConsole.java
index f0df217590bd..04101fc83c4d 100644
---
a/core/camel-console/src/main/java/org/apache/camel/impl/console/RouteGroupDevConsole.java
+++
b/core/camel-console/src/main/java/org/apache/camel/impl/console/RouteGroupDevConsole.java
@@ -124,6 +124,11 @@ public class RouteGroupDevConsole extends
AbstractDevConsole {
String ago = TimeUtils.printSince(last.getTime());
sb.append(String.format("%n Since Last Completed: %s",
ago));
}
+ last = mrg.getLastExchangeFailureHandledTimestamp();
+ if (last != null) {
+ String ago = TimeUtils.printSince(last.getTime());
+ sb.append(String.format("%n Since Last Failure Handled:
%s", ago));
+ }
last = mrg.getLastExchangeFailureTimestamp();
if (last != null) {
String ago = TimeUtils.printSince(last.getTime());
@@ -191,6 +196,10 @@ public class RouteGroupDevConsole extends
AbstractDevConsole {
if (last != null) {
stats.put("lastCompletedExchangeTimestamp", last.getTime());
}
+ last = mrg.getLastExchangeFailureHandledTimestamp();
+ if (last != null) {
+ stats.put("lastFailureHandledExchangeTimestamp",
last.getTime());
+ }
last = mrg.getLastExchangeFailureTimestamp();
if (last != null) {
stats.put("lastFailedExchangeTimestamp", last.getTime());
diff --git
a/core/camel-management-api/src/main/java/org/apache/camel/api/management/mbean/ManagedPerformanceCounterMBean.java
b/core/camel-management-api/src/main/java/org/apache/camel/api/management/mbean/ManagedPerformanceCounterMBean.java
index b7b1f83d72ad..53a38810e07c 100644
---
a/core/camel-management-api/src/main/java/org/apache/camel/api/management/mbean/ManagedPerformanceCounterMBean.java
+++
b/core/camel-management-api/src/main/java/org/apache/camel/api/management/mbean/ManagedPerformanceCounterMBean.java
@@ -78,6 +78,9 @@ public interface ManagedPerformanceCounterMBean extends
ManagedCounterMBean {
@ManagedAttribute(description = "First Exchange Completed ExchangeId")
String getFirstExchangeCompletedExchangeId();
+ @ManagedAttribute(description = "Last Exchange Failure Handled Timestamp")
+ Date getLastExchangeFailureHandledTimestamp();
+
@ManagedAttribute(description = "Last Exchange Failed Timestamp")
Date getLastExchangeFailureTimestamp();
diff --git
a/core/camel-management/src/main/java/org/apache/camel/management/mbean/ManagedPerformanceCounter.java
b/core/camel-management/src/main/java/org/apache/camel/management/mbean/ManagedPerformanceCounter.java
index f60378d8a5f6..150434e4ec30 100644
---
a/core/camel-management/src/main/java/org/apache/camel/management/mbean/ManagedPerformanceCounter.java
+++
b/core/camel-management/src/main/java/org/apache/camel/management/mbean/ManagedPerformanceCounter.java
@@ -53,6 +53,7 @@ public abstract class ManagedPerformanceCounter extends
ManagedCounter
private Statistic lastExchangeCreatedTimestamp;
private Statistic lastExchangeCompletedTimestamp;
private String lastExchangeCompletedExchangeId;
+ private Statistic lastExchangeFailureHandledTimestamp;
private Statistic lastExchangeFailureTimestamp;
private String lastExchangeFailureExchangeId;
private boolean statisticsEnabled = true;
@@ -79,6 +80,7 @@ public abstract class ManagedPerformanceCounter extends
ManagedCounter
this.firstExchangeFailureTimestamp = new StatisticValue();
this.lastExchangeCreatedTimestamp = new StatisticValue();
this.lastExchangeCompletedTimestamp = new StatisticValue();
+ this.lastExchangeFailureHandledTimestamp = new StatisticValue();
this.lastExchangeFailureTimestamp = new StatisticValue();
}
@@ -104,6 +106,7 @@ public abstract class ManagedPerformanceCounter extends
ManagedCounter
lastExchangeCreatedTimestamp.reset();
lastExchangeCompletedTimestamp.reset();
lastExchangeCompletedExchangeId = null;
+ lastExchangeFailureHandledTimestamp.reset();
lastExchangeFailureTimestamp.reset();
lastExchangeFailureExchangeId = null;
}
@@ -212,6 +215,12 @@ public abstract class ManagedPerformanceCounter extends
ManagedCounter
return firstExchangeCompletedExchangeId;
}
+ @Override
+ public Date getLastExchangeFailureHandledTimestamp() {
+ long value = lastExchangeFailureHandledTimestamp.getValue();
+ return value > 0 ? new Date(value) : null;
+ }
+
@Override
public Date getLastExchangeFailureTimestamp() {
long value = lastExchangeFailureTimestamp.getValue();
@@ -261,6 +270,7 @@ public abstract class ManagedPerformanceCounter extends
ManagedCounter
if (ExchangeHelper.isFailureHandled(exchange)) {
failuresHandled.increment();
+
lastExchangeFailureHandledTimestamp.updateValue(System.currentTimeMillis());
}
if (exchange.isExternalRedelivered()) {
externalRedeliveries.increment();
@@ -349,6 +359,8 @@ public abstract class ManagedPerformanceCounter extends
ManagedCounter
sb.append(String.format(" lastExchangeCompletedTimestamp=\"%s\"",
dateAsString(lastExchangeCompletedTimestamp.getValue())));
sb.append(String.format(" lastExchangeCompletedExchangeId=\"%s\"",
nullSafe(lastExchangeCompletedExchangeId)));
+ sb.append(String.format("
lastExchangeFailureHandledTimestamp=\"%s\"",
+
dateAsString(lastExchangeFailureHandledTimestamp.getValue())));
sb.append(String.format(" lastExchangeFailureTimestamp=\"%s\"",
dateAsString(lastExchangeFailureTimestamp.getValue())));
sb.append(String.format(" lastExchangeFailureExchangeId=\"%s\"",
nullSafe(lastExchangeFailureExchangeId)));
@@ -389,6 +401,7 @@ public abstract class ManagedPerformanceCounter extends
ManagedCounter
jo.put("lastExchangeCreatedTimestamp",
lastExchangeCreatedTimestamp.getValue());
jo.put("lastExchangeCompletedTimestamp",
lastExchangeCompletedTimestamp.getValue());
jo.put("lastExchangeCompletedExchangeId",
lastExchangeCompletedExchangeId);
+ jo.put("lastExchangeFailureHandledTimestamp",
lastExchangeFailureHandledTimestamp.getValue());
jo.put("lastExchangeFailureTimestamp",
lastExchangeFailureTimestamp.getValue());
jo.put("lastExchangeFailureExchangeId",
lastExchangeFailureExchangeId);
}
diff --git
a/core/camel-management/src/test/java/org/apache/camel/management/ManagedFailureHandledTimestampTest.java
b/core/camel-management/src/test/java/org/apache/camel/management/ManagedFailureHandledTimestampTest.java
new file mode 100644
index 000000000000..35c57d66411d
--- /dev/null
+++
b/core/camel-management/src/test/java/org/apache/camel/management/ManagedFailureHandledTimestampTest.java
@@ -0,0 +1,81 @@
+/*
+ * 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.management;
+
+import java.util.Date;
+
+import javax.management.MBeanServer;
+import javax.management.ObjectName;
+
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.condition.DisabledOnOs;
+import org.junit.jupiter.api.condition.OS;
+
+import static
org.apache.camel.management.DefaultManagementObjectNameStrategy.TYPE_ROUTE;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+
+@DisabledOnOs(OS.AIX)
+public class ManagedFailureHandledTimestampTest extends ManagementTestSupport {
+
+ @Test
+ public void testLastExchangeFailureHandledTimestamp() throws Exception {
+ MBeanServer mbeanServer = getMBeanServer();
+
+ MockEndpoint dead = getMockEndpoint("mock:dead");
+ dead.expectedMessageCount(1);
+
+ template.sendBody("direct:start", "Hello World");
+
+ assertMockEndpointsSatisfied();
+
+ ObjectName on = getCamelObjectName(TYPE_ROUTE, "route1");
+
+ Long completed = (Long) mbeanServer.getAttribute(on,
"ExchangesCompleted");
+ assertEquals(1, completed.longValue());
+
+ Long failed = (Long) mbeanServer.getAttribute(on, "ExchangesFailed");
+ assertEquals(0, failed.longValue());
+
+ Long failuresHandled = (Long) mbeanServer.getAttribute(on,
"FailuresHandled");
+ assertEquals(1, failuresHandled.longValue());
+
+ Date handledTs = (Date) mbeanServer.getAttribute(on,
"LastExchangeFailureHandledTimestamp");
+ assertNotNull(handledTs);
+
+ Date failureTs = (Date) mbeanServer.getAttribute(on,
"LastExchangeFailureTimestamp");
+ assertNull(failureTs);
+ }
+
+ @Override
+ protected RouteBuilder createRouteBuilder() {
+ return new RouteBuilder() {
+ @Override
+ public void configure() {
+ errorHandler(deadLetterChannel("mock:dead"));
+
+ from("direct:start")
+ .process(exchange -> {
+ throw new IllegalArgumentException("Forced");
+ });
+ }
+ };
+ }
+}