This is an automated email from the ASF dual-hosted git repository.

squakez pushed a commit to branch camel-4.22.x
in repository https://gitbox.apache.org/repos/asf/camel.git

commit 78da6c24e3ee77f62e324dbd5f2b9bb3b7441a97
Author: Pasquale Congiusti <[email protected]>
AuthorDate: Fri Oct 30 10:11:19 2026 +0100

    chore(main): expose metrics recordLastExchangeTimestamp parameter
    
    Ref CAMEL-26415
---
 .../prometheus/MicrometerPrometheusConfigurer.java |  6 ++++++
 .../prometheus/MicrometerPrometheus.java           | 15 ++++++++++++++
 .../MicrometerExchangeEventNotifier.java           |  3 ++-
 ...eterExchangeEventNotifierLastTimestampTest.java | 18 ++++++++++++++---
 .../MetricsConfigurationPropertiesConfigurer.java  |  7 +++++++
 .../camel-main-configuration-metadata.json         |  1 +
 core/camel-main/src/main/docs/main.adoc            |  4 +++-
 .../camel/main/MetricsConfigurationProperties.java | 23 ++++++++++++++++++++++
 8 files changed, 72 insertions(+), 5 deletions(-)

diff --git 
a/components/camel-micrometer-prometheus/src/generated/java/org/apache/camel/component/micrometer/prometheus/MicrometerPrometheusConfigurer.java
 
b/components/camel-micrometer-prometheus/src/generated/java/org/apache/camel/component/micrometer/prometheus/MicrometerPrometheusConfigurer.java
index 9b435fe0b430..e17823ba5a0c 100644
--- 
a/components/camel-micrometer-prometheus/src/generated/java/org/apache/camel/component/micrometer/prometheus/MicrometerPrometheusConfigurer.java
+++ 
b/components/camel-micrometer-prometheus/src/generated/java/org/apache/camel/component/micrometer/prometheus/MicrometerPrometheusConfigurer.java
@@ -49,6 +49,8 @@ public class MicrometerPrometheusConfigurer extends 
org.apache.camel.support.com
         case "namingstrategy":
         case "namingStrategy": target.setNamingStrategy(property(camelContext, 
java.lang.String.class, value)); return true;
         case "path": target.setPath(property(camelContext, 
java.lang.String.class, value)); return true;
+        case "recordlastexchangetimestamp":
+        case "recordLastExchangeTimestamp": 
target.setRecordLastExchangeTimestamp(property(camelContext, boolean.class, 
value)); return true;
         case "routepolicylevel":
         case "routePolicyLevel": 
target.setRoutePolicyLevel(property(camelContext, java.lang.String.class, 
value)); return true;
         case "skipcamelinfo":
@@ -88,6 +90,8 @@ public class MicrometerPrometheusConfigurer extends 
org.apache.camel.support.com
         case "namingstrategy":
         case "namingStrategy": return java.lang.String.class;
         case "path": return java.lang.String.class;
+        case "recordlastexchangetimestamp":
+        case "recordLastExchangeTimestamp": return boolean.class;
         case "routepolicylevel":
         case "routePolicyLevel": return java.lang.String.class;
         case "skipcamelinfo":
@@ -128,6 +132,8 @@ public class MicrometerPrometheusConfigurer extends 
org.apache.camel.support.com
         case "namingstrategy":
         case "namingStrategy": return target.getNamingStrategy();
         case "path": return target.getPath();
+        case "recordlastexchangetimestamp":
+        case "recordLastExchangeTimestamp": return 
target.isRecordLastExchangeTimestamp();
         case "routepolicylevel":
         case "routePolicyLevel": return target.getRoutePolicyLevel();
         case "skipcamelinfo":
diff --git 
a/components/camel-micrometer-prometheus/src/main/java/org/apache/camel/component/micrometer/prometheus/MicrometerPrometheus.java
 
b/components/camel-micrometer-prometheus/src/main/java/org/apache/camel/component/micrometer/prometheus/MicrometerPrometheus.java
index d4f8ad9feb02..c5d4e6f666e8 100644
--- 
a/components/camel-micrometer-prometheus/src/main/java/org/apache/camel/component/micrometer/prometheus/MicrometerPrometheus.java
+++ 
b/components/camel-micrometer-prometheus/src/main/java/org/apache/camel/component/micrometer/prometheus/MicrometerPrometheus.java
@@ -96,6 +96,8 @@ public class MicrometerPrometheus extends ServiceSupport 
implements CamelMetrics
     @Metadata(defaultValue = "true")
     private boolean baseEndpointURIExchangeEventNotifier = true;
     @Metadata(defaultValue = "true")
+    private boolean recordLastExchangeTimestamp = true;
+    @Metadata(defaultValue = "true")
     private boolean enableRouteEventNotifier = true;
     @Metadata(defaultValue = "false")
     private boolean enableInstrumentedThreadPoolFactory;
@@ -198,6 +200,18 @@ public class MicrometerPrometheus extends ServiceSupport 
implements CamelMetrics
         this.baseEndpointURIExchangeEventNotifier = 
baseEndpointURIExchangeEventNotifier;
     }
 
+    public boolean isRecordLastExchangeTimestamp() {
+        return recordLastExchangeTimestamp;
+    }
+
+    /**
+     * Whether to record the last exchange processed timestamp as a Gauge 
metric (camel.exchanges.last.timestamp). Set
+     * to false to disable this metric entirely.
+     */
+    public void setRecordLastExchangeTimestamp(boolean 
recordLastExchangeTimestamp) {
+        this.recordLastExchangeTimestamp = recordLastExchangeTimestamp;
+    }
+
     public boolean isEnableRouteEventNotifier() {
         return enableRouteEventNotifier;
     }
@@ -381,6 +395,7 @@ public class MicrometerPrometheus extends ServiceSupport 
implements CamelMetrics
                 }
             }
             
notifier.setBaseEndpointURI(isBaseEndpointURIExchangeEventNotifier());
+            
notifier.setRecordLastExchangeTimestamp(isRecordLastExchangeTimestamp());
             if ("legacy".equalsIgnoreCase(namingStrategy)) {
                 notifier.setNamingStrategy(
                         new 
MicrometerExchangeEventNotifierNamingStrategyLegacy(isBaseEndpointURIExchangeEventNotifier()));
diff --git 
a/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/eventnotifier/MicrometerExchangeEventNotifier.java
 
b/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/eventnotifier/MicrometerExchangeEventNotifier.java
index 8782584bfa89..22a4b684376b 100644
--- 
a/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/eventnotifier/MicrometerExchangeEventNotifier.java
+++ 
b/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/eventnotifier/MicrometerExchangeEventNotifier.java
@@ -111,12 +111,13 @@ public class MicrometerExchangeEventNotifier extends 
AbstractMicrometerEventNoti
 
         // Register once at startup — backing value updated via AtomicLong on 
each exchange
         if (recordLastExchangeTimestamp) {
-            Gauge.builder(
+            Meter meter = Gauge.builder(
                     MicrometerConstants.CAMEL_EXCHANGE_LAST_TIME_METER_NAME,
                     lastExchangeTimestampHolder,
                     AtomicLong::get)
                     .description("Last exchange processed time in milliseconds 
since the Unix epoch")
                     .register(getMeterRegistry());
+            
meterMap.put(MicrometerConstants.CAMEL_EXCHANGE_LAST_TIME_METER_NAME, meter);
         }
 
         // need to be able to add/remove meter accordingly to route changes
diff --git 
a/components/camel-micrometer/src/test/java/org/apache/camel/component/micrometer/eventnotifier/MicrometerExchangeEventNotifierLastTimestampTest.java
 
b/components/camel-micrometer/src/test/java/org/apache/camel/component/micrometer/eventnotifier/MicrometerExchangeEventNotifierLastTimestampTest.java
index b215b7ce616e..8a1e8a5cf4e1 100644
--- 
a/components/camel-micrometer/src/test/java/org/apache/camel/component/micrometer/eventnotifier/MicrometerExchangeEventNotifierLastTimestampTest.java
+++ 
b/components/camel-micrometer/src/test/java/org/apache/camel/component/micrometer/eventnotifier/MicrometerExchangeEventNotifierLastTimestampTest.java
@@ -19,7 +19,9 @@ package org.apache.camel.component.micrometer.eventnotifier;
 import java.util.concurrent.atomic.AtomicInteger;
 
 import io.micrometer.core.instrument.Gauge;
+import io.micrometer.core.instrument.composite.CompositeMeterRegistry;
 import io.micrometer.core.instrument.search.RequiredSearch;
+import io.micrometer.core.instrument.search.Search;
 import io.micrometer.core.instrument.simple.SimpleMeterRegistry;
 import org.apache.camel.builder.RouteBuilder;
 import org.apache.camel.component.micrometer.MicrometerConstants;
@@ -67,7 +69,6 @@ public class MicrometerExchangeEventNotifierLastTimestampTest 
extends AbstractMi
         mock.assertIsSatisfied();
 
         // The gauge should have been registered once at doStart() — never 
queried again per exchange.
-        // With the current implementation this assertion FAILS because find() 
is called `count` times.
         assertThat(findCallsAfterStart.get())
                 .as("MeterRegistry.find() for the last-timestamp gauge must 
not be called on every exchange")
                 .isEqualTo(0);
@@ -78,6 +79,17 @@ public class 
MicrometerExchangeEventNotifierLastTimestampTest extends AbstractMi
         assertThat(gauge.value()).as("last-timestamp gauge must hold a 
non-zero timestamp").isGreaterThan(0);
     }
 
+    @Test
+    public void testLastTimestampGaugeRemovedOnStop() {
+        Gauge gauge = 
meterRegistry.find(MicrometerConstants.CAMEL_EXCHANGE_LAST_TIME_METER_NAME).gauge();
+        assertThat(gauge).as("last-timestamp gauge must be registered after 
start").isNotNull();
+
+        context.stop();
+
+        gauge = 
meterRegistry.find(MicrometerConstants.CAMEL_EXCHANGE_LAST_TIME_METER_NAME).gauge();
+        assertThat(gauge).as("last-timestamp gauge must be removed after 
stop").isNull();
+    }
+
     @Override
     protected RouteBuilder createRouteBuilder() {
         return new RouteBuilder() {
@@ -92,7 +104,7 @@ public class 
MicrometerExchangeEventNotifierLastTimestampTest extends AbstractMi
     // Spy registry that intercepts find() calls for the last-timestamp meter 
name
     // 
---------------------------------------------------------------------------
 
-    private static class CountingCompositeMeterRegistry extends 
io.micrometer.core.instrument.composite.CompositeMeterRegistry {
+    private static class CountingCompositeMeterRegistry extends 
CompositeMeterRegistry {
 
         private final AtomicInteger counter;
 
@@ -111,7 +123,7 @@ public class 
MicrometerExchangeEventNotifierLastTimestampTest extends AbstractMi
         }
 
         @Override
-        public io.micrometer.core.instrument.search.Search find(String name) {
+        public Search find(String name) {
             if 
(MicrometerConstants.CAMEL_EXCHANGE_LAST_TIME_METER_NAME.equals(name)) {
                 counter.incrementAndGet();
             }
diff --git 
a/core/camel-main/src/generated/java/org/apache/camel/main/MetricsConfigurationPropertiesConfigurer.java
 
b/core/camel-main/src/generated/java/org/apache/camel/main/MetricsConfigurationPropertiesConfigurer.java
index dafd87e4da31..cb91edef2f3f 100644
--- 
a/core/camel-main/src/generated/java/org/apache/camel/main/MetricsConfigurationPropertiesConfigurer.java
+++ 
b/core/camel-main/src/generated/java/org/apache/camel/main/MetricsConfigurationPropertiesConfigurer.java
@@ -35,6 +35,7 @@ public class MetricsConfigurationPropertiesConfigurer extends 
org.apache.camel.s
         map.put("LogMetricsOnShutdownFilters", java.lang.String.class);
         map.put("NamingStrategy", java.lang.String.class);
         map.put("Path", java.lang.String.class);
+        map.put("RecordLastExchangeTimestamp", boolean.class);
         map.put("RoutePolicyLevel", java.lang.String.class);
         map.put("SkipCamelInfo", boolean.class);
         map.put("TextFormatVersion", java.lang.String.class);
@@ -68,6 +69,8 @@ public class MetricsConfigurationPropertiesConfigurer extends 
org.apache.camel.s
         case "namingstrategy":
         case "namingStrategy": target.setNamingStrategy(property(camelContext, 
java.lang.String.class, value)); return true;
         case "path": target.setPath(property(camelContext, 
java.lang.String.class, value)); return true;
+        case "recordlastexchangetimestamp":
+        case "recordLastExchangeTimestamp": 
target.setRecordLastExchangeTimestamp(property(camelContext, boolean.class, 
value)); return true;
         case "routepolicylevel":
         case "routePolicyLevel": 
target.setRoutePolicyLevel(property(camelContext, java.lang.String.class, 
value)); return true;
         case "skipcamelinfo":
@@ -109,6 +112,8 @@ public class MetricsConfigurationPropertiesConfigurer 
extends org.apache.camel.s
         case "namingstrategy":
         case "namingStrategy": return java.lang.String.class;
         case "path": return java.lang.String.class;
+        case "recordlastexchangetimestamp":
+        case "recordLastExchangeTimestamp": return boolean.class;
         case "routepolicylevel":
         case "routePolicyLevel": return java.lang.String.class;
         case "skipcamelinfo":
@@ -146,6 +151,8 @@ public class MetricsConfigurationPropertiesConfigurer 
extends org.apache.camel.s
         case "namingstrategy":
         case "namingStrategy": return target.getNamingStrategy();
         case "path": return target.getPath();
+        case "recordlastexchangetimestamp":
+        case "recordLastExchangeTimestamp": return 
target.isRecordLastExchangeTimestamp();
         case "routepolicylevel":
         case "routePolicyLevel": return target.getRoutePolicyLevel();
         case "skipcamelinfo":
diff --git 
a/core/camel-main/src/generated/resources/META-INF/camel-main-configuration-metadata.json
 
b/core/camel-main/src/generated/resources/META-INF/camel-main-configuration-metadata.json
index 224b3dc6a9a8..cf6633eef363 100644
--- 
a/core/camel-main/src/generated/resources/META-INF/camel-main-configuration-metadata.json
+++ 
b/core/camel-main/src/generated/resources/META-INF/camel-main-configuration-metadata.json
@@ -338,6 +338,7 @@
     { "name": "camel.metrics.logMetricsOnShutdownFilters", "required": false, 
"description": "List of metrics (comma separated) to log when application is 
shutting down. You can use character to log any metrics containing the 
wildcard, for example camel.exchanges. (default to all metrics available).", 
"sourceType": "org.apache.camel.main.MetricsConfigurationProperties", "type": 
"string", "javaType": "java.lang.String", "secret": false },
     { "name": "camel.metrics.namingStrategy", "required": false, 
"description": "Controls the name style to use for metrics. Default = uses 
micrometer naming convention. Legacy = uses the classic naming style 
(camelCase)", "sourceType": 
"org.apache.camel.main.MetricsConfigurationProperties", "type": "enum", 
"javaType": "java.lang.String", "defaultValue": "default", "secret": false, 
"enum": [ "default", "legacy" ] },
     { "name": "camel.metrics.path", "required": false, "description": "The 
path endpoint used to expose the metrics.", "sourceType": 
"org.apache.camel.main.MetricsConfigurationProperties", "type": "string", 
"javaType": "java.lang.String", "defaultValue": "\/observe\/metrics", "secret": 
false },
+    { "name": "camel.metrics.recordLastExchangeTimestamp", "required": false, 
"description": "Whether to record the last exchange processed timestamp as a 
Gauge metric (camel.exchanges.last.timestamp). Set to false to disable this 
metric entirely.", "sourceType": 
"org.apache.camel.main.MetricsConfigurationProperties", "type": "boolean", 
"javaType": "boolean", "defaultValue": true, "secret": false },
     { "name": "camel.metrics.routePolicyLevel", "required": false, 
"description": "Sets the level of information to capture. all = both context 
and routes.", "sourceType": 
"org.apache.camel.main.MetricsConfigurationProperties", "type": "enum", 
"javaType": "java.lang.String", "defaultValue": "all", "secret": false, "enum": 
[ "all", "route", "context" ] },
     { "name": "camel.metrics.skipCamelInfo", "required": false, "description": 
"Skip the evaluation of app.info metric which contains runtime provider 
information (default, false).", "sourceType": 
"org.apache.camel.main.MetricsConfigurationProperties", "type": "boolean", 
"javaType": "boolean", "defaultValue": false, "secret": false },
     { "name": "camel.metrics.textFormatVersion", "required": false, 
"description": "The text-format version to use with Prometheus scraping. 0.0.4 
= text\/plain; version=0.0.4; charset=utf-8 1.0.0 = 
application\/openmetrics-text; version=1.0.0; charset=utf-8", "sourceType": 
"org.apache.camel.main.MetricsConfigurationProperties", "type": "enum", 
"javaType": "java.lang.String", "defaultValue": "0.0.4", "secret": false, 
"enum": [ "0.0.4", "1.0.0" ] },
diff --git a/core/camel-main/src/main/docs/main.adoc 
b/core/camel-main/src/main/docs/main.adoc
index 636a639b5a7a..ab108ef88d18 100644
--- a/core/camel-main/src/main/docs/main.adoc
+++ b/core/camel-main/src/main/docs/main.adoc
@@ -646,7 +646,7 @@ The camel.mdc supports 3 options, which are listed below.
 
 
 === Camel Micrometer Metrics configurations
-The camel.metrics supports 16 options, which are listed below.
+The camel.metrics supports 18 options, which are listed below.
 
 [width="100%",cols="2,5,^1,2",options="header"]
 |===
@@ -662,8 +662,10 @@ The camel.metrics supports 16 options, which are listed 
below.
 | `camel.metrics.enableRoutePolicy` | Set whether to enable the 
MicrometerRoutePolicyFactory for capturing metrics on route processing times. | 
true | boolean
 | `camel.metrics.logMetricsOnShutdown` | Log metrics when application is 
shutting down. (default, false). | false | boolean
 | `camel.metrics.logMetricsOnShutdownFilters` | List of metrics (comma 
separated) to log when application is shutting down. You can use character to 
log any metrics containing the wildcard, for example camel.exchanges. (default 
to all metrics available). |  | String
+| `camel.metrics.logMetricsOnShutdownFormat` | Format used to log metrics when 
application is shutting down. Either json (default) or prometheus format. | 
json | String
 | `camel.metrics.namingStrategy` | Controls the name style to use for metrics. 
Default = uses micrometer naming convention. Legacy = uses the classic naming 
style (camelCase) | default | String
 | `camel.metrics.path` | The path endpoint used to expose the metrics. | 
/observe/metrics | String
+| `camel.metrics.recordLastExchangeTimestamp` | Whether to record the last 
exchange processed timestamp as a Gauge metric 
(camel.exchanges.last.timestamp). Set to false to disable this metric entirely. 
| true | boolean
 | `camel.metrics.routePolicyLevel` | Sets the level of information to capture. 
all = both context and routes. | all | String
 | `camel.metrics.skipCamelInfo` | Skip the evaluation of app.info metric which 
contains runtime provider information (default, false). | false | boolean
 | `camel.metrics.textFormatVersion` | The text-format version to use with 
Prometheus scraping. 0.0.4 = text/plain; version=0.0.4; charset=utf-8 1.0.0 = 
application/openmetrics-text; version=1.0.0; charset=utf-8 | 0.0.4 | String
diff --git 
a/core/camel-main/src/main/java/org/apache/camel/main/MetricsConfigurationProperties.java
 
b/core/camel-main/src/main/java/org/apache/camel/main/MetricsConfigurationProperties.java
index 79cb48c8cf83..fa95cfb5a70e 100644
--- 
a/core/camel-main/src/main/java/org/apache/camel/main/MetricsConfigurationProperties.java
+++ 
b/core/camel-main/src/main/java/org/apache/camel/main/MetricsConfigurationProperties.java
@@ -41,6 +41,8 @@ public class MetricsConfigurationProperties implements 
BootstrapCloseable {
     @Metadata(defaultValue = "true")
     private boolean baseEndpointURIExchangeEventNotifier = true;
     @Metadata(defaultValue = "true")
+    private boolean recordLastExchangeTimestamp = true;
+    @Metadata(defaultValue = "true")
     private boolean enableRouteEventNotifier = true;
     @Metadata(defaultValue = "false")
     private boolean enableInstrumentedThreadPoolFactory;
@@ -155,6 +157,18 @@ public class MetricsConfigurationProperties implements 
BootstrapCloseable {
         this.baseEndpointURIExchangeEventNotifier = 
baseEndpointURIExchangeEventNotifier;
     }
 
+    public boolean isRecordLastExchangeTimestamp() {
+        return recordLastExchangeTimestamp;
+    }
+
+    /**
+     * Whether to record the last exchange processed timestamp as a Gauge 
metric (camel.exchanges.last.timestamp). Set
+     * to false to disable this metric entirely.
+     */
+    public void setRecordLastExchangeTimestamp(boolean 
recordLastExchangeTimestamp) {
+        this.recordLastExchangeTimestamp = recordLastExchangeTimestamp;
+    }
+
     public boolean isEnableRouteEventNotifier() {
         return enableRouteEventNotifier;
     }
@@ -399,4 +413,13 @@ public class MetricsConfigurationProperties implements 
BootstrapCloseable {
         return this;
     }
 
+    /**
+     * Whether to record the last exchange processed timestamp as a Gauge 
metric (camel.exchanges.last.timestamp). Set
+     * to false to disable this metric entirely.
+     */
+    public MetricsConfigurationProperties 
withRecordLastExchangeTimestamp(boolean recordLastExchangeTimestamp) {
+        this.recordLastExchangeTimestamp = recordLastExchangeTimestamp;
+        return this;
+    }
+
 }

Reply via email to