You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by da...@apache.org on 2023/12/19 12:29:37 UTC
(camel) branch main updated: CAMEL-20249: camel-micrometer - Should remove meters when stopping to avoid having old unused meters when reloading routes or redeploying apps (#12486)
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 f9f850b6b2f CAMEL-20249: camel-micrometer - Should remove meters when stopping to avoid having old unused meters when reloading routes or redeploying apps (#12486)
f9f850b6b2f is described below
commit f9f850b6b2f2bc57110019a521eadd36edafbb92
Author: Claus Ibsen <cl...@gmail.com>
AuthorDate: Tue Dec 19 13:29:30 2023 +0100
CAMEL-20249: camel-micrometer - Should remove meters when stopping to avoid having old unused meters when reloading routes or redeploying apps (#12486)
CAMEL-20249: camel-micrometer - Should remove meters when stopping to avoid having old unused meters when reloading routes or redeploying apps (#12486)
---
.../main/camel-main-configuration-metadata.json | 1 +
.../prometheus/MicrometerPrometheusConfigurer.java | 6 ++
.../prometheus/MicrometerPrometheus.java | 64 ++++++++++++++++++++--
.../src/main/docs/micrometer-component.adoc | 5 +-
.../component/micrometer/MicrometerConsole.java | 2 +-
.../component/micrometer/MicrometerConstants.java | 8 +--
.../MicrometerExchangeEventNotifier.java | 34 +++++++++++-
...rometerExchangeEventNotifierNamingStrategy.java | 23 ++++++--
.../MicrometerRouteEventNotifier.java | 31 ++++++++++-
...MicrometerRouteEventNotifierNamingStrategy.java | 13 +++++
.../routepolicy/MicrometerRoutePolicy.java | 33 +++++++++++
.../MetricsConfigurationPropertiesConfigurer.java | 6 ++
.../camel-main-configuration-metadata.json | 1 +
core/camel-main/src/main/docs/main.adoc | 3 +-
.../camel/main/MetricsConfigurationProperties.java | 21 +++++++
.../jbang/core/commands/process/ListMetric.java | 59 ++++++++++++++++----
16 files changed, 277 insertions(+), 33 deletions(-)
diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/main/camel-main-configuration-metadata.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/main/camel-main-configuration-metadata.json
index d7d8b717315..3a1734b8152 100644
--- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/main/camel-main-configuration-metadata.json
+++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/main/camel-main-configuration-metadata.json
@@ -173,6 +173,7 @@
{ "name": "camel.lra.localParticipantContextPath", "description": "The context-path for the local participant. Is default \/lra-participant", "sourceType": "org.apache.camel.main.LraConfigurationProperties", "type": "string", "javaType": "java.lang.String", "defaultValue": "\/lra-participant" },
{ "name": "camel.lra.localParticipantUrl", "description": "The URL for the local participant", "sourceType": "org.apache.camel.main.LraConfigurationProperties", "type": "string", "javaType": "java.lang.String" },
{ "name": "camel.metrics.binders", "description": "Additional Micrometer binders to include such as jvm-memory, processor, jvm-thread, and so forth. Multiple binders can be separated by comma. The following binders currently is available from Micrometer: class-loader, commons-object-pool2, file-descriptor, hystrix-metrics-binder, jvm-compilation, jvm-gc, jvm-heap-pressure, jvm-info, jvm-memory, jvm-thread, log4j2, logback, processor, uptime", "sourceType": "org.apache.camel.main.Metr [...]
+ { "name": "camel.metrics.clearOnReload", "description": "Clear the captured metrics data when Camel is reloading routes such as when using Camel JBang.", "sourceType": "org.apache.camel.main.MetricsConfigurationProperties", "type": "boolean", "javaType": "boolean", "defaultValue": true },
{ "name": "camel.metrics.enabled", "description": "To enable Micrometer metrics.", "sourceType": "org.apache.camel.main.MetricsConfigurationProperties", "type": "boolean", "javaType": "boolean", "defaultValue": "false" },
{ "name": "camel.metrics.enableExchangeEventNotifier", "description": "Set whether to enable the MicrometerExchangeEventNotifier for capturing metrics on exchange processing times.", "sourceType": "org.apache.camel.main.MetricsConfigurationProperties", "type": "boolean", "javaType": "boolean", "defaultValue": true },
{ "name": "camel.metrics.enableMessageHistory", "description": "Set whether to enable the MicrometerMessageHistoryFactory for capturing metrics on individual route node processing times. Depending on the number of configured route nodes, there is the potential to create a large volume of metrics. Therefore, this option is disabled by default.", "sourceType": "org.apache.camel.main.MetricsConfigurationProperties", "type": "boolean", "javaType": "boolean", "defaultValue": "false" },
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 479105554d7..3466314c9cd 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
@@ -25,6 +25,8 @@ public class MicrometerPrometheusConfigurer extends org.apache.camel.support.com
case "Binders": target.setBinders(property(camelContext, java.lang.String.class, value)); return true;
case "camelcontext":
case "CamelContext": target.setCamelContext(property(camelContext, org.apache.camel.CamelContext.class, value)); return true;
+ case "clearonreload":
+ case "ClearOnReload": target.setClearOnReload(property(camelContext, boolean.class, value)); return true;
case "enableexchangeeventnotifier":
case "EnableExchangeEventNotifier": target.setEnableExchangeEventNotifier(property(camelContext, boolean.class, value)); return true;
case "enablemessagehistory":
@@ -48,6 +50,8 @@ public class MicrometerPrometheusConfigurer extends org.apache.camel.support.com
case "Binders": return java.lang.String.class;
case "camelcontext":
case "CamelContext": return org.apache.camel.CamelContext.class;
+ case "clearonreload":
+ case "ClearOnReload": return boolean.class;
case "enableexchangeeventnotifier":
case "EnableExchangeEventNotifier": return boolean.class;
case "enablemessagehistory":
@@ -72,6 +76,8 @@ public class MicrometerPrometheusConfigurer extends org.apache.camel.support.com
case "Binders": return target.getBinders();
case "camelcontext":
case "CamelContext": return target.getCamelContext();
+ case "clearonreload":
+ case "ClearOnReload": return target.isClearOnReload();
case "enableexchangeeventnotifier":
case "EnableExchangeEventNotifier": return target.isEnableExchangeEventNotifier();
case "enablemessagehistory":
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 d8cab4db2e4..63289d4b668 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
@@ -18,11 +18,13 @@ package org.apache.camel.component.micrometer.prometheus;
import java.io.Closeable;
import java.io.IOException;
+import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.StringJoiner;
+import io.micrometer.core.instrument.Meter;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.binder.MeterBinder;
import io.micrometer.prometheus.PrometheusConfig;
@@ -49,12 +51,14 @@ import org.apache.camel.component.micrometer.routepolicy.MicrometerRoutePolicyNa
import org.apache.camel.component.platform.http.PlatformHttpComponent;
import org.apache.camel.component.platform.http.main.MainHttpServer;
import org.apache.camel.component.platform.http.vertx.VertxPlatformHttpRouter;
+import org.apache.camel.spi.CamelEvent;
import org.apache.camel.spi.CamelMetricsService;
import org.apache.camel.spi.Configurer;
import org.apache.camel.spi.ManagementStrategy;
import org.apache.camel.spi.Metadata;
import org.apache.camel.spi.Registry;
import org.apache.camel.spi.annotations.JdkService;
+import org.apache.camel.support.SimpleEventNotifierSupport;
import org.apache.camel.support.service.ServiceSupport;
import org.apache.camel.util.IOHelper;
import org.apache.camel.util.ObjectHelper;
@@ -86,6 +90,8 @@ public class MicrometerPrometheus extends ServiceSupport implements CamelMetrics
private boolean enableExchangeEventNotifier = true;
@Metadata(defaultValue = "true")
private boolean enableRouteEventNotifier = true;
+ @Metadata(defaultValue = "true")
+ private boolean clearOnReload = true;
@Metadata(defaultValue = "0.0.4", enums = "0.0.4,1.0.0")
private String textFormatVersion = "0.0.4";
@Metadata
@@ -107,7 +113,7 @@ public class MicrometerPrometheus extends ServiceSupport implements CamelMetrics
/**
* Controls the name style to use for metrics.
- *
+ * <p>
* Default = uses micrometer naming convention. Legacy = uses the classic naming style (camelCase)
*/
public void setNamingStrategy(String namingStrategy) {
@@ -132,7 +138,7 @@ public class MicrometerPrometheus extends ServiceSupport implements CamelMetrics
/**
* Set whether to enable the MicrometerMessageHistoryFactory for capturing metrics on individual route node
* processing times.
- *
+ * <p>
* Depending on the number of configured route nodes, there is the potential to create a large volume of metrics.
* Therefore, this option is disabled by default.
*/
@@ -163,13 +169,24 @@ public class MicrometerPrometheus extends ServiceSupport implements CamelMetrics
this.enableRouteEventNotifier = enableRouteEventNotifier;
}
+ public boolean isClearOnReload() {
+ return clearOnReload;
+ }
+
+ /**
+ * Clear the captured metrics data when Camel is reloading routes such as when using Camel JBang.
+ */
+ public void setClearOnReload(boolean clearOnReload) {
+ this.clearOnReload = clearOnReload;
+ }
+
public String getTextFormatVersion() {
return textFormatVersion;
}
/**
* The text-format version to use with Prometheus scraping.
- *
+ * <p>
* 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
*/
@@ -184,7 +201,7 @@ public class MicrometerPrometheus extends ServiceSupport implements CamelMetrics
/**
* Additional Micrometer binders to include such as jvm-memory, processor, jvm-thread, and so forth. Multiple
* binders can be separated by comma.
- *
+ * <p>
* The following binders currently is available from Micrometer: class-loader, commons-object-pool2,
* file-descriptor, hystrix-metrics-binder, jvm-compilation, jvm-gc, jvm-heap-pressure, jvm-info, jvm-memory,
* jvm-thread, log4j2, logback, processor, uptime
@@ -257,6 +274,45 @@ public class MicrometerPrometheus extends ServiceSupport implements CamelMetrics
factory.setMeterRegistry(meterRegistry);
camelContext.setMessageHistoryFactory(factory);
}
+
+ if (clearOnReload) {
+ camelContext.getManagementStrategy().addEventNotifier(new SimpleEventNotifierSupport() {
+
+ @Override
+ public boolean isEnabled(CamelEvent event) {
+ return event instanceof CamelEvent.RouteReloadedEvent;
+ }
+
+ @Override
+ public void notify(CamelEvent event) throws Exception {
+ // when reloading then there may be more routes in the same batch, so we only want
+ // to log the summary at the end
+ if (event instanceof CamelEvent.RouteReloadedEvent) {
+ CamelEvent.RouteReloadedEvent re = (CamelEvent.RouteReloadedEvent) event;
+ if (re.getIndex() >= re.getTotal()) {
+ LOG.info("Resetting Micrometer Registry after reloading routes");
+
+ // remove all meters (not counters) that are from Camel and associated routes via routeId as tag
+ List<Meter> toRemove = new ArrayList<>();
+ for (Meter m : meterRegistry.getMeters()) {
+ String n = m.getId().getName();
+ boolean camel = n.startsWith("camel_") || n.startsWith("camel.");
+ boolean keep = n.startsWith("camel.exchanges.") || n.startsWith("camel_exchanges_");
+ // remove camel but keep those special camel.exchanges. counters
+ boolean remove = camel && !keep;
+ if (remove) {
+ String t = m.getId().getTag("routeId");
+ if (t != null) {
+ toRemove.add(m);
+ }
+ }
+ }
+ toRemove.forEach(meterRegistry::remove);
+ }
+ }
+ }
+ });
+ }
}
@Override
diff --git a/components/camel-micrometer/src/main/docs/micrometer-component.adoc b/components/camel-micrometer/src/main/docs/micrometer-component.adoc
index 4326d9173b4..539862d0af9 100644
--- a/components/camel-micrometer/src/main/docs/micrometer-component.adoc
+++ b/components/camel-micrometer/src/main/docs/micrometer-component.adoc
@@ -83,8 +83,9 @@ Some Camel specific metrics are available out of the box.
|=====================================================
|Name |Type |Description
|camel.message.history|timer |Sample of performance of each node in the route when message history is enabled
-|camel.routes.added |gauge |Number of routes added
-|camel.routes.running |gauge |Number of routes runnning
+|camel.routes.added |gauge |Number of routes in total
+|camel.routes.reloaded |gauge |Number of routes that has been reloaded
+|camel.routes.running |gauge |Number of routes currently running
|camel.exchanges.inflight |gauge |Route inflight messages
|camel.exchanges.total |counter |Total number of processed exchanges
|camel.exchanges.succeeded |counter |Number of successfully completed exchange
diff --git a/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/MicrometerConsole.java b/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/MicrometerConsole.java
index 4c87e4ada99..6cbff3403a3 100644
--- a/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/MicrometerConsole.java
+++ b/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/MicrometerConsole.java
@@ -45,7 +45,7 @@ public class MicrometerConsole extends AbstractDevConsole {
StringBuilder sb = new StringBuilder();
MeterRegistry mr = lookupMeterRegistry();
- sb.append(String.format("MeterRegistry: %s\n", mr.getClass().getName()));
+ sb.append(String.format("MeterRegistry: %s\n\n", mr.getClass().getName()));
int i = 0;
for (Meter m : mr.getMeters()) {
diff --git a/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/MicrometerConstants.java b/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/MicrometerConstants.java
index 67dd47b0b45..d9008beafe4 100644
--- a/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/MicrometerConstants.java
+++ b/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/MicrometerConstants.java
@@ -41,6 +41,7 @@ public final class MicrometerConstants {
public static final String HEADER_METRIC_TAGS = HEADER_PREFIX + "Tags";
public static final String DEFAULT_CAMEL_MESSAGE_HISTORY_METER_NAME = "camel.message.history";
+
public static final String DEFAULT_CAMEL_ROUTE_POLICY_EXCHANGES_FAILED_METER_NAME = "camel.exchanges.failed";
public static final String DEFAULT_CAMEL_ROUTE_POLICY_EXCHANGES_SUCCEEDED_METER_NAME = "camel.exchanges.succeeded";
public static final String DEFAULT_CAMEL_ROUTE_POLICY_EXCHANGES_TOTAL_METER_NAME = "camel.exchanges.total";
@@ -48,15 +49,16 @@ public final class MicrometerConstants {
= "camel.exchanges.failures.handled";
public static final String DEFAULT_CAMEL_ROUTE_POLICY_EXCHANGES_EXTERNAL_REDELIVERIES_METER_NAME
= "camel.exchanges.external.redeliveries";
+ public static final String DEFAULT_CAMEL_ROUTES_EXCHANGES_INFLIGHT = "camel.exchanges.inflight";
+
public static final String DEFAULT_CAMEL_ROUTE_POLICY_METER_NAME = "camel.route.policy";
public static final String DEFAULT_CAMEL_ROUTE_POLICY_LONGMETER_NAME = "camel.route.policy.long.task";
public static final String DEFAULT_CAMEL_EXCHANGE_EVENT_METER_NAME = "camel.exchange.event.notifier";
public static final String DEFAULT_CAMEL_ROUTES_ADDED = "camel.routes.added";
public static final String DEFAULT_CAMEL_ROUTES_RUNNING = "camel.routes.running";
- public static final String DEFAULT_CAMEL_ROUTES_EXCHANGES_INFLIGHT = "camel.exchanges.inflight";
+ public static final String DEFAULT_CAMEL_ROUTES_RELOADED = "camel.routes.reloaded";
public static final String ROUTE_ID_TAG = "routeId";
- public static final String ROUTE_DESCRIPTION_TAG = "routeDescription";
public static final String NODE_ID_TAG = "nodeId";
public static final String FAILED_TAG = "failed";
public static final String CAMEL_CONTEXT_TAG = "camelContext";
@@ -67,8 +69,6 @@ public final class MicrometerConstants {
public static final String ENDPOINT_NAME = "endpointName";
public static final Predicate<Meter.Id> CAMEL_METERS = id -> id.getTag(CAMEL_CONTEXT_TAG) != null;
- public static final Predicate<Meter.Id> TIMERS = id -> id.getType() == Meter.Type.TIMER;
- public static final Predicate<Meter.Id> DISTRIBUTION_SUMMARIES = id -> id.getType() == Meter.Type.DISTRIBUTION_SUMMARY;
public static final Predicate<Meter.Id> ALWAYS = id -> true;
private MicrometerConstants() {
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 d4fc998b69f..61ce90e9526 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
@@ -16,10 +16,13 @@
*/
package org.apache.camel.component.micrometer.eventnotifier;
+import java.util.HashMap;
+import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.function.Predicate;
import io.micrometer.core.instrument.Gauge;
+import io.micrometer.core.instrument.Meter;
import io.micrometer.core.instrument.Tags;
import io.micrometer.core.instrument.Timer;
import org.apache.camel.Exchange;
@@ -30,9 +33,12 @@ import org.apache.camel.spi.CamelEvent.ExchangeEvent;
import org.apache.camel.spi.CamelEvent.ExchangeFailedEvent;
import org.apache.camel.spi.CamelEvent.ExchangeSentEvent;
import org.apache.camel.spi.InflightRepository;
+import org.apache.camel.support.SimpleEventNotifierSupport;
public class MicrometerExchangeEventNotifier extends AbstractMicrometerEventNotifier<ExchangeEvent> {
private InflightRepository inflightRepository;
+
+ private final Map<String, Meter> meterMap = new HashMap<>();
private Predicate<Exchange> ignoreExchanges = exchange -> false;
private MicrometerExchangeEventNotifierNamingStrategy namingStrategy
= MicrometerExchangeEventNotifierNamingStrategy.DEFAULT;
@@ -59,8 +65,31 @@ public class MicrometerExchangeEventNotifier extends AbstractMicrometerEventNoti
@Override
protected void doStart() throws Exception {
- inflightRepository = getCamelContext().getInflightRepository();
super.doStart();
+
+ inflightRepository = getCamelContext().getInflightRepository();
+
+ // need to be able to remove meter if a route is removed
+ getCamelContext().getManagementStrategy().addEventNotifier(new SimpleEventNotifierSupport() {
+ @Override
+ public void notify(CamelEvent event) throws Exception {
+ if (event instanceof CamelEvent.RouteRemovedEvent rre) {
+ String id = rre.getRoute().getRouteId();
+ Meter meter = meterMap.remove(id);
+ if (meter != null) {
+ getMeterRegistry().remove(meter);
+ }
+ }
+ }
+ });
+ }
+
+ @Override
+ protected void doStop() throws Exception {
+ super.doStop();
+
+ meterMap.values().forEach(m -> getMeterRegistry().remove(m));
+ meterMap.clear();
}
@Override
@@ -82,10 +111,11 @@ public class MicrometerExchangeEventNotifier extends AbstractMicrometerEventNoti
if (exchange.getFromRouteId() != null && exchange.getFromEndpoint() != null) {
String name = namingStrategy.getInflightExchangesName(exchange, exchange.getFromEndpoint());
Tags tags = namingStrategy.getInflightExchangesTags(exchangeEvent, exchange.getFromEndpoint());
- Gauge.builder(name, () -> getInflightExchangesInRoute(exchangeEvent))
+ Meter meter = Gauge.builder(name, () -> getInflightExchangesInRoute(exchangeEvent))
.description("Route inflight messages")
.tags(tags)
.register(getMeterRegistry());
+ meterMap.put(exchange.getFromRouteId(), meter);
}
}
diff --git a/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/eventnotifier/MicrometerExchangeEventNotifierNamingStrategy.java b/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/eventnotifier/MicrometerExchangeEventNotifierNamingStrategy.java
index fdaa1c91053..065c7c1c38e 100644
--- a/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/eventnotifier/MicrometerExchangeEventNotifierNamingStrategy.java
+++ b/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/eventnotifier/MicrometerExchangeEventNotifierNamingStrategy.java
@@ -69,12 +69,23 @@ public interface MicrometerExchangeEventNotifierNamingStrategy {
// use sanitized uri to not reveal sensitive information
uri = endpoint.toString();
}
- return Tags.of(
- CAMEL_CONTEXT_TAG, event.getExchange().getContext().getName(),
- SERVICE_NAME, MicrometerEventNotifierService.class.getSimpleName(),
- EVENT_TYPE_TAG, event.getClass().getSimpleName(),
- ENDPOINT_NAME, uri,
- FAILED_TAG, Boolean.toString(event.getExchange().isFailed()));
+ String routeId = event.getExchange().getFromRouteId();
+ if (routeId != null) {
+ return Tags.of(
+ CAMEL_CONTEXT_TAG, event.getExchange().getContext().getName(),
+ SERVICE_NAME, MicrometerEventNotifierService.class.getSimpleName(),
+ EVENT_TYPE_TAG, event.getClass().getSimpleName(),
+ ROUTE_ID_TAG, routeId,
+ ENDPOINT_NAME, uri,
+ FAILED_TAG, Boolean.toString(event.getExchange().isFailed()));
+ } else {
+ return Tags.of(
+ CAMEL_CONTEXT_TAG, event.getExchange().getContext().getName(),
+ SERVICE_NAME, MicrometerEventNotifierService.class.getSimpleName(),
+ EVENT_TYPE_TAG, event.getClass().getSimpleName(),
+ ENDPOINT_NAME, uri,
+ FAILED_TAG, Boolean.toString(event.getExchange().isFailed()));
+ }
}
default Tags getInflightExchangesTags(ExchangeEvent event, Endpoint endpoint) {
diff --git a/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/eventnotifier/MicrometerRouteEventNotifier.java b/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/eventnotifier/MicrometerRouteEventNotifier.java
index c5617dd819b..6c638e031ce 100644
--- a/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/eventnotifier/MicrometerRouteEventNotifier.java
+++ b/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/eventnotifier/MicrometerRouteEventNotifier.java
@@ -22,6 +22,7 @@ import io.micrometer.core.instrument.Gauge;
import org.apache.camel.spi.CamelEvent;
import org.apache.camel.spi.CamelEvent.RouteAddedEvent;
import org.apache.camel.spi.CamelEvent.RouteEvent;
+import org.apache.camel.spi.CamelEvent.RouteReloadedEvent;
import org.apache.camel.spi.CamelEvent.RouteRemovedEvent;
import org.apache.camel.spi.CamelEvent.RouteStartedEvent;
import org.apache.camel.spi.CamelEvent.RouteStoppedEvent;
@@ -30,6 +31,10 @@ public class MicrometerRouteEventNotifier extends AbstractMicrometerEventNotifie
private final AtomicLong routesAdded = new AtomicLong();
private final AtomicLong routesRunning = new AtomicLong();
+ private final AtomicLong routesReloaded = new AtomicLong();
+ private Gauge gaugeAdded;
+ private Gauge gaugeRunning;
+ private Gauge gaugeReloaded;
private MicrometerRouteEventNotifierNamingStrategy namingStrategy = MicrometerRouteEventNotifierNamingStrategy.DEFAULT;
public MicrometerRouteEventNotifier() {
@@ -47,16 +52,36 @@ public class MicrometerRouteEventNotifier extends AbstractMicrometerEventNotifie
@Override
protected void doStart() throws Exception {
super.doStart();
- Gauge.builder(namingStrategy.getRouteAddedName(), routesAdded, value -> (double) value.get())
+
+ gaugeAdded = Gauge.builder(namingStrategy.getRouteAddedName(), routesAdded, value -> (double) value.get())
+ .baseUnit("routes")
+ .tags(namingStrategy.getTags(getCamelContext()))
+ .register(getMeterRegistry());
+ gaugeRunning = Gauge.builder(namingStrategy.getRouteRunningName(), routesRunning, value -> (double) value.get())
.baseUnit("routes")
.tags(namingStrategy.getTags(getCamelContext()))
.register(getMeterRegistry());
- Gauge.builder(namingStrategy.getRouteRunningName(), routesRunning, value -> (double) value.get())
+ gaugeReloaded = Gauge.builder(namingStrategy.getRouteReloadedName(), routesReloaded, value -> (double) value.get())
.baseUnit("routes")
.tags(namingStrategy.getTags(getCamelContext()))
.register(getMeterRegistry());
}
+ @Override
+ protected void doStop() throws Exception {
+ super.doStop();
+
+ if (gaugeAdded != null) {
+ getMeterRegistry().remove(gaugeAdded);
+ }
+ if (gaugeRunning != null) {
+ getMeterRegistry().remove(gaugeRunning);
+ }
+ if (gaugeReloaded != null) {
+ getMeterRegistry().remove(gaugeReloaded);
+ }
+ }
+
@Override
public void notify(CamelEvent eventObject) {
if (eventObject instanceof RouteAddedEvent) {
@@ -67,6 +92,8 @@ public class MicrometerRouteEventNotifier extends AbstractMicrometerEventNotifie
routesRunning.incrementAndGet();
} else if (eventObject instanceof RouteStoppedEvent) {
routesRunning.decrementAndGet();
+ } else if (eventObject instanceof RouteReloadedEvent) {
+ routesReloaded.incrementAndGet();
}
}
}
diff --git a/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/eventnotifier/MicrometerRouteEventNotifierNamingStrategy.java b/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/eventnotifier/MicrometerRouteEventNotifierNamingStrategy.java
index 094567ee790..693275c80ad 100644
--- a/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/eventnotifier/MicrometerRouteEventNotifierNamingStrategy.java
+++ b/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/eventnotifier/MicrometerRouteEventNotifierNamingStrategy.java
@@ -26,6 +26,7 @@ import org.apache.camel.spi.CamelEvent.RouteEvent;
import static org.apache.camel.component.micrometer.MicrometerConstants.CAMEL_CONTEXT_TAG;
import static org.apache.camel.component.micrometer.MicrometerConstants.DEFAULT_CAMEL_ROUTES_ADDED;
+import static org.apache.camel.component.micrometer.MicrometerConstants.DEFAULT_CAMEL_ROUTES_RELOADED;
import static org.apache.camel.component.micrometer.MicrometerConstants.DEFAULT_CAMEL_ROUTES_RUNNING;
import static org.apache.camel.component.micrometer.MicrometerConstants.EVENT_TYPE_TAG;
import static org.apache.camel.component.micrometer.MicrometerConstants.SERVICE_NAME;
@@ -48,6 +49,11 @@ public interface MicrometerRouteEventNotifierNamingStrategy {
public String getRouteRunningName() {
return DEFAULT_CAMEL_ROUTES_RUNNING;
}
+
+ @Override
+ public String getRouteReloadedName() {
+ return DEFAULT_CAMEL_ROUTES_RELOADED;
+ }
};
/**
@@ -64,6 +70,11 @@ public interface MicrometerRouteEventNotifierNamingStrategy {
return formatName(DEFAULT_CAMEL_ROUTES_RUNNING);
}
+ @Override
+ public String getRouteReloadedName() {
+ return formatName(DEFAULT_CAMEL_ROUTES_RELOADED);
+ }
+
@Override
public String formatName(String name) {
return MicrometerUtils.legacyName(name);
@@ -78,6 +89,8 @@ public interface MicrometerRouteEventNotifierNamingStrategy {
String getRouteRunningName();
+ String getRouteReloadedName();
+
default Tags getTags(CamelContext camelContext) {
return Tags.of(
SERVICE_NAME, MicrometerEventNotifierService.class.getSimpleName(),
diff --git a/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/routepolicy/MicrometerRoutePolicy.java b/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/routepolicy/MicrometerRoutePolicy.java
index a53dc85753f..264cb58d6cf 100644
--- a/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/routepolicy/MicrometerRoutePolicy.java
+++ b/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/routepolicy/MicrometerRoutePolicy.java
@@ -220,6 +220,7 @@ public class MicrometerRoutePolicy extends RoutePolicySupport implements NonMana
@Override
public void onInit(Route route) {
super.onInit(route);
+
if (getMeterRegistry() == null) {
setMeterRegistry(MicrometerUtils.getOrCreateMeterRegistry(
route.getCamelContext().getRegistry(), METRICS_REGISTRY_NAME));
@@ -239,7 +240,10 @@ public class MicrometerRoutePolicy extends RoutePolicySupport implements NonMana
} catch (Exception e) {
throw RuntimeCamelException.wrapRuntimeCamelException(e);
}
+ }
+ @Override
+ public void onStart(Route route) {
// create statistics holder
// for now we record only all the timings of a complete exchange (responses)
// we have in-flight / total statistics already from camel-core
@@ -247,6 +251,35 @@ public class MicrometerRoutePolicy extends RoutePolicySupport implements NonMana
it -> new MetricsStatistics(getMeterRegistry(), it, getNamingStrategy(), configuration));
}
+ @Override
+ public void onRemove(Route route) {
+ // route is removed, so remove metrics from micrometer
+ MetricsStatistics stats = statisticsMap.remove(route);
+ if (stats != null) {
+ if (stats.exchangesSucceeded != null) {
+ meterRegistry.remove(stats.exchangesSucceeded);
+ }
+ if (stats.exchangesFailed != null) {
+ meterRegistry.remove(stats.exchangesFailed);
+ }
+ if (stats.exchangesTotal != null) {
+ meterRegistry.remove(stats.exchangesTotal);
+ }
+ if (stats.externalRedeliveries != null) {
+ meterRegistry.remove(stats.externalRedeliveries);
+ }
+ if (stats.failuresHandled != null) {
+ meterRegistry.remove(stats.failuresHandled);
+ }
+ if (stats.timer != null) {
+ meterRegistry.remove(stats.timer);
+ }
+ if (stats.longTaskTimer != null) {
+ meterRegistry.remove(stats.longTaskTimer);
+ }
+ }
+ }
+
@Override
public void onExchangeBegin(Route route, Exchange exchange) {
Optional.ofNullable(statisticsMap.get(route))
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 9d53c533439..59fd581efe0 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
@@ -23,6 +23,8 @@ public class MetricsConfigurationPropertiesConfigurer extends org.apache.camel.s
switch (ignoreCase ? name.toLowerCase() : name) {
case "binders":
case "Binders": target.setBinders(property(camelContext, java.lang.String.class, value)); return true;
+ case "clearonreload":
+ case "ClearOnReload": target.setClearOnReload(property(camelContext, boolean.class, value)); return true;
case "enableexchangeeventnotifier":
case "EnableExchangeEventNotifier": target.setEnableExchangeEventNotifier(property(camelContext, boolean.class, value)); return true;
case "enablemessagehistory":
@@ -46,6 +48,8 @@ public class MetricsConfigurationPropertiesConfigurer extends org.apache.camel.s
switch (ignoreCase ? name.toLowerCase() : name) {
case "binders":
case "Binders": return java.lang.String.class;
+ case "clearonreload":
+ case "ClearOnReload": return boolean.class;
case "enableexchangeeventnotifier":
case "EnableExchangeEventNotifier": return boolean.class;
case "enablemessagehistory":
@@ -70,6 +74,8 @@ public class MetricsConfigurationPropertiesConfigurer extends org.apache.camel.s
switch (ignoreCase ? name.toLowerCase() : name) {
case "binders":
case "Binders": return target.getBinders();
+ case "clearonreload":
+ case "ClearOnReload": return target.isClearOnReload();
case "enableexchangeeventnotifier":
case "EnableExchangeEventNotifier": return target.isEnableExchangeEventNotifier();
case "enablemessagehistory":
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 d7d8b717315..3a1734b8152 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
@@ -173,6 +173,7 @@
{ "name": "camel.lra.localParticipantContextPath", "description": "The context-path for the local participant. Is default \/lra-participant", "sourceType": "org.apache.camel.main.LraConfigurationProperties", "type": "string", "javaType": "java.lang.String", "defaultValue": "\/lra-participant" },
{ "name": "camel.lra.localParticipantUrl", "description": "The URL for the local participant", "sourceType": "org.apache.camel.main.LraConfigurationProperties", "type": "string", "javaType": "java.lang.String" },
{ "name": "camel.metrics.binders", "description": "Additional Micrometer binders to include such as jvm-memory, processor, jvm-thread, and so forth. Multiple binders can be separated by comma. The following binders currently is available from Micrometer: class-loader, commons-object-pool2, file-descriptor, hystrix-metrics-binder, jvm-compilation, jvm-gc, jvm-heap-pressure, jvm-info, jvm-memory, jvm-thread, log4j2, logback, processor, uptime", "sourceType": "org.apache.camel.main.Metr [...]
+ { "name": "camel.metrics.clearOnReload", "description": "Clear the captured metrics data when Camel is reloading routes such as when using Camel JBang.", "sourceType": "org.apache.camel.main.MetricsConfigurationProperties", "type": "boolean", "javaType": "boolean", "defaultValue": true },
{ "name": "camel.metrics.enabled", "description": "To enable Micrometer metrics.", "sourceType": "org.apache.camel.main.MetricsConfigurationProperties", "type": "boolean", "javaType": "boolean", "defaultValue": "false" },
{ "name": "camel.metrics.enableExchangeEventNotifier", "description": "Set whether to enable the MicrometerExchangeEventNotifier for capturing metrics on exchange processing times.", "sourceType": "org.apache.camel.main.MetricsConfigurationProperties", "type": "boolean", "javaType": "boolean", "defaultValue": true },
{ "name": "camel.metrics.enableMessageHistory", "description": "Set whether to enable the MicrometerMessageHistoryFactory for capturing metrics on individual route node processing times. Depending on the number of configured route nodes, there is the potential to create a large volume of metrics. Therefore, this option is disabled by default.", "sourceType": "org.apache.camel.main.MetricsConfigurationProperties", "type": "boolean", "javaType": "boolean", "defaultValue": "false" },
diff --git a/core/camel-main/src/main/docs/main.adoc b/core/camel-main/src/main/docs/main.adoc
index 7541bf137cf..87322318756 100644
--- a/core/camel-main/src/main/docs/main.adoc
+++ b/core/camel-main/src/main/docs/main.adoc
@@ -360,12 +360,13 @@ The camel.opentelemetry supports 4 options, which are listed below.
=== Camel Micrometer Metrics configurations
-The camel.metrics supports 8 options, which are listed below.
+The camel.metrics supports 9 options, which are listed below.
[width="100%",cols="2,5,^1,2",options="header"]
|===
| Name | Description | Default | Type
| *camel.metrics.binders* | Additional Micrometer binders to include such as jvm-memory, processor, jvm-thread, and so forth. Multiple binders can be separated by comma. The following binders currently is available from Micrometer: class-loader, commons-object-pool2, file-descriptor, hystrix-metrics-binder, jvm-compilation, jvm-gc, jvm-heap-pressure, jvm-info, jvm-memory, jvm-thread, log4j2, logback, processor, uptime | | String
+| *camel.metrics.clearOnReload* | Clear the captured metrics data when Camel is reloading routes such as when using Camel JBang. | true | boolean
| *camel.metrics.enabled* | To enable Micrometer metrics. | false | boolean
| *camel.metrics.enableExchange{zwsp}EventNotifier* | Set whether to enable the MicrometerExchangeEventNotifier for capturing metrics on exchange processing times. | true | boolean
| *camel.metrics.enableMessage{zwsp}History* | Set whether to enable the MicrometerMessageHistoryFactory for capturing metrics on individual route node processing times. Depending on the number of configured route nodes, there is the potential to create a large volume of metrics. Therefore, this option is disabled by default. | false | boolean
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 f5c7772149c..108c9dfc2a8 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
@@ -38,6 +38,8 @@ public class MetricsConfigurationProperties implements BootstrapCloseable {
private boolean enableExchangeEventNotifier = true;
@Metadata(defaultValue = "true")
private boolean enableRouteEventNotifier = true;
+ @Metadata(defaultValue = "true")
+ private boolean clearOnReload = true;
@Metadata(defaultValue = "0.0.4", enums = "0.0.4,1.0.0")
private String textFormatVersion = "0.0.4";
@Metadata
@@ -124,6 +126,17 @@ public class MetricsConfigurationProperties implements BootstrapCloseable {
this.enableRouteEventNotifier = enableRouteEventNotifier;
}
+ public boolean isClearOnReload() {
+ return clearOnReload;
+ }
+
+ /**
+ * Clear the captured metrics data when Camel is reloading routes such as when using Camel JBang.
+ */
+ public void setClearOnReload(boolean clearOnReload) {
+ this.clearOnReload = clearOnReload;
+ }
+
public String getTextFormatVersion() {
return textFormatVersion;
}
@@ -214,6 +227,14 @@ public class MetricsConfigurationProperties implements BootstrapCloseable {
return this;
}
+ /**
+ * Clear the captured metrics data when Camel is reloading routes such as when using Camel JBang.
+ */
+ public MetricsConfigurationProperties withClearOnReload(boolean clearOnReload) {
+ this.clearOnReload = clearOnReload;
+ return this;
+ }
+
/**
* The text-format version to use with Prometheus scraping.
*
diff --git a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/process/ListMetric.java b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/process/ListMetric.java
index 6427b955a08..4b5beba36c5 100644
--- a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/process/ListMetric.java
+++ b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/process/ListMetric.java
@@ -19,6 +19,7 @@ package org.apache.camel.dsl.jbang.core.commands.process;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
+import java.util.StringJoiner;
import com.github.freva.asciitable.AsciiTable;
import com.github.freva.asciitable.Column;
@@ -48,6 +49,10 @@ public class ListMetric extends ProcessWatchCommand {
description = "Filter metric by type or name")
String filter;
+ @CommandLine.Option(names = { "--tags" },
+ description = "Show metric tags", defaultValue = "false")
+ boolean tags;
+
@CommandLine.Option(names = { "--custom" },
description = "Only show custom metrics", defaultValue = "false")
boolean custom;
@@ -95,7 +100,8 @@ public class ListMetric extends ProcessWatchCommand {
row.type = "counter";
row.metricName = jo.getString("name");
row.metricDescription = jo.getString("description");
- row.metricRouteId = extractRouteId(jo);
+ row.metricId = extractId(jo);
+ row.tags = extractTags(jo);
row.count = jo.getDouble("count");
if (custom && row.metricName.startsWith("Camel")) {
@@ -117,7 +123,8 @@ public class ListMetric extends ProcessWatchCommand {
row.type = "timer";
row.metricName = jo.getString("name");
row.metricDescription = jo.getString("description");
- row.metricRouteId = extractRouteId(jo);
+ row.metricId = extractId(jo);
+ row.tags = extractTags(jo);
row.count = jo.getDouble("count");
row.mean = jo.getDouble("mean");
row.max = jo.getDouble("max");
@@ -142,7 +149,8 @@ public class ListMetric extends ProcessWatchCommand {
row.type = "gauge";
row.metricName = jo.getString("name");
row.metricDescription = jo.getString("description");
- row.metricRouteId = extractRouteId(jo);
+ row.metricId = extractId(jo);
+ row.tags = extractTags(jo);
row.count = jo.getDouble("value");
if (custom && row.metricName.startsWith("Camel")) {
@@ -164,7 +172,8 @@ public class ListMetric extends ProcessWatchCommand {
row.type = "distribution";
row.metricName = jo.getString("name");
row.metricDescription = jo.getString("description");
- row.metricRouteId = extractRouteId(jo);
+ row.metricId = extractId(jo);
+ row.tags = extractTags(jo);
row.count = jo.getDouble("value");
row.mean = jo.getDouble("mean");
row.max = jo.getDouble("max");
@@ -196,8 +205,8 @@ public class ListMetric extends ProcessWatchCommand {
new Column().header("TYPE").dataAlign(HorizontalAlign.LEFT).with(r -> r.type),
new Column().header("METRIC").dataAlign(HorizontalAlign.LEFT).maxWidth(40, OverflowBehaviour.ELLIPSIS_RIGHT)
.with(r -> r.metricName),
- new Column().header("ROUTE").dataAlign(HorizontalAlign.LEFT).maxWidth(25, OverflowBehaviour.ELLIPSIS_RIGHT)
- .with(r -> r.metricRouteId),
+ new Column().header("ID").dataAlign(HorizontalAlign.LEFT).maxWidth(40, OverflowBehaviour.ELLIPSIS_RIGHT)
+ .with(r -> r.metricId),
new Column().header("VALUE").headerAlign(HorizontalAlign.RIGHT).dataAlign(HorizontalAlign.RIGHT)
.with(r -> getNumber(r.count)),
new Column().header("MEAN").headerAlign(HorizontalAlign.RIGHT).dataAlign(HorizontalAlign.RIGHT)
@@ -205,7 +214,10 @@ public class ListMetric extends ProcessWatchCommand {
new Column().header("MAX").headerAlign(HorizontalAlign.RIGHT).dataAlign(HorizontalAlign.RIGHT)
.with(r -> getNumber(r.max)),
new Column().header("TOTAL").headerAlign(HorizontalAlign.RIGHT).dataAlign(HorizontalAlign.RIGHT)
- .with(r -> getNumber(r.total)))));
+ .with(r -> getNumber(r.total)),
+ new Column().header("TAGS").visible(tags).dataAlign(HorizontalAlign.LEFT)
+ .maxWidth(60, OverflowBehaviour.NEWLINE)
+ .with(r -> r.tags))));
}
return 0;
@@ -237,7 +249,7 @@ public class ListMetric extends ProcessWatchCommand {
// sort by metric/route
answer = o1.metricName.compareToIgnoreCase(o2.metricName) * negate;
if (answer == 0) {
- answer = o1.metricRouteId.compareToIgnoreCase(o2.metricRouteId) * negate;
+ answer = o1.metricId.compareToIgnoreCase(o2.metricId) * negate;
}
}
return answer;
@@ -253,19 +265,43 @@ public class ListMetric extends ProcessWatchCommand {
return s;
}
- private String extractRouteId(JsonObject jo) {
+ private String extractId(JsonObject jo) {
List<JsonObject> tags = jo.getCollection("tags");
+ String routeId = null;
+ String nodeId = null;
if (tags != null) {
for (JsonObject t : tags) {
String k = t.getString("key");
if ("routeId".equals(k)) {
- return t.getString("value");
+ routeId = t.getString("value");
+ } else if ("nodeId".equals(k)) {
+ nodeId = t.getString("value");
}
}
}
+ if (routeId != null && nodeId != null) {
+ return routeId + "/" + nodeId;
+ } else if (routeId != null) {
+ return routeId;
+ } else if (nodeId != null) {
+ return nodeId;
+ }
return "";
}
+ private String extractTags(JsonObject jo) {
+ StringJoiner sj = new StringJoiner(" ");
+ List<JsonObject> tags = jo.getCollection("tags");
+ if (tags != null) {
+ for (JsonObject t : tags) {
+ String k = t.getString("key");
+ String v = t.getString("value");
+ sj.add(k + "=" + v);
+ }
+ }
+ return sj.toString();
+ }
+
private static class Row implements Cloneable {
String pid;
String name;
@@ -274,7 +310,8 @@ public class ListMetric extends ProcessWatchCommand {
String type;
String metricName;
String metricDescription;
- String metricRouteId;
+ String metricId;
+ String tags;
double count;
double mean;
double max;