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 2024/03/06 15:03:43 UTC

(camel) branch main updated: Mm (#13393)

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 0fec0d45c09 Mm (#13393)
0fec0d45c09 is described below

commit 0fec0d45c09237402eaef0f5a766940a9bddbbda
Author: Claus Ibsen <cl...@gmail.com>
AuthorDate: Wed Mar 6 16:03:37 2024 +0100

    Mm (#13393)
    
    CAMEL-20461: camel-micrometer - Add statistics for context level
---
 .../main/camel-main-configuration-metadata.json    |   1 +
 .../prometheus/MicrometerPrometheusConfigurer.java |   6 ++
 .../prometheus/MicrometerPrometheus.java           |  25 +++++
 .../src/main/docs/micrometer-component.adoc        |   2 +
 .../micrometer/json/AbstractMicrometerService.java |   2 +-
 .../routepolicy/ContextMetricsStatistics.java      |  71 +++++++++++++
 .../routepolicy/MicrometerRoutePolicy.java         | 114 +++++++++++++++------
 .../MicrometerRoutePolicyConfiguration.java        |  18 ++++
 .../routepolicy/MicrometerRoutePolicyFactory.java  |  39 ++++++-
 .../MicrometerRoutePolicyNamingStrategy.java       |  23 ++++-
 .../micrometer/routepolicy/RouteMetric.java        |  29 ++++++
 .../messagehistory/ManagedMessageHistoryTest.java  |  11 +-
 .../AbstractMicrometerRoutePolicyTest.java         |  50 +++++----
 .../LegacyMicrometerRoutePolicyTest.java           |   1 +
 .../ManagedMicrometerRoutePolicyTest.java          |   5 +
 .../routepolicy/MicrometerContextPolicyTest.java   |  77 ++++++++++++++
 .../MicrometerRoutePolicyConfigrationTest.java     |   1 +
 .../SharedMicrometerRoutePolicyTest.java           |   2 +-
 ....java => ZMicrometerContextOnlyPolicyTest.java} |  60 +++++------
 .../MetricsConfigurationPropertiesConfigurer.java  |   6 ++
 .../camel-main-configuration-metadata.json         |   1 +
 core/camel-main/src/main/docs/main.adoc            |   3 +-
 .../camel/main/MetricsConfigurationProperties.java |  21 ++++
 .../ROOT/pages/camel-4x-upgrade-guide-4_5.adoc     |  16 +++
 .../jbang/core/commands/process/ListMetric.java    |   5 +-
 25 files changed, 493 insertions(+), 96 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 2c8368e3122..2d3a5c16711 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
@@ -182,6 +182,7 @@
     { "name": "camel.metrics.enableRouteEventNotifier", "description": "Set whether to enable the MicrometerRouteEventNotifier for capturing metrics on the total number of routes and total number of routes running.", "sourceType": "org.apache.camel.main.MetricsConfigurationProperties", "type": "boolean", "javaType": "boolean", "defaultValue": true },
     { "name": "camel.metrics.enableRoutePolicy", "description": "Set whether to enable the MicrometerRoutePolicyFactory for capturing metrics on route processing times.", "sourceType": "org.apache.camel.main.MetricsConfigurationProperties", "type": "boolean", "javaType": "boolean", "defaultValue": true },
     { "name": "camel.metrics.namingStrategy", "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": "string", "javaType": "java.lang.String", "defaultValue": "default", "enum": [ "default", "legacy" ] },
+    { "name": "camel.metrics.routePolicyLevel", "description": "Sets the level of information to capture. all = both context and routes.", "sourceType": "org.apache.camel.main.MetricsConfigurationProperties", "type": "string", "javaType": "java.lang.String", "defaultValue": "all", "enum": [ "all", "route", "context" ] },
     { "name": "camel.metrics.textFormatVersion", "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": "string", "javaType": "java.lang.String", "defaultValue": "0.0.4", "enum": [ "0.0.4", "1.0.0" ] },
     { "name": "camel.opentelemetry.enabled", "description": "To enable OpenTelemetry", "sourceType": "org.apache.camel.main.OtelConfigurationProperties", "type": "boolean", "javaType": "boolean", "defaultValue": "false" },
     { "name": "camel.opentelemetry.encoding", "description": "Sets whether the header keys need to be encoded (connector specific) or not. The value is a boolean. Dashes need for instances to be encoded for JMS property keys.", "sourceType": "org.apache.camel.main.OtelConfigurationProperties", "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 3466314c9cd..211bcde62d7 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
@@ -37,6 +37,8 @@ public class MicrometerPrometheusConfigurer extends org.apache.camel.support.com
         case "EnableRoutePolicy": target.setEnableRoutePolicy(property(camelContext, boolean.class, value)); return true;
         case "namingstrategy":
         case "NamingStrategy": target.setNamingStrategy(property(camelContext, java.lang.String.class, value)); return true;
+        case "routepolicylevel":
+        case "RoutePolicyLevel": target.setRoutePolicyLevel(property(camelContext, java.lang.String.class, value)); return true;
         case "textformatversion":
         case "TextFormatVersion": target.setTextFormatVersion(property(camelContext, java.lang.String.class, value)); return true;
         default: return false;
@@ -62,6 +64,8 @@ public class MicrometerPrometheusConfigurer extends org.apache.camel.support.com
         case "EnableRoutePolicy": return boolean.class;
         case "namingstrategy":
         case "NamingStrategy": return java.lang.String.class;
+        case "routepolicylevel":
+        case "RoutePolicyLevel": return java.lang.String.class;
         case "textformatversion":
         case "TextFormatVersion": return java.lang.String.class;
         default: return null;
@@ -88,6 +92,8 @@ public class MicrometerPrometheusConfigurer extends org.apache.camel.support.com
         case "EnableRoutePolicy": return target.isEnableRoutePolicy();
         case "namingstrategy":
         case "NamingStrategy": return target.getNamingStrategy();
+        case "routepolicylevel":
+        case "RoutePolicyLevel": return target.getRoutePolicyLevel();
         case "textformatversion":
         case "TextFormatVersion": return target.getTextFormatVersion();
         default: return null;
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 5ef866cdf95..7174f6c0831 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
@@ -84,6 +84,8 @@ public class MicrometerPrometheus extends ServiceSupport implements CamelMetrics
     private String namingStrategy;
     @Metadata(defaultValue = "true")
     private boolean enableRoutePolicy = true;
+    @Metadata(defaultValue = "all", enums = "all,route,context")
+    private String routePolicyLevel = "all";
     @Metadata(defaultValue = "false")
     private boolean enableMessageHistory;
     @Metadata(defaultValue = "true")
@@ -131,6 +133,17 @@ public class MicrometerPrometheus extends ServiceSupport implements CamelMetrics
         this.enableRoutePolicy = enableRoutePolicy;
     }
 
+    public String getRoutePolicyLevel() {
+        return routePolicyLevel;
+    }
+
+    /**
+     * Sets the level of information to capture. all = both context and routes.
+     */
+    public void setRoutePolicyLevel(String routePolicyLevel) {
+        this.routePolicyLevel = routePolicyLevel;
+    }
+
     public boolean isEnableMessageHistory() {
         return enableMessageHistory;
     }
@@ -240,7 +253,19 @@ public class MicrometerPrometheus extends ServiceSupport implements CamelMetrics
             if ("legacy".equalsIgnoreCase(namingStrategy)) {
                 factory.setNamingStrategy(MicrometerRoutePolicyNamingStrategy.LEGACY);
             }
+            if ("all".equalsIgnoreCase(routePolicyLevel)) {
+                factory.getPolicyConfiguration().setContextEnabled(true);
+                factory.getPolicyConfiguration().setRouteEnabled(true);
+            } else if ("context".equalsIgnoreCase(routePolicyLevel)) {
+                factory.getPolicyConfiguration().setContextEnabled(true);
+                factory.getPolicyConfiguration().setRouteEnabled(false);
+            } else {
+                factory.getPolicyConfiguration().setContextEnabled(false);
+                factory.getPolicyConfiguration().setRouteEnabled(true);
+            }
             factory.setMeterRegistry(meterRegistry);
+            // ensure factory will be started and stopped
+            camelContext.addService(factory);
             camelContext.addRoutePolicyFactory(factory);
         }
 
diff --git a/components/camel-micrometer/src/main/docs/micrometer-component.adoc b/components/camel-micrometer/src/main/docs/micrometer-component.adoc
index b296a54dd9e..45a0540c927 100644
--- a/components/camel-micrometer/src/main/docs/micrometer-component.adoc
+++ b/components/camel-micrometer/src/main/docs/micrometer-component.adoc
@@ -407,6 +407,8 @@ following options:
 [width="100%",options="header"]
 |=======================================================================
 |Name |Default |Description
+|contextEnabled | true | whether to include counter for context level metrics
+|routeEnabled | true | whether to include counter for route level metrics
 |additionalCounters | true | activates all additional counters
 |exchangesSucceeded | true | activates counter for succeeded exchanges
 |exchangesFailed | true | activates counter for failed exchanges
diff --git a/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/json/AbstractMicrometerService.java b/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/json/AbstractMicrometerService.java
index fcfff985ca3..574cd8bf596 100644
--- a/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/json/AbstractMicrometerService.java
+++ b/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/json/AbstractMicrometerService.java
@@ -137,6 +137,6 @@ public class AbstractMicrometerService extends ServiceSupport {
 
     @Override
     protected void doStop() {
-
+        // noop
     }
 }
diff --git a/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/routepolicy/ContextMetricsStatistics.java b/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/routepolicy/ContextMetricsStatistics.java
new file mode 100644
index 00000000000..1e39d8d683a
--- /dev/null
+++ b/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/routepolicy/ContextMetricsStatistics.java
@@ -0,0 +1,71 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.component.micrometer.routepolicy;
+
+import io.micrometer.core.instrument.MeterRegistry;
+import org.apache.camel.CamelContext;
+import org.apache.camel.Exchange;
+import org.apache.camel.spi.UnitOfWork;
+
+final class ContextMetricsStatistics extends MicrometerRoutePolicy.MetricsStatistics {
+
+    private final boolean registerKamelets;
+    private final boolean registerTemplates;
+
+    ContextMetricsStatistics(MeterRegistry meterRegistry, CamelContext camelContext,
+                             MicrometerRoutePolicyNamingStrategy namingStrategy,
+                             MicrometerRoutePolicyConfiguration configuration, boolean registerKamelets,
+                             boolean registerTemplates) {
+        super(meterRegistry, camelContext, null, namingStrategy, configuration);
+        this.registerKamelets = registerKamelets;
+        this.registerTemplates = registerTemplates;
+    }
+
+    @Override
+    public void onExchangeBegin(Exchange exchange) {
+        // this metric is triggered for every route
+        // so we must only trigger on the root level, otherwise this metric
+        // total counter will be incorrect. For example if an exchange is routed via 3 routes
+        // we should only count this as 1 instead of 3.
+        UnitOfWork uow = exchange.getUnitOfWork();
+        if (uow != null) {
+            int level = uow.routeStackLevel(registerTemplates, registerKamelets);
+            if (level <= 1) {
+                super.onExchangeBegin(exchange);
+            }
+        } else {
+            super.onExchangeBegin(exchange);
+        }
+    }
+
+    @Override
+    public void onExchangeDone(Exchange exchange) {
+        // this metric is triggered for every route
+        // so we must only trigger on the root level, otherwise this metric
+        // total counter will be incorrect. For example if an exchange is routed via 3 routes
+        // we should only count this as 1 instead of 3.
+        UnitOfWork uow = exchange.getUnitOfWork();
+        if (uow != null) {
+            int level = uow.routeStackLevel(registerTemplates, registerKamelets);
+            if (level <= 1) {
+                super.onExchangeDone(exchange);
+            }
+        } else {
+            super.onExchangeDone(exchange);
+        }
+    }
+}
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 74681d8797d..7df31be65bd 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
@@ -26,6 +26,7 @@ import io.micrometer.core.instrument.LongTaskTimer;
 import io.micrometer.core.instrument.MeterRegistry;
 import io.micrometer.core.instrument.Tags;
 import io.micrometer.core.instrument.Timer;
+import org.apache.camel.CamelContext;
 import org.apache.camel.Exchange;
 import org.apache.camel.NonManagedService;
 import org.apache.camel.Route;
@@ -48,6 +49,7 @@ import static org.apache.camel.component.micrometer.MicrometerConstants.SERVICE_
  */
 public class MicrometerRoutePolicy extends RoutePolicySupport implements NonManagedService {
 
+    private final MicrometerRoutePolicyFactory factory;
     private MeterRegistry meterRegistry;
     private boolean prettyPrint;
     private TimeUnit durationUnit = TimeUnit.MILLISECONDS;
@@ -55,11 +57,13 @@ public class MicrometerRoutePolicy extends RoutePolicySupport implements NonMana
     private MicrometerRoutePolicyConfiguration configuration = MicrometerRoutePolicyConfiguration.DEFAULT;
 
     private final Map<Route, MetricsStatistics> statisticsMap = new HashMap<>();
+    private RouteMetric contextStatistic;
     boolean registerKamelets;
     boolean registerTemplates = true;
 
-    private static final class MetricsStatistics {
+    static class MetricsStatistics implements RouteMetric {
         private final MeterRegistry meterRegistry;
+        private final CamelContext camelContext;
         private final Route route;
         private final MicrometerRoutePolicyNamingStrategy namingStrategy;
         private final MicrometerRoutePolicyConfiguration configuration;
@@ -71,12 +75,13 @@ public class MicrometerRoutePolicy extends RoutePolicySupport implements NonMana
         private Timer timer;
         private LongTaskTimer longTaskTimer;
 
-        private MetricsStatistics(MeterRegistry meterRegistry, Route route,
-                                  MicrometerRoutePolicyNamingStrategy namingStrategy,
-                                  MicrometerRoutePolicyConfiguration configuration) {
+        MetricsStatistics(MeterRegistry meterRegistry, CamelContext camelContext, Route route,
+                          MicrometerRoutePolicyNamingStrategy namingStrategy,
+                          MicrometerRoutePolicyConfiguration configuration) {
             this.configuration = ObjectHelper.notNull(configuration, "MicrometerRoutePolicyConfiguration", this);
             this.meterRegistry = ObjectHelper.notNull(meterRegistry, "MeterRegistry", this);
             this.namingStrategy = ObjectHelper.notNull(namingStrategy, "MicrometerRoutePolicyNamingStrategy", this);
+            this.camelContext = camelContext;
             this.route = route;
             if (configuration.isAdditionalCounters()) {
                 initAdditionalCounters();
@@ -106,8 +111,8 @@ public class MicrometerRoutePolicy extends RoutePolicySupport implements NonMana
             }
             if (configuration.isLongTask()) {
                 LongTaskTimer.Builder builder = LongTaskTimer.builder(namingStrategy.getLongTaskName(route))
-                        .tags(namingStrategy.getTags(route))
-                        .description("Route long task metric");
+                        .tags(route != null ? namingStrategy.getTags(route) : namingStrategy.getTags(camelContext))
+                        .description(route != null ? "Route long task metric" : "CamelContext long task metric");
                 if (configuration.getLongTaskInitiator() != null) {
                     configuration.getLongTaskInitiator().accept(builder);
                 }
@@ -128,8 +133,8 @@ public class MicrometerRoutePolicy extends RoutePolicySupport implements NonMana
             if (sample != null) {
                 if (timer == null) {
                     Timer.Builder builder = Timer.builder(namingStrategy.getName(route))
-                            .tags(namingStrategy.getTags(route))
-                            .description("Route performance metrics");
+                            .tags(route != null ? namingStrategy.getTags(route) : namingStrategy.getTags(camelContext))
+                            .description(route != null ? "Route performance metrics" : "CamelContext performance metrics");
                     if (configuration.getTimerInitiator() != null) {
                         configuration.getTimerInitiator().accept(builder);
                     }
@@ -147,6 +152,30 @@ public class MicrometerRoutePolicy extends RoutePolicySupport implements NonMana
             }
         }
 
+        public void remove() {
+            if (exchangesSucceeded != null) {
+                meterRegistry.remove(exchangesSucceeded);
+            }
+            if (exchangesFailed != null) {
+                meterRegistry.remove(exchangesFailed);
+            }
+            if (exchangesTotal != null) {
+                meterRegistry.remove(exchangesTotal);
+            }
+            if (externalRedeliveries != null) {
+                meterRegistry.remove(externalRedeliveries);
+            }
+            if (failuresHandled != null) {
+                meterRegistry.remove(failuresHandled);
+            }
+            if (timer != null) {
+                meterRegistry.remove(timer);
+            }
+            if (longTaskTimer != null) {
+                meterRegistry.remove(longTaskTimer);
+            }
+        }
+
         private void updateAdditionalCounters(Exchange exchange) {
             if (exchangesTotal != null) {
                 exchangesTotal.increment();
@@ -169,17 +198,32 @@ public class MicrometerRoutePolicy extends RoutePolicySupport implements NonMana
         }
 
         private String propertyName(Exchange exchange) {
-            return String.format("%s-%s-%s", DEFAULT_CAMEL_ROUTE_POLICY_METER_NAME, route.getId(), exchange.getExchangeId());
+            String id;
+            if (route != null) {
+                id = route.getId();
+            } else {
+                id = "context:" + camelContext.getName();
+            }
+            return String.format("%s-%s-%s", DEFAULT_CAMEL_ROUTE_POLICY_METER_NAME, id, exchange.getExchangeId());
         }
 
         private Counter createCounter(String meterName, String description) {
             return Counter.builder(meterName)
-                    .tags(namingStrategy.getExchangeStatusTags(route))
+                    .tags(route != null
+                            ? namingStrategy.getExchangeStatusTags(route) : namingStrategy.getExchangeStatusTags(camelContext))
                     .description(description)
                     .register(meterRegistry);
         }
     }
 
+    public MicrometerRoutePolicy() {
+        this.factory = null;
+    }
+
+    public MicrometerRoutePolicy(MicrometerRoutePolicyFactory factory) {
+        this.factory = factory;
+    }
+
     public MeterRegistry getMeterRegistry() {
         return meterRegistry;
     }
@@ -249,6 +293,18 @@ public class MicrometerRoutePolicy extends RoutePolicySupport implements NonMana
         } catch (Exception e) {
             throw RuntimeCamelException.wrapRuntimeCamelException(e);
         }
+
+        if (factory != null && configuration.isContextEnabled() && contextStatistic == null) {
+            contextStatistic = factory.createOrGetContextMetric(this);
+        }
+    }
+
+    boolean isRegisterKamelets() {
+        return registerKamelets;
+    }
+
+    boolean isRegisterTemplates() {
+        return registerTemplates;
     }
 
     @Override
@@ -258,13 +314,17 @@ public class MicrometerRoutePolicy extends RoutePolicySupport implements NonMana
         // we have in-flight / total statistics already from camel-core
         statisticsMap.computeIfAbsent(route,
                 it -> {
+                    boolean skip = !configuration.isRouteEnabled();
                     // skip routes that should not be included
-                    boolean skip = (it.isCreatedByKamelet() && !registerKamelets)
-                            || (it.isCreatedByRouteTemplate() && !registerTemplates);
+                    if (!skip) {
+                        skip = (it.isCreatedByKamelet() && !registerKamelets)
+                                || (it.isCreatedByRouteTemplate() && !registerTemplates);
+                    }
                     if (skip) {
                         return null;
                     }
-                    return new MetricsStatistics(getMeterRegistry(), it, getNamingStrategy(), configuration);
+                    return new MetricsStatistics(
+                            getMeterRegistry(), it.getCamelContext(), it, getNamingStrategy(), configuration);
                 });
     }
 
@@ -273,38 +333,24 @@ public class MicrometerRoutePolicy extends RoutePolicySupport implements NonMana
         // 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);
-            }
+            stats.remove();
         }
     }
 
     @Override
     public void onExchangeBegin(Route route, Exchange exchange) {
+        if (contextStatistic != null) {
+            contextStatistic.onExchangeBegin(exchange);
+        }
         Optional.ofNullable(statisticsMap.get(route))
                 .ifPresent(statistics -> statistics.onExchangeBegin(exchange));
     }
 
     @Override
     public void onExchangeDone(Route route, Exchange exchange) {
+        if (contextStatistic != null) {
+            contextStatistic.onExchangeDone(exchange);
+        }
         Optional.ofNullable(statisticsMap.get(route))
                 .ifPresent(statistics -> statistics.onExchangeDone(exchange));
     }
diff --git a/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/routepolicy/MicrometerRoutePolicyConfiguration.java b/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/routepolicy/MicrometerRoutePolicyConfiguration.java
index 1c1f57966e5..6b1f9eca8be 100644
--- a/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/routepolicy/MicrometerRoutePolicyConfiguration.java
+++ b/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/routepolicy/MicrometerRoutePolicyConfiguration.java
@@ -23,6 +23,8 @@ import io.micrometer.core.instrument.Timer;
 
 public class MicrometerRoutePolicyConfiguration {
     public static final MicrometerRoutePolicyConfiguration DEFAULT = new MicrometerRoutePolicyConfiguration();
+    private boolean contextEnabled = true;
+    private boolean routeEnabled = true;
     private boolean additionalCounters = true;
     private boolean exchangesSucceeded = true;
     private boolean exchangesFailed = true;
@@ -33,6 +35,22 @@ public class MicrometerRoutePolicyConfiguration {
     private Consumer<Timer.Builder> timerInitiator;
     private Consumer<LongTaskTimer.Builder> longTaskInitiator;
 
+    public boolean isContextEnabled() {
+        return contextEnabled;
+    }
+
+    public void setContextEnabled(boolean contextEnabled) {
+        this.contextEnabled = contextEnabled;
+    }
+
+    public boolean isRouteEnabled() {
+        return routeEnabled;
+    }
+
+    public void setRouteEnabled(boolean routeEnabled) {
+        this.routeEnabled = routeEnabled;
+    }
+
     public boolean isAdditionalCounters() {
         return additionalCounters;
     }
diff --git a/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/routepolicy/MicrometerRoutePolicyFactory.java b/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/routepolicy/MicrometerRoutePolicyFactory.java
index dde82239a9f..446b1de8034 100644
--- a/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/routepolicy/MicrometerRoutePolicyFactory.java
+++ b/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/routepolicy/MicrometerRoutePolicyFactory.java
@@ -20,22 +20,39 @@ import java.util.concurrent.TimeUnit;
 
 import io.micrometer.core.instrument.MeterRegistry;
 import org.apache.camel.CamelContext;
+import org.apache.camel.CamelContextAware;
 import org.apache.camel.NamedNode;
+import org.apache.camel.NonManagedService;
+import org.apache.camel.StaticService;
 import org.apache.camel.spi.RoutePolicy;
 import org.apache.camel.spi.RoutePolicyFactory;
+import org.apache.camel.support.service.ServiceSupport;
 
 /**
  * A {@link org.apache.camel.spi.RoutePolicyFactory} to plugin and use metrics for gathering route utilization
  * statistics
  */
-public class MicrometerRoutePolicyFactory implements RoutePolicyFactory {
+public class MicrometerRoutePolicyFactory extends ServiceSupport
+        implements RoutePolicyFactory, CamelContextAware, NonManagedService, StaticService {
 
+    private CamelContext camelContext;
     private MeterRegistry meterRegistry;
+    private RouteMetric contextMetric;
     private boolean prettyPrint = true;
     private TimeUnit durationUnit = TimeUnit.MILLISECONDS;
     private MicrometerRoutePolicyNamingStrategy namingStrategy = MicrometerRoutePolicyNamingStrategy.DEFAULT;
     private MicrometerRoutePolicyConfiguration policyConfiguration = MicrometerRoutePolicyConfiguration.DEFAULT;
 
+    @Override
+    public CamelContext getCamelContext() {
+        return camelContext;
+    }
+
+    @Override
+    public void setCamelContext(CamelContext camelContext) {
+        this.camelContext = camelContext;
+    }
+
     /**
      * To use a specific {@link io.micrometer.core.instrument.MeterRegistry} instance.
      * <p/>
@@ -90,9 +107,19 @@ public class MicrometerRoutePolicyFactory implements RoutePolicyFactory {
         this.policyConfiguration = policyConfiguration;
     }
 
+    public RouteMetric createOrGetContextMetric(MicrometerRoutePolicy policy) {
+        if (contextMetric == null) {
+            contextMetric = new ContextMetricsStatistics(
+                    policy.getMeterRegistry(), camelContext,
+                    policy.getNamingStrategy(), policy.getConfiguration(),
+                    policy.isRegisterKamelets(), policy.isRegisterTemplates());
+        }
+        return contextMetric;
+    }
+
     @Override
     public RoutePolicy createRoutePolicy(CamelContext camelContext, String routeId, NamedNode routeDefinition) {
-        MicrometerRoutePolicy answer = new MicrometerRoutePolicy();
+        MicrometerRoutePolicy answer = new MicrometerRoutePolicy(this);
         answer.setMeterRegistry(getMeterRegistry());
         answer.setPrettyPrint(isPrettyPrint());
         answer.setDurationUnit(getDurationUnit());
@@ -101,4 +128,12 @@ public class MicrometerRoutePolicyFactory implements RoutePolicyFactory {
         return answer;
     }
 
+    @Override
+    protected void doShutdown() throws Exception {
+        super.doShutdown();
+        if (contextMetric != null) {
+            contextMetric.remove();
+            contextMetric = null;
+        }
+    }
 }
diff --git a/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/routepolicy/MicrometerRoutePolicyNamingStrategy.java b/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/routepolicy/MicrometerRoutePolicyNamingStrategy.java
index f3e05e5e534..3ec3d138897 100644
--- a/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/routepolicy/MicrometerRoutePolicyNamingStrategy.java
+++ b/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/routepolicy/MicrometerRoutePolicyNamingStrategy.java
@@ -20,6 +20,7 @@ import java.util.function.Predicate;
 
 import io.micrometer.core.instrument.Meter;
 import io.micrometer.core.instrument.Tags;
+import org.apache.camel.CamelContext;
 import org.apache.camel.Route;
 import org.apache.camel.component.micrometer.MicrometerUtils;
 
@@ -87,13 +88,31 @@ public interface MicrometerRoutePolicyNamingStrategy {
         return Tags.of(
                 CAMEL_CONTEXT_TAG, route.getCamelContext().getName(),
                 SERVICE_NAME, MicrometerRoutePolicyService.class.getSimpleName(),
-                ROUTE_ID_TAG, route.getId());
+                ROUTE_ID_TAG, route.getId(),
+                EVENT_TYPE_TAG, "route");
+    }
+
+    default Tags getTags(CamelContext camelContext) {
+        return Tags.of(
+                CAMEL_CONTEXT_TAG, camelContext.getName(),
+                SERVICE_NAME, MicrometerRoutePolicyService.class.getSimpleName(),
+                ROUTE_ID_TAG, "",
+                EVENT_TYPE_TAG, "context");
     }
 
     default Tags getExchangeStatusTags(Route route) {
         return Tags.of(
                 CAMEL_CONTEXT_TAG, route.getCamelContext().getName(),
                 SERVICE_NAME, MicrometerRoutePolicyService.class.getSimpleName(),
-                ROUTE_ID_TAG, route.getId());
+                ROUTE_ID_TAG, route.getId(),
+                EVENT_TYPE_TAG, "route");
+    }
+
+    default Tags getExchangeStatusTags(CamelContext camelContext) {
+        return Tags.of(
+                CAMEL_CONTEXT_TAG, camelContext.getName(),
+                SERVICE_NAME, MicrometerRoutePolicyService.class.getSimpleName(),
+                ROUTE_ID_TAG, "",
+                EVENT_TYPE_TAG, "context");
     }
 }
diff --git a/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/routepolicy/RouteMetric.java b/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/routepolicy/RouteMetric.java
new file mode 100644
index 00000000000..89d0b1f5cc9
--- /dev/null
+++ b/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/routepolicy/RouteMetric.java
@@ -0,0 +1,29 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.component.micrometer.routepolicy;
+
+import org.apache.camel.Exchange;
+
+public interface RouteMetric {
+
+    void onExchangeBegin(Exchange exchange);
+
+    void onExchangeDone(Exchange exchange);
+
+    void remove();
+
+}
diff --git a/components/camel-micrometer/src/test/java/org/apache/camel/component/micrometer/messagehistory/ManagedMessageHistoryTest.java b/components/camel-micrometer/src/test/java/org/apache/camel/component/micrometer/messagehistory/ManagedMessageHistoryTest.java
index 7125f9d5ab8..030b6a6cd08 100644
--- a/components/camel-micrometer/src/test/java/org/apache/camel/component/micrometer/messagehistory/ManagedMessageHistoryTest.java
+++ b/components/camel-micrometer/src/test/java/org/apache/camel/component/micrometer/messagehistory/ManagedMessageHistoryTest.java
@@ -63,6 +63,16 @@ public class ManagedMessageHistoryTest extends CamelTestSupport {
         meterRegistry.add(new JmxMeterRegistry(CamelJmxConfig.DEFAULT, Clock.SYSTEM, HierarchicalNameMapper.DEFAULT));
     }
 
+    @Override
+    public void tearDown() throws Exception {
+        super.tearDown();
+        if (meterRegistry != null) {
+            meterRegistry.clear();
+            meterRegistry.close();
+            meterRegistry = null;
+        }
+    }
+
     @Override
     protected CamelContext createCamelContext() throws Exception {
         CamelContext context = super.createCamelContext();
@@ -71,7 +81,6 @@ public class ManagedMessageHistoryTest extends CamelTestSupport {
         factory.setPrettyPrint(true);
         factory.setMeterRegistry(meterRegistry);
         context.setMessageHistoryFactory(factory);
-
         return context;
     }
 
diff --git a/components/camel-micrometer/src/test/java/org/apache/camel/component/micrometer/routepolicy/AbstractMicrometerRoutePolicyTest.java b/components/camel-micrometer/src/test/java/org/apache/camel/component/micrometer/routepolicy/AbstractMicrometerRoutePolicyTest.java
index feb1dd369c5..a144ce2b24a 100644
--- a/components/camel-micrometer/src/test/java/org/apache/camel/component/micrometer/routepolicy/AbstractMicrometerRoutePolicyTest.java
+++ b/components/camel-micrometer/src/test/java/org/apache/camel/component/micrometer/routepolicy/AbstractMicrometerRoutePolicyTest.java
@@ -16,15 +16,11 @@
  */
 package org.apache.camel.component.micrometer.routepolicy;
 
-import io.micrometer.core.instrument.Clock;
-import io.micrometer.core.instrument.composite.CompositeMeterRegistry;
+import io.micrometer.core.instrument.MeterRegistry;
 import io.micrometer.core.instrument.simple.SimpleMeterRegistry;
-import io.micrometer.core.instrument.util.HierarchicalNameMapper;
-import io.micrometer.jmx.JmxMeterRegistry;
-import org.apache.camel.BindToRegistry;
 import org.apache.camel.CamelContext;
-import org.apache.camel.component.micrometer.CamelJmxConfig;
 import org.apache.camel.component.micrometer.MicrometerConstants;
+import org.apache.camel.support.LifecycleStrategySupport;
 import org.apache.camel.test.junit5.CamelTestSupport;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -32,23 +28,22 @@ import org.slf4j.LoggerFactory;
 public abstract class AbstractMicrometerRoutePolicyTest extends CamelTestSupport {
 
     protected final Logger log = LoggerFactory.getLogger(getClass());
-    protected CompositeMeterRegistry meterRegistry;
+    protected MeterRegistry meterRegistry;
 
-    @Override
-    protected boolean useJmx() {
-        return true;
-    }
-
-    @BindToRegistry(MicrometerConstants.METRICS_REGISTRY_NAME)
-    public CompositeMeterRegistry addRegistry() {
-        meterRegistry = new CompositeMeterRegistry();
-        meterRegistry.add(new SimpleMeterRegistry());
-        meterRegistry.add(new JmxMeterRegistry(CamelJmxConfig.DEFAULT, Clock.SYSTEM, HierarchicalNameMapper.DEFAULT));
-        return meterRegistry;
+    protected MicrometerRoutePolicyFactory createMicrometerRoutePolicyFactory() {
+        MicrometerRoutePolicyFactory factory = new MicrometerRoutePolicyFactory();
+        factory.getPolicyConfiguration().setContextEnabled(false);
+        return factory;
     }
 
-    protected MicrometerRoutePolicyFactory createMicrometerRoutePolicyFactory() {
-        return new MicrometerRoutePolicyFactory();
+    @Override
+    public void tearDown() throws Exception {
+        super.tearDown();
+        if (meterRegistry != null) {
+            meterRegistry.clear();
+            meterRegistry.close();
+            meterRegistry = null;
+        }
     }
 
     protected String formatMetricName(String name) {
@@ -57,10 +52,25 @@ public abstract class AbstractMicrometerRoutePolicyTest extends CamelTestSupport
 
     @Override
     protected CamelContext createCamelContext() throws Exception {
+        meterRegistry = new SimpleMeterRegistry();
         CamelContext context = super.createCamelContext();
         MicrometerRoutePolicyFactory factory = createMicrometerRoutePolicyFactory();
+        factory.setCamelContext(context);
         factory.setMeterRegistry(meterRegistry);
         context.addRoutePolicyFactory(factory);
+        context.getRegistry().bind(MicrometerConstants.METRICS_REGISTRY_NAME, meterRegistry);
+        context.addService(factory);
+        // TODO: CAMEL-20522
+        context.addLifecycleStrategy(new LifecycleStrategySupport() {
+            @Override
+            public void onContextStopped(CamelContext context) {
+                if (meterRegistry != null) {
+                    meterRegistry.clear();
+                    meterRegistry.close();
+                    meterRegistry = null;
+                }
+            }
+        });
         return context;
     }
 
diff --git a/components/camel-micrometer/src/test/java/org/apache/camel/component/micrometer/routepolicy/LegacyMicrometerRoutePolicyTest.java b/components/camel-micrometer/src/test/java/org/apache/camel/component/micrometer/routepolicy/LegacyMicrometerRoutePolicyTest.java
index f3989e47bd2..ed49b797a41 100644
--- a/components/camel-micrometer/src/test/java/org/apache/camel/component/micrometer/routepolicy/LegacyMicrometerRoutePolicyTest.java
+++ b/components/camel-micrometer/src/test/java/org/apache/camel/component/micrometer/routepolicy/LegacyMicrometerRoutePolicyTest.java
@@ -26,6 +26,7 @@ public class LegacyMicrometerRoutePolicyTest extends MicrometerRoutePolicyTest {
     @Override
     protected MicrometerRoutePolicyFactory createMicrometerRoutePolicyFactory() {
         MicrometerRoutePolicyFactory factory = new MicrometerRoutePolicyFactory();
+        factory.getPolicyConfiguration().setContextEnabled(false);
         factory.setNamingStrategy(MicrometerRoutePolicyNamingStrategy.LEGACY);
         return factory;
     }
diff --git a/components/camel-micrometer/src/test/java/org/apache/camel/component/micrometer/routepolicy/ManagedMicrometerRoutePolicyTest.java b/components/camel-micrometer/src/test/java/org/apache/camel/component/micrometer/routepolicy/ManagedMicrometerRoutePolicyTest.java
index 634ce915819..399655f58d0 100644
--- a/components/camel-micrometer/src/test/java/org/apache/camel/component/micrometer/routepolicy/ManagedMicrometerRoutePolicyTest.java
+++ b/components/camel-micrometer/src/test/java/org/apache/camel/component/micrometer/routepolicy/ManagedMicrometerRoutePolicyTest.java
@@ -33,6 +33,11 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
 
 public class ManagedMicrometerRoutePolicyTest extends AbstractMicrometerRoutePolicyTest {
 
+    @Override
+    protected boolean useJmx() {
+        return true;
+    }
+
     protected MBeanServer getMBeanServer() {
         return context.getManagementStrategy().getManagementAgent().getMBeanServer();
     }
diff --git a/components/camel-micrometer/src/test/java/org/apache/camel/component/micrometer/routepolicy/MicrometerContextPolicyTest.java b/components/camel-micrometer/src/test/java/org/apache/camel/component/micrometer/routepolicy/MicrometerContextPolicyTest.java
new file mode 100644
index 00000000000..5ed646ffd70
--- /dev/null
+++ b/components/camel-micrometer/src/test/java/org/apache/camel/component/micrometer/routepolicy/MicrometerContextPolicyTest.java
@@ -0,0 +1,77 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.component.micrometer.routepolicy;
+
+import io.micrometer.core.instrument.Timer;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.junit.jupiter.api.Test;
+
+import static org.apache.camel.component.micrometer.MicrometerConstants.DEFAULT_CAMEL_ROUTE_POLICY_METER_NAME;
+import static org.apache.camel.component.micrometer.MicrometerConstants.EVENT_TYPE_TAG;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class MicrometerContextPolicyTest extends AbstractMicrometerRoutePolicyTest {
+
+    private static final long DELAY_FOO = 20;
+    private static final long DELAY_BAR = 50;
+
+    @Override
+    protected MicrometerRoutePolicyFactory createMicrometerRoutePolicyFactory() {
+        MicrometerRoutePolicyFactory factory = super.createMicrometerRoutePolicyFactory();
+        factory.getPolicyConfiguration().setContextEnabled(true);
+        return factory;
+    }
+
+    @Test
+    public void testMetricsRoutePolicy() throws Exception {
+        int count = 10;
+        MockEndpoint mockEndpoint = getMockEndpoint("mock:result");
+        mockEndpoint.expectedMessageCount(count);
+
+        for (int i = 0; i < count; i++) {
+            if (i % 2 == 0) {
+                template.sendBody("direct:foo", "Hello " + i);
+            } else {
+                template.sendBody("direct:bar", "Hello " + i);
+            }
+        }
+
+        MockEndpoint.assertIsSatisfied(context);
+
+        Timer contextTimer
+                = meterRegistry.find(formatMetricName(DEFAULT_CAMEL_ROUTE_POLICY_METER_NAME)).tag(EVENT_TYPE_TAG, "context")
+                        .timer();
+        assertEquals(count, contextTimer.count());
+    }
+
+    @Override
+    protected RouteBuilder createRouteBuilder() {
+        return new RouteBuilder() {
+            @Override
+            public void configure() {
+                from("direct:foo").routeId("foo")
+                        .delay(DELAY_FOO)
+                        .to("mock:result");
+
+                from("direct:bar").routeId("bar")
+                        .delay(DELAY_BAR)
+                        .to("mock:result");
+            }
+        };
+    }
+}
diff --git a/components/camel-micrometer/src/test/java/org/apache/camel/component/micrometer/routepolicy/MicrometerRoutePolicyConfigrationTest.java b/components/camel-micrometer/src/test/java/org/apache/camel/component/micrometer/routepolicy/MicrometerRoutePolicyConfigrationTest.java
index db6d8d05aeb..a408b84862d 100644
--- a/components/camel-micrometer/src/test/java/org/apache/camel/component/micrometer/routepolicy/MicrometerRoutePolicyConfigrationTest.java
+++ b/components/camel-micrometer/src/test/java/org/apache/camel/component/micrometer/routepolicy/MicrometerRoutePolicyConfigrationTest.java
@@ -32,6 +32,7 @@ public class MicrometerRoutePolicyConfigrationTest extends AbstractMicrometerRou
     protected MicrometerRoutePolicyFactory createMicrometerRoutePolicyFactory() {
         MicrometerRoutePolicyFactory factory = new MicrometerRoutePolicyFactory();
         MicrometerRoutePolicyConfiguration policyConfiguration = new MicrometerRoutePolicyConfiguration();
+        policyConfiguration.setContextEnabled(false);
         policyConfiguration.setExchangesSucceeded(false);
         policyConfiguration.setExchangesFailed(false);
         policyConfiguration.setExchangesTotal(false);
diff --git a/components/camel-micrometer/src/test/java/org/apache/camel/component/micrometer/routepolicy/SharedMicrometerRoutePolicyTest.java b/components/camel-micrometer/src/test/java/org/apache/camel/component/micrometer/routepolicy/SharedMicrometerRoutePolicyTest.java
index 966382beb48..536b82204de 100644
--- a/components/camel-micrometer/src/test/java/org/apache/camel/component/micrometer/routepolicy/SharedMicrometerRoutePolicyTest.java
+++ b/components/camel-micrometer/src/test/java/org/apache/camel/component/micrometer/routepolicy/SharedMicrometerRoutePolicyTest.java
@@ -34,7 +34,7 @@ public class SharedMicrometerRoutePolicyTest extends CamelTestSupport {
 
     protected MeterRegistry meterRegistry = new SimpleMeterRegistry();
 
-    protected MicrometerRoutePolicy singletonPolicy = new MicrometerRoutePolicy();
+    protected MicrometerRoutePolicy singletonPolicy = new MicrometerRoutePolicy(null);
 
     @Test
     public void testSharedPolicy() throws Exception {
diff --git a/components/camel-micrometer/src/test/java/org/apache/camel/component/micrometer/routepolicy/ManagedMicrometerRoutePolicyTest.java b/components/camel-micrometer/src/test/java/org/apache/camel/component/micrometer/routepolicy/ZMicrometerContextOnlyPolicyTest.java
similarity index 50%
copy from components/camel-micrometer/src/test/java/org/apache/camel/component/micrometer/routepolicy/ManagedMicrometerRoutePolicyTest.java
copy to components/camel-micrometer/src/test/java/org/apache/camel/component/micrometer/routepolicy/ZMicrometerContextOnlyPolicyTest.java
index 634ce915819..87941faddb4 100644
--- a/components/camel-micrometer/src/test/java/org/apache/camel/component/micrometer/routepolicy/ManagedMicrometerRoutePolicyTest.java
+++ b/components/camel-micrometer/src/test/java/org/apache/camel/component/micrometer/routepolicy/ZMicrometerContextOnlyPolicyTest.java
@@ -16,56 +16,51 @@
  */
 package org.apache.camel.component.micrometer.routepolicy;
 
-import java.util.List;
-
-import javax.management.MBeanServer;
-import javax.management.ObjectName;
-
-import io.micrometer.core.instrument.Meter;
+import io.micrometer.core.instrument.Timer;
 import org.apache.camel.builder.RouteBuilder;
 import org.apache.camel.component.mock.MockEndpoint;
 import org.junit.jupiter.api.Test;
 
+import static org.apache.camel.component.micrometer.MicrometerConstants.DEFAULT_CAMEL_ROUTE_POLICY_METER_NAME;
+import static org.apache.camel.component.micrometer.MicrometerConstants.EVENT_TYPE_TAG;
 import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertFalse;
-import static org.junit.jupiter.api.Assertions.assertNotNull;
-import static org.junit.jupiter.api.Assertions.assertTrue;
 
-public class ManagedMicrometerRoutePolicyTest extends AbstractMicrometerRoutePolicyTest {
+/**
+ * Must run last
+ */
+public class ZMicrometerContextOnlyPolicyTest extends AbstractMicrometerRoutePolicyTest {
+
+    private static final long DELAY_FOO = 20;
+    private static final long DELAY_BAR = 50;
 
-    protected MBeanServer getMBeanServer() {
-        return context.getManagementStrategy().getManagementAgent().getMBeanServer();
+    @Override
+    protected MicrometerRoutePolicyFactory createMicrometerRoutePolicyFactory() {
+        MicrometerRoutePolicyFactory factory = super.createMicrometerRoutePolicyFactory();
+        factory.getPolicyConfiguration().setContextEnabled(true);
+        factory.getPolicyConfiguration().setRouteEnabled(false);
+        return factory;
     }
 
     @Test
     public void testMetricsRoutePolicy() throws Exception {
         int count = 10;
-        getMockEndpoint("mock:result").expectedMessageCount(count);
+        MockEndpoint mockEndpoint = getMockEndpoint("mock:result");
+        mockEndpoint.expectedMessageCount(count);
 
         for (int i = 0; i < count; i++) {
             if (i % 2 == 0) {
-                template.sendBody("seda:foo", "Hello " + i);
+                template.sendBody("direct:foo", "Hello " + i);
             } else {
-                template.sendBody("seda:bar", "Hello " + i);
+                template.sendBody("direct:bar", "Hello " + i);
             }
         }
 
         MockEndpoint.assertIsSatisfied(context);
 
-        // there should be 13 names
-        List<Meter> meters = meterRegistry.getMeters();
-        assertEquals(13, meters.size());
-
-        String name = String.format("org.apache.camel:context=%s,type=services,name=MicrometerRoutePolicyService",
-                context.getManagementName());
-        ObjectName on = ObjectName.getInstance(name);
-        String json = (String) getMBeanServer().invoke(on, "dumpStatisticsAsJson", null, null);
-        assertNotNull(json);
-        log.info(json);
-
-        assertFalse(json.contains("\"name\" : \"test\""));  // the MicrometerRoutePolicy does NOT display producer metrics
-        assertTrue(json.contains("\"routeId\" : \"bar\""));
-        assertTrue(json.contains("\"routeId\" : \"foo\""));
+        Timer contextTimer
+                = meterRegistry.find(formatMetricName(DEFAULT_CAMEL_ROUTE_POLICY_METER_NAME)).tag(EVENT_TYPE_TAG, "context")
+                        .timer();
+        assertEquals(count, contextTimer.count());
     }
 
     @Override
@@ -73,11 +68,12 @@ public class ManagedMicrometerRoutePolicyTest extends AbstractMicrometerRoutePol
         return new RouteBuilder() {
             @Override
             public void configure() {
-                from("seda:foo").routeId("foo")
-                        .to("micrometer:counter:test")
+                from("direct:foo").routeId("foo")
+                        .delay(DELAY_FOO)
                         .to("mock:result");
 
-                from("seda:bar").routeId("bar")
+                from("direct:bar").routeId("bar")
+                        .delay(DELAY_BAR)
                         .to("mock:result");
             }
         };
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 59fd581efe0..a035e5cc26c 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,8 @@ public class MetricsConfigurationPropertiesConfigurer extends org.apache.camel.s
         case "Enabled": target.setEnabled(property(camelContext, boolean.class, value)); return true;
         case "namingstrategy":
         case "NamingStrategy": target.setNamingStrategy(property(camelContext, java.lang.String.class, value)); return true;
+        case "routepolicylevel":
+        case "RoutePolicyLevel": target.setRoutePolicyLevel(property(camelContext, java.lang.String.class, value)); return true;
         case "textformatversion":
         case "TextFormatVersion": target.setTextFormatVersion(property(camelContext, java.lang.String.class, value)); return true;
         default: return false;
@@ -62,6 +64,8 @@ public class MetricsConfigurationPropertiesConfigurer extends org.apache.camel.s
         case "Enabled": return boolean.class;
         case "namingstrategy":
         case "NamingStrategy": return java.lang.String.class;
+        case "routepolicylevel":
+        case "RoutePolicyLevel": return java.lang.String.class;
         case "textformatversion":
         case "TextFormatVersion": return java.lang.String.class;
         default: return null;
@@ -88,6 +92,8 @@ public class MetricsConfigurationPropertiesConfigurer extends org.apache.camel.s
         case "Enabled": return target.isEnabled();
         case "namingstrategy":
         case "NamingStrategy": return target.getNamingStrategy();
+        case "routepolicylevel":
+        case "RoutePolicyLevel": return target.getRoutePolicyLevel();
         case "textformatversion":
         case "TextFormatVersion": return target.getTextFormatVersion();
         default: return null;
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 2c8368e3122..2d3a5c16711 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
@@ -182,6 +182,7 @@
     { "name": "camel.metrics.enableRouteEventNotifier", "description": "Set whether to enable the MicrometerRouteEventNotifier for capturing metrics on the total number of routes and total number of routes running.", "sourceType": "org.apache.camel.main.MetricsConfigurationProperties", "type": "boolean", "javaType": "boolean", "defaultValue": true },
     { "name": "camel.metrics.enableRoutePolicy", "description": "Set whether to enable the MicrometerRoutePolicyFactory for capturing metrics on route processing times.", "sourceType": "org.apache.camel.main.MetricsConfigurationProperties", "type": "boolean", "javaType": "boolean", "defaultValue": true },
     { "name": "camel.metrics.namingStrategy", "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": "string", "javaType": "java.lang.String", "defaultValue": "default", "enum": [ "default", "legacy" ] },
+    { "name": "camel.metrics.routePolicyLevel", "description": "Sets the level of information to capture. all = both context and routes.", "sourceType": "org.apache.camel.main.MetricsConfigurationProperties", "type": "string", "javaType": "java.lang.String", "defaultValue": "all", "enum": [ "all", "route", "context" ] },
     { "name": "camel.metrics.textFormatVersion", "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": "string", "javaType": "java.lang.String", "defaultValue": "0.0.4", "enum": [ "0.0.4", "1.0.0" ] },
     { "name": "camel.opentelemetry.enabled", "description": "To enable OpenTelemetry", "sourceType": "org.apache.camel.main.OtelConfigurationProperties", "type": "boolean", "javaType": "boolean", "defaultValue": "false" },
     { "name": "camel.opentelemetry.encoding", "description": "Sets whether the header keys need to be encoded (connector specific) or not. The value is a boolean. Dashes need for instances to be encoded for JMS property keys.", "sourceType": "org.apache.camel.main.OtelConfigurationProperties", "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 3274ceb9c04..7f70eafc246 100644
--- a/core/camel-main/src/main/docs/main.adoc
+++ b/core/camel-main/src/main/docs/main.adoc
@@ -386,7 +386,7 @@ The camel.opentelemetry supports 4 options, which are listed below.
 
 
 === Camel Micrometer Metrics configurations
-The camel.metrics supports 9 options, which are listed below.
+The camel.metrics supports 10 options, which are listed below.
 
 [width="100%",cols="2,5,^1,2",options="header"]
 |===
@@ -399,6 +399,7 @@ The camel.metrics supports 9 options, which are listed below.
 | *camel.metrics.enableRouteEvent{zwsp}Notifier* | Set whether to enable the MicrometerRouteEventNotifier for capturing metrics on the total number of routes and total number of routes running. | true | boolean
 | *camel.metrics.enableRoute{zwsp}Policy* | Set whether to enable the MicrometerRoutePolicyFactory for capturing metrics on route processing times. | true | boolean
 | *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.routePolicyLevel* | Sets the level of information to capture. all = both context and routes. | all | String
 | *camel.metrics.textFormat{zwsp}Version* | 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 108c9dfc2a8..772d3242c17 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
@@ -33,6 +33,8 @@ public class MetricsConfigurationProperties implements BootstrapCloseable {
     private String namingStrategy;
     @Metadata(defaultValue = "true")
     private boolean enableRoutePolicy = true;
+    @Metadata(defaultValue = "all", enums = "all,route,context")
+    private String routePolicyLevel = "all";
     private boolean enableMessageHistory;
     @Metadata(defaultValue = "true")
     private boolean enableExchangeEventNotifier = true;
@@ -88,6 +90,17 @@ public class MetricsConfigurationProperties implements BootstrapCloseable {
         this.enableRoutePolicy = enableRoutePolicy;
     }
 
+    public String getRoutePolicyLevel() {
+        return routePolicyLevel;
+    }
+
+    /**
+     * Sets the level of information to capture. all = both context and routes.
+     */
+    public void setRoutePolicyLevel(String routePolicyLevel) {
+        this.routePolicyLevel = routePolicyLevel;
+    }
+
     public boolean isEnableMessageHistory() {
         return enableMessageHistory;
     }
@@ -180,6 +193,14 @@ public class MetricsConfigurationProperties implements BootstrapCloseable {
         return this;
     }
 
+    /**
+     * Sets the level of information to capture. all = both context and routes.
+     */
+    public MetricsConfigurationProperties withRoutePolicyLevel(String routePolicyLevel) {
+        this.routePolicyLevel = routePolicyLevel;
+        return this;
+    }
+
     /**
      * To enable Micrometer metrics.
      */
diff --git a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_5.adoc b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_5.adoc
index 9229bcda797..f783da38174 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_5.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_5.adoc
@@ -151,6 +151,22 @@ micrometer and metrics no longer include statistics for those Kamelet routes.
 The old behaviour can be enabled by setting `registerRoutesCreateByKamelet=true`
 on the `ManagementAgent` object. See more in the xref:jmx.adoc[JMX] documentation.
 
+Added context level metrics to `camel-micrometer`. The metrics with key `camel.route.policy` now include
+tag `eventType` that specifies if the metrics is for a route or the entire camel context.
+You can turn off context level metrics, by setting `contenxtEnabled=false` on the factory such as:
+
+[source,java]
+----
+factory.getPolicyConfiguration().setContextEnabled(false);
+----
+
+This can also be done easily from `application.properties` such as:
+
+[source,properties]
+----
+camel.metrics.routePolicyLevel=route
+----
+
 === camel-openapi-java and camel-rest-openapi
 
 Dropped support for the old Swagger 2.0 spec. Only OpenAPI v3 specs is supported now.
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 289c93a2dcb..c92d63dd714 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
@@ -46,7 +46,7 @@ public class ListMetric extends ProcessWatchCommand {
     String sort;
 
     @CommandLine.Option(names = { "--filter" },
-                        description = "Filter metric by type or name")
+                        description = "Filter metric by type, name or tags")
     String filter;
 
     @CommandLine.Option(names = { "--tags" },
@@ -136,7 +136,8 @@ public class ListMetric extends ProcessWatchCommand {
                                     if (!all && getNumber(row.count).isEmpty()) {
                                         continue;
                                     }
-                                    if (filter == null || row.type.equals(filter) || row.metricName.contains(filter)) {
+                                    if (filter == null || row.type.equals(filter) || row.metricName.contains(filter)
+                                            || row.tags.contains(filter)) {
                                         rows.add(row);
                                     }
                                 }