This is an automated email from the ASF dual-hosted git repository. squakez pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/camel.git
commit edc1252f3fea05d5831b4df3667bd5c2d4bca27b 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 | 3 ++- .../camel/main/MetricsConfigurationProperties.java | 23 ++++++++++++++++++++++ 8 files changed, 71 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 d10ef882340c..73da4bd6a9a5 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 @@ -50,6 +50,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": @@ -89,6 +91,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": @@ -129,6 +133,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 d9df02a1ea43..0804f67b057a 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; } @@ -380,6 +394,7 @@ public class MicrometerPrometheus extends ServiceSupport implements CamelMetrics notifier.setLogMetricsOnShutdownFilters(meterFilters); } 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 7937c8ca1115..ebf3980110df 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 @@ -114,12 +114,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 b0470c3bc4ec..cb887727dc66 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 @@ -37,6 +37,7 @@ public class MetricsConfigurationPropertiesConfigurer extends org.apache.camel.s map.put("LogMetricsOnShutdownFormat", 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); @@ -72,6 +73,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": @@ -115,6 +118,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": @@ -154,6 +159,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 96dd30c090c1..f75856d4b773 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 @@ -351,6 +351,7 @@ { "name": "camel.metrics.logMetricsOnShutdownFormat", "required": false, "description": "Format used to log metrics when application is shutting down. Either json (default) or prometheus format.", "sourceType": "org.apache.camel.main.MetricsConfigurationProperties", "type": "enum", "javaType": "java.lang.String", "defaultValue": "json", "secret": false, "enum": [ "json", "prometheus" ] }, { "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 30168fb26589..b433a673a690 100644 --- a/core/camel-main/src/main/docs/main.adoc +++ b/core/camel-main/src/main/docs/main.adoc @@ -655,7 +655,7 @@ The camel.mdc supports 3 options, which are listed below. === Camel Micrometer Metrics configurations -The camel.metrics supports 17 options, which are listed below. +The camel.metrics supports 18 options, which are listed below. [width="100%",cols="2,5,^1,2",options="header"] |=== @@ -674,6 +674,7 @@ The camel.metrics supports 17 options, which are listed below. | `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 a58bd6ee3f78..556e9d0d4691 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; @@ -157,6 +159,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; } @@ -420,4 +434,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; + } + }
