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