You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by ac...@apache.org on 2018/05/17 09:28:48 UTC

[camel] 01/16: CAMEL-11600:initial work

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

acosentino pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/camel.git

commit 949feeb014b26dab4afc09424519226e47eb2560
Author: Christian Ohr <ch...@gmail.com>
AuthorDate: Fri Apr 27 17:06:29 2018 +0200

    CAMEL-11600:initial work
---
 bom/camel-bom/pom.xml                              |   5 +
 components/camel-micrometer/pom.xml                | 114 ++++
 .../src/main/docs/micrometer-component.adoc        | 640 +++++++++++++++++++++
 .../micrometer/AbstractMicrometerProducer.java     | 127 ++++
 .../component/micrometer/CounterProducer.java      |  64 +++
 .../DistributionStatisticConfigFilter.java         |  73 +++
 .../micrometer/DistributionSummaryProducer.java    |  62 ++
 .../camel/component/micrometer/MetricsType.java    |  55 ++
 .../component/micrometer/MicrometerComponent.java  | 147 +++++
 .../component/micrometer/MicrometerConstants.java  |  32 ++
 .../component/micrometer/MicrometerEndpoint.java   | 137 +++++
 .../micrometer/MicrometerTimerAction.java          |  23 +
 .../camel/component/micrometer/TimerProducer.java  |  94 +++
 .../micrometer/json/AbstractMicrometerService.java | 116 ++++
 .../micrometer/json/MicrometerModule.java          | 284 +++++++++
 .../messagehistory/MicrometerMessageHistory.java   |  63 ++
 .../MicrometerMessageHistoryFactory.java           | 156 +++++
 .../MicrometerMessageHistoryMBean.java             |  29 +
 .../MicrometerMessageHistoryService.java           |  29 +
 .../routepolicy/MicrometerRegistryMBean.java       |  29 +
 .../routepolicy/MicrometerRegistryService.java     |  30 +
 .../routepolicy/MicrometerRoutePolicy.java         | 185 ++++++
 .../routepolicy/MicrometerRoutePolicyFactory.java  | 104 ++++
 .../spi/InstrumentedThreadPoolFactory.java         |  79 +++
 .../spi/TimedScheduledExecutorService.java         |  48 ++
 .../services/org/apache/camel/component/micrometer |  18 +
 .../micrometer/AbstractMicrometerProducerTest.java | 214 +++++++
 .../component/micrometer/CounterEndpointTest.java  |  98 ++++
 .../component/micrometer/CounterProducerTest.java  | 211 +++++++
 .../component/micrometer/CounterRouteTest.java     | 157 +++++
 .../DistributionSummaryEndpointTest.java           |  85 +++
 .../DistributionSummaryProducerTest.java           | 133 +++++
 .../micrometer/DistributionSummaryRouteTest.java   | 111 ++++
 .../micrometer/MetricComponentSpringTest.java      |  94 +++
 .../component/micrometer/MetricsTypeTest.java      |  35 ++
 .../micrometer/MicrometerComponentRouteTest.java   |  86 +++
 .../micrometer/MicrometerComponentTest.java        | 186 ++++++
 .../micrometer/MicrometerEndpointTest.java         | 102 ++++
 .../component/micrometer/TimerEndpointTest.java    |  87 +++
 .../component/micrometer/TimerProducerTest.java    | 260 +++++++++
 .../camel/component/micrometer/TimerRouteTest.java | 159 +++++
 ...ExceptionRouteMicrometerMessageHistoryTest.java | 101 ++++
 .../messagehistory/ManagedMessageHistoryTest.java  | 135 +++++
 .../MicrometerMessageHistoryTest.java              |  88 +++
 .../SpringMicrometerMessageHistoryTest.java        |  64 +++
 .../AbstractMicrometerRoutePolicyTest.java         |  40 ++
 .../ManagedMicrometerRoutePolicyTest.java          |  84 +++
 ...MicrometerRoutePolicyMulticastSubRouteTest.java |  66 +++
 .../MicrometerRoutePolicySubRouteTest.java         |  58 ++
 .../routepolicy/MicrometerRoutePolicyTest.java     |  61 ++
 .../spi/InstrumentedThreadPoolFactoryTest.java     | 113 ++++
 .../src/test/resources/log4j2.properties           |  28 +
 .../org.mockito.plugins.MockMaker                  |   1 +
 .../SpringMetricsMessageHistoryTest.xml            |  47 ++
 components/pom.xml                                 |   1 +
 parent/pom.xml                                     |   6 +
 56 files changed, 5654 insertions(+)

diff --git a/bom/camel-bom/pom.xml b/bom/camel-bom/pom.xml
index 9f8ecd3..bb1171c 100644
--- a/bom/camel-bom/pom.xml
+++ b/bom/camel-bom/pom.xml
@@ -1650,6 +1650,11 @@
       </dependency>
       <dependency>
         <groupId>org.apache.camel</groupId>
+        <artifactId>camel-micrometer</artifactId>
+        <version>${project.version}</version>
+      </dependency>
+      <dependency>
+        <groupId>org.apache.camel</groupId>
         <artifactId>camel-milo</artifactId>
         <version>${project.version}</version>
       </dependency>
diff --git a/components/camel-micrometer/pom.xml b/components/camel-micrometer/pom.xml
new file mode 100644
index 0000000..93aa07a
--- /dev/null
+++ b/components/camel-micrometer/pom.xml
@@ -0,0 +1,114 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    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.
+
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>org.apache.camel</groupId>
+    <artifactId>components</artifactId>
+    <version>2.22.0-SNAPSHOT</version>
+  </parent>
+
+  <artifactId>camel-micrometer</artifactId>
+  <packaging>jar</packaging>
+  <name>Camel :: Micrometer</name>
+  <description>Camel Micrometer based monitoring component</description>
+
+  <properties>
+    <camel.osgi.export.pkg>
+      org.apache.camel.component.micrometer.*
+    </camel.osgi.export.pkg>
+    <camel.osgi.export.service>org.apache.camel.spi.ComponentResolver;component=micrometer</camel.osgi.export.service>
+  </properties>
+
+  <dependencies>
+
+    <dependency>
+      <groupId>org.apache.camel</groupId>
+      <artifactId>camel-core</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>com.fasterxml.jackson.core</groupId>
+      <artifactId>jackson-databind</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>io.micrometer</groupId>
+      <artifactId>micrometer-core</artifactId>
+      <version>${micrometer-version}</version>
+    </dependency>
+    <!-- testing -->
+    <dependency>
+      <groupId>org.apache.camel</groupId>
+      <artifactId>camel-test</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.camel</groupId>
+      <artifactId>camel-spring</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.camel</groupId>
+      <artifactId>camel-spring-javaconfig</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.camel</groupId>
+      <artifactId>${camel-test-spring-artifactId}</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.mockito</groupId>
+      <artifactId>mockito-core</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.hamcrest</groupId>
+      <artifactId>java-hamcrest</artifactId>
+      <version>${hamcrest-version}</version>
+      <scope>test</scope>
+    </dependency>
+
+    <!-- logging -->   
+    <dependency>
+      <groupId>org.apache.logging.log4j</groupId>
+      <artifactId>log4j-api</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.logging.log4j</groupId>
+      <artifactId>log4j-core</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.logging.log4j</groupId>
+      <artifactId>log4j-slf4j-impl</artifactId>
+      <scope>test</scope>
+    </dependency>
+
+  </dependencies>
+
+</project>
diff --git a/components/camel-micrometer/src/main/docs/micrometer-component.adoc b/components/camel-micrometer/src/main/docs/micrometer-component.adoc
new file mode 100644
index 0000000..d4bb70f
--- /dev/null
+++ b/components/camel-micrometer/src/main/docs/micrometer-component.adoc
@@ -0,0 +1,640 @@
+[[metrics-component]]
+== Metrics Component
+ifdef::env-github[]
+*Available as of Camel version 2.14.0*
+
+:caution-caption: :boom:
+:important-caption: :exclamation:
+:note-caption: :information_source:
+:tip-caption: :bulb:
+:warning-caption: :warning:
+endif::[]
+
+### Metrics Component
+
+The **metrics:** component allows to collect various metrics directly
+from Camel routes. Supported metric types
+are link:#MetricsComponent-counter[counter], link:#MetricsComponent-histogram[histogram],
+link:#MetricsComponent-meter[meter], link:#MetricsComponent-timer[timer] and link:#MetricsComponent-gauge[gauge]. http://metrics.dropwizard.io[Metrics] provides
+simple way to measure behaviour of application. Configurable
+reporting backend is enabling different integration options for
+collecting and visualizing statistics. The component also provides
+a `MetricsRoutePolicyFactory` which allows to expose route statistics
+using Dropwizard Metrics, see bottom of page for details.
+
+Maven users will need to add the following dependency to their `pom.xml`
+for this component:
+
+[source,xml]
+----
+<dependency>
+    <groupId>org.apache.camel</groupId>
+    <artifactId>camel-metrics</artifactId>
+    <version>x.x.x</version>
+    <!-- use the same version as your Camel core version -->
+</dependency>
+----
+
+### URI format
+
+[source]
+----
+metrics:[ meter | counter | histogram | timer | gauge ]:metricname[?options]
+----
+
+### Options
+
+// component options: START
+The Metrics component supports 2 options which are listed below.
+
+
+
+[width="100%",cols="2,5,^1,2",options="header"]
+|===
+| Name | Description | Default | Type
+| *metricRegistry* (advanced) | To use a custom configured MetricRegistry. |  | MetricRegistry
+| *resolveProperty Placeholders* (advanced) | Whether the component should resolve property placeholders on itself when starting. Only properties which are of String type can use property placeholders. | true | boolean
+|===
+// component options: END
+
+
+// endpoint options: START
+The Metrics endpoint is configured using URI syntax:
+
+----
+metrics:metricsType:meterName
+----
+
+with the following path and query parameters:
+
+==== Path Parameters (2 parameters):
+
+
+[width="100%",cols="2,5,^1,2",options="header"]
+|===
+| Name | Description | Default | Type
+| *metricsType* | *Required* Type of metrics |  | MetricsType
+| *meterName* | *Required* Name of metrics |  | String
+|===
+
+
+==== Query Parameters (7 parameters):
+
+
+[width="100%",cols="2,5,^1,2",options="header"]
+|===
+| Name | Description | Default | Type
+| *action* (producer) | Action when using timer type |  | MetricsTimerAction
+| *decrement* (producer) | Decrement value when using counter type |  | Long
+| *increment* (producer) | Increment value when using counter type |  | Long
+| *mark* (producer) | Mark when using meter type |  | Long
+| *subject* (producer) | Subject value when using gauge type |  | Object
+| *value* (producer) | Value value when using histogram type |  | Long
+| *synchronous* (advanced) | Sets whether synchronous processing should be strictly used, or Camel is allowed to use asynchronous processing (if supported). | false | boolean
+|===
+// endpoint options: END
+
+
+
+### [[MetricsComponent-registry]]Metric Registry
+
+Camel Metrics component uses by default a `MetricRegistry` instance with
+a `Slf4jReporter` that has a 60 second reporting interval.
+This default registry can be replaced with a custom one by providing
+a `MetricRegistry` bean. If multiple `MetricRegistry` beans exist in the
+application, the one with name `metricRegistry` is used.
+
+For example using Spring Java Configuration:
+
+[source,java]
+----
+@Configuration
+public static class MyConfig extends SingleRouteCamelConfiguration {
+
+    @Bean
+    @Override
+    public RouteBuilder route() {
+        return new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                // define Camel routes here
+            }
+        };
+    }
+
+    @Bean(name = MetricsComponent.METRIC_REGISTRY_NAME)
+    public MetricRegistry getMetricRegistry() {
+        MetricRegistry registry = ...;
+        return registry;
+    }
+}
+----
+
+Or using CDI:
+[source,java]
+----
+class MyBean extends RouteBuilder {
+
+    @Override
+    public void configure() {
+      from("...")
+          // Register the 'my-meter' meter in the MetricRegistry below
+          .to("metrics:meter:my-meter");
+    }
+
+    @Produces
+    // If multiple MetricRegistry beans
+    // @Named(MetricsComponent.METRIC_REGISTRY_NAME)
+    MetricRegistry registry() {
+        MetricRegistry registry = new MetricRegistry();
+        // ...
+        return registry;
+    }
+}
+----
+
+CAUTION: `MetricRegistry` uses internal thread(s) for reporting. There is no
+public API in version DropWizard `3.0.1` for users to clean up on exit. Thus using
+Camel Metrics Component leads to Java classloader leak and my cause
+`OutOfMemoryErrors` in some cases.
+
+### Usage
+
+Each metric has type and name. Supported types are
+link:#MetricsComponent-counter[counter],
+link:#MetricsComponent-histogram[histogram], link:#MetricsComponent-meter[meter],
+link:#MetricsComponent-timer[timer] and link:#MetricsComponent-gauge[gauge].
+Metric name is simple string. If metric type is not provided then type meter is used by default.
+
+#### Headers
+
+Metric name defined in URI can be overridden by using header with name
+`CamelMetricsName`.
+
+For example
+
+[source,java]
+----
+from("direct:in")
+    .setHeader(MetricsConstants.HEADER_METRIC_NAME, constant("new.name"))
+    .to("metrics:counter:name.not.used")
+    .to("direct:out");
+----
+
+will update counter with name `new.name` instead of `name.not.used`.
+
+All Metrics specific headers are removed from the message once Metrics
+endpoint finishes processing of exchange. While processing exchange
+Metrics endpoint will catch all exceptions and write log entry using
+level `warn`.
+
+### [[MetricsComponent-counter]]Metrics type counter
+
+[source]
+----
+metrics:counter:metricname[?options]
+----
+
+#### Options
+
+[width="100%",cols="10%,10%,80%",options="header",]
+|=====================================================
+|Name |Default |Description
+|increment  |- |Long value to add to the counter
+|decrement |- |Long value to subtract from the counter
+|=====================================================
+
+If neither `increment` or `decrement` is defined then counter value will
+be incremented by one. If `increment` and `decrement` are both defined
+only increment operation is called. 
+
+[source,java]
+----
+// update counter simple.counter by 7
+from("direct:in")
+    .to("metric:counter:simple.counter?increment=7")
+    .to("direct:out");
+----
+
+[source,java]
+----
+// increment counter simple.counter by 1
+from("direct:in")
+    .to("metric:counter:simple.counter")
+    .to("direct:out");
+----
+
+[source,java]
+----
+// decrement counter simple.counter by 3
+from("direct:in")
+    .to("metric:counter:simple.counter?decrement=3")
+    .to("direct:out");
+----
+
+#### Headers
+
+Message headers can be used to override `increment` and `decrement`
+values specified in Metrics component URI.
+
+[width="100%",cols="10%,80%,10%",options="header",]
+|====================================================================
+|Name |Description |Expected type
+|CamelMetricsCounterIncrement  |Override increment value in URI |Long
+|CamelMetricsCounterDecrement  |Override decrement value in URI |Long
+|====================================================================
+
+[source,java]
+----
+// update counter simple.counter by 417
+from("direct:in")
+    .setHeader(MetricsConstants.HEADER_COUNTER_INCREMENT, constant(417L))
+    .to("metric:counter:simple.counter?increment=7")
+    .to("direct:out");
+----
+
+[source,java]
+----
+// updates counter using simple language to evaluate body.length
+from("direct:in")
+    .setHeader(MetricsConstants.HEADER_COUNTER_INCREMENT, simple("${body.length}"))
+    .to("metrics:counter:body.length")
+    .to("mock:out");
+
+----
+
+### [[MetricsComponent-histogram]]Metric type histogram
+
+[source]
+----
+metrics:histogram:metricname[?options]
+----
+
+#### Options
+
+[width="100%",cols="10%,10%,80%",options="header",]
+|===================================
+|Name |Default |Description
+|value |- |Value to use in histogram
+|===================================
+
+If no `value` is not set nothing is added to histogram and warning is
+logged.
+
+[source,java]
+----
+// adds value 9923 to simple.histogram
+from("direct:in")
+    .to("metric:histogram:simple.histogram?value=9923")
+    .to("direct:out");
+----
+
+[source,java]
+----
+// nothing is added to simple.histogram; warning is logged
+from("direct:in")
+    .to("metric:histogram:simple.histogram")
+    .to("direct:out");
+
+----
+
+#### Headers
+
+Message header can be used to override value specified in Metrics
+component URI.
+
+[width="100%",cols="10%,80%,10%",options="header",]
+|=================================================================
+|Name |Description |Expected type
+|CamelMetricsHistogramValue |Override histogram value in URI |Long
+|=================================================================
+
+[source,java]
+----
+// adds value 992 to simple.histogram
+from("direct:in")
+    .setHeader(MetricsConstants.HEADER_HISTOGRAM_VALUE, constant(992L))
+    .to("metric:histogram:simple.histogram?value=700")
+    .to("direct:out")
+
+----
+
+### [[MetricsComponent-meter]]Metric type meter
+
+[source]
+----
+metrics:meter:metricname[?options]
+----
+
+#### Options
+
+[width="100%",cols="10%,10%,80%",options="header",]
+|===================================
+|Name |Default |Description
+|mark  |- |Long value to use as mark
+|===================================
+
+If `mark` is not set then `meter.mark()` is called without argument.
+
+[source,java]
+----
+// marks simple.meter without value
+from("direct:in")
+    .to("metric:simple.meter")
+    .to("direct:out");
+----
+
+[source,java]
+----
+// marks simple.meter with value 81
+from("direct:in")
+    .to("metric:meter:simple.meter?mark=81")
+    .to("direct:out");
+----
+
+#### Headers
+
+Message header can be used to override `mark` value specified in Metrics
+component URI.
+
+[width="100%",cols="10%,80%,10%",options="header",]
+|=======================================================
+|Name |Description |Expected type
+|CamelMetricsMeterMark |Override mark value in URI |Long
+|=======================================================
+
+[source,java]
+----
+// updates meter simple.meter with value 345
+from("direct:in")
+    .setHeader(MetricsConstants.HEADER_METER_MARK, constant(345L))
+    .to("metric:meter:simple.meter?mark=123")
+    .to("direct:out");
+----
+
+### [[MetricsComponent-timer]]Metrics type timer
+
+[source]
+----
+metrics:timer:metricname[?options]
+----
+
+#### Options
+
+[width="100%",cols="10%,10%,80%",options="header",]
+|==========================
+|Name |Default |Description
+|action |- |start or stop
+|==========================
+
+If no `action` or invalid value is provided then warning is logged
+without any timer update. If action `start` is called on already running
+timer or `stop` is called on not running timer then nothing is updated
+and warning is logged.
+
+[source,java]
+----
+// measure time taken by route "calculate"
+from("direct:in")
+    .to("metrics:timer:simple.timer?action=start")
+    .to("direct:calculate")
+    .to("metrics:timer:simple.timer?action=stop");
+----
+
+`TimerContext` objects are stored as Exchange properties between
+different Metrics component calls.
+
+#### Headers
+
+Message header can be used to override action value specified in Metrics
+component URI.
+
+[width="100%",cols="10%,80%,10%",options="header",]
+|=======================================================================
+|Name |Description |Expected type
+|CamelMetricsTimerAction |Override timer action in URI
+|`org.apache.camel.component.metrics.timer.TimerEndpoint.TimerAction`
+|=======================================================================
+
+[source,java]
+----
+// sets timer action using header
+from("direct:in")
+    .setHeader(MetricsConstants.HEADER_TIMER_ACTION, TimerAction.start)
+    .to("metric:timer:simple.timer")
+    .to("direct:out");
+----
+
+### [[MetricsComponent-gauge]]Metric type gauge
+
+[source]
+----
+metrics:gauge:metricname[?options]
+----
+
+#### Options
+
+[width="100%",cols="10%,10%,80%",options="header",]
+|=====================================================
+|Name |Default |Description
+|subject  |- |Any object to be observed by the gauge
+|=====================================================
+
+If `subject` is not defined it's simply ignored, i.e. the gauge is not registered.
+
+[source,java]
+----
+// update gauge "simple.gauge" by a bean "mySubjectBean"
+from("direct:in")
+    .to("metric:gauge:simple.gauge?subject=#mySubjectBean")
+    .to("direct:out");
+----
+
+#### Headers
+
+Message headers can be used to override `subject` values specified in Metrics component URI.
+Note: if `CamelMetricsName` header is specified, then new gauge is registered in addition to
+default one specified in a URI.
+
+[width="100%",cols="10%,80%,10%",options="header",]
+|====================================================================
+|Name |Description |Expected type
+|CamelMetricsGaugeSubject  |Override subject value in URI |Object
+|====================================================================
+
+[source,java]
+----
+// update gauge simple.gauge by a String literal "myUpdatedSubject"
+from("direct:in")
+    .setHeader(MetricsConstants.HEADER_GAUGE_SUBJECT, constant("myUpdatedSubject"))
+    .to("metric:counter:simple.gauge?subject=#mySubjectBean")
+    .to("direct:out");
+----
+
+### MetricsRoutePolicyFactory
+
+This factory allows to add a RoutePolicy for each
+route which exposes route utilization statistics using Dropwizard metrics.
+This factory can be used in Java and XML as the examples below
+demonstrates. 
+
+NOTE: Instead of using the MetricsRoutePolicyFactory you can define a
+MetricsRoutePolicy per route you want to instrument, in case you only
+want to instrument a few selected routes.
+
+From Java you just add the factory to the `CamelContext` as shown below:
+
+[source,java]
+----
+context.addRoutePolicyFactory(new MetricsRoutePolicyFactory());
+----
+
+And from XML DSL you define a <bean> as follows:
+
+[source,xml]
+----
+  <!-- use camel-metrics route policy to gather metrics for all routes -->
+  <bean id="metricsRoutePolicyFactory" class="org.apache.camel.component.metrics.routepolicy.MetricsRoutePolicyFactory"/>
+----
+
+The `MetricsRoutePolicyFactory` and `MetricsRoutePolicy` supports the
+following options:
+
+[width="100%",cols="10%,10%,80%",options="header",]
+|=======================================================================
+|Name |Default |Description
+|useJmx |false |Whether to report fine grained statistics to JMX by
+using the `com.codahale.metrics.JmxReporter`. +
+Notice that if JMX is enabled on CamelContext
+then a `MetricsRegistryService` mbean is enlisted under the services
+type in the JMX tree. That mbean has a single operation to output the
+statistics using json. Setting `useJmx` to true is only needed if you
+want fine grained mbeans per statistics type.
+
+|jmxDomain |org.apache.camel.metrics |The JMX domain name
+
+|prettyPrint |false |Whether to use pretty print when outputting
+statistics in json format
+
+|metricsRegistry |  |Allow to use a shared
+`com.codahale.metrics.MetricRegistry`. If none is provided then Camel
+will create a shared instance used by the this CamelContext.
+
+|rateUnit |TimeUnit.SECONDS |The unit to use for rate in the metrics
+reporter or when dumping the statistics as json.
+
+|durationUnit |TimeUnit.MILLISECONDS |The unit to use for duration in
+the metrics reporter or when dumping the statistics as json.
+
+|namePattern |`##name##.##routeId##.##type##` |*Camel 2.17:* The name
+pattern to use. Uses dot as separators, but you can change that. The
+values `##name##`, `##routeId##`, and `##type##` will be replaced with actual
+value. Where `###name###` is the name of the CamelContext. `###routeId###`
+is the name of the route. And `###type###` is the value of responses.
+|=======================================================================
+
+ 
+
+From Java code tou can get hold of
+the `com.codahale.metrics.MetricRegistry` from the
+`org.apache.camel.component.metrics.routepolicy.MetricsRegistryService`
+as shown below:
+
+[source,java]
+----
+MetricRegistryService registryService = context.hasService(MetricsRegistryService.class);
+if (registryService != null) {
+  MetricsRegistry registry = registryService.getMetricsRegistry();
+  ...
+}
+----
+
+### MetricsMessageHistoryFactory
+
+*Available as of Camel 2.17*
+
+This factory allows to use metrics to
+capture Message History performance
+statistics while routing messages. It works by using a metrics Timer for
+each node in all the routes. This factory can be used in Java and XML as
+the examples below demonstrates. 
+
+From Java you just set the factory to the `CamelContext` as shown below:
+
+[source,java]
+----
+context.setMessageHistoryFactory(new MetricsMessageHistoryFactory());
+----
+
+And from XML DSL you define a <bean> as follows:
+
+[source,xml]
+----
+  <!-- use camel-metrics message history to gather metrics for all messages being routed -->
+  <bean id="metricsMessageHistoryFactory" class="org.apache.camel.component.metrics.messagehistory.MetricsMessageHistoryFactory"/>
+----
+
+The following options is supported on the factory:
+
+[width="100%",cols="10%,10%,80%",options="header",]
+|=======================================================================
+|Name |Default |Description
+|useJmx |false |Whether to report fine grained statistics to JMX by
+using the `com.codahale.metrics.JmxReporter`. +
+Notice that if JMX is enabled on CamelContext
+then a `MetricsRegistryService` mbean is enlisted under the services
+type in the JMX tree. That mbean has a single operation to output the
+statistics using json. Setting `useJmx` to true is only needed if you
+want fine grained mbeans per statistics type.
+
+|jmxDomain |org.apache.camel.metrics |The JMX domain name
+
+|prettyPrint |false |Whether to use pretty print when outputting
+statistics in json format
+
+|metricsRegistry |  |Allow to use a shared
+`com.codahale.metrics.MetricRegistry`. If none is provided then Camel
+will create a shared instance used by the this CamelContext.
+
+|rateUnit |TimeUnit.SECONDS |The unit to use for rate in the metrics
+reporter or when dumping the statistics as json.
+
+|durationUnit |TimeUnit.MILLISECONDS |The unit to use for duration in
+the metrics reporter or when dumping the statistics as json.
+
+|namePattern |`##name##.##routeId##.###id###.##type##` |The name pattern
+to use. Uses dot as separators, but you can change that. The values
+`##name##`, `##routeId##`, `##type##`, and `###id###` will be replaced with
+actual value. Where `###name###` is the name of the CamelContext.
+`###routeId###` is the name of the route. The `###id###` pattern represents
+the node id. And `###type###` is the value of history.
+|=======================================================================
+
+At runtime the metrics can be accessed from Java API or JMX which allows
+to gather the data as json output.
+
+From Java code you can do get the service from the CamelContext as
+shown:
+
+[source,java]
+----
+MetricsMessageHistoryService service = context.hasService(MetricsMessageHistoryService.class);
+String json = service.dumpStatisticsAsJson();
+----
+
+And the JMX API the MBean is registered in the `type=services` tree
+with `name=MetricsMessageHistoryService`.
+
+### InstrumentedThreadPoolFactory
+
+*Available as of Camel 2.18*
+
+This factory allows you to gather performance information about Camel Thread Pools by injecting a InstrumentedThreadPoolFactory
+which collects information from inside of Camel.
+See more details at Advanced configuration of CamelContext using Spring
+
+
+### See Also
+
+* The `camel-example-cdi-metrics` example that illustrates the integration
+  between Camel, Metrics and CDI.
diff --git a/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/AbstractMicrometerProducer.java b/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/AbstractMicrometerProducer.java
new file mode 100644
index 0000000..6667681
--- /dev/null
+++ b/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/AbstractMicrometerProducer.java
@@ -0,0 +1,127 @@
+/**
+ * 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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;
+
+import io.micrometer.core.instrument.Meter;
+import io.micrometer.core.instrument.MeterRegistry;
+import io.micrometer.core.instrument.Tag;
+import io.micrometer.core.instrument.search.Search;
+import org.apache.camel.Exchange;
+import org.apache.camel.Message;
+import org.apache.camel.impl.DefaultProducer;
+import org.apache.camel.util.ObjectHelper;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.List;
+import java.util.Optional;
+import java.util.function.Function;
+
+public abstract class AbstractMicrometerProducer<T extends Meter> extends DefaultProducer {
+
+    public static final String HEADER_PATTERN = MicrometerConstants.HEADER_PREFIX + "*";
+    private static final Logger LOG = LoggerFactory.getLogger(AbstractMicrometerProducer.class);
+
+    private String prefix = MicrometerConstants.HEADER_PREFIX;
+
+    public AbstractMicrometerProducer(MicrometerEndpoint endpoint) {
+        super(endpoint);
+    }
+
+    @Override
+    public MicrometerEndpoint getEndpoint() {
+        return (MicrometerEndpoint) super.getEndpoint();
+    }
+
+    public String getPrefix() {
+        return prefix;
+    }
+
+    public void setPrefix(String prefix) {
+        this.prefix = prefix;
+    }
+
+    @Override
+    public void process(Exchange exchange) throws Exception {
+        Message in = exchange.getIn();
+        String defaultMetricsName = getEndpoint().getMetricsName();
+        String finalMetricsName = getMetricsName(in, defaultMetricsName);
+        List<Tag> defaultTags = getEndpoint().getTags();
+        List<Tag> finalTags = getTags(in, defaultTags);
+        try {
+            doProcess(exchange, finalMetricsName, finalTags);
+        } catch (Exception e) {
+            exchange.setException(e);
+        } finally {
+            clearMetricsHeaders(in);
+        }
+    }
+
+    protected abstract Function<Search, T> search();
+
+    protected abstract Function<MeterRegistry, T> register(String name, List<Tag> tags);
+
+    protected void doProcess(Exchange exchange, String name, List<Tag> tags) {
+        T meter = getMeter(name, tags);
+        try {
+            doProcess(exchange, getEndpoint(), meter);
+        } catch (Exception e) {
+            exchange.setException(e);
+        } finally {
+            clearMetricsHeaders(exchange.getIn());
+        }
+    }
+
+    protected T getMeter(String name, List<Tag> tags) {
+        MeterRegistry registry = getEndpoint().getRegistry();
+        return Optional.ofNullable(
+                search().apply(registry.find(name))
+        ).orElseGet(() ->
+                register(name, tags).apply(registry));
+    }
+
+    protected abstract void doProcess(Exchange exchange, MicrometerEndpoint endpoint, T meter);
+
+    public String getMetricsName(Message in, String defaultValue) {
+        return getStringHeader(in, MicrometerConstants.HEADER_METRIC_NAME, defaultValue);
+    }
+
+    public List<Tag> getTags(Message in, List<Tag> defaultTags) {
+        return getTagHeader(in, MicrometerConstants.HEADER_METRIC_TAGS, defaultTags);
+    }
+
+    public String getStringHeader(Message in, String header, String defaultValue) {
+        String headerValue = in.getHeader(header, String.class);
+        return ObjectHelper.isNotEmpty(headerValue) ? headerValue : defaultValue;
+    }
+
+    public Long getLongHeader(Message in, String header, Long defaultValue) {
+        return in.getHeader(header, defaultValue, Long.class);
+    }
+
+    public Double getDoubleHeader(Message in, String header, Double defaultValue) {
+        return in.getHeader(header, defaultValue, Double.class);
+    }
+
+    public List<Tag> getTagHeader(Message in, String header, List<Tag> defaultTags) {
+        return in.getHeader(header, defaultTags, List.class);
+    }
+
+    protected boolean clearMetricsHeaders(Message in) {
+        return in.removeHeaders(HEADER_PATTERN);
+    }
+}
diff --git a/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/CounterProducer.java b/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/CounterProducer.java
new file mode 100644
index 0000000..19793ea
--- /dev/null
+++ b/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/CounterProducer.java
@@ -0,0 +1,64 @@
+/**
+ * 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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;
+
+import io.micrometer.core.instrument.Counter;
+import io.micrometer.core.instrument.MeterRegistry;
+import io.micrometer.core.instrument.Tag;
+import io.micrometer.core.instrument.search.Search;
+import org.apache.camel.Exchange;
+import org.apache.camel.Message;
+
+import java.util.List;
+import java.util.Optional;
+import java.util.function.Function;
+
+import static org.apache.camel.component.micrometer.MicrometerConstants.HEADER_COUNTER_DECREMENT;
+import static org.apache.camel.component.micrometer.MicrometerConstants.HEADER_COUNTER_INCREMENT;
+
+public class CounterProducer extends AbstractMicrometerProducer<Counter> {
+
+    public CounterProducer(MicrometerEndpoint endpoint) {
+        super(endpoint);
+    }
+
+    @Override
+    protected Function<Search, Counter> search() {
+        return Search::counter;
+    }
+
+    @Override
+    protected Function<MeterRegistry, Counter> register(String name, List<Tag> tags) {
+        return meterRegistry -> meterRegistry.counter(name, tags);
+    }
+
+    @Override
+    protected void doProcess(Exchange exchange, MicrometerEndpoint endpoint, Counter counter) {
+        Message in = exchange.getIn();
+        Double increment = endpoint.getIncrement();
+        Double decrement = endpoint.getDecrement();
+        Double finalIncrement = getDoubleHeader(in, HEADER_COUNTER_INCREMENT, increment);
+        Double finalDecrement = getDoubleHeader(in, HEADER_COUNTER_DECREMENT, decrement);
+        if (finalIncrement != null) {
+            counter.increment(finalIncrement);
+        } else if (finalDecrement != null) {
+            counter.increment(-finalDecrement);
+        } else {
+            counter.increment();
+        }
+    }
+}
diff --git a/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/DistributionStatisticConfigFilter.java b/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/DistributionStatisticConfigFilter.java
new file mode 100644
index 0000000..8f16c7e
--- /dev/null
+++ b/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/DistributionStatisticConfigFilter.java
@@ -0,0 +1,73 @@
+package org.apache.camel.component.micrometer;
+
+import io.micrometer.core.instrument.Meter;
+import io.micrometer.core.instrument.config.MeterFilter;
+import io.micrometer.core.instrument.distribution.DistributionStatisticConfig;
+
+import java.time.Duration;
+
+/**
+ * Filter for adding common distribution statistics for all Timers and Histograms.
+ * Configure and add this to the {@link io.micrometer.core.instrument.MeterRegistry}
+ * if desired.
+ */
+public class DistributionStatisticConfigFilter implements MeterFilter {
+
+    private String prefix = MicrometerConstants.HEADER_PREFIX;
+    private Long maximumExpectedValue;
+    private Long minimumExpectedValue;
+    private Boolean enabled;
+    private Integer bufferLength;
+    private Duration expiry;
+    private double[] percentiles = new double[] { 0.5D, 0.75D, 0.9D, 0.99D, 0.999D };
+    private long[] slas;
+
+    @Override
+    public DistributionStatisticConfig configure(Meter.Id id, DistributionStatisticConfig config) {
+        if (id.getName().startsWith(prefix)) {
+            return DistributionStatisticConfig.builder()
+                    .percentilesHistogram(enabled)
+                    .percentiles(percentiles)
+                    .maximumExpectedValue(maximumExpectedValue)
+                    .minimumExpectedValue(minimumExpectedValue)
+                    .sla(slas)
+                    .bufferLength(bufferLength)
+                    .expiry(expiry)
+                    .build()
+                    .merge(config);
+        }
+        return config;
+    }
+
+    public void setPrefix(String prefix) {
+        this.prefix = prefix;
+    }
+
+    public void setMaximumExpectedValue(Long maximumExpectedValue) {
+        this.maximumExpectedValue = maximumExpectedValue;
+    }
+
+    public void setMinimumExpectedValue(Long minimumExpectedValue) {
+        this.minimumExpectedValue = minimumExpectedValue;
+    }
+
+    public void setEnabled(Boolean enabled) {
+        this.enabled = enabled;
+    }
+
+    public void setBufferLength(Integer bufferLength) {
+        this.bufferLength = bufferLength;
+    }
+
+    public void setExpiry(Duration expiry) {
+        this.expiry = expiry;
+    }
+
+    public void setPercentiles(double[] percentiles) {
+        this.percentiles = percentiles;
+    }
+
+    public void setSlas(long[] slas) {
+        this.slas = slas;
+    }
+}
diff --git a/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/DistributionSummaryProducer.java b/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/DistributionSummaryProducer.java
new file mode 100644
index 0000000..f6b5299
--- /dev/null
+++ b/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/DistributionSummaryProducer.java
@@ -0,0 +1,62 @@
+/**
+ * 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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;
+
+import io.micrometer.core.instrument.DistributionSummary;
+import io.micrometer.core.instrument.MeterRegistry;
+import io.micrometer.core.instrument.Tag;
+import io.micrometer.core.instrument.search.Search;
+import org.apache.camel.Exchange;
+import org.apache.camel.Message;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.List;
+import java.util.Optional;
+import java.util.function.Function;
+
+import static org.apache.camel.component.micrometer.MicrometerConstants.HEADER_HISTOGRAM_VALUE;
+
+public class DistributionSummaryProducer extends AbstractMicrometerProducer<DistributionSummary> {
+
+    private static final Logger LOG = LoggerFactory.getLogger(DistributionSummaryProducer.class);
+
+    public DistributionSummaryProducer(MicrometerEndpoint endpoint) {
+        super(endpoint);
+    }
+
+    @Override
+    protected Function<Search, DistributionSummary> search() {
+        return Search::summary;
+    }
+
+    @Override
+    protected Function<MeterRegistry, DistributionSummary> register(String name, List<Tag> tags) {
+        return meterRegistry -> meterRegistry.summary(name, tags);
+    }
+
+    @Override
+    protected void doProcess(Exchange exchange, MicrometerEndpoint endpoint, DistributionSummary summary) {
+        Double value = endpoint.getValue();
+        Double finalValue = getDoubleHeader(exchange.getIn(), HEADER_HISTOGRAM_VALUE, value);
+        if (finalValue != null) {
+            summary.record(finalValue);
+        } else {
+            LOG.warn("Cannot update histogram \"{}\" with null value", summary.getId().getName());
+        }
+    }
+}
diff --git a/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/MetricsType.java b/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/MetricsType.java
new file mode 100644
index 0000000..af9303d
--- /dev/null
+++ b/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/MetricsType.java
@@ -0,0 +1,55 @@
+/**
+ * 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;
+
+import java.util.EnumSet;
+import java.util.HashMap;
+import java.util.Map;
+
+public enum MetricsType {
+
+    GAUGE("gauge"),
+    COUNTER("counter"),
+    FUNCTION_COUNTER("functionCounter"),
+    TIMER("timer"),
+    FUNCTION_TIMER("functionTimer"),
+    LONG_TASK_TIMER("longTaskTimer"),
+    DISTRIBUTION_SUMMARY("summary");
+
+    private static final Map<String, MetricsType> MAP = new HashMap<>();
+
+    private final String name;
+
+    static {
+        for (MetricsType type : EnumSet.allOf(MetricsType.class)) {
+            MAP.put(type.name, type);
+        }
+    }
+
+    MetricsType(String name) {
+        this.name = name;
+    }
+
+    @Override
+    public String toString() {
+        return name;
+    }
+
+    public static MetricsType getByName(String name) {
+        return MAP.get(name);
+    }
+}
diff --git a/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/MicrometerComponent.java b/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/MicrometerComponent.java
new file mode 100644
index 0000000..9c02404
--- /dev/null
+++ b/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/MicrometerComponent.java
@@ -0,0 +1,147 @@
+/**
+ * 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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;
+
+import io.micrometer.core.instrument.MeterRegistry;
+import io.micrometer.core.instrument.Metrics;
+import io.micrometer.core.instrument.Tag;
+import io.micrometer.core.instrument.simple.SimpleMeterRegistry;
+import org.apache.camel.Endpoint;
+import org.apache.camel.RuntimeCamelException;
+import org.apache.camel.impl.UriEndpointComponent;
+import org.apache.camel.spi.Metadata;
+import org.apache.camel.spi.Registry;
+import org.apache.camel.util.StringHelper;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+/**
+ * Represents the component that manages metrics endpoints.
+ */
+public class MicrometerComponent extends UriEndpointComponent {
+
+    public static final String METRICS_REGISTRY = "metricsRegistry";
+    public static final MetricsType DEFAULT_METER_TYPE = MetricsType.TIMER;
+
+    private static final Logger LOG = LoggerFactory.getLogger(MicrometerComponent.class);
+
+    @Metadata(label = "advanced")
+    private MeterRegistry metricsRegistry;
+
+    @Metadata(label = "advanced")
+    private String prefix;
+
+    public MicrometerComponent() {
+        super(MicrometerEndpoint.class);
+    }
+
+    @Override
+    protected Endpoint createEndpoint(String uri, String remaining, Map<String, Object> parameters) throws Exception {
+        if (metricsRegistry == null) {
+            Registry camelRegistry = getCamelContext().getRegistry();
+            metricsRegistry = getOrCreateMeterRegistry(camelRegistry, METRICS_REGISTRY);
+        }
+        if (prefix == null) {
+            prefix = MicrometerConstants.HEADER_PREFIX;
+        }
+        String metricsName = getMetricsName(remaining, prefix);
+        MetricsType metricsType = getMetricsType(remaining);
+        List<Tag> tags = getMetricsTag(parameters);
+
+        LOG.debug("Metrics type: {}; name: {}; tags: {}", metricsType, metricsName, tags);
+        Endpoint endpoint = new MicrometerEndpoint(uri, this, metricsRegistry, metricsType, metricsName, tags);
+        setProperties(endpoint, parameters);
+        return endpoint;
+    }
+
+    String getMetricsName(String remaining, String prefix) {
+        String name = StringHelper.after(remaining, ":");
+        return prefix + "." + (name == null ? remaining : name);
+    }
+
+    MetricsType getMetricsType(String remaining) {
+        String name = StringHelper.before(remaining, ":");
+        MetricsType type;
+        if (name == null) {
+            type = DEFAULT_METER_TYPE;
+        } else {
+            type = MetricsType.getByName(name);
+        }
+        if (type == null) {
+            throw new RuntimeCamelException("Unknown meter type \"" + name + "\"");
+        }
+        return type;
+    }
+
+    List<Tag> getMetricsTag(Map<String, Object> parameters) {
+        String tagString = getAndRemoveParameter(parameters, "tags", String.class, "");
+        if (tagString != null && !tagString.isEmpty()) {
+            String[] tags = tagString.split("/s*,/s*");
+            return Stream.of(tags)
+                    .map(s -> s.split(":"))
+                    .map(s -> Tag.of(s[0], s[1]))
+                    .collect(Collectors.toList());
+        }
+        return Collections.emptyList();
+    }
+
+    MeterRegistry getOrCreateMeterRegistry(Registry camelRegistry, String registryName) {
+        LOG.debug("Looking up MeterRegistry from Camel Registry for name \"{}\"", registryName);
+        MeterRegistry result = getMeterRegistryFromCamelRegistry(camelRegistry, registryName);
+        if (result == null) {
+            LOG.debug("MetricRegistry not found from Camel Registry for name \"{}\"", registryName);
+            LOG.info("Creating new default MeterRegistry");
+            result = createMeterRegistry();
+        }
+        return result;
+    }
+
+    MeterRegistry getMeterRegistryFromCamelRegistry(Registry camelRegistry, String registryName) {
+        MeterRegistry registry = camelRegistry.lookupByNameAndType(registryName, MeterRegistry.class);
+        if (registry != null) {
+            return registry;
+        } else {
+            Set<MeterRegistry> registries = camelRegistry.findByType(MeterRegistry.class);
+            if (registries.size() == 1) {
+                return registries.iterator().next();
+            }
+        }
+        return null;
+    }
+
+    MeterRegistry createMeterRegistry() {
+        return new SimpleMeterRegistry();
+    }
+
+    public MeterRegistry getMetricsRegistry() {
+        return metricsRegistry;
+    }
+
+    /**
+     * To use a custom configured MetricRegistry.
+     */
+    public void setMetricsRegistry(MeterRegistry metricsRegistry) {
+        this.metricsRegistry = metricsRegistry;
+    }
+}
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
new file mode 100644
index 0000000..382fa1c
--- /dev/null
+++ b/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/MicrometerConstants.java
@@ -0,0 +1,32 @@
+/**
+ * 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;
+
+public final class MicrometerConstants {
+
+    public static final String HEADER_PREFIX = "CamelMetrics";
+    public static final String HEADER_TIMER_ACTION = HEADER_PREFIX + "TimerAction";
+    public static final String HEADER_HISTOGRAM_VALUE = HEADER_PREFIX + "HistogramValue";
+    public static final String HEADER_COUNTER_DECREMENT = HEADER_PREFIX + "CounterDecrement";
+    public static final String HEADER_COUNTER_INCREMENT = HEADER_PREFIX + "CounterIncrement";
+    public static final String HEADER_METRIC_NAME = HEADER_PREFIX + "Name";
+    public static final String HEADER_METRIC_TAGS = HEADER_PREFIX + "Tags";
+
+    private MicrometerConstants() {
+    }
+
+}
diff --git a/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/MicrometerEndpoint.java b/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/MicrometerEndpoint.java
new file mode 100644
index 0000000..2d37f24
--- /dev/null
+++ b/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/MicrometerEndpoint.java
@@ -0,0 +1,137 @@
+/**
+ * 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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;
+
+import io.micrometer.core.instrument.MeterRegistry;
+import io.micrometer.core.instrument.Tag;
+import org.apache.camel.*;
+import org.apache.camel.impl.DefaultEndpoint;
+import org.apache.camel.spi.Metadata;
+import org.apache.camel.spi.UriEndpoint;
+import org.apache.camel.spi.UriParam;
+import org.apache.camel.spi.UriPath;
+
+import java.util.List;
+
+/**
+ * To collect various metrics directly from Camel routes using the DropWizard metrics library.
+ */
+@UriEndpoint(firstVersion = "2.22.0", scheme = "micrometer", title = "Micrometer", syntax = "micrometer:metricsType:meterName", producerOnly = true, label = "monitoring")
+public class MicrometerEndpoint extends DefaultEndpoint {
+
+    protected MeterRegistry registry;
+
+    @UriPath(description = "Type of metrics")
+    @Metadata(required = "true")
+    protected final MetricsType metricsType;
+    @UriPath(description = "Name of metrics")
+    @Metadata(required = "true")
+    protected final String metricsName;
+    @UriPath(description = "Tags of metrics")
+    protected final List<Tag> tags;
+    @UriParam(description = "Action when using timer type")
+    private MicrometerTimerAction action;
+    @UriParam(description = "Value value when using histogram type")
+    private Double value;
+    @UriParam(description = "Increment value when using counter type")
+    private Double increment;
+    @UriParam(description = "Decrement value when using counter type")
+    private Double decrement;
+
+    public MicrometerEndpoint(String uri, Component component, MeterRegistry registry, MetricsType metricsType, String metricsName, List<Tag> tags) {
+        super(uri, component);
+        this.registry = registry;
+        this.metricsType = metricsType;
+        this.metricsName = metricsName;
+        this.tags = tags;
+    }
+
+    @Override
+    public Consumer createConsumer(Processor processor) throws Exception {
+        throw new RuntimeCamelException("Cannot consume from " + getClass().getSimpleName() + ": " + getEndpointUri());
+    }
+
+    @Override
+    public Producer createProducer() throws Exception {
+        if (metricsType == MetricsType.COUNTER) {
+            return new CounterProducer(this);
+        } else if (metricsType == MetricsType.DISTRIBUTION_SUMMARY) {
+            return new DistributionSummaryProducer(this);
+        } else if (metricsType == MetricsType.TIMER) {
+            return new TimerProducer(this);
+        } else {
+            throw new IllegalArgumentException("Metrics type " + metricsType + " is not supported");
+        }
+    }
+
+    @Override
+    public boolean isSingleton() {
+        return true;
+    }
+
+    public MeterRegistry getRegistry() {
+        return registry;
+    }
+
+    public String getMetricsName() {
+        return metricsName;
+    }
+
+    public List<Tag> getTags() {
+        return tags;
+    }
+
+    public MetricsType getMetricsType() {
+        return metricsType;
+    }
+
+    public MicrometerTimerAction getAction() {
+        return action;
+    }
+
+    public void setAction(MicrometerTimerAction action) {
+        this.action = action;
+    }
+
+    public Double getValue() {
+        return value;
+    }
+
+    public void setValue(Double value) {
+        this.value = value;
+    }
+
+    public Double getIncrement() {
+        return increment;
+    }
+
+    public void setIncrement(Double increment) {
+        this.increment = increment;
+    }
+
+    public Double getDecrement() {
+        return decrement;
+    }
+
+    public void setDecrement(Double decrement) {
+        this.decrement = decrement;
+    }
+
+    void setRegistry(MeterRegistry registry) {
+        this.registry = registry;
+    }
+}
diff --git a/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/MicrometerTimerAction.java b/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/MicrometerTimerAction.java
new file mode 100644
index 0000000..e86568e
--- /dev/null
+++ b/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/MicrometerTimerAction.java
@@ -0,0 +1,23 @@
+/**
+ * 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;
+
+public enum MicrometerTimerAction {
+
+    start, stop
+
+}
diff --git a/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/TimerProducer.java b/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/TimerProducer.java
new file mode 100644
index 0000000..e174b63
--- /dev/null
+++ b/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/TimerProducer.java
@@ -0,0 +1,94 @@
+/**
+ * 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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;
+
+import io.micrometer.core.instrument.MeterRegistry;
+import io.micrometer.core.instrument.Tag;
+import io.micrometer.core.instrument.Timer;
+import io.micrometer.core.instrument.search.Search;
+import org.apache.camel.Exchange;
+import org.apache.camel.Message;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.List;
+import java.util.function.Function;
+
+import static org.apache.camel.component.micrometer.MicrometerConstants.HEADER_TIMER_ACTION;
+
+public class TimerProducer extends AbstractMicrometerProducer<Timer> {
+
+    private static final Logger LOG = LoggerFactory.getLogger(TimerProducer.class);
+
+    public TimerProducer(MicrometerEndpoint endpoint) {
+        super(endpoint);
+    }
+
+    @Override
+    protected Function<Search, Timer> search() {
+        return Search::timer;
+    }
+
+    @Override
+    protected Function<MeterRegistry, Timer> register(String name, List<Tag> tags) {
+        return meterRegistry -> meterRegistry.timer(name, tags);
+    }
+
+    @Override
+    protected void doProcess(Exchange exchange, MicrometerEndpoint endpoint, Timer timer) {
+        String propertyName = getPropertyName(timer.getId().getName());
+        Timer.Sample sample = getTimerSampleFromExchange(exchange, propertyName);
+        sample.stop(timer);
+        exchange.removeProperty(propertyName);
+    }
+
+    @Override
+    protected void doProcess(Exchange exchange, String metricsName, List<Tag> tags) {
+        MeterRegistry registry = getEndpoint().getRegistry();
+        Message in = exchange.getIn();
+        MicrometerTimerAction action = getEndpoint().getAction();
+        MicrometerTimerAction finalAction = in.getHeader(HEADER_TIMER_ACTION, action, MicrometerTimerAction.class);
+        if (finalAction == MicrometerTimerAction.start) {
+            handleStart(exchange, registry, metricsName);
+        } else if (finalAction == MicrometerTimerAction.stop) {
+            if (getTimerSampleFromExchange(exchange, getPropertyName(metricsName)) != null) {
+                doProcess(exchange, getEndpoint(), getMeter(metricsName, tags));
+            }
+        } else {
+            LOG.warn("No action provided for timer \"{}\"", metricsName);
+        }
+    }
+
+    void handleStart(Exchange exchange, MeterRegistry registry, String metricsName) {
+        String propertyName = getPropertyName(metricsName);
+        Timer.Sample sample = getTimerSampleFromExchange(exchange, propertyName);
+        if (sample == null) {
+            sample = Timer.start(registry);
+            exchange.setProperty(propertyName, sample);
+        } else {
+            LOG.warn("Timer \"{}\" already running", metricsName);
+        }
+    }
+
+    String getPropertyName(String metricsName) {
+        return "timer:" + metricsName;
+    }
+
+    Timer.Sample getTimerSampleFromExchange(Exchange exchange, String propertyName) {
+        return exchange.getProperty(propertyName, Timer.Sample.class);
+    }
+}
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
new file mode 100644
index 0000000..d88fdf9
--- /dev/null
+++ b/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/json/AbstractMicrometerService.java
@@ -0,0 +1,116 @@
+package org.apache.camel.component.micrometer.json;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.ObjectWriter;
+import io.micrometer.core.instrument.MeterRegistry;
+import io.micrometer.core.instrument.simple.SimpleMeterRegistry;
+import org.apache.camel.CamelContext;
+import org.apache.camel.component.micrometer.MicrometerComponent;
+import org.apache.camel.spi.Registry;
+import org.apache.camel.support.ServiceSupport;
+import org.apache.camel.util.ObjectHelper;
+
+import java.time.temporal.ChronoUnit;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * @author Christian Ohr
+ */
+public class AbstractMicrometerService extends ServiceSupport {
+
+    private CamelContext camelContext;
+    private MeterRegistry meterRegistry;
+    private boolean prettyPrint = true;
+    private boolean supportAggregablePercentiles;
+    private TimeUnit durationUnit = TimeUnit.MILLISECONDS;
+    private transient ObjectMapper mapper;
+    private transient ObjectMapper secondsMapper;
+
+    public MeterRegistry getMeterRegistry() {
+        return meterRegistry;
+    }
+
+    public void setMeterRegistry(MeterRegistry meterRegistry) {
+        this.meterRegistry = meterRegistry;
+    }
+
+    public CamelContext getCamelContext() {
+        return camelContext;
+    }
+
+    public void setCamelContext(CamelContext camelContext) {
+        this.camelContext = camelContext;
+    }
+
+    public boolean isPrettyPrint() {
+        return prettyPrint;
+    }
+
+    public void setPrettyPrint(boolean prettyPrint) {
+        this.prettyPrint = prettyPrint;
+    }
+
+    public TimeUnit getDurationUnit() {
+        return durationUnit;
+    }
+
+    public void setDurationUnit(TimeUnit durationUnit) {
+        this.durationUnit = durationUnit;
+    }
+
+    public boolean isSupportAggregablePercentiles() {
+        return supportAggregablePercentiles;
+    }
+
+    public void setSupportAggregablePercentiles(boolean supportAggregablePercentiles) {
+        this.supportAggregablePercentiles = supportAggregablePercentiles;
+    }
+
+    public String dumpStatisticsAsJson() {
+        ObjectWriter writer = mapper.writer();
+        if (isPrettyPrint()) {
+            writer = writer.withDefaultPrettyPrinter();
+        }
+        try {
+            return writer.writeValueAsString(getMeterRegistry());
+        } catch (JsonProcessingException e) {
+            throw ObjectHelper.wrapRuntimeCamelException(e);
+        }
+    }
+
+    public String dumpStatisticsAsJsonTimeUnitSeconds() {
+        ObjectWriter writer = secondsMapper.writer();
+        if (isPrettyPrint()) {
+            writer = writer.withDefaultPrettyPrinter();
+        }
+        try {
+            return writer.writeValueAsString(getMeterRegistry());
+        } catch (JsonProcessingException e) {
+            throw ObjectHelper.wrapRuntimeCamelException(e);
+        }
+    }
+
+    @Override
+    protected void doStart() throws Exception {
+        if (meterRegistry == null) {
+            Registry camelRegistry = getCamelContext().getRegistry();
+            meterRegistry = camelRegistry.lookupByNameAndType(MicrometerComponent.METRICS_REGISTRY, MeterRegistry.class);
+            // create a new metricsRegistry by default
+            if (meterRegistry == null) {
+                meterRegistry = new SimpleMeterRegistry();
+            }
+        }
+
+        // json mapper
+        this.mapper = new ObjectMapper().registerModule(new MicrometerModule(getDurationUnit(), supportAggregablePercentiles));
+        this.secondsMapper = getDurationUnit() == TimeUnit.SECONDS ?
+                this.mapper :
+                new ObjectMapper().registerModule(new MicrometerModule(TimeUnit.SECONDS, supportAggregablePercentiles));
+    }
+
+    @Override
+    protected void doStop() throws Exception {
+
+    }
+}
diff --git a/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/json/MicrometerModule.java b/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/json/MicrometerModule.java
new file mode 100644
index 0000000..7487e8f
--- /dev/null
+++ b/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/json/MicrometerModule.java
@@ -0,0 +1,284 @@
+/**
+ * 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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.json;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.core.Version;
+import com.fasterxml.jackson.databind.Module;
+import com.fasterxml.jackson.databind.SerializerProvider;
+import com.fasterxml.jackson.databind.module.SimpleSerializers;
+import com.fasterxml.jackson.databind.ser.std.StdSerializer;
+import io.micrometer.core.instrument.*;
+import io.micrometer.core.instrument.Timer;
+import io.micrometer.core.instrument.composite.CompositeMeterRegistry;
+import io.micrometer.core.instrument.distribution.HistogramSnapshot;
+import io.micrometer.core.instrument.search.Search;
+
+import java.io.IOException;
+import java.util.*;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+/**
+ * @author Christian Ohr
+ */
+public class MicrometerModule extends Module {
+
+    static final Version VERSION = new Version(1, 0, 2, "", "io.micrometer", "micrometer-core");
+
+    private final TimeUnit timeUnit;
+    private final boolean supportAggregablePercentiles;
+
+    public MicrometerModule(TimeUnit timeUnit, boolean supportAggregablePercentiles) {
+        this.timeUnit = timeUnit;
+        this.supportAggregablePercentiles = supportAggregablePercentiles;
+    }
+
+    @Override
+    public String getModuleName() {
+        return "micrometer";
+    }
+
+    @Override
+    public Version version() {
+        return VERSION;
+    }
+
+
+    private static class IdSerializer extends StdSerializer<Meter.Id> {
+        private IdSerializer() {
+            super(Meter.Id.class);
+        }
+
+        @Override
+        public void serialize(Meter.Id id, JsonGenerator json, SerializerProvider provider) throws IOException {
+            json.writeStartObject();
+            json.writeStringField("name", id.getName());
+            json.writeObjectField("tags", id.getTags());
+            json.writeEndObject();
+        }
+    }
+
+    private static class TagSerializer extends StdSerializer<Tag> {
+        private TagSerializer() {
+            super(Tag.class);
+        }
+
+        @Override
+        public void serialize(Tag tag, JsonGenerator json, SerializerProvider provider) throws IOException {
+            json.writeStartObject();
+            json.writeStringField(tag.getKey(), tag.getValue());
+            json.writeEndObject();
+        }
+    }
+
+    private abstract static class MeterSerializer<T extends Meter> extends StdSerializer<T> {
+
+        private MeterSerializer(Class<T> clazz) {
+            super(clazz);
+        }
+
+        @Override
+        public final void serialize(T meter, JsonGenerator json, SerializerProvider provider) throws IOException {
+            json.writeStartObject();
+            json.writeObjectField("id", meter.getId());
+            serializeStatistics(meter, json, provider);
+            json.writeEndObject();
+        }
+
+        protected abstract void serializeStatistics(T meter, JsonGenerator json, SerializerProvider provider) throws IOException;
+    }
+
+    private static class GaugeSerializer extends MeterSerializer<Gauge> {
+        private GaugeSerializer() {
+            super(Gauge.class);
+        }
+
+        @Override
+        protected void serializeStatistics(Gauge gauge, JsonGenerator json, SerializerProvider provider) throws IOException {
+            json.writeNumberField("value", gauge.value());
+        }
+    }
+
+    private static class CounterSerializer extends MeterSerializer<Counter> {
+        private CounterSerializer() {
+            super(Counter.class);
+        }
+
+        @Override
+        protected void serializeStatistics(Counter counter, JsonGenerator json, SerializerProvider provider) throws IOException {
+            json.writeNumberField("count", counter.count());
+        }
+    }
+
+    private static class FunctionCounterSerializer extends MeterSerializer<FunctionCounter> {
+        private FunctionCounterSerializer() {
+            super(FunctionCounter.class);
+        }
+
+        @Override
+        protected void serializeStatistics(FunctionCounter counter, JsonGenerator json, SerializerProvider provider) throws IOException {
+            json.writeNumberField("count", counter.count());
+        }
+    }
+
+    private static class TimerSerializer extends MeterSerializer<AbstractTimer> {
+
+        private final TimeUnit timeUnit;
+        private final boolean supportAggregablePercentiles;
+
+        private TimerSerializer(TimeUnit timeUnit, boolean supportAggregablePercentiles) {
+            super(AbstractTimer.class);
+            this.timeUnit = timeUnit;
+            this.supportAggregablePercentiles = supportAggregablePercentiles;
+        }
+
+        @Override
+        protected void serializeStatistics(AbstractTimer timer, JsonGenerator json, SerializerProvider provider) throws IOException {
+            HistogramSnapshot snapshot = timer.takeSnapshot(supportAggregablePercentiles);
+            json.writeNumberField("count", snapshot.count());
+            json.writeNumberField("max", snapshot.max(timeUnit));
+            json.writeNumberField("mean", snapshot.mean(timeUnit));
+            json.writeNumberField("total", snapshot.total(timeUnit));
+            double[] percentiles = timer.statsConfig().getPercentiles();
+            for (int idx = 0; idx < percentiles.length; idx++) {
+                json.writeNumberField(String.format("p%0.3d", percentiles[idx]), snapshot.percentileValues()[idx].value(timeUnit));
+            }
+        }
+    }
+
+    private static class FunctionTimerSerializer extends MeterSerializer<FunctionTimer> {
+
+        private final TimeUnit timeUnit;
+
+        private FunctionTimerSerializer(TimeUnit timeUnit) {
+            super(FunctionTimer.class);
+            this.timeUnit = timeUnit;
+        }
+
+        @Override
+        protected void serializeStatistics(FunctionTimer timer, JsonGenerator json, SerializerProvider provider) throws IOException {
+            json.writeNumberField("count", timer.count());
+            json.writeNumberField("mean", timer.mean(timeUnit));
+            json.writeNumberField("total", timer.totalTime(timeUnit));
+        }
+    }
+
+    private static class LongTaskTimerSerializer extends MeterSerializer<LongTaskTimer> {
+
+        private final TimeUnit timeUnit;
+
+        private LongTaskTimerSerializer(TimeUnit timeUnit) {
+            super(LongTaskTimer.class);
+            this.timeUnit = timeUnit;
+        }
+
+        @Override
+        protected void serializeStatistics(LongTaskTimer timer, JsonGenerator json, SerializerProvider provider) throws IOException {
+            json.writeNumberField("activeTasks", timer.activeTasks());
+            json.writeNumberField("duration", timer.duration(timeUnit));
+        }
+    }
+
+    private static class DistributionSummarySerializer extends MeterSerializer<AbstractDistributionSummary> {
+
+        private final TimeUnit timeUnit;
+        private final boolean supportAggregablePercentiles;
+
+        public DistributionSummarySerializer(TimeUnit timeUnit, boolean supportAggregablePercentiles) {
+            super(AbstractDistributionSummary.class);
+            this.timeUnit = timeUnit;
+            this.supportAggregablePercentiles = supportAggregablePercentiles;
+        }
+
+        @Override
+        protected void serializeStatistics(AbstractDistributionSummary distributionSummary,
+                                           JsonGenerator json,
+                                           SerializerProvider provider) throws IOException {
+            HistogramSnapshot snapshot = distributionSummary.takeSnapshot(supportAggregablePercentiles);
+            json.writeNumberField("count", snapshot.count());
+            json.writeNumberField("max", snapshot.max(timeUnit));
+            json.writeNumberField("mean", snapshot.mean(timeUnit));
+            json.writeNumberField("total", snapshot.total(timeUnit));
+            double[] percentiles = distributionSummary.statsConfig().getPercentiles();
+            for (int idx = 0; idx < percentiles.length; idx++) {
+                json.writeNumberField(String.format("p%0.3d", percentiles[idx]), snapshot.percentileValues()[idx].value(timeUnit));
+            }
+        }
+    }
+
+
+    private static class MeterRegistrySerializer extends StdSerializer<MeterRegistry> {
+
+        private MeterRegistrySerializer() {
+            super(MeterRegistry.class);
+        }
+
+
+        @Override
+        public void serialize(MeterRegistry registry,
+                              JsonGenerator json,
+                              SerializerProvider provider) throws IOException {
+
+            json.writeStartObject();
+            json.writeStringField("version", VERSION.toString());
+            json.writeObjectField("gauges", meters(registry, Gauge.class));
+            json.writeObjectField("counters", meters(registry, Counter.class));
+            json.writeObjectField("functionCounters", meters(registry, FunctionCounter.class));
+            json.writeObjectField("timers", meters(registry, Timer.class));
+            json.writeObjectField("functionTimers", meters(registry, FunctionTimer.class));
+            json.writeObjectField("longTaskTimers", meters(registry, LongTaskTimer.class));
+            json.writeObjectField("distributionSummaries", meters(registry, DistributionSummary.class));
+            json.writeEndObject();
+        }
+
+        private Map<String, List<Meter>> meters(MeterRegistry meterRegistry, Class<? extends Meter> clazz) {
+            if (meterRegistry instanceof CompositeMeterRegistry) {
+                Map<String, List<Meter>> map = new TreeMap<>();
+                ((CompositeMeterRegistry) meterRegistry).getRegistries().forEach(reg ->
+                        meters(meterRegistry, clazz).entrySet().forEach(
+                                entry -> map.merge(entry.getKey(), entry.getValue(), (m1, m2) ->
+                                        Stream.concat(m1.stream(), m2.stream()).collect(Collectors.toList()))
+                        ));
+                return map;
+            }
+            return Search.in(meterRegistry).meters().stream()
+                    .filter(clazz::isInstance)
+                    .collect(Collectors.toMap(
+                            meter -> meter.getId().getName(),
+                            meter -> Collections.singletonList(meter)));
+        }
+    }
+
+
+    @Override
+    public void setupModule(SetupContext setupContext) {
+        setupContext.addSerializers(new SimpleSerializers(Arrays.asList(
+                new IdSerializer(),
+                new TagSerializer(),
+                new GaugeSerializer(),
+                new CounterSerializer(),
+                new FunctionCounterSerializer(),
+                new TimerSerializer(timeUnit, supportAggregablePercentiles),
+                new FunctionTimerSerializer(timeUnit),
+                new LongTaskTimerSerializer(timeUnit),
+                new DistributionSummarySerializer(timeUnit, supportAggregablePercentiles),
+                new MeterRegistrySerializer()
+        )));
+    }
+}
diff --git a/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/messagehistory/MicrometerMessageHistory.java b/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/messagehistory/MicrometerMessageHistory.java
new file mode 100644
index 0000000..200b204
--- /dev/null
+++ b/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/messagehistory/MicrometerMessageHistory.java
@@ -0,0 +1,63 @@
+/**
+ * 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.messagehistory;
+
+import io.micrometer.core.instrument.MeterRegistry;
+import io.micrometer.core.instrument.Tag;
+import io.micrometer.core.instrument.Timer;
+import org.apache.camel.MessageHistory;
+import org.apache.camel.NamedNode;
+import org.apache.camel.impl.DefaultMessageHistory;
+
+import java.time.Duration;
+import java.util.Arrays;
+
+/**
+ * A micrometer metrics based {@link MessageHistory}
+ */
+public class MicrometerMessageHistory extends DefaultMessageHistory {
+
+    private final Timer.Sample sample;
+    private final MeterRegistry meterRegistry;
+    private final String name;
+
+    private Duration minimumExpectedValue = Duration.ofMillis(1L);
+    private Duration maximumExpectedValue = Duration.ofSeconds(30L);
+
+    public MicrometerMessageHistory(MeterRegistry meterRegistry, String routeId, NamedNode namedNode, String name, long timestamp) {
+        super(routeId, namedNode, timestamp);
+        this.meterRegistry = meterRegistry;
+        this.name = name;
+        this.sample = Timer.start(meterRegistry);
+    }
+
+    @Override
+    public void nodeProcessingDone() {
+        super.nodeProcessingDone();
+        Timer timer = Timer.builder(name)
+                .tag("camelService", "messageHistory")
+                .tag("routeId", getRouteId())
+                .tag("nodeId", getNode().getId())
+                .register(meterRegistry);
+        sample.stop(timer);
+    }
+
+    public String toString() {
+        return "MicrometerMessageHistory[routeId=" + getRouteId() + ", node=" + getNode().getId() + ']';
+    }
+
+}
diff --git a/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/messagehistory/MicrometerMessageHistoryFactory.java b/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/messagehistory/MicrometerMessageHistoryFactory.java
new file mode 100644
index 0000000..6c9661d4
--- /dev/null
+++ b/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/messagehistory/MicrometerMessageHistoryFactory.java
@@ -0,0 +1,156 @@
+/**
+ * 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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.messagehistory;
+
+import io.micrometer.core.instrument.MeterRegistry;
+import org.apache.camel.*;
+import org.apache.camel.component.micrometer.MicrometerConstants;
+import org.apache.camel.spi.MessageHistoryFactory;
+import org.apache.camel.support.ServiceSupport;
+import org.apache.camel.util.ObjectHelper;
+
+import java.util.Date;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * A factory to setup and use {@link MicrometerMessageHistory} as message history implementation.
+ */
+public class MicrometerMessageHistoryFactory extends ServiceSupport implements CamelContextAware, StaticService, NonManagedService, MessageHistoryFactory {
+
+    private CamelContext camelContext;
+    private MicrometerMessageHistoryService messageHistoryService;
+    private MeterRegistry meterRegistry;
+    private boolean prettyPrint = true;
+    private TimeUnit durationUnit = TimeUnit.MILLISECONDS;
+    private String prefix = MicrometerConstants.HEADER_PREFIX;
+    private String namePattern = "##prefix##.##name##.##routeId##.##id##.##type##";
+
+    @Override
+    public CamelContext getCamelContext() {
+        return camelContext;
+    }
+
+    @Override
+    public void setCamelContext(CamelContext camelContext) {
+        this.camelContext = camelContext;
+    }
+
+    public MeterRegistry getMeterRegistry() {
+        return meterRegistry;
+    }
+
+    /**
+     * To use a specific {@link MeterRegistry} instance.
+     * <p/>
+     * If no instance has been configured, then Camel will create a shared instance to be used.
+     */
+    public void setMeterRegistry(MeterRegistry meterRegistry) {
+        this.meterRegistry = meterRegistry;
+    }
+
+    public boolean isPrettyPrint() {
+        return prettyPrint;
+    }
+
+    /**
+     * Whether to use pretty print when outputting JSon
+     */
+    public void setPrettyPrint(boolean prettyPrint) {
+        this.prettyPrint = prettyPrint;
+    }
+
+    public TimeUnit getDurationUnit() {
+        return durationUnit;
+    }
+
+    /**
+     * Sets the time unit to use for timing the duration of processing a message in the route
+     */
+    public void setDurationUnit(TimeUnit durationUnit) {
+        this.durationUnit = durationUnit;
+    }
+
+    public String getNamePattern() {
+        return namePattern;
+    }
+
+    public void setNamePattern(String namePattern) {
+        this.namePattern = namePattern;
+    }
+
+    public String getPrefix() {
+        return prefix;
+    }
+
+    public void setPrefix(String prefix) {
+        this.prefix = prefix;
+    }
+
+    @Override
+    @Deprecated
+    public MessageHistory newMessageHistory(String routeId, NamedNode namedNode, Date date) {
+        return newMessageHistory(routeId, namedNode, date.getTime());
+    }
+
+    @Override
+    public MessageHistory newMessageHistory(String routeId, NamedNode namedNode, long timestamp) {
+        MicrometerMessageHistory messageHistory = new MicrometerMessageHistory(meterRegistry, routeId, namedNode,
+                createName("history", routeId, namedNode.getId()),
+                timestamp);
+        return messageHistory;
+    }
+
+    private String createName(String type, String routeId, String id) {
+        String name = camelContext.getManagementName() != null ? camelContext.getManagementName() : camelContext.getName();
+
+        String answer = namePattern;
+        answer = answer.replaceFirst("##prefix##", name);
+        answer = answer.replaceFirst("##name##", name);
+        answer = answer.replaceFirst("##routeId##", routeId);
+        answer = answer.replaceFirst("##id##", id);
+        answer = answer.replaceFirst("##type##", type);
+        return answer;
+    }
+
+    @Override
+    protected void doStart() throws Exception {
+        try {
+            messageHistoryService = camelContext.hasService(MicrometerMessageHistoryService.class);
+            if (messageHistoryService == null) {
+                messageHistoryService = new MicrometerMessageHistoryService();
+                messageHistoryService.setMeterRegistry(getMeterRegistry());
+                messageHistoryService.setPrettyPrint(isPrettyPrint());
+                messageHistoryService.setDurationUnit(getDurationUnit());
+                camelContext.addService(messageHistoryService);
+            }
+        } catch (Exception e) {
+            throw ObjectHelper.wrapRuntimeCamelException(e);
+        }
+
+        // use metrics registry from service if not explicit configured
+        if (meterRegistry == null) {
+            meterRegistry = messageHistoryService.getMeterRegistry();
+        }
+
+        ObjectHelper.notNull(meterRegistry, "meterRegistry", this);
+    }
+
+    @Override
+    protected void doStop() throws Exception {
+        // noop
+    }
+}
diff --git a/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/messagehistory/MicrometerMessageHistoryMBean.java b/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/messagehistory/MicrometerMessageHistoryMBean.java
new file mode 100644
index 0000000..a1e1467
--- /dev/null
+++ b/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/messagehistory/MicrometerMessageHistoryMBean.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.messagehistory;
+
+import org.apache.camel.api.management.ManagedOperation;
+
+public interface MicrometerMessageHistoryMBean {
+
+    @ManagedOperation(description = "Dumps the statistics as json")
+    String dumpStatisticsAsJson();
+
+    @ManagedOperation(description = "Dumps the statistics as json using seconds for time units")
+    String dumpStatisticsAsJsonTimeUnitSeconds();
+
+}
diff --git a/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/messagehistory/MicrometerMessageHistoryService.java b/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/messagehistory/MicrometerMessageHistoryService.java
new file mode 100644
index 0000000..f0f84e3
--- /dev/null
+++ b/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/messagehistory/MicrometerMessageHistoryService.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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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.messagehistory;
+
+import org.apache.camel.CamelContextAware;
+import org.apache.camel.StaticService;
+import org.apache.camel.api.management.ManagedResource;
+import org.apache.camel.component.micrometer.json.AbstractMicrometerService;
+
+/**
+ * Service holding the {@link MicrometerMessageHistory} which registers all message history metrics.
+ */
+@ManagedResource(description = "MicrometerMessageHistory")
+public final class MicrometerMessageHistoryService extends AbstractMicrometerService implements CamelContextAware, StaticService, MicrometerMessageHistoryMBean {
+}
\ No newline at end of file
diff --git a/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/routepolicy/MicrometerRegistryMBean.java b/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/routepolicy/MicrometerRegistryMBean.java
new file mode 100644
index 0000000..ad05864
--- /dev/null
+++ b/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/routepolicy/MicrometerRegistryMBean.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.api.management.ManagedOperation;
+
+public interface MicrometerRegistryMBean {
+
+    @ManagedOperation(description = "Dumps the statistics as json")
+    String dumpStatisticsAsJson();
+
+    @ManagedOperation(description = "Dumps the statistics as json using seconds for time units")
+    String dumpStatisticsAsJsonTimeUnitSeconds();
+
+}
diff --git a/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/routepolicy/MicrometerRegistryService.java b/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/routepolicy/MicrometerRegistryService.java
new file mode 100644
index 0000000..c9d2385
--- /dev/null
+++ b/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/routepolicy/MicrometerRegistryService.java
@@ -0,0 +1,30 @@
+/**
+ * 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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.CamelContextAware;
+import org.apache.camel.StaticService;
+import org.apache.camel.api.management.ManagedResource;
+import org.apache.camel.component.micrometer.json.AbstractMicrometerService;
+
+/**
+ * Service holding the {@link MeterRegistry} which registers all metrics.
+ */
+@ManagedResource(description = "MicrometerRegistry")
+public final class MicrometerRegistryService extends AbstractMicrometerService implements CamelContextAware, StaticService, MicrometerRegistryMBean {
+}
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
new file mode 100644
index 0000000..6cb8bfb
--- /dev/null
+++ b/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/routepolicy/MicrometerRoutePolicy.java
@@ -0,0 +1,185 @@
+/**
+ * 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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 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;
+import org.apache.camel.component.micrometer.MicrometerConstants;
+import org.apache.camel.support.RoutePolicySupport;
+import org.apache.camel.util.ObjectHelper;
+import org.apache.camel.util.ServiceHelper;
+
+import java.util.concurrent.TimeUnit;
+import java.util.regex.Matcher;
+
+/**
+ * A {@link org.apache.camel.spi.RoutePolicy} which gathers statistics and reports them using {@link MeterRegistry}.
+ * <p/>
+ * The metrics is reported in JMX by default, but this can be configured.
+ */
+public class MicrometerRoutePolicy extends RoutePolicySupport implements NonManagedService {
+
+    private MeterRegistry meterRegistry;
+    private MicrometerRegistryService registryService;
+    private boolean prettyPrint;
+    private TimeUnit durationUnit = TimeUnit.MILLISECONDS;
+    private MetricsStatistics statistics;
+    private Route route;
+    private String prefix = MicrometerConstants.HEADER_PREFIX;
+    private String namePattern = "##name##.##routeId##.##type##";
+
+
+    private static final class MetricsStatistics {
+        private final MeterRegistry meterRegistry;
+        private final Route route;
+        private final String name;
+
+        private MetricsStatistics(MeterRegistry meterRegistry, Route route, String name) {
+            this.meterRegistry = meterRegistry;
+            this.route = route;
+            this.name = name;
+        }
+
+        public void onExchangeBegin(Exchange exchange) {
+            Timer.Sample sample = Timer.start(meterRegistry);
+            exchange.setProperty("MicrometerRoutePolicy-" + route.getId(), sample);
+        }
+
+        public void onExchangeDone(Exchange exchange) {
+            Timer.Sample sample = (Timer.Sample) exchange.removeProperty("MicrometerRoutePolicy-" + route.getId());
+            if (sample != null) {
+                Timer timer = Timer.builder(name)
+                        .description(route.getDescription())
+                        .tag("camelService", "routePolicy")
+                        .tag("route", route.getId())
+                        .register(meterRegistry);
+                sample.stop(timer);
+            }
+        }
+
+    }
+
+
+    public MeterRegistry getMeterRegistry() {
+        return meterRegistry;
+    }
+
+    public void setMeterRegistry(MeterRegistry meterRegistry) {
+        this.meterRegistry = meterRegistry;
+    }
+
+    public boolean isPrettyPrint() {
+        return prettyPrint;
+    }
+
+    public void setPrettyPrint(boolean prettyPrint) {
+        this.prettyPrint = prettyPrint;
+    }
+
+    public TimeUnit getDurationUnit() {
+        return durationUnit;
+    }
+
+    public void setDurationUnit(TimeUnit durationUnit) {
+        this.durationUnit = durationUnit;
+    }
+
+    public String getNamePattern() {
+        return namePattern;
+    }
+
+    public String getPrefix() {
+        return prefix;
+    }
+
+    public void setPrefix(String prefix) {
+        this.prefix = prefix;
+    }
+
+    /**
+     * The name pattern to use.
+     * <p/>
+     * Uses dot as separators, but you can change that.
+     * The values <tt>##name##</tt>, <tt>##routeId##</tt>, and <tt>##type##</tt> will be replaced with actual value.
+     */
+    public void setNamePattern(String namePattern) {
+        this.namePattern = namePattern;
+    }
+
+    @Override
+    public void onInit(Route route) {
+        super.onInit(route);
+
+        this.route = route;
+        try {
+            registryService = route.getRouteContext().getCamelContext().hasService(MicrometerRegistryService.class);
+            if (registryService == null) {
+                registryService = new MicrometerRegistryService();
+                registryService.setMeterRegistry(getMeterRegistry());
+                registryService.setPrettyPrint(isPrettyPrint());
+                registryService.setDurationUnit(getDurationUnit());
+                route.getRouteContext().getCamelContext().addService(registryService);
+            }
+        } catch (Exception e) {
+            throw ObjectHelper.wrapRuntimeCamelException(e);
+        }
+
+        // ensure registry service is started
+        try {
+            ServiceHelper.startService(registryService);
+        } catch (Exception e) {
+            throw ObjectHelper.wrapRuntimeCamelException(e);
+        }
+
+        // create statistics holder
+        // for know we record only all the timings of a complete exchange (responses)
+        // we have in-flight / total statistics already from camel-core
+        statistics = new MetricsStatistics(meterRegistry, route, createName("responses"));
+    }
+
+    private String createName(String type) {
+        CamelContext context = route.getRouteContext().getCamelContext();
+        String name = context.getManagementName() != null ? context.getManagementName() : context.getName();
+
+        String answer = namePattern;
+        answer = answer.replaceFirst("##prefix##", prefix);
+        answer = answer.replaceFirst("##name##", name);
+        answer = answer.replaceFirst("##routeId##", Matcher.quoteReplacement(route.getId()));
+        answer = answer.replaceFirst("##type##", type);
+        return answer;
+    }
+
+    @Override
+    public void onExchangeBegin(Route route, Exchange exchange) {
+
+        if (statistics != null) {
+            statistics.onExchangeBegin(exchange);
+        }
+    }
+
+    @Override
+    public void onExchangeDone(Route route, Exchange exchange) {
+        if (statistics != null) {
+            statistics.onExchangeDone(exchange);
+        }
+    }
+
+}
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
new file mode 100644
index 0000000..fbe485c
--- /dev/null
+++ b/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/routepolicy/MicrometerRoutePolicyFactory.java
@@ -0,0 +1,104 @@
+/**
+ * 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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.component.micrometer.MicrometerConstants;
+import org.apache.camel.model.RouteDefinition;
+import org.apache.camel.spi.RoutePolicy;
+import org.apache.camel.spi.RoutePolicyFactory;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * A {@link org.apache.camel.spi.RoutePolicyFactory} to plugin and use metrics for gathering route utilization statistics
+ */
+public class MicrometerRoutePolicyFactory implements RoutePolicyFactory {
+
+    private MeterRegistry meterRegistry;
+    private boolean prettyPrint = true;
+    private TimeUnit durationUnit = TimeUnit.MILLISECONDS;
+    private String prefix = MicrometerConstants.HEADER_PREFIX;
+    private String namePattern = "##prefix##.##name##.##routeId##.##type##";
+
+    /**
+     * To use a specific {@link io.micrometer.core.instrument.MeterRegistry} instance.
+     * <p/>
+     * If no instance has been configured, then Camel will create a shared instance to be used.
+     */
+    public void setMeterRegistry(MeterRegistry meterRegistry) {
+        this.meterRegistry = meterRegistry;
+    }
+
+    public MeterRegistry getMeterRegistry() {
+        return meterRegistry;
+    }
+
+    public boolean isPrettyPrint() {
+        return prettyPrint;
+    }
+
+    /**
+     * Whether to use pretty print when outputting JSon
+     */
+    public void setPrettyPrint(boolean prettyPrint) {
+        this.prettyPrint = prettyPrint;
+    }
+
+    /**
+     * Sets the time unit to use for requests per unit (eg requests per second)
+     */
+    public TimeUnit getDurationUnit() {
+        return durationUnit;
+    }
+
+    /**
+     * Sets the time unit to use for timing the duration of processing a message in the route
+     */
+    public void setDurationUnit(TimeUnit durationUnit) {
+        this.durationUnit = durationUnit;
+    }
+
+    public String getNamePattern() {
+        return namePattern;
+    }
+
+    public void setNamePattern(String namePattern) {
+        this.namePattern = namePattern;
+    }
+
+    public String getPrefix() {
+        return prefix;
+    }
+
+    public void setPrefix(String prefix) {
+        this.prefix = prefix;
+    }
+
+    @Override
+    public RoutePolicy createRoutePolicy(CamelContext camelContext, String routeId, RouteDefinition routeDefinition) {
+        MicrometerRoutePolicy answer = new MicrometerRoutePolicy();
+        answer.setMeterRegistry(getMeterRegistry());
+        answer.setPrettyPrint(isPrettyPrint());
+        answer.setDurationUnit(getDurationUnit());
+        answer.setPrefix(prefix);
+        answer.setNamePattern(namePattern);
+        return answer;
+    }
+
+}
diff --git a/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/spi/InstrumentedThreadPoolFactory.java b/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/spi/InstrumentedThreadPoolFactory.java
new file mode 100644
index 0000000..0120586
--- /dev/null
+++ b/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/spi/InstrumentedThreadPoolFactory.java
@@ -0,0 +1,79 @@
+/**
+ * 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.spi;
+
+import java.util.Collections;
+import java.util.concurrent.*;
+import java.util.concurrent.atomic.AtomicLong;
+
+import io.micrometer.core.instrument.MeterRegistry;
+import io.micrometer.core.instrument.binder.jvm.ExecutorServiceMetrics;
+import org.apache.camel.impl.DefaultThreadPoolFactory;
+import org.apache.camel.spi.ThreadPoolFactory;
+import org.apache.camel.spi.ThreadPoolProfile;
+import org.apache.camel.util.ObjectHelper;
+
+/**
+ * This implements a {@link ThreadPoolFactory} and generates an Instrumented versions of ExecutorService used to
+ * monitor performance of each thread using Metrics.
+ */
+public class InstrumentedThreadPoolFactory implements ThreadPoolFactory {
+
+    private static final AtomicLong counter = new AtomicLong();
+    private MeterRegistry meterRegistry;
+    private ThreadPoolFactory threadPoolFactory;
+    private String prefix = "instrumented-delegate-";
+
+    public InstrumentedThreadPoolFactory(MeterRegistry meterRegistry) {
+        this(meterRegistry, new DefaultThreadPoolFactory());
+    }
+
+    public InstrumentedThreadPoolFactory(MeterRegistry metricRegistry, ThreadPoolFactory threadPoolFactory) {
+        ObjectHelper.notNull(metricRegistry, "meterRegistry", this);
+        ObjectHelper.notNull(threadPoolFactory, "threadPoolFactory", this);
+        this.meterRegistry = metricRegistry;
+        this.threadPoolFactory = threadPoolFactory;
+    }
+
+    @Override
+    public ExecutorService newCachedThreadPool(ThreadFactory threadFactory) {
+        ExecutorService executorService = threadPoolFactory.newCachedThreadPool(threadFactory);
+        return ExecutorServiceMetrics.monitor(meterRegistry, executorService, name(prefix));
+    }
+
+    @Override
+    public ExecutorService newThreadPool(ThreadPoolProfile profile, ThreadFactory threadFactory) {
+        ExecutorService executorService = threadPoolFactory.newThreadPool(profile, threadFactory);
+        return ExecutorServiceMetrics.monitor(meterRegistry, executorService, name(profile.getId()));
+    }
+
+    @Override
+    public ScheduledExecutorService newScheduledThreadPool(ThreadPoolProfile profile, ThreadFactory threadFactory) {
+        ScheduledExecutorService executorService = threadPoolFactory.newScheduledThreadPool(profile, threadFactory);
+        new ExecutorServiceMetrics(executorService, name(profile.getId()), Collections.emptySet()).bindTo(meterRegistry);
+        return new TimedScheduledExecutorService(meterRegistry, executorService, name(prefix), Collections.emptySet());
+    }
+
+    public void setPrefix(String prefix) {
+        this.prefix = prefix;
+    }
+
+    private String name(String prefix) {
+        return prefix + counter.incrementAndGet();
+    }
+
+}
diff --git a/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/spi/TimedScheduledExecutorService.java b/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/spi/TimedScheduledExecutorService.java
new file mode 100644
index 0000000..f47248f
--- /dev/null
+++ b/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/spi/TimedScheduledExecutorService.java
@@ -0,0 +1,48 @@
+package org.apache.camel.component.micrometer.spi;
+
+import io.micrometer.core.instrument.MeterRegistry;
+import io.micrometer.core.instrument.Tag;
+import io.micrometer.core.instrument.Tags;
+import io.micrometer.core.instrument.Timer;
+import io.micrometer.core.instrument.internal.TimedExecutorService;
+
+import java.util.concurrent.Callable;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * @author Christian Ohr
+ */
+public class TimedScheduledExecutorService extends TimedExecutorService implements ScheduledExecutorService {
+
+    private final ScheduledExecutorService delegate;
+    private final Timer timer;
+
+    public TimedScheduledExecutorService(MeterRegistry registry, ScheduledExecutorService delegate, String executorServiceName, Iterable<Tag> tags) {
+        super(registry, delegate, executorServiceName, tags);
+        this.delegate = delegate;
+        this.timer = registry.timer("executor",
+                Tags.concat(tags, "name", executorServiceName));
+    }
+
+    @Override
+    public ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit) {
+        return delegate.schedule(timer.wrap(command), delay, unit);
+    }
+
+    @Override
+    public <V> ScheduledFuture<V> schedule(Callable<V> callable, long delay, TimeUnit unit) {
+        return delegate.schedule(timer.wrap(callable), delay, unit);
+    }
+
+    @Override
+    public ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit) {
+        return delegate.scheduleAtFixedRate(timer.wrap(command), initialDelay, period, unit);
+    }
+
+    @Override
+    public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit) {
+        return delegate.scheduleWithFixedDelay(timer.wrap(command), initialDelay, delay, unit);
+    }
+}
diff --git a/components/camel-micrometer/src/main/resources/META-INF/services/org/apache/camel/component/micrometer b/components/camel-micrometer/src/main/resources/META-INF/services/org/apache/camel/component/micrometer
new file mode 100644
index 0000000..9aefc06
--- /dev/null
+++ b/components/camel-micrometer/src/main/resources/META-INF/services/org/apache/camel/component/micrometer
@@ -0,0 +1,18 @@
+#
+# 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.
+#
+
+class=org.apache.camel.component.micrometer.MicrometerComponent
diff --git a/components/camel-micrometer/src/test/java/org/apache/camel/component/micrometer/AbstractMicrometerProducerTest.java b/components/camel-micrometer/src/test/java/org/apache/camel/component/micrometer/AbstractMicrometerProducerTest.java
new file mode 100644
index 0000000..5343e5f
--- /dev/null
+++ b/components/camel-micrometer/src/test/java/org/apache/camel/component/micrometer/AbstractMicrometerProducerTest.java
@@ -0,0 +1,214 @@
+/**
+ * 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;
+
+import io.micrometer.core.instrument.Meter;
+import io.micrometer.core.instrument.MeterRegistry;
+import io.micrometer.core.instrument.Tag;
+import org.apache.camel.Exchange;
+import org.apache.camel.Message;
+import org.apache.camel.impl.DefaultCamelContext;
+import org.apache.camel.impl.DefaultMessage;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.InOrder;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.junit.MockitoJUnitRunner;
+
+import java.util.List;
+import java.util.function.Function;
+
+import static org.apache.camel.component.micrometer.AbstractMicrometerProducer.HEADER_PATTERN;
+import static org.apache.camel.component.micrometer.MicrometerConstants.HEADER_HISTOGRAM_VALUE;
+import static org.apache.camel.component.micrometer.MicrometerConstants.HEADER_METRIC_NAME;
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertThat;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.when;
+
+@RunWith(MockitoJUnitRunner.class)
+public class AbstractMicrometerProducerTest {
+
+    public static final String METRIC_NAME = "a metric";
+
+    @Mock
+    private MicrometerEndpoint endpoint;
+
+    @Mock
+    private Exchange exchange;
+
+    @Mock
+    private Message in;
+
+    @Mock
+    private MeterRegistry registry;
+
+    private AbstractMicrometerProducer okProducer;
+
+    private AbstractMicrometerProducer failProducer;
+
+    private InOrder inOrder;
+
+    @Before
+    public void setUp() throws Exception {
+        okProducer = new AbstractMicrometerProducer(endpoint) {
+
+            @Override
+            protected Function search() {
+                return null;
+            }
+
+            @Override
+            protected Function register(String name, List list) {
+                return null;
+            }
+
+            @Override
+            protected void doProcess(Exchange exchange, MicrometerEndpoint endpoint, Meter meter) {
+            }
+        };
+        failProducer = new AbstractMicrometerProducer(endpoint) {
+
+            @Override
+            protected Function search() {
+                return null;
+            }
+
+            @Override
+            protected Function register(String name, List list) {
+                return null;
+            }
+
+            @Override
+            protected void doProcess(Exchange exchange, MicrometerEndpoint endpoint, Meter meter) {
+                throw new RuntimeException("Muchos problemos");
+            }
+        };
+        inOrder = Mockito.inOrder(endpoint, exchange, in, registry);
+        when(exchange.getIn()).thenReturn(in);
+        when(endpoint.getMetricsName()).thenReturn(METRIC_NAME);
+        when(endpoint.getRegistry()).thenReturn(registry);
+    }
+
+    @Test
+    public void testDoProcess() throws Exception {
+        when(in.getHeader(HEADER_METRIC_NAME, String.class)).thenReturn(null);
+        when(in.removeHeaders(HEADER_PATTERN)).thenReturn(true);
+        okProducer.process(exchange);
+        inOrder.verify(exchange, times(1)).getIn();
+        inOrder.verify(endpoint, times(1)).getMetricsName();
+        inOrder.verify(in, times(1)).getHeader(HEADER_METRIC_NAME, String.class);
+        inOrder.verify(endpoint, times(1)).getRegistry();
+        inOrder.verify(in, times(1)).removeHeaders(HEADER_PATTERN);
+        inOrder.verifyNoMoreInteractions();
+    }
+
+    @Test
+    public void testDoProcessWithException() throws Exception {
+        when(in.getHeader(HEADER_METRIC_NAME, String.class)).thenReturn(null);
+        when(in.removeHeaders(HEADER_PATTERN)).thenReturn(true);
+        failProducer.process(exchange);
+        inOrder.verify(exchange, times(1)).getIn();
+        inOrder.verify(endpoint, times(1)).getMetricsName();
+        inOrder.verify(in, times(1)).getHeader(HEADER_METRIC_NAME, String.class);
+        inOrder.verify(endpoint, times(1)).getRegistry();
+        inOrder.verify(in, times(1)).removeHeaders(HEADER_PATTERN);
+        inOrder.verifyNoMoreInteractions();
+    }
+
+    @Test
+    public void testGetMetricsName() throws Exception {
+        when(in.getHeader(HEADER_METRIC_NAME, String.class)).thenReturn("A");
+        assertThat(okProducer.getMetricsName(in, "value"), is("A"));
+        inOrder.verify(in, times(1)).getHeader(HEADER_METRIC_NAME, String.class);
+        inOrder.verifyNoMoreInteractions();
+    }
+
+    @Test
+    public void testGetMetricsNameNotSet() throws Exception {
+        when(in.getHeader(HEADER_METRIC_NAME, String.class)).thenReturn(null);
+        assertThat(okProducer.getMetricsName(in, "name"), is("name"));
+        inOrder.verify(in, times(1)).getHeader(HEADER_METRIC_NAME, String.class);
+        inOrder.verifyNoMoreInteractions();
+    }
+
+    @Test
+    public void testGetStringHeader() throws Exception {
+        when(in.getHeader(HEADER_METRIC_NAME, String.class)).thenReturn("A");
+        assertThat(okProducer.getStringHeader(in, HEADER_METRIC_NAME, "value"), is("A"));
+        inOrder.verify(in, times(1)).getHeader(HEADER_METRIC_NAME, String.class);
+        inOrder.verifyNoMoreInteractions();
+    }
+
+    @Test
+    public void testGetStringHeaderWithNullValue() throws Exception {
+        when(in.getHeader(HEADER_METRIC_NAME, String.class)).thenReturn(null);
+        assertThat(okProducer.getStringHeader(in, HEADER_METRIC_NAME, "value"), is("value"));
+        inOrder.verify(in, times(1)).getHeader(HEADER_METRIC_NAME, String.class);
+        inOrder.verifyNoMoreInteractions();
+    }
+
+    @Test
+    public void testGetStringHeaderWithWhiteSpaces() throws Exception {
+        when(in.getHeader(HEADER_METRIC_NAME, String.class)).thenReturn(" ");
+        assertThat(okProducer.getStringHeader(in, HEADER_METRIC_NAME, "value"), is("value"));
+        inOrder.verify(in, times(1)).getHeader(HEADER_METRIC_NAME, String.class);
+        inOrder.verifyNoMoreInteractions();
+    }
+
+    @Test
+    public void testGetStringHeaderWithEmptySrting() throws Exception {
+        when(in.getHeader(HEADER_METRIC_NAME, String.class)).thenReturn("");
+        assertThat(okProducer.getStringHeader(in, HEADER_METRIC_NAME, "value"), is("value"));
+        inOrder.verify(in, times(1)).getHeader(HEADER_METRIC_NAME, String.class);
+        inOrder.verifyNoMoreInteractions();
+    }
+
+    @Test
+    public void testGetLongHeader() throws Exception {
+        when(in.getHeader(HEADER_HISTOGRAM_VALUE, 19L, Long.class)).thenReturn(201L);
+        assertThat(okProducer.getLongHeader(in, HEADER_HISTOGRAM_VALUE, 19L), is(201L));
+        inOrder.verify(in, times(1)).getHeader(HEADER_HISTOGRAM_VALUE, 19L, Long.class);
+        inOrder.verifyNoMoreInteractions();
+    }
+
+    @Test
+    public void testClearMetricsHeaders() throws Exception {
+        when(in.removeHeaders(HEADER_PATTERN)).thenReturn(true);
+        assertThat(okProducer.clearMetricsHeaders(in), is(true));
+        inOrder.verify(in, times(1)).removeHeaders(HEADER_PATTERN);
+        inOrder.verifyNoMoreInteractions();
+    }
+
+    @Test
+    public void testClearRealHeaders() throws Exception {
+        Message msg = new DefaultMessage(new DefaultCamelContext());
+        Object val = new Object();
+        msg.setHeader(HEADER_HISTOGRAM_VALUE, 109L);
+        msg.setHeader(HEADER_METRIC_NAME, "the metric");
+        msg.setHeader("notRemoved", val);
+        assertThat(msg.getHeaders().size(), is(3));
+        assertThat(msg.getHeader(HEADER_HISTOGRAM_VALUE, Long.class), is(109L));
+        assertThat(msg.getHeader(HEADER_METRIC_NAME, String.class), is("the metric"));
+        assertThat(msg.getHeader("notRemoved"), is(val));
+        okProducer.clearMetricsHeaders(msg);
+        assertThat(msg.getHeaders().size(), is(1));
+        assertThat(msg.getHeader("notRemoved"), is(val));
+    }
+}
diff --git a/components/camel-micrometer/src/test/java/org/apache/camel/component/micrometer/CounterEndpointTest.java b/components/camel-micrometer/src/test/java/org/apache/camel/component/micrometer/CounterEndpointTest.java
new file mode 100644
index 0000000..6817798
--- /dev/null
+++ b/components/camel-micrometer/src/test/java/org/apache/camel/component/micrometer/CounterEndpointTest.java
@@ -0,0 +1,98 @@
+/**
+ * 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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;
+
+import io.micrometer.core.instrument.MeterRegistry;
+import org.apache.camel.Producer;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.InOrder;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.junit.MockitoJUnitRunner;
+
+import java.util.Collections;
+
+import static org.hamcrest.Matchers.*;
+import static org.junit.Assert.assertThat;
+
+@RunWith(MockitoJUnitRunner.class)
+public class CounterEndpointTest {
+
+    private static final String METRICS_NAME = "metrics.name";
+    private static final Double VALUE = new Long(System.currentTimeMillis()).doubleValue();
+
+    @Mock
+    private MeterRegistry registry;
+
+    private MicrometerEndpoint endpoint;
+
+    private InOrder inOrder;
+
+    @Before
+    public void setUp() throws Exception {
+        endpoint = new MicrometerEndpoint(null, null, registry, MetricsType.COUNTER, METRICS_NAME, Collections.emptyList());
+        inOrder = Mockito.inOrder(registry);
+    }
+
+    @After
+    public void tearDown() {
+        inOrder.verifyNoMoreInteractions();
+    }
+
+    @Test
+    public void testCounterEndpoint() throws Exception {
+        assertThat(endpoint.getRegistry(), is(registry));
+        assertThat(endpoint.getMetricsName(), is(METRICS_NAME));
+        assertThat(endpoint.getIncrement(), is(nullValue()));
+        assertThat(endpoint.getDecrement(), is(nullValue()));
+    }
+
+    @Test
+    public void testCreateProducer() throws Exception {
+        Producer producer = endpoint.createProducer();
+        assertThat(producer, is(notNullValue()));
+        assertThat(producer, is(instanceOf(CounterProducer.class)));
+    }
+
+    @Test
+    public void testGetIncrement() throws Exception {
+        assertThat(endpoint.getIncrement(), is(nullValue()));
+    }
+
+    @Test
+    public void testSetIncrement() throws Exception {
+        assertThat(endpoint.getIncrement(), is(nullValue()));
+        endpoint.setIncrement(VALUE);
+        assertThat(endpoint.getIncrement(), is(VALUE));
+    }
+
+    @Test
+    public void testGetDecrement() throws Exception {
+        assertThat(endpoint.getDecrement(), is(nullValue()));
+    }
+
+    @Test
+    public void testSetDecrement() throws Exception {
+        assertThat(endpoint.getDecrement(), is(nullValue()));
+        endpoint.setDecrement(VALUE);
+        assertThat(endpoint.getDecrement(), is(VALUE));
+    }
+
+}
diff --git a/components/camel-micrometer/src/test/java/org/apache/camel/component/micrometer/CounterProducerTest.java b/components/camel-micrometer/src/test/java/org/apache/camel/component/micrometer/CounterProducerTest.java
new file mode 100644
index 0000000..79f84a1
--- /dev/null
+++ b/components/camel-micrometer/src/test/java/org/apache/camel/component/micrometer/CounterProducerTest.java
@@ -0,0 +1,211 @@
+/**
+ * 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;
+
+import io.micrometer.core.instrument.Counter;
+import io.micrometer.core.instrument.MeterRegistry;
+import io.micrometer.core.instrument.search.Search;
+import org.apache.camel.Exchange;
+import org.apache.camel.Message;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.InOrder;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.junit.MockitoJUnitRunner;
+
+import java.util.Collections;
+
+import static org.apache.camel.component.micrometer.AbstractMicrometerProducer.HEADER_PATTERN;
+import static org.apache.camel.component.micrometer.MicrometerConstants.HEADER_COUNTER_DECREMENT;
+import static org.apache.camel.component.micrometer.MicrometerConstants.HEADER_COUNTER_INCREMENT;
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertThat;
+import static org.mockito.Mockito.*;
+
+@RunWith(MockitoJUnitRunner.class)
+public class CounterProducerTest {
+
+    private static final String METRICS_NAME = "metrics.name";
+    private static final Double INCREMENT = 100000D;
+    private static final Double DECREMENT = 91929199D;
+
+    @Mock
+    private MicrometerEndpoint endpoint;
+
+    @Mock
+    private Exchange exchange;
+
+    @Mock
+    private MeterRegistry registry;
+
+    @Mock
+    private Search search;
+
+    @Mock
+    private Counter counter;
+
+    @Mock
+    private Message in;
+
+    private CounterProducer producer;
+
+    private InOrder inOrder;
+
+    @Before
+    public void setUp() throws Exception {
+        endpoint = mock(MicrometerEndpoint.class);
+        producer = new CounterProducer(endpoint);
+        inOrder = Mockito.inOrder(endpoint, exchange, registry, counter, search, in);
+        when(endpoint.getRegistry()).thenReturn(registry);
+        when(registry.find(METRICS_NAME)).thenReturn(search);
+        when(search.counter()).thenReturn(counter);
+        when(exchange.getIn()).thenReturn(in);
+    }
+
+    @Test
+    public void testCounterProducer() throws Exception {
+        assertThat(producer.getEndpoint().equals(endpoint), is(true));
+    }
+
+    @Test
+    public void testProcessWithIncrementOnly() throws Exception {
+        Object action = null;
+        when(endpoint.getIncrement()).thenReturn(INCREMENT);
+        when(endpoint.getDecrement()).thenReturn(null);
+        when(in.getHeader(HEADER_COUNTER_INCREMENT, INCREMENT, Double.class)).thenReturn(INCREMENT);
+        producer.doProcess(exchange, METRICS_NAME, Collections.emptyList());
+        inOrder.verify(endpoint, times(1)).getRegistry();
+        inOrder.verify(registry, times(1)).find(METRICS_NAME);
+        inOrder.verify(search, times(1)).counter();
+        inOrder.verify(exchange, times(1)).getIn();
+        inOrder.verify(endpoint, times(1)).getIncrement();
+        inOrder.verify(endpoint, times(1)).getDecrement();
+        inOrder.verify(in, times(1)).getHeader(HEADER_COUNTER_INCREMENT, INCREMENT, Double.class);
+        inOrder.verify(in, times(1)).getHeader(HEADER_COUNTER_DECREMENT, action, Double.class);
+        inOrder.verify(counter, times(1)).increment(INCREMENT);
+        inOrder.verify(exchange, times(1)).getIn();
+        inOrder.verify(in, times(1)).removeHeaders(HEADER_PATTERN);
+        inOrder.verifyNoMoreInteractions();
+    }
+
+    @Test
+    public void testProcessWithDecrementOnly() throws Exception {
+        Object action = null;
+        when(endpoint.getIncrement()).thenReturn(null);
+        when(endpoint.getDecrement()).thenReturn(DECREMENT);
+        when(in.getHeader(HEADER_COUNTER_DECREMENT, DECREMENT, Double.class)).thenReturn(DECREMENT);
+        producer.doProcess(exchange, METRICS_NAME, Collections.emptyList());
+        inOrder.verify(endpoint, times(1)).getRegistry();
+        inOrder.verify(registry, times(1)).find(METRICS_NAME);
+        inOrder.verify(search, times(1)).counter();
+        inOrder.verify(exchange, times(1)).getIn();
+        inOrder.verify(endpoint, times(1)).getIncrement();
+        inOrder.verify(endpoint, times(1)).getDecrement();
+        inOrder.verify(in, times(1)).getHeader(HEADER_COUNTER_INCREMENT, action, Double.class);
+        inOrder.verify(in, times(1)).getHeader(HEADER_COUNTER_DECREMENT, DECREMENT, Double.class);
+        inOrder.verify(counter, times(1)).increment(-DECREMENT);
+        inOrder.verify(exchange, times(1)).getIn();
+        inOrder.verify(in, times(1)).removeHeaders(HEADER_PATTERN);
+        inOrder.verifyNoMoreInteractions();
+    }
+
+    @Test
+    public void testDoProcessWithIncrementAndDecrement() throws Exception {
+        when(endpoint.getIncrement()).thenReturn(INCREMENT);
+        when(endpoint.getDecrement()).thenReturn(DECREMENT);
+        when(in.getHeader(HEADER_COUNTER_INCREMENT, INCREMENT, Double.class)).thenReturn(INCREMENT);
+        when(in.getHeader(HEADER_COUNTER_DECREMENT, DECREMENT, Double.class)).thenReturn(DECREMENT);
+
+        producer.doProcess(exchange, METRICS_NAME, Collections.emptyList());
+        inOrder.verify(endpoint, times(1)).getRegistry();
+        inOrder.verify(registry, times(1)).find(METRICS_NAME);
+        inOrder.verify(search, times(1)).counter();
+        inOrder.verify(exchange, times(1)).getIn();
+        inOrder.verify(endpoint, times(1)).getIncrement();
+        inOrder.verify(endpoint, times(1)).getDecrement();
+        inOrder.verify(in, times(1)).getHeader(HEADER_COUNTER_INCREMENT, INCREMENT, Double.class);
+        inOrder.verify(in, times(1)).getHeader(HEADER_COUNTER_DECREMENT, DECREMENT, Double.class);
+        inOrder.verify(counter, times(1)).increment(INCREMENT);
+        inOrder.verify(exchange, times(1)).getIn();
+        inOrder.verify(in, times(1)).removeHeaders(HEADER_PATTERN);
+        inOrder.verifyNoMoreInteractions();
+    }
+
+    @Test
+    public void testProcessWithOutIncrementAndDecrement() throws Exception {
+        Object action = null;
+        when(endpoint.getIncrement()).thenReturn(null);
+        when(endpoint.getDecrement()).thenReturn(null);
+        producer.doProcess(exchange, METRICS_NAME, Collections.emptyList());
+        inOrder.verify(endpoint, times(1)).getRegistry();
+        inOrder.verify(registry, times(1)).find(METRICS_NAME);
+        inOrder.verify(search, times(1)).counter();
+        inOrder.verify(exchange, times(1)).getIn();
+        inOrder.verify(endpoint, times(1)).getIncrement();
+        inOrder.verify(endpoint, times(1)).getDecrement();
+        inOrder.verify(in, times(1)).getHeader(HEADER_COUNTER_INCREMENT, action, Double.class);
+        inOrder.verify(in, times(1)).getHeader(HEADER_COUNTER_DECREMENT, action, Double.class);
+        inOrder.verify(counter, times(1)).increment();
+        inOrder.verify(exchange, times(1)).getIn();
+        inOrder.verify(in, times(1)).removeHeaders(HEADER_PATTERN);
+        inOrder.verifyNoMoreInteractions();
+    }
+
+    @Test
+    public void testProcessOverridingIncrement() throws Exception {
+        when(endpoint.getIncrement()).thenReturn(INCREMENT);
+        when(endpoint.getDecrement()).thenReturn(DECREMENT);
+        when(in.getHeader(HEADER_COUNTER_INCREMENT, INCREMENT, Double.class)).thenReturn(INCREMENT + 1);
+        when(in.getHeader(HEADER_COUNTER_DECREMENT, DECREMENT, Double.class)).thenReturn(DECREMENT);
+        producer.doProcess(exchange, METRICS_NAME, Collections.emptyList());
+        inOrder.verify(endpoint, times(1)).getRegistry();
+        inOrder.verify(registry, times(1)).find(METRICS_NAME);
+        inOrder.verify(search, times(1)).counter();
+        inOrder.verify(exchange, times(1)).getIn();
+        inOrder.verify(endpoint, times(1)).getIncrement();
+        inOrder.verify(endpoint, times(1)).getDecrement();
+        inOrder.verify(in, times(1)).getHeader(HEADER_COUNTER_INCREMENT, INCREMENT, Double.class);
+        inOrder.verify(in, times(1)).getHeader(HEADER_COUNTER_DECREMENT, DECREMENT, Double.class);
+        inOrder.verify(counter, times(1)).increment(INCREMENT + 1);
+        inOrder.verify(exchange, times(1)).getIn();
+        inOrder.verify(in, times(1)).removeHeaders(HEADER_PATTERN);
+        inOrder.verifyNoMoreInteractions();
+    }
+
+    @Test
+    public void testProcessOverridingDecrement() throws Exception {
+        Object action = null;
+        when(endpoint.getIncrement()).thenReturn(null);
+        when(endpoint.getDecrement()).thenReturn(DECREMENT);
+        when(in.getHeader(HEADER_COUNTER_DECREMENT, DECREMENT, Double.class)).thenReturn(DECREMENT - 1);
+        producer.doProcess(exchange, METRICS_NAME, Collections.emptyList());
+        inOrder.verify(endpoint, times(1)).getRegistry();
+        inOrder.verify(registry, times(1)).find(METRICS_NAME);
+        inOrder.verify(search, times(1)).counter();
+        inOrder.verify(exchange, times(1)).getIn();
+        inOrder.verify(endpoint, times(1)).getIncrement();
+        inOrder.verify(endpoint, times(1)).getDecrement();
+        inOrder.verify(in, times(1)).getHeader(HEADER_COUNTER_INCREMENT, action, Double.class);
+        inOrder.verify(in, times(1)).getHeader(HEADER_COUNTER_DECREMENT, DECREMENT, Double.class);
+        inOrder.verify(counter, times(1)).increment(-DECREMENT + 1);
+        inOrder.verify(exchange, times(1)).getIn();
+        inOrder.verify(in, times(1)).removeHeaders(HEADER_PATTERN);
+        inOrder.verifyNoMoreInteractions();
+    }
+}
diff --git a/components/camel-micrometer/src/test/java/org/apache/camel/component/micrometer/CounterRouteTest.java b/components/camel-micrometer/src/test/java/org/apache/camel/component/micrometer/CounterRouteTest.java
new file mode 100644
index 0000000..43bb056
--- /dev/null
+++ b/components/camel-micrometer/src/test/java/org/apache/camel/component/micrometer/CounterRouteTest.java
@@ -0,0 +1,157 @@
+/**
+ * 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;
+
+import io.micrometer.core.instrument.MeterRegistry;
+import io.micrometer.core.instrument.simple.SimpleMeterRegistry;
+import org.apache.camel.EndpointInject;
+import org.apache.camel.Produce;
+import org.apache.camel.ProducerTemplate;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.spring.javaconfig.SingleRouteCamelConfiguration;
+import org.apache.camel.test.spring.CamelSpringDelegatingTestContextLoader;
+import org.apache.camel.test.spring.CamelSpringRunner;
+import org.apache.camel.test.spring.MockEndpoints;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.test.context.ContextConfiguration;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.apache.camel.component.micrometer.MicrometerComponent.METRICS_REGISTRY;
+import static org.apache.camel.component.micrometer.MicrometerConstants.*;
+import static org.junit.Assert.assertEquals;
+
+@RunWith(CamelSpringRunner.class)
+@ContextConfiguration(
+        classes = { CounterRouteTest.TestConfig.class },
+        loader = CamelSpringDelegatingTestContextLoader.class)
+@MockEndpoints
+public class CounterRouteTest {
+
+    @EndpointInject(uri = "mock:out")
+    private MockEndpoint endpoint;
+
+    @Produce(uri = "direct:in-1")
+    private ProducerTemplate producer1;
+
+    @Produce(uri = "direct:in-2")
+    private ProducerTemplate producer2;
+
+    @Produce(uri = "direct:in-3")
+    private ProducerTemplate producer3;
+
+    @Produce(uri = "direct:in-4")
+    private ProducerTemplate producer4;
+
+    private MeterRegistry registry;
+
+
+    @Configuration
+    public static class TestConfig extends SingleRouteCamelConfiguration {
+
+        @Bean
+        @Override
+        public RouteBuilder route() {
+            return new RouteBuilder() {
+
+                @Override
+                public void configure() throws Exception {
+                    from("direct:in-1")
+                            .to("micrometer:counter:A?increment=5")
+                            .to("mock:out");
+
+                    from("direct:in-2")
+                            .to("micrometer:counter:B?decrement=9")
+                            .to("mock:out");
+
+                    from("direct:in-3")
+                            .setHeader(HEADER_COUNTER_INCREMENT, constant(417L))
+                            .to("micrometer:counter:C")
+                            .to("mock:out");
+
+                    from("direct:in-4")
+                            .setHeader(HEADER_COUNTER_INCREMENT, simple("${body.length}"))
+                            .to("micrometer:counter:D")
+                            .to("mock:out");
+                }
+            };
+        }
+
+        @Bean(name = METRICS_REGISTRY)
+        public MeterRegistry getMetricRegistry() {
+            return new SimpleMeterRegistry();
+        }
+    }
+
+    @Before
+    public void setup() {
+        registry = endpoint.getCamelContext().getRegistry().lookupByNameAndType(METRICS_REGISTRY, MeterRegistry.class);
+    }
+
+    @After
+    public void tearDown() {
+        endpoint.reset();
+    }
+
+    @Test
+    public void testOverrideMetricsName() throws Exception {
+        endpoint.expectedMessageCount(1);
+        producer1.sendBodyAndHeader(new Object(), HEADER_METRIC_NAME, "A1");
+        assertEquals(5.0D, registry.find("A1").counter().count(), 0.01D);
+        endpoint.assertIsSatisfied();
+    }
+
+    @Test
+    public void testOverrideIncrement() throws Exception {
+        endpoint.expectedMessageCount(1);
+        producer1.sendBodyAndHeader(new Object(), HEADER_COUNTER_INCREMENT, 14.0D);
+        assertEquals(14.0D, registry.find(MicrometerConstants.HEADER_PREFIX + "." + "A").counter().count(), 0.01D);
+        endpoint.assertIsSatisfied();
+    }
+
+    @Test
+    public void testOverrideDecrement() throws Exception {
+        endpoint.expectedMessageCount(1);
+        producer2.sendBodyAndHeader(new Object(), HEADER_COUNTER_DECREMENT, 7.0D);
+        assertEquals(-7.0D, registry.find(MicrometerConstants.HEADER_PREFIX + "." + "B").counter().count(), 0.01D);
+        endpoint.assertIsSatisfied();
+    }
+
+    @Test
+    public void testOverrideUsingConstantValue() throws Exception {
+        endpoint.expectedMessageCount(1);
+        producer3.sendBody(new Object());
+        assertEquals(417.0D, registry.find(MicrometerConstants.HEADER_PREFIX + "." + "C").counter().count(), 0.01D);
+        endpoint.assertIsSatisfied();
+    }
+
+    @Test
+    public void testOverrideUsingScriptEvaluation() throws Exception {
+        endpoint.expectedMessageCount(1);
+        String message = "Hello from Camel Metrics!";
+        producer4.sendBody(message);
+        assertEquals(message.length(), registry.find(MicrometerConstants.HEADER_PREFIX + "." + "D").counter().count(), 0.01D);
+        endpoint.assertIsSatisfied();
+    }
+}
diff --git a/components/camel-micrometer/src/test/java/org/apache/camel/component/micrometer/DistributionSummaryEndpointTest.java b/components/camel-micrometer/src/test/java/org/apache/camel/component/micrometer/DistributionSummaryEndpointTest.java
new file mode 100644
index 0000000..2ed4e92
--- /dev/null
+++ b/components/camel-micrometer/src/test/java/org/apache/camel/component/micrometer/DistributionSummaryEndpointTest.java
@@ -0,0 +1,85 @@
+/**
+ * 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;
+
+import io.micrometer.core.instrument.MeterRegistry;
+import org.apache.camel.Producer;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.InOrder;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.junit.MockitoJUnitRunner;
+
+import java.util.Collections;
+
+import static org.hamcrest.Matchers.*;
+import static org.junit.Assert.assertThat;
+
+@RunWith(MockitoJUnitRunner.class)
+public class DistributionSummaryEndpointTest {
+
+    private static final String METRICS_NAME = "metrics.name";
+    private static final Double VALUE = new Long(System.currentTimeMillis()).doubleValue();
+
+    @Mock
+    private MeterRegistry registry;
+
+    private MicrometerEndpoint endpoint;
+
+    private InOrder inOrder;
+
+    @Before
+    public void setUp() throws Exception {
+        endpoint = new MicrometerEndpoint(null, null, registry, MetricsType.DISTRIBUTION_SUMMARY, METRICS_NAME, Collections.emptyList());
+        inOrder = Mockito.inOrder(registry);
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        inOrder.verifyNoMoreInteractions();
+    }
+
+    @Test
+    public void testHistogramEndpoint() throws Exception {
+        assertThat(endpoint, is(notNullValue()));
+        assertThat(endpoint.getRegistry(), is(registry));
+        assertThat(endpoint.getMetricsName(), is(METRICS_NAME));
+    }
+
+    @Test
+    public void testCreateProducer() throws Exception {
+        Producer producer = endpoint.createProducer();
+        assertThat(producer, is(notNullValue()));
+        assertThat(producer, is(instanceOf(DistributionSummaryProducer.class)));
+    }
+
+    @Test
+    public void testGetValue() throws Exception {
+        assertThat(endpoint.getValue(), is(nullValue()));
+    }
+
+    @Test
+    public void testSetValue() throws Exception {
+        assertThat(endpoint.getValue(), is(nullValue()));
+        endpoint.setValue(VALUE);
+        assertThat(endpoint.getValue(), is(VALUE));
+    }
+
+}
diff --git a/components/camel-micrometer/src/test/java/org/apache/camel/component/micrometer/DistributionSummaryProducerTest.java b/components/camel-micrometer/src/test/java/org/apache/camel/component/micrometer/DistributionSummaryProducerTest.java
new file mode 100644
index 0000000..36e3ee5
--- /dev/null
+++ b/components/camel-micrometer/src/test/java/org/apache/camel/component/micrometer/DistributionSummaryProducerTest.java
@@ -0,0 +1,133 @@
+/**
+ * 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;
+
+import io.micrometer.core.instrument.DistributionSummary;
+import io.micrometer.core.instrument.MeterRegistry;
+import io.micrometer.core.instrument.search.Search;
+import org.apache.camel.Exchange;
+import org.apache.camel.Message;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.InOrder;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.junit.MockitoJUnitRunner;
+
+import java.util.Collections;
+
+import static org.apache.camel.component.micrometer.AbstractMicrometerProducer.HEADER_PATTERN;
+import static org.apache.camel.component.micrometer.MicrometerConstants.HEADER_HISTOGRAM_VALUE;
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.when;
+
+@RunWith(MockitoJUnitRunner.class)
+public class DistributionSummaryProducerTest {
+
+    private static final String METRICS_NAME = "metrics.name";
+    private static final Double VALUE = new Long(System.currentTimeMillis()).doubleValue();
+
+    @Mock
+    private MicrometerEndpoint endpoint;
+
+    @Mock
+    private MeterRegistry registry;
+
+    @Mock
+    private Search search;
+
+    @Mock
+    private DistributionSummary histogram;
+
+    @Mock
+    private Exchange exchange;
+
+    @Mock
+    private Message in;
+
+    private DistributionSummaryProducer producer;
+
+    private InOrder inOrder;
+
+    @Before
+    public void setUp() throws Exception {
+        endpoint = mock(MicrometerEndpoint.class);
+        producer = new DistributionSummaryProducer(endpoint);
+        inOrder = Mockito.inOrder(endpoint, registry, histogram, search, exchange, in);
+        when(endpoint.getRegistry()).thenReturn(registry);
+        when(registry.find(METRICS_NAME)).thenReturn(search);
+        when(search.summary()).thenReturn(histogram);
+        when(exchange.getIn()).thenReturn(in);
+    }
+
+    @Test
+    public void testHistogramProducer() throws Exception {
+        assertThat(producer.getEndpoint().equals(endpoint), is(true));
+    }
+
+    @Test
+    public void testProcessValueSet() throws Exception {
+        when(endpoint.getValue()).thenReturn(VALUE);
+        when(in.getHeader(HEADER_HISTOGRAM_VALUE, VALUE, Double.class)).thenReturn(VALUE);
+        producer.doProcess(exchange, METRICS_NAME, Collections.emptyList());
+        inOrder.verify(registry, times(1)).find(METRICS_NAME);
+        inOrder.verify(search, times(1)).summary();
+        inOrder.verify(endpoint, times(1)).getValue();
+        inOrder.verify(exchange, times(1)).getIn();
+        inOrder.verify(in, times(1)).getHeader(HEADER_HISTOGRAM_VALUE, VALUE, Double.class);
+        inOrder.verify(histogram, times(1)).record(VALUE);
+        inOrder.verify(exchange, times(1)).getIn();
+        inOrder.verify(in, times(1)).removeHeaders(HEADER_PATTERN);
+        inOrder.verifyNoMoreInteractions();
+    }
+
+    @Test
+    public void testProcessValueNotSet() throws Exception {
+        Object action = null;
+        when(endpoint.getValue()).thenReturn(null);
+        producer.doProcess(exchange, METRICS_NAME, Collections.emptyList());
+        inOrder.verify(registry, times(1)).find(METRICS_NAME);
+        inOrder.verify(search, times(1)).summary();
+        inOrder.verify(endpoint, times(1)).getValue();
+        inOrder.verify(exchange, times(1)).getIn();
+        inOrder.verify(in, times(1)).getHeader(HEADER_HISTOGRAM_VALUE, action, Double.class);
+        inOrder.verify(exchange, times(1)).getIn();
+        inOrder.verify(in, times(1)).removeHeaders(HEADER_PATTERN);
+        inOrder.verifyNoMoreInteractions();
+    }
+
+    @Test
+    public void testProcessOverrideValue() throws Exception {
+        when(endpoint.getValue()).thenReturn(VALUE);
+        when(in.getHeader(HEADER_HISTOGRAM_VALUE, VALUE, Double.class)).thenReturn(VALUE + 3.0d);
+        producer.doProcess(exchange, METRICS_NAME, Collections.emptyList());
+        inOrder.verify(registry, times(1)).find(METRICS_NAME);
+        inOrder.verify(search, times(1)).summary();
+        inOrder.verify(endpoint, times(1)).getValue();
+        inOrder.verify(exchange, times(1)).getIn();
+        inOrder.verify(in, times(1)).getHeader(HEADER_HISTOGRAM_VALUE, VALUE, Double.class);
+        inOrder.verify(histogram, times(1)).record(VALUE + 3);
+        inOrder.verify(exchange, times(1)).getIn();
+        inOrder.verify(in, times(1)).removeHeaders(HEADER_PATTERN);
+        inOrder.verifyNoMoreInteractions();
+    }
+
+}
diff --git a/components/camel-micrometer/src/test/java/org/apache/camel/component/micrometer/DistributionSummaryRouteTest.java b/components/camel-micrometer/src/test/java/org/apache/camel/component/micrometer/DistributionSummaryRouteTest.java
new file mode 100644
index 0000000..ba6b995
--- /dev/null
+++ b/components/camel-micrometer/src/test/java/org/apache/camel/component/micrometer/DistributionSummaryRouteTest.java
@@ -0,0 +1,111 @@
+/**
+ * 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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;
+
+import io.micrometer.core.instrument.DistributionSummary;
+import io.micrometer.core.instrument.MeterRegistry;
+import io.micrometer.core.instrument.distribution.HistogramSnapshot;
+import io.micrometer.core.instrument.simple.SimpleMeterRegistry;
+import org.apache.camel.EndpointInject;
+import org.apache.camel.Produce;
+import org.apache.camel.ProducerTemplate;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.spring.javaconfig.SingleRouteCamelConfiguration;
+import org.apache.camel.test.spring.CamelSpringDelegatingTestContextLoader;
+import org.apache.camel.test.spring.CamelSpringRunner;
+import org.apache.camel.test.spring.MockEndpoints;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.test.context.ContextConfiguration;
+
+import static org.apache.camel.component.micrometer.MicrometerComponent.METRICS_REGISTRY;
+import static org.apache.camel.component.micrometer.MicrometerConstants.HEADER_HISTOGRAM_VALUE;
+import static org.apache.camel.component.micrometer.MicrometerConstants.HEADER_METRIC_NAME;
+import static org.junit.Assert.assertEquals;
+
+@RunWith(CamelSpringRunner.class)
+@ContextConfiguration(
+        classes = {DistributionSummaryRouteTest.TestConfig.class},
+        loader = CamelSpringDelegatingTestContextLoader.class)
+@MockEndpoints
+public class DistributionSummaryRouteTest {
+
+    @EndpointInject(uri = "mock:out")
+    private MockEndpoint endpoint;
+
+    @Produce(uri = "direct:in")
+    private ProducerTemplate producer;
+
+    private MeterRegistry registry;
+
+    @Configuration
+    public static class TestConfig extends SingleRouteCamelConfiguration {
+
+        @Bean
+        @Override
+        public RouteBuilder route() {
+            return new RouteBuilder() {
+
+                @Override
+                public void configure() throws Exception {
+                    from("direct:in")
+                            .to("micrometer:summary:A?value=332491")
+                            .to("mock:out");
+                }
+            };
+        }
+
+        @Bean(name = METRICS_REGISTRY)
+        public MeterRegistry getMetricRegistry() {
+            return new SimpleMeterRegistry();
+        }
+    }
+
+    @Before
+    public void setup() {
+        registry = endpoint.getCamelContext().getRegistry().lookupByNameAndType(METRICS_REGISTRY, MeterRegistry.class);
+    }
+
+    @After
+    public void tearDown() {
+        endpoint.reset();
+    }
+
+    @Test
+    public void testOverrideMetricsName() throws Exception {
+        endpoint.expectedMessageCount(1);
+        producer.sendBodyAndHeader(new Object(), HEADER_METRIC_NAME, "B");
+        assertEquals(1L, registry.find("B").summary().count());
+        endpoint.assertIsSatisfied();
+    }
+
+    @Test
+    public void testOverrideValue() throws Exception {
+        endpoint.expectedMessageCount(1);
+        producer.sendBodyAndHeader(new Object(), HEADER_HISTOGRAM_VALUE, 181L);
+        DistributionSummary summary = registry.find(MicrometerConstants.HEADER_PREFIX + "." + "A").summary();
+        assertEquals(1L, summary.count());
+        HistogramSnapshot snapshot = summary.takeSnapshot(false);
+        assertEquals(181.0D, snapshot.total(), 0.01D);
+        endpoint.assertIsSatisfied();
+    }
+}
diff --git a/components/camel-micrometer/src/test/java/org/apache/camel/component/micrometer/MetricComponentSpringTest.java b/components/camel-micrometer/src/test/java/org/apache/camel/component/micrometer/MetricComponentSpringTest.java
new file mode 100644
index 0000000..b107550
--- /dev/null
+++ b/components/camel-micrometer/src/test/java/org/apache/camel/component/micrometer/MetricComponentSpringTest.java
@@ -0,0 +1,94 @@
+/**
+ * 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;
+
+import io.micrometer.core.instrument.Counter;
+import io.micrometer.core.instrument.MeterRegistry;
+import io.micrometer.core.instrument.search.Search;
+import org.apache.camel.EndpointInject;
+import org.apache.camel.Produce;
+import org.apache.camel.ProducerTemplate;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.spring.javaconfig.SingleRouteCamelConfiguration;
+import org.apache.camel.test.spring.CamelSpringDelegatingTestContextLoader;
+import org.apache.camel.test.spring.CamelSpringRunner;
+import org.apache.camel.test.spring.MockEndpoints;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.InOrder;
+import org.mockito.Mockito;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.test.context.ContextConfiguration;
+
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.when;
+
+@RunWith(CamelSpringRunner.class)
+@ContextConfiguration(
+        classes = { MetricComponentSpringTest.TestConfig.class },
+        loader = CamelSpringDelegatingTestContextLoader.class)
+@MockEndpoints
+public class MetricComponentSpringTest {
+
+    @EndpointInject(uri = "mock:out")
+    private MockEndpoint endpoint;
+
+    @Produce(uri = "direct:in")
+    private ProducerTemplate producer;
+
+    @Configuration
+    public static class TestConfig extends SingleRouteCamelConfiguration {
+
+        @Bean
+        @Override
+        public RouteBuilder route() {
+            return new RouteBuilder() {
+
+                @Override
+                public void configure() throws Exception {
+                    from("direct:in")
+                            .to("micrometer:counter:A?increment=512")
+                            .to("mock:out");
+                }
+            };
+        }
+
+        @Bean(name = MicrometerComponent.METRICS_REGISTRY)
+        public MeterRegistry getMetricRegistry() {
+            return Mockito.mock(MeterRegistry.class);
+        }
+    }
+
+    @Test
+    public void testMetricsRegistryFromCamelRegistry() throws Exception {
+        MeterRegistry mockRegistry = endpoint.getCamelContext().getRegistry().lookupByNameAndType(MicrometerComponent.METRICS_REGISTRY, MeterRegistry.class);
+        Counter mockCounter = Mockito.mock(Counter.class);
+        Search mockSearch = Mockito.mock(Search.class);
+        InOrder inOrder = Mockito.inOrder(mockRegistry, mockCounter, mockSearch);
+        when(mockRegistry.find(MicrometerConstants.HEADER_PREFIX + "." + "A")).thenReturn(mockSearch);
+        when(mockSearch.counter()).thenReturn(mockCounter);
+        endpoint.expectedMessageCount(1);
+        producer.sendBody(new Object());
+        endpoint.assertIsSatisfied();
+        inOrder.verify(mockRegistry, times(1)).find(MicrometerConstants.HEADER_PREFIX + "." + "A");
+        inOrder.verify(mockSearch, times(1)).counter();
+        inOrder.verify(mockCounter, times(1)).increment(512D);
+        inOrder.verifyNoMoreInteractions();
+    }
+}
diff --git a/components/camel-micrometer/src/test/java/org/apache/camel/component/micrometer/MetricsTypeTest.java b/components/camel-micrometer/src/test/java/org/apache/camel/component/micrometer/MetricsTypeTest.java
new file mode 100644
index 0000000..a670559
--- /dev/null
+++ b/components/camel-micrometer/src/test/java/org/apache/camel/component/micrometer/MetricsTypeTest.java
@@ -0,0 +1,35 @@
+/**
+ * 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;
+
+import java.util.EnumSet;
+
+import org.junit.Test;
+
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertThat;
+
+public class MetricsTypeTest {
+
+    @Test
+    public void testGetByName() throws Exception {
+        for (MetricsType type : EnumSet.allOf(MetricsType.class)) {
+            MetricsType t = MetricsType.getByName(type.toString());
+            assertThat(t, is(type));
+        }
+    }
+}
diff --git a/components/camel-micrometer/src/test/java/org/apache/camel/component/micrometer/MicrometerComponentRouteTest.java b/components/camel-micrometer/src/test/java/org/apache/camel/component/micrometer/MicrometerComponentRouteTest.java
new file mode 100644
index 0000000..718ecdf
--- /dev/null
+++ b/components/camel-micrometer/src/test/java/org/apache/camel/component/micrometer/MicrometerComponentRouteTest.java
@@ -0,0 +1,86 @@
+/**
+ * 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;
+
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.camel.Produce;
+import org.apache.camel.ProducerTemplate;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.test.junit4.CamelTestSupport;
+import org.junit.Test;
+
+import static org.apache.camel.component.micrometer.MicrometerConstants.HEADER_HISTOGRAM_VALUE;
+import static org.apache.camel.component.micrometer.MicrometerConstants.HEADER_METRIC_NAME;
+import static org.apache.camel.component.micrometer.MicrometerConstants.HEADER_PREFIX;
+
+public class MicrometerComponentRouteTest extends CamelTestSupport {
+
+    @Produce(uri = "direct:start-1")
+    protected ProducerTemplate template1;
+
+    @Test
+    public void testMetrics() throws Exception {
+        MockEndpoint mock = getMockEndpoint("mock:result");
+        mock.expectedMinimumMessageCount(1);
+        template1.sendBody(new Object());
+        assertMockEndpointsSatisfied();
+    }
+
+    @Test
+    public void testMessageContentDelivery() throws Exception {
+        MockEndpoint mock = getMockEndpoint("mock:result");
+        String body = "Message Body";
+        String header1 = "Header 1";
+        String header2 = "Header 2";
+        Object value1 = new Date();
+        Object value2 = System.currentTimeMillis();
+        mock.expectedBodiesReceived(body);
+        mock.expectedHeaderReceived(header1, value1);
+        mock.expectedHeaderReceived(header2, value2);
+        Map<String, Object> headers = new HashMap<>();
+        headers.put(header1, value1);
+        headers.put(header2, value2);
+        template1.sendBodyAndHeaders(body, headers);
+        assertMockEndpointsSatisfied();
+    }
+
+    @Override
+    protected RouteBuilder createRouteBuilder() throws Exception {
+        return new RouteBuilder() {
+            @Override
+            public void configure() {
+                from("direct:start-1")
+                        .to("micrometer:timer:T?action=start")
+                        .to("micrometer:counter://A")
+                        .to("micrometer:counter://B")
+                        .to("micrometer:counter:C?increment=19291")
+                        .to("micrometer:counter:C?decrement=19292")
+                        .to("micrometer:counter:C")
+                        .to("micrometer:summary:E")
+                        .to("micrometer:timer:T")
+                        .to("micrometer:summary:E?value=12000000031")
+                        .to("micrometer:timer:T?action=stop")
+                        .to("mock:result");
+
+            }
+        };
+    }
+}
diff --git a/components/camel-micrometer/src/test/java/org/apache/camel/component/micrometer/MicrometerComponentTest.java b/components/camel-micrometer/src/test/java/org/apache/camel/component/micrometer/MicrometerComponentTest.java
new file mode 100644
index 0000000..5c18a61
--- /dev/null
+++ b/components/camel-micrometer/src/test/java/org/apache/camel/component/micrometer/MicrometerComponentTest.java
@@ -0,0 +1,186 @@
+/**
+ * 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;
+
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.HashMap;
+import java.util.Map;
+
+import io.micrometer.core.instrument.MeterRegistry;
+import org.apache.camel.CamelContext;
+import org.apache.camel.Endpoint;
+import org.apache.camel.RuntimeCamelException;
+import org.apache.camel.TypeConverter;
+import org.apache.camel.spi.Registry;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.InOrder;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.junit.MockitoJUnitRunner;
+
+import static org.hamcrest.Matchers.instanceOf;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.not;
+import static org.hamcrest.Matchers.notNullValue;
+import static org.junit.Assert.assertThat;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.when;
+
+@RunWith(MockitoJUnitRunner.class)
+public class MicrometerComponentTest {
+
+    @Mock
+    private CamelContext camelContext;
+
+    @Mock
+    private TypeConverter typeConverter;
+
+    @Mock
+    private Registry camelRegistry;
+
+    @Mock
+    private MeterRegistry metricRegistry;
+
+    private InOrder inOrder;
+
+    private MicrometerComponent component;
+
+    @Before
+    public void setUp() throws Exception {
+        component = new MicrometerComponent();
+        inOrder = Mockito.inOrder(camelContext, camelRegistry, metricRegistry, typeConverter);
+    }
+
+    @Test
+    public void testCreateEndpoint() throws Exception {
+        component.setCamelContext(camelContext);
+        when(camelContext.getRegistry()).thenReturn(camelRegistry);
+        when(camelContext.getTypeConverter()).thenReturn(typeConverter);
+        when(typeConverter.convertTo(String.class, "key:value")).thenReturn("key:value");
+        when(camelRegistry.lookupByNameAndType(MicrometerComponent.METRICS_REGISTRY, MeterRegistry.class)).thenReturn(metricRegistry);
+
+        Map<String, Object> params = new HashMap<>();
+        params.put("tags", "key:value");
+        Endpoint result = component.createEndpoint("micrometer:counter:counter", "counter:counter", params);
+        assertThat(result, is(notNullValue()));
+        assertThat(result, is(instanceOf(MicrometerEndpoint.class)));
+        MicrometerEndpoint me = (MicrometerEndpoint) result;
+        assertThat(me.getMetricsName(), is(MicrometerConstants.HEADER_PREFIX + "." + "counter"));
+        assertThat(me.getRegistry(), is(metricRegistry));
+        inOrder.verify(camelContext, times(1)).getRegistry();
+        inOrder.verify(camelRegistry, times(1)).lookupByNameAndType(MicrometerComponent.METRICS_REGISTRY, MeterRegistry.class);
+        inOrder.verify(camelContext, times(1)).getTypeConverter();
+        inOrder.verify(typeConverter, times(1)).convertTo(String.class, "key:value");
+        inOrder.verify(camelContext, times(1)).getTypeConverter();
+        inOrder.verifyNoMoreInteractions();
+    }
+
+    @Test
+    public void testCreateNewEndpointForCounter() throws Exception {
+        Endpoint endpoint = new MicrometerEndpoint(null, null, metricRegistry, MetricsType.COUNTER, "a name", Collections.emptyList());
+        assertThat(endpoint, is(notNullValue()));
+        assertThat(endpoint, is(instanceOf(MicrometerEndpoint.class)));
+    }
+
+    @Test
+    public void testCreateNewEndpointForGauge() throws Exception {
+        MicrometerEndpoint endpoint = new MicrometerEndpoint(null, null, metricRegistry, MetricsType.GAUGE, "a name", Collections.emptyList());
+        assertThat(endpoint, is(notNullValue()));
+        assertThat(endpoint, is(instanceOf(MicrometerEndpoint.class)));
+    }
+
+    @Test
+    public void testCreateNewEndpointForHistogram() throws Exception {
+        Endpoint endpoint = new MicrometerEndpoint(null, null, metricRegistry, MetricsType.DISTRIBUTION_SUMMARY, "a name", Collections.emptyList());
+        assertThat(endpoint, is(notNullValue()));
+        assertThat(endpoint, is(instanceOf(MicrometerEndpoint.class)));
+    }
+
+    @Test
+    public void testCreateNewEndpointForTimer() throws Exception {
+        Endpoint endpoint = new MicrometerEndpoint(null, null, metricRegistry, MetricsType.TIMER, "a name", Collections.emptyList());
+        assertThat(endpoint, is(notNullValue()));
+        assertThat(endpoint, is(instanceOf(MicrometerEndpoint.class)));
+    }
+
+    @Test
+    public void testGetMetricsType() throws Exception {
+        for (MetricsType type : EnumSet.allOf(MetricsType.class)) {
+            assertThat(component.getMetricsType(type.toString() + ":metrics-name"), is(type));
+        }
+    }
+
+    @Test
+    public void testGetMetricsTypeNotSet() throws Exception {
+        assertThat(component.getMetricsType("no-metrics-type"), is(MicrometerComponent.DEFAULT_METER_TYPE));
+    }
+
+    @Test(expected = RuntimeCamelException.class)
+    public void testGetMetricsTypeNotFound() throws Exception {
+        component.getMetricsType("unknown-metrics:metrics-name");
+    }
+
+    @Test
+    public void testGetOrCreateMetricRegistryFoundInCamelRegistry() throws Exception {
+        when(camelRegistry.lookupByNameAndType("name", MeterRegistry.class)).thenReturn(metricRegistry);
+        MeterRegistry result = component.getOrCreateMeterRegistry(camelRegistry, "name");
+        assertThat(result, is(metricRegistry));
+        inOrder.verify(camelRegistry, times(1)).lookupByNameAndType("name", MeterRegistry.class);
+        inOrder.verifyNoMoreInteractions();
+    }
+
+    @Test
+    public void testGetOrCreateMetricRegistryFoundInCamelRegistryByType() throws Exception {
+        when(camelRegistry.lookupByNameAndType("name", MeterRegistry.class)).thenReturn(null);
+        when(camelRegistry.findByType(MeterRegistry.class)).thenReturn(Collections.singleton(metricRegistry));
+        MeterRegistry result = component.getOrCreateMeterRegistry(camelRegistry, "name");
+        assertThat(result, is(metricRegistry));
+        inOrder.verify(camelRegistry, times(1)).lookupByNameAndType("name", MeterRegistry.class);
+        inOrder.verify(camelRegistry, times(1)).findByType(MeterRegistry.class);
+        inOrder.verifyNoMoreInteractions();
+    }
+
+    @Test
+    public void testGetOrCreateMetricRegistryNotFoundInCamelRegistry() throws Exception {
+        when(camelRegistry.lookupByNameAndType("name", MeterRegistry.class)).thenReturn(null);
+        when(camelRegistry.findByType(MeterRegistry.class)).thenReturn(Collections.<MeterRegistry>emptySet());
+        MeterRegistry result = component.getOrCreateMeterRegistry(camelRegistry, "name");
+        assertThat(result, is(notNullValue()));
+        assertThat(result, is(not(metricRegistry)));
+        inOrder.verify(camelRegistry, times(1)).lookupByNameAndType("name", MeterRegistry.class);
+        inOrder.verify(camelRegistry, times(1)).findByType(MeterRegistry.class);
+        inOrder.verifyNoMoreInteractions();
+    }
+
+    @Test
+    public void testGetMetricRegistryFromCamelRegistry() throws Exception {
+        when(camelRegistry.lookupByNameAndType("name", MeterRegistry.class)).thenReturn(metricRegistry);
+        MeterRegistry result = component.getMeterRegistryFromCamelRegistry(camelRegistry, "name");
+        assertThat(result, is(metricRegistry));
+        inOrder.verify(camelRegistry, times(1)).lookupByNameAndType("name", MeterRegistry.class);
+        inOrder.verifyNoMoreInteractions();
+    }
+
+    @Test
+    public void testCreateMetricRegistry() throws Exception {
+        MeterRegistry registry = component.createMeterRegistry();
+        assertThat(registry, is(notNullValue()));
+    }
+}
diff --git a/components/camel-micrometer/src/test/java/org/apache/camel/component/micrometer/MicrometerEndpointTest.java b/components/camel-micrometer/src/test/java/org/apache/camel/component/micrometer/MicrometerEndpointTest.java
new file mode 100644
index 0000000..6a27ea3
--- /dev/null
+++ b/components/camel-micrometer/src/test/java/org/apache/camel/component/micrometer/MicrometerEndpointTest.java
@@ -0,0 +1,102 @@
+/**
+ * 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;
+
+import io.micrometer.core.instrument.MeterRegistry;
+import org.apache.camel.*;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.InOrder;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.junit.MockitoJUnitRunner;
+
+import java.util.Collections;
+
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertThat;
+
+@RunWith(MockitoJUnitRunner.class)
+public class MicrometerEndpointTest {
+
+    private static final String METRICS_NAME = "metrics.name";
+
+    @Mock
+    private MeterRegistry registry;
+
+    @Mock
+    private Processor processor;
+
+    @Mock
+    private Exchange exchange;
+
+    @Mock
+    private Message in;
+
+    private MicrometerEndpoint endpoint;
+
+    private InOrder inOrder;
+
+    @Before
+    public void setUp() throws Exception {
+        endpoint = new MicrometerEndpoint(null, null, registry, MetricsType.COUNTER, METRICS_NAME, Collections.emptyList()) {
+            @Override
+            public Producer createProducer() throws Exception {
+                return null;
+            }
+
+            @Override
+            protected String createEndpointUri() {
+                return "not real endpoint";
+            }
+        };
+        inOrder = Mockito.inOrder(registry, processor, exchange, in);
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        inOrder.verifyNoMoreInteractions();
+    }
+
+    @Test
+    public void testAbstractMetricsEndpoint() throws Exception {
+        assertThat(endpoint.getMetricsName(), is(METRICS_NAME));
+        assertThat(endpoint.getRegistry(), is(registry));
+    }
+
+    @Test(expected = RuntimeCamelException.class)
+    public void testCreateConsumer() throws Exception {
+        endpoint.createConsumer(processor);
+    }
+
+    @Test
+    public void testIsSingleton() throws Exception {
+        assertThat(endpoint.isSingleton(), is(true));
+    }
+
+    @Test
+    public void testGetRegistry() throws Exception {
+        assertThat(endpoint.getRegistry(), is(registry));
+    }
+
+    @Test
+    public void testGetMetricsName() throws Exception {
+        assertThat(endpoint.getMetricsName(), is(METRICS_NAME));
+    }
+}
diff --git a/components/camel-micrometer/src/test/java/org/apache/camel/component/micrometer/TimerEndpointTest.java b/components/camel-micrometer/src/test/java/org/apache/camel/component/micrometer/TimerEndpointTest.java
new file mode 100644
index 0000000..620e760
--- /dev/null
+++ b/components/camel-micrometer/src/test/java/org/apache/camel/component/micrometer/TimerEndpointTest.java
@@ -0,0 +1,87 @@
+/**
+ * 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;
+
+import io.micrometer.core.instrument.MeterRegistry;
+import org.apache.camel.Producer;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.InOrder;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.junit.MockitoJUnitRunner;
+
+import java.util.Collections;
+
+import static org.hamcrest.Matchers.instanceOf;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.notNullValue;
+import static org.hamcrest.Matchers.nullValue;
+import static org.junit.Assert.assertThat;
+
+@RunWith(MockitoJUnitRunner.class)
+public class TimerEndpointTest {
+
+    private static final String METRICS_NAME = "metrics.name";
+
+    @Mock
+    private MeterRegistry registry;
+
+    private MicrometerEndpoint endpoint;
+
+    private InOrder inOrder;
+
+    @Before
+    public void setUp() throws Exception {
+        endpoint = new MicrometerEndpoint(null, null, registry, MetricsType.TIMER, METRICS_NAME, Collections.emptyList());
+        inOrder = Mockito.inOrder(registry);
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        inOrder.verifyNoMoreInteractions();
+    }
+
+    @Test
+    public void testTimerEndpoint() throws Exception {
+        assertThat(endpoint, is(notNullValue()));
+        assertThat(endpoint.getRegistry(), is(registry));
+        assertThat(endpoint.getMetricsName(), is(METRICS_NAME));
+    }
+
+    @Test
+    public void testCreateProducer() throws Exception {
+        Producer producer = endpoint.createProducer();
+        assertThat(producer, is(notNullValue()));
+        assertThat(producer, is(instanceOf(TimerProducer.class)));
+    }
+
+    @Test
+    public void testGetAction() throws Exception {
+        assertThat(endpoint.getAction(), is(nullValue()));
+    }
+
+    @Test
+    public void testSetAction() throws Exception {
+        assertThat(endpoint.getAction(), is(nullValue()));
+        endpoint.setAction(MicrometerTimerAction.start);
+        assertThat(endpoint.getAction(), is(MicrometerTimerAction.start));
+    }
+
+}
diff --git a/components/camel-micrometer/src/test/java/org/apache/camel/component/micrometer/TimerProducerTest.java b/components/camel-micrometer/src/test/java/org/apache/camel/component/micrometer/TimerProducerTest.java
new file mode 100644
index 0000000..b727466
--- /dev/null
+++ b/components/camel-micrometer/src/test/java/org/apache/camel/component/micrometer/TimerProducerTest.java
@@ -0,0 +1,260 @@
+/**
+ * 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;
+
+import io.micrometer.core.instrument.Clock;
+import io.micrometer.core.instrument.MeterRegistry;
+import io.micrometer.core.instrument.Timer;
+import io.micrometer.core.instrument.search.Search;
+import org.apache.camel.Exchange;
+import org.apache.camel.Message;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentMatchers;
+import org.mockito.InOrder;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.junit.MockitoJUnitRunner;
+
+import java.util.Collections;
+
+import static org.apache.camel.component.micrometer.AbstractMicrometerProducer.HEADER_PATTERN;
+import static org.apache.camel.component.micrometer.MicrometerConstants.HEADER_TIMER_ACTION;
+import static org.hamcrest.Matchers.*;
+import static org.junit.Assert.assertThat;
+import static org.mockito.Mockito.*;
+
+@RunWith(MockitoJUnitRunner.class)
+public class TimerProducerTest {
+
+    private static final String METRICS_NAME = "metrics.name";
+    private static final String PROPERTY_NAME = "timer" + ":" + METRICS_NAME;
+
+    @Mock
+    private MicrometerEndpoint endpoint;
+
+    @Mock
+    private Exchange exchange;
+
+    @Mock
+    private MeterRegistry registry;
+
+    @Mock
+    private MeterRegistry.Config config;
+
+    @Mock
+    private Clock clock;
+
+    @Mock
+    private Search search;
+
+    @Mock
+    private Timer timer;
+
+    @Mock
+    private Timer.Sample sample;
+
+    @Mock
+    private Message in;
+
+    private TimerProducer producer;
+
+    @Mock
+    private InOrder inOrder;
+
+    @Before
+    public void setUp() throws Exception {
+        producer = new TimerProducer(endpoint);
+        inOrder = Mockito.inOrder(endpoint, exchange, registry, timer, sample, in, search, config, clock);
+
+        when(exchange.getIn()).thenReturn(in);
+    }
+
+    @Test
+    public void testTimerProducer() throws Exception {
+        assertThat(producer, is(notNullValue()));
+        assertThat(producer.getEndpoint().equals(endpoint), is(true));
+    }
+
+    @Test
+    public void testProcessStart() throws Exception {
+        when(endpoint.getAction()).thenReturn(MicrometerTimerAction.start);
+        when(in.getHeader(HEADER_TIMER_ACTION, MicrometerTimerAction.start, MicrometerTimerAction.class)).thenReturn(MicrometerTimerAction.start);
+        when(exchange.getProperty(PROPERTY_NAME, Timer.Sample.class)).thenReturn(null);
+        producer.doProcess(exchange, METRICS_NAME, Collections.emptyList());
+        inOrder.verify(exchange, times(1)).getIn();
+        inOrder.verify(endpoint, times(1)).getAction();
+        inOrder.verify(in, times(1)).getHeader(HEADER_TIMER_ACTION, MicrometerTimerAction.start, MicrometerTimerAction.class);
+        inOrder.verify(exchange, times(1)).getProperty(PROPERTY_NAME, Timer.Sample.class);
+        inOrder.verify(registry, times(1)).timer(METRICS_NAME);
+        // inOrder.verify(timer, times(1)).time();
+        inOrder.verify(exchange, times(1)).setProperty(PROPERTY_NAME, sample);
+        inOrder.verifyNoMoreInteractions();
+    }
+
+    @Test
+    public void testProcessStartWithOverride() throws Exception {
+        when(endpoint.getAction()).thenReturn(MicrometerTimerAction.start);
+        when(in.getHeader(HEADER_TIMER_ACTION, MicrometerTimerAction.start, MicrometerTimerAction.class)).thenReturn(MicrometerTimerAction.stop);
+        when(exchange.getProperty(PROPERTY_NAME, Timer.Sample.class)).thenReturn(sample);
+        producer.doProcess(exchange, METRICS_NAME, Collections.emptyList());
+        inOrder.verify(exchange, times(1)).getIn();
+        inOrder.verify(endpoint, times(1)).getAction();
+        inOrder.verify(in, times(1)).getHeader(HEADER_TIMER_ACTION, MicrometerTimerAction.start, MicrometerTimerAction.class);
+        inOrder.verify(exchange, times(1)).getProperty(PROPERTY_NAME, Timer.Sample.class);
+        inOrder.verify(sample, times(1)).stop(timer);
+        inOrder.verify(exchange, times(1)).getIn();
+        inOrder.verify(in, times(1)).removeHeaders(HEADER_PATTERN);
+        inOrder.verifyNoMoreInteractions();
+    }
+
+    @Test
+    public void testProcessStop() throws Exception {
+        when(endpoint.getAction()).thenReturn(MicrometerTimerAction.stop);
+        when(in.getHeader(HEADER_TIMER_ACTION, MicrometerTimerAction.stop, MicrometerTimerAction.class)).thenReturn(MicrometerTimerAction.stop);
+        when(exchange.getProperty(PROPERTY_NAME, Timer.Sample.class)).thenReturn(sample);
+        producer.doProcess(exchange, METRICS_NAME, Collections.emptyList());
+        inOrder.verify(exchange, times(1)).getIn();
+        inOrder.verify(endpoint, times(1)).getAction();
+        inOrder.verify(in, times(1)).getHeader(HEADER_TIMER_ACTION, MicrometerTimerAction.stop, MicrometerTimerAction.class);
+        inOrder.verify(exchange, times(1)).getProperty(PROPERTY_NAME, Timer.Sample.class);
+        inOrder.verify(sample, times(1)).stop(timer);
+        inOrder.verify(exchange, times(1)).getIn();
+        inOrder.verify(in, times(1)).removeHeaders(HEADER_PATTERN);
+        inOrder.verifyNoMoreInteractions();
+    }
+
+    @Test
+    public void testProcessStopWithOverride() throws Exception {
+        when(endpoint.getAction()).thenReturn(MicrometerTimerAction.stop);
+        when(in.getHeader(HEADER_TIMER_ACTION, MicrometerTimerAction.stop, MicrometerTimerAction.class)).thenReturn(MicrometerTimerAction.start);
+        when(exchange.getProperty(PROPERTY_NAME, Timer.Sample.class)).thenReturn(null);
+        producer.doProcess(exchange, METRICS_NAME, Collections.emptyList());
+        inOrder.verify(exchange, times(1)).getIn();
+        inOrder.verify(endpoint, times(1)).getAction();
+        inOrder.verify(in, times(1)).getHeader(HEADER_TIMER_ACTION, MicrometerTimerAction.stop, MicrometerTimerAction.class);
+        inOrder.verify(exchange, times(1)).getProperty(PROPERTY_NAME, Timer.Sample.class);
+        inOrder.verify(registry, times(1)).timer(METRICS_NAME);
+        // inOrder.verify(timer, times(1)).time();
+        inOrder.verify(exchange, times(1)).setProperty(PROPERTY_NAME, sample);
+        inOrder.verify(exchange, times(1)).getIn();
+        inOrder.verify(in, times(1)).removeHeaders(HEADER_PATTERN);
+        inOrder.verifyNoMoreInteractions();
+    }
+
+    @Test
+    public void testProcessNoAction() throws Exception {
+        when(endpoint.getAction()).thenReturn(null);
+        producer.doProcess(exchange, METRICS_NAME, Collections.emptyList());
+        inOrder.verify(exchange, times(1)).getIn();
+        inOrder.verify(endpoint, times(1)).getAction();
+        inOrder.verify(in, times(1)).getHeader(HEADER_TIMER_ACTION, (Object) null, MicrometerTimerAction.class);
+        inOrder.verify(exchange, times(1)).getIn();
+        inOrder.verify(in, times(1)).removeHeaders(HEADER_PATTERN);
+        inOrder.verifyNoMoreInteractions();
+    }
+
+    @Test
+    public void testProcessNoActionOverride() throws Exception {
+        Object action = null;
+        when(endpoint.getAction()).thenReturn(null);
+        when(in.getHeader(HEADER_TIMER_ACTION, action, MicrometerTimerAction.class)).thenReturn(MicrometerTimerAction.start);
+        producer.doProcess(exchange, METRICS_NAME, Collections.emptyList());
+        inOrder.verify(exchange, times(1)).getIn();
+        inOrder.verify(endpoint, times(1)).getAction();
+        inOrder.verify(in, times(1)).getHeader(HEADER_TIMER_ACTION, action, MicrometerTimerAction.class);
+        inOrder.verify(exchange, times(1)).getProperty(PROPERTY_NAME, Timer.Sample.class);
+        inOrder.verify(registry, times(1)).timer(METRICS_NAME);
+        // inOrder.verify(timer, times(1)).time();
+        inOrder.verify(exchange, times(1)).setProperty(PROPERTY_NAME, sample);
+        inOrder.verify(exchange, times(1)).getIn();
+        inOrder.verify(in, times(1)).removeHeaders(HEADER_PATTERN);
+        inOrder.verifyNoMoreInteractions();
+    }
+
+    @Test
+    public void testHandleStart() throws Exception {
+        when(exchange.getProperty(PROPERTY_NAME, Timer.Sample.class)).thenReturn(null);
+        when(registry.config()).thenReturn(config);
+        when(config.clock()).thenReturn(clock);
+        doNothing().when(exchange).setProperty(ArgumentMatchers.eq(METRICS_NAME), ArgumentMatchers.any(Timer.Sample.class));
+        producer.handleStart(exchange, registry, METRICS_NAME);
+        inOrder.verify(exchange, times(1)).getProperty(PROPERTY_NAME, Timer.Sample.class);
+        inOrder.verify(exchange, times(1)).setProperty(ArgumentMatchers.eq(METRICS_NAME), ArgumentMatchers.any(Timer.Sample.class));
+        inOrder.verify(exchange, times(1)).getIn();
+        inOrder.verify(in, times(1)).removeHeaders(HEADER_PATTERN);
+        inOrder.verifyNoMoreInteractions();
+    }
+
+    @Test
+    public void testHandleStartAlreadyRunning() throws Exception {
+        when(exchange.getProperty(PROPERTY_NAME, Timer.Sample.class)).thenReturn(sample);
+        producer.handleStart(exchange, registry, METRICS_NAME);
+        inOrder.verify(exchange, times(1)).getProperty(PROPERTY_NAME, Timer.Sample.class);
+        inOrder.verify(exchange, times(1)).getIn();
+        inOrder.verify(in, times(1)).removeHeaders(HEADER_PATTERN);
+        inOrder.verifyNoMoreInteractions();
+    }
+
+    @Test
+    public void testHandleStop() throws Exception {
+        when(exchange.getProperty(PROPERTY_NAME, Timer.Sample.class)).thenReturn(sample);
+        producer.doProcess(exchange, METRICS_NAME, Collections.emptyList());
+        inOrder.verify(exchange, times(1)).getProperty(PROPERTY_NAME, Timer.Sample.class);
+        inOrder.verify(sample, times(1)).stop(timer);
+        inOrder.verify(exchange, times(1)).removeProperty(PROPERTY_NAME);
+        inOrder.verify(exchange, times(1)).getIn();
+        inOrder.verify(in, times(1)).removeHeaders(HEADER_PATTERN);
+        inOrder.verifyNoMoreInteractions();
+    }
+
+    @Test
+    public void testHandleStopContextNotFound() throws Exception {
+        when(exchange.getProperty(PROPERTY_NAME, Timer.Sample.class)).thenReturn(null);
+        producer.doProcess(exchange, METRICS_NAME, Collections.emptyList());
+        inOrder.verify(exchange, times(1)).getProperty(PROPERTY_NAME, Timer.Sample.class);
+        inOrder.verify(exchange, times(1)).getIn();
+        inOrder.verify(in, times(1)).removeHeaders(HEADER_PATTERN);
+        inOrder.verifyNoMoreInteractions();
+    }
+
+    @Test
+    public void testGetPropertyName() throws Exception {
+        assertThat(producer.getPropertyName(METRICS_NAME), is("timer" + ":" + METRICS_NAME));
+    }
+
+    @Test
+    public void testGetTimerContextFromExchange() throws Exception {
+        when(exchange.getProperty(PROPERTY_NAME, Timer.Sample.class)).thenReturn(sample);
+        assertThat(producer.getTimerSampleFromExchange(exchange, PROPERTY_NAME), is(sample));
+        inOrder.verify(exchange, times(1)).getProperty(PROPERTY_NAME, Timer.Sample.class);
+        //inOrder.verify(exchange, times(1)).getIn();
+        //inOrder.verify(in, times(1)).removeHeaders(HEADER_PATTERN);
+        inOrder.verifyNoMoreInteractions();
+    }
+
+    @Test
+    public void testGetTimerContextFromExchangeNotFound() throws Exception {
+        when(exchange.getProperty(PROPERTY_NAME, Timer.Sample.class)).thenReturn(null);
+        assertThat(producer.getTimerSampleFromExchange(exchange, PROPERTY_NAME), is(nullValue()));
+        inOrder.verify(exchange, times(1)).getProperty(PROPERTY_NAME, Timer.Sample.class);
+        //inOrder.verify(exchange, times(1)).getIn();
+        //inOrder.verify(in, times(1)).removeHeaders(HEADER_PATTERN);
+        inOrder.verifyNoMoreInteractions();
+    }
+}
diff --git a/components/camel-micrometer/src/test/java/org/apache/camel/component/micrometer/TimerRouteTest.java b/components/camel-micrometer/src/test/java/org/apache/camel/component/micrometer/TimerRouteTest.java
new file mode 100644
index 0000000..8d5c5ba
--- /dev/null
+++ b/components/camel-micrometer/src/test/java/org/apache/camel/component/micrometer/TimerRouteTest.java
@@ -0,0 +1,159 @@
+/**
+ * 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;
+
+import io.micrometer.core.instrument.Clock;
+import io.micrometer.core.instrument.Counter;
+import io.micrometer.core.instrument.MeterRegistry;
+import io.micrometer.core.instrument.Timer;
+import io.micrometer.core.instrument.search.Search;
+import io.micrometer.core.instrument.simple.SimpleMeterRegistry;
+import org.apache.camel.EndpointInject;
+import org.apache.camel.Produce;
+import org.apache.camel.ProducerTemplate;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.spring.javaconfig.SingleRouteCamelConfiguration;
+import org.apache.camel.test.spring.CamelSpringDelegatingTestContextLoader;
+import org.apache.camel.test.spring.CamelSpringRunner;
+import org.apache.camel.test.spring.MockEndpoints;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.InOrder;
+import org.mockito.Mockito;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.test.context.ContextConfiguration;
+
+import java.util.concurrent.TimeUnit;
+
+import static org.apache.camel.component.micrometer.MicrometerComponent.METRICS_REGISTRY;
+import static org.apache.camel.component.micrometer.MicrometerConstants.HEADER_METRIC_NAME;
+import static org.apache.camel.component.micrometer.MicrometerConstants.HEADER_TIMER_ACTION;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.*;
+import static org.mockito.Mockito.mock;
+
+@RunWith(CamelSpringRunner.class)
+@ContextConfiguration(
+        classes = { TimerRouteTest.TestConfig.class },
+        loader = CamelSpringDelegatingTestContextLoader.class)
+@MockEndpoints
+public class TimerRouteTest {
+
+    @EndpointInject(uri = "mock:out")
+    private MockEndpoint endpoint;
+
+    @Produce(uri = "direct:in-1")
+    private ProducerTemplate producer1;
+
+    @Produce(uri = "direct:in-2")
+    private ProducerTemplate producer2;
+
+    @Produce(uri = "direct:in-3")
+    private ProducerTemplate producer3;
+
+    private MeterRegistry registry;
+
+    @Configuration
+    public static class TestConfig extends SingleRouteCamelConfiguration {
+
+        @Bean
+        @Override
+        public RouteBuilder route() {
+            return new RouteBuilder() {
+
+                @Override
+                public void configure() throws Exception {
+                    from("direct:in-1")
+                            .setHeader(HEADER_METRIC_NAME, constant("B"))
+                            .to("micrometer:timer:A?action=start")
+                            .delay(100L)
+                            .setHeader(HEADER_METRIC_NAME, constant("B"))
+                            .to("micrometer:timer:A?action=stop")
+                            .to("mock:out");
+
+                    from("direct:in-2")
+                            .setHeader(HEADER_TIMER_ACTION, constant(MicrometerTimerAction.start))
+                            .to("micrometer:timer:A")
+                            .delay(100L)
+                            .setHeader(HEADER_TIMER_ACTION, constant(MicrometerTimerAction.stop))
+                            .to("micrometer:timer:A")
+                            .to("mock:out");
+
+                    from("direct:in-3")
+                            .to("micrometer:timer:C?action=start")
+                            .delay(100L)
+                            .to("micrometer:timer:C?action=stop")
+                            .to("mock:out");
+                }
+            };
+        }
+
+        @Bean(name = METRICS_REGISTRY)
+        public MeterRegistry getMetricRegistry() {
+            return new SimpleMeterRegistry();
+        }
+    }
+
+    @Before
+    public void setup() {
+        registry = endpoint.getCamelContext().getRegistry().lookupByNameAndType(METRICS_REGISTRY, MeterRegistry.class);
+    }
+
+    @After
+    public void tearDown() {
+        endpoint.reset();
+    }
+
+    @Test
+    public void testOverrideMetricsName() throws Exception {
+        Object body = new Object();
+        endpoint.expectedBodiesReceived(body);
+        producer1.sendBody(body);
+        Timer timer = registry.find("B").timer();
+        assertEquals(1L, timer.count());
+        assertTrue(timer.max(TimeUnit.MILLISECONDS) > 0.0D);
+        endpoint.assertIsSatisfied();
+    }
+
+
+    @Test
+    public void testOverrideNoAction() throws Exception {
+        Object body = new Object();
+        endpoint.expectedBodiesReceived(body);
+        producer2.sendBody(body);
+        Timer timer = registry.find(MicrometerConstants.HEADER_PREFIX + "." + "A").timer();
+        assertEquals(1L, timer.count());
+        assertTrue(timer.max(TimeUnit.MILLISECONDS) > 0.0D);
+        endpoint.assertIsSatisfied();
+    }
+
+    @Test
+    public void testNormal() throws Exception {
+        Object body = new Object();
+        endpoint.expectedBodiesReceived(body);
+        producer3.sendBody(body);
+        Timer timer = registry.find(MicrometerConstants.HEADER_PREFIX + "." + "A").timer();
+        assertEquals(1L, timer.count());
+        assertTrue(timer.max(TimeUnit.MILLISECONDS) > 0.0D);
+        endpoint.assertIsSatisfied();
+    }
+}
diff --git a/components/camel-micrometer/src/test/java/org/apache/camel/component/micrometer/messagehistory/ExceptionRouteMicrometerMessageHistoryTest.java b/components/camel-micrometer/src/test/java/org/apache/camel/component/micrometer/messagehistory/ExceptionRouteMicrometerMessageHistoryTest.java
new file mode 100644
index 0000000..0675e27
--- /dev/null
+++ b/components/camel-micrometer/src/test/java/org/apache/camel/component/micrometer/messagehistory/ExceptionRouteMicrometerMessageHistoryTest.java
@@ -0,0 +1,101 @@
+/**
+ * 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.messagehistory;
+
+import io.micrometer.core.instrument.MeterRegistry;
+import io.micrometer.core.instrument.Metrics;
+import io.micrometer.core.instrument.simple.SimpleMeterRegistry;
+import org.apache.camel.CamelContext;
+import org.apache.camel.Exchange;
+import org.apache.camel.Processor;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.test.junit4.CamelTestSupport;
+import org.junit.Test;
+
+public class ExceptionRouteMicrometerMessageHistoryTest extends CamelTestSupport {
+
+    private MeterRegistry registry = new SimpleMeterRegistry();
+
+    @Override
+    protected CamelContext createCamelContext() throws Exception {
+        CamelContext context = super.createCamelContext();
+
+        MicrometerMessageHistoryFactory factory = new MicrometerMessageHistoryFactory();
+        factory.setMeterRegistry(registry);
+        context.setMessageHistoryFactory(factory);
+
+        return context;
+    }
+
+    @Test
+    public void testMetricsHistory() throws Exception {
+        getMockEndpoint("mock:foo").expectedMessageCount(5);
+        getMockEndpoint("mock:bar").expectedMessageCount(5);
+        getMockEndpoint("mock:baz").expectedMessageCount(0);
+        getMockEndpoint("mock:exception").expectedMessageCount(5);
+
+        for (int i = 0; i < 10; i++) {
+            if (i % 2 == 0) {
+                template.sendBody("seda:foo", "Hello " + i);
+            } else {
+                template.sendBody("seda:bar", "Hello " + i);
+            }
+        }
+
+        assertMockEndpointsSatisfied();
+
+        // there should be 3 names
+        assertEquals(5, registry.getMeters().size());
+
+        // get the message history service
+        MicrometerMessageHistoryService service = context.hasService(MicrometerMessageHistoryService.class);
+        assertNotNull(service);
+        String json = service.dumpStatisticsAsJson();
+        assertNotNull(json);
+        log.info(json);
+
+        assertTrue(json.contains("foo.history"));
+        assertTrue(json.contains("bar.history"));
+        assertTrue(json.contains("exception.history"));
+    }
+
+    @Override
+    protected RouteBuilder createRouteBuilder() throws Exception {
+        return new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                onException(Exception.class)
+                        .routeId("ExceptionRoute")
+                        .log("Exception received.")
+                        .to("mock:exception").id("exception");
+
+                from("seda:foo")
+                        .to("mock:foo").id("foo");
+
+                from("seda:bar")
+                        .to("mock:bar").id("bar")
+                        .process(new Processor() {
+                            @Override
+                            public void process(Exchange exchange) throws Exception {
+                                throw new Exception("Metrics Exception");
+                            }
+                        })
+                        .to("mock:baz").id("baz");
+            }
+        };
+    }
+}
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
new file mode 100644
index 0000000..5883d1e
--- /dev/null
+++ b/components/camel-micrometer/src/test/java/org/apache/camel/component/micrometer/messagehistory/ManagedMessageHistoryTest.java
@@ -0,0 +1,135 @@
+/**
+ * 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.messagehistory;
+
+import java.util.Set;
+import javax.management.MBeanServer;
+import javax.management.ObjectName;
+
+import io.micrometer.core.instrument.MeterRegistry;
+import io.micrometer.core.instrument.Metrics;
+import io.micrometer.core.instrument.simple.SimpleMeterRegistry;
+import org.apache.camel.CamelContext;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.micrometer.MicrometerComponent;
+import org.apache.camel.impl.JndiRegistry;
+import org.apache.camel.test.junit4.CamelTestSupport;
+import org.junit.Ignore;
+import org.junit.Test;
+
+@Ignore
+public class ManagedMessageHistoryTest extends CamelTestSupport {
+
+    private MeterRegistry registry = new SimpleMeterRegistry();
+
+    @Override
+    protected boolean useJmx() {
+        return true;
+    }
+
+    protected MBeanServer getMBeanServer() {
+        return context.getManagementStrategy().getManagementAgent().getMBeanServer();
+    }
+    
+    // Setup the common MetricsRegistry for MetricsComponent and MetricsMessageHistoryFactory to use
+    @Override
+    protected JndiRegistry createRegistry() throws Exception {
+        JndiRegistry registry = super.createRegistry();
+        registry.bind(MicrometerComponent.METRICS_REGISTRY, registry);
+        return registry;
+    }
+
+    @Override
+    protected CamelContext createCamelContext() throws Exception {
+        CamelContext context = super.createCamelContext();
+
+        MicrometerMessageHistoryFactory factory = new MicrometerMessageHistoryFactory();
+        factory.setPrettyPrint(true);
+        factory.setMeterRegistry(registry);
+        context.setMessageHistoryFactory(factory);
+
+        return context;
+    }
+
+    @Test
+    public void testMessageHistory() throws Exception {
+        getMockEndpoint("mock:foo").expectedMessageCount(5);
+        getMockEndpoint("mock:bar").expectedMessageCount(5);
+        getMockEndpoint("mock:baz").expectedMessageCount(5);
+
+        for (int i = 0; i < 10; i++) {
+            if (i % 2 == 0) {
+                template.sendBody("seda:foo", "Hello " + i);
+            } else {
+                template.sendBody("seda:bar", "Hello " + i);
+            }
+        }
+
+        assertMockEndpointsSatisfied();
+
+        // there should be 3 names
+        assertEquals(3, registry.getMeters().size());
+
+        // there should be 3 mbeans
+        Set<ObjectName> set = getMBeanServer().queryNames(new ObjectName("org.apache.camel.micrometer:*"), null);
+        assertEquals(3, set.size());
+
+        // get the message history service using JMX
+        String name = String.format("org.apache.camel:context=%s,type=services,name=MicrometerMessageHistoryService", context.getManagementName());
+        ObjectName on = ObjectName.getInstance(name);
+        String json = (String) getMBeanServer().invoke(on, "dumpStatisticsAsJson", null, null);
+        assertNotNull(json);
+        log.info(json);
+        
+        assertTrue(json.contains("foo.history"));
+        assertTrue(json.contains("bar.history"));
+        assertTrue(json.contains("baz.history"));
+
+        // reset
+        getMBeanServer().invoke(on, "reset", null, null);
+
+        resetMocks();
+        getMockEndpoint("mock:foo").expectedMessageCount(1);
+
+        template.sendBody("seda:foo", "Hello Again");
+
+        assertMockEndpointsSatisfied();
+
+        json = (String) getMBeanServer().invoke(on, "dumpStatisticsAsJson", null, null);
+        assertNotNull(json);
+        log.info(json);
+
+        assertTrue(json.contains("foo.history"));
+        assertFalse(json.contains("bar.history"));
+        assertFalse(json.contains("baz.history"));
+    }
+
+    @Override
+    protected RouteBuilder createRouteBuilder() throws Exception {
+        return new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                from("seda:foo")
+                        .to("mock:foo").id("foo");
+
+                from("seda:bar")
+                        .to("mock:bar").id("bar")
+                        .to("mock:baz").id("baz");
+            }
+        };
+    }
+}
diff --git a/components/camel-micrometer/src/test/java/org/apache/camel/component/micrometer/messagehistory/MicrometerMessageHistoryTest.java b/components/camel-micrometer/src/test/java/org/apache/camel/component/micrometer/messagehistory/MicrometerMessageHistoryTest.java
new file mode 100644
index 0000000..93b9d18
--- /dev/null
+++ b/components/camel-micrometer/src/test/java/org/apache/camel/component/micrometer/messagehistory/MicrometerMessageHistoryTest.java
@@ -0,0 +1,88 @@
+/**
+ * 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.messagehistory;
+
+import io.micrometer.core.instrument.MeterRegistry;
+import io.micrometer.core.instrument.Metrics;
+import io.micrometer.core.instrument.simple.SimpleMeterRegistry;
+import org.apache.camel.CamelContext;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.test.junit4.CamelTestSupport;
+import org.junit.Test;
+
+public class MicrometerMessageHistoryTest extends CamelTestSupport {
+
+    private MeterRegistry registry = new SimpleMeterRegistry();
+
+    @Override
+    protected CamelContext createCamelContext() throws Exception {
+        CamelContext context = super.createCamelContext();
+
+        MicrometerMessageHistoryFactory factory = new MicrometerMessageHistoryFactory();
+        factory.setMeterRegistry(registry);
+        factory.setPrettyPrint(true);
+        context.setMessageHistoryFactory(factory);
+
+        return context;
+    }
+
+    @Test
+    public void testMetricsHistory() throws Exception {
+        getMockEndpoint("mock:foo").expectedMessageCount(5);
+        getMockEndpoint("mock:bar").expectedMessageCount(5);
+        getMockEndpoint("mock:baz").expectedMessageCount(5);
+
+        for (int i = 0; i < 10; i++) {
+            if (i % 2 == 0) {
+                template.sendBody("seda:foo", "Hello " + i);
+            } else {
+                template.sendBody("seda:bar", "Hello " + i);
+            }
+        }
+
+        assertMockEndpointsSatisfied();
+
+        // there should be 3 names
+        assertEquals(3, registry.getMeters().size());
+
+        // get the message history service
+        MicrometerMessageHistoryService service = context.hasService(MicrometerMessageHistoryService.class);
+        assertNotNull(service);
+        String json = service.dumpStatisticsAsJson();
+        assertNotNull(json);
+        log.info(json);
+
+        assertTrue(json.contains("foo.history"));
+        assertTrue(json.contains("bar.history"));
+        assertTrue(json.contains("baz.history"));
+    }
+
+    @Override
+    protected RouteBuilder createRouteBuilder() throws Exception {
+        return new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                from("seda:foo")
+                    .to("mock:foo").id("foo");
+
+                from("seda:bar")
+                    .to("mock:bar").id("bar")
+                    .to("mock:baz").id("baz");
+            }
+        };
+    }
+}
diff --git a/components/camel-micrometer/src/test/java/org/apache/camel/component/micrometer/messagehistory/SpringMicrometerMessageHistoryTest.java b/components/camel-micrometer/src/test/java/org/apache/camel/component/micrometer/messagehistory/SpringMicrometerMessageHistoryTest.java
new file mode 100644
index 0000000..f456e63
--- /dev/null
+++ b/components/camel-micrometer/src/test/java/org/apache/camel/component/micrometer/messagehistory/SpringMicrometerMessageHistoryTest.java
@@ -0,0 +1,64 @@
+/**
+ * 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.messagehistory;
+
+import io.micrometer.core.instrument.MeterRegistry;
+import org.apache.camel.test.spring.CamelSpringTestSupport;
+import org.junit.Test;
+import org.springframework.context.support.AbstractApplicationContext;
+import org.springframework.context.support.ClassPathXmlApplicationContext;
+
+public class SpringMicrometerMessageHistoryTest extends CamelSpringTestSupport {
+
+    @Override
+    protected AbstractApplicationContext createApplicationContext() {
+        return new ClassPathXmlApplicationContext("org/apache/camel/component/micrometer/messagehistory/SpringMetricsMessageHistoryTest.xml");
+    }
+
+    @Test
+    public void testMetricsHistory() throws Exception {
+        getMockEndpoint("mock:foo").expectedMessageCount(5);
+        getMockEndpoint("mock:bar").expectedMessageCount(5);
+        getMockEndpoint("mock:baz").expectedMessageCount(5);
+
+        for (int i = 0; i < 10; i++) {
+            if (i % 2 == 0) {
+                template.sendBody("seda:foo", "Hello " + i);
+            } else {
+                template.sendBody("seda:bar", "Hello " + i);
+            }
+        }
+
+        assertMockEndpointsSatisfied();
+
+        // there should be 3 names
+        MeterRegistry registry = context.getRegistry().findByType(MeterRegistry.class).iterator().next();
+        assertEquals(3, registry.getMeters().size());
+
+        // get the message history service
+        MicrometerMessageHistoryService service = context.hasService(MicrometerMessageHistoryService.class);
+        assertNotNull(service);
+        String json = service.dumpStatisticsAsJson();
+        assertNotNull(json);
+        log.info(json);
+
+        assertTrue(json.contains("foo.history"));
+        assertTrue(json.contains("bar.history"));
+        assertTrue(json.contains("baz.history"));
+    }
+
+}
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
new file mode 100644
index 0000000..f545c10
--- /dev/null
+++ b/components/camel-micrometer/src/test/java/org/apache/camel/component/micrometer/routepolicy/AbstractMicrometerRoutePolicyTest.java
@@ -0,0 +1,40 @@
+package org.apache.camel.component.micrometer.routepolicy;
+
+import io.micrometer.core.instrument.MeterRegistry;
+import io.micrometer.core.instrument.Metrics;
+import io.micrometer.core.instrument.simple.SimpleMeterRegistry;
+import org.apache.camel.CamelContext;
+import org.apache.camel.component.micrometer.MicrometerComponent;
+import org.apache.camel.impl.JndiRegistry;
+import org.apache.camel.test.junit4.CamelTestSupport;
+
+/**
+ * @author Christian Ohr
+ */
+public class AbstractMicrometerRoutePolicyTest extends CamelTestSupport {
+
+    protected MeterRegistry registry = new SimpleMeterRegistry();
+
+    @Override
+    protected boolean useJmx() {
+        return true;
+    }
+
+    @Override
+    protected JndiRegistry createRegistry() throws Exception {
+        JndiRegistry registry = super.createRegistry();
+        registry.bind(MicrometerComponent.METRICS_REGISTRY, this.registry);
+        return registry;
+    }
+
+    @Override
+    protected CamelContext createCamelContext() throws Exception {
+        CamelContext context = super.createCamelContext();
+
+        MicrometerRoutePolicyFactory factory = new MicrometerRoutePolicyFactory();
+        factory.setMeterRegistry(registry);
+        context.addRoutePolicyFactory(factory);
+
+        return context;
+    }
+}
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
new file mode 100644
index 0000000..8b97a76
--- /dev/null
+++ b/components/camel-micrometer/src/test/java/org/apache/camel/component/micrometer/routepolicy/ManagedMicrometerRoutePolicyTest.java
@@ -0,0 +1,84 @@
+/**
+ * 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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.Meter;
+import org.apache.camel.builder.RouteBuilder;
+import org.junit.Ignore;
+import org.junit.Test;
+
+import javax.management.MBeanServer;
+import javax.management.ObjectName;
+import java.util.List;
+import java.util.Set;
+
+@Ignore
+public class ManagedMicrometerRoutePolicyTest extends AbstractMicrometerRoutePolicyTest {
+
+
+    protected MBeanServer getMBeanServer() {
+        return context.getManagementStrategy().getManagementAgent().getMBeanServer();
+    }
+
+    @Test
+    public void testMetricsRoutePolicy() throws Exception {
+        getMockEndpoint("mock:result").expectedMessageCount(10);
+
+        for (int i = 0; i < 10; i++) {
+            if (i % 2 == 0) {
+                template.sendBody("seda:foo", "Hello " + i);
+            } else {
+                template.sendBody("seda:bar", "Hello " + i);
+            }
+        }
+
+        assertMockEndpointsSatisfied();
+
+        // there should be 3 names
+        List<Meter> meters = registry.getMeters();
+        assertEquals(3, meters.size());
+
+        // there should be 3 mbeans
+        Set<ObjectName> set = getMBeanServer().queryNames(new ObjectName("org.apache.camel.micrometer:*"), null);
+        assertEquals(3, set.size());
+
+        String name = String.format("org.apache.camel:context=%s,type=services,name=MicrometerRegistryService", context.getManagementName());
+        ObjectName on = ObjectName.getInstance(name);
+        String json = (String) getMBeanServer().invoke(on, "dumpStatisticsAsJson", null, null);
+        assertNotNull(json);
+        log.info(json);
+
+        assertTrue(json.contains("test"));
+        assertTrue(json.contains("bar.responses"));
+        assertTrue(json.contains("foo.responses"));
+    }
+
+    @Override
+    protected RouteBuilder createRouteBuilder() throws Exception {
+        return new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                from("seda:foo").routeId("foo")
+                        .to("micrometer:counter:test")
+                        .to("mock:result");
+
+                from("seda:bar").routeId("bar")
+                        .to("mock:result");
+            }
+        };
+    }
+}
diff --git a/components/camel-micrometer/src/test/java/org/apache/camel/component/micrometer/routepolicy/MicrometerRoutePolicyMulticastSubRouteTest.java b/components/camel-micrometer/src/test/java/org/apache/camel/component/micrometer/routepolicy/MicrometerRoutePolicyMulticastSubRouteTest.java
new file mode 100644
index 0000000..970afdd
--- /dev/null
+++ b/components/camel-micrometer/src/test/java/org/apache/camel/component/micrometer/routepolicy/MicrometerRoutePolicyMulticastSubRouteTest.java
@@ -0,0 +1,66 @@
+/**
+ * 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.Meter;
+import io.micrometer.core.instrument.Timer;
+import org.apache.camel.builder.RouteBuilder;
+import org.junit.Test;
+
+import java.util.List;
+
+/**
+ * CAMEL-9226 - check metrics are counted correctly in multicast sub-routes
+ */
+public class MicrometerRoutePolicyMulticastSubRouteTest extends AbstractMicrometerRoutePolicyTest {
+
+    @Test
+    public void testMetricsRoutePolicy() throws Exception {
+        getMockEndpoint("mock:foo").expectedMessageCount(1);
+        getMockEndpoint("mock:bar1").expectedMessageCount(1);
+        getMockEndpoint("mock:bar2").expectedMessageCount(1);
+
+        template.sendBody("direct:multicast", "Hello World");
+
+        assertMockEndpointsSatisfied();
+
+        // there should be 3 names
+        List<Meter> meters = registry.getMeters();
+        assertEquals(3, meters.size());
+
+
+        meters.forEach(meter -> {
+            Timer timer = (Timer) meter;
+            assertEquals("Timer " + timer.getId() + " should have count of 1",  1, timer.count());
+        });
+    }
+
+    @Override
+    protected RouteBuilder createRouteBuilder() throws Exception {
+        return new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                from("direct:foo").routeId("foo").to("mock:foo");
+
+                from("direct:bar").routeId("bar").multicast().to("mock:bar1", "mock:bar2");
+
+                from("direct:multicast").routeId("multicast").multicast().to("direct:foo", "direct:bar");
+
+            }
+        };
+    }
+}
diff --git a/components/camel-micrometer/src/test/java/org/apache/camel/component/micrometer/routepolicy/MicrometerRoutePolicySubRouteTest.java b/components/camel-micrometer/src/test/java/org/apache/camel/component/micrometer/routepolicy/MicrometerRoutePolicySubRouteTest.java
new file mode 100644
index 0000000..6e2c45e
--- /dev/null
+++ b/components/camel-micrometer/src/test/java/org/apache/camel/component/micrometer/routepolicy/MicrometerRoutePolicySubRouteTest.java
@@ -0,0 +1,58 @@
+/**
+ * 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.Meter;
+import io.micrometer.core.instrument.Timer;
+import org.apache.camel.builder.RouteBuilder;
+import org.junit.Test;
+
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+public class MicrometerRoutePolicySubRouteTest extends AbstractMicrometerRoutePolicyTest {
+
+    @Test
+    public void testMetricsRoutePolicy() throws Exception {
+        getMockEndpoint("mock:foo").expectedMessageCount(1);
+        getMockEndpoint("mock:bar").expectedMessageCount(1);
+
+        template.sendBody("direct:foo", "Hello World");
+
+        assertMockEndpointsSatisfied();
+
+        // there should be 2 names
+        List<Meter> meters = registry.getMeters();
+        assertEquals(2, meters.size());
+        meters.forEach(meter -> assertTrue(meter instanceof Timer));
+    }
+
+    @Override
+    protected RouteBuilder createRouteBuilder() throws Exception {
+        return new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                from("direct:foo").routeId("foo")
+                    .to("direct:bar")
+                    .to("mock:foo");
+
+                from("direct:bar").routeId("bar")
+                    .to("mock:bar");
+            }
+        };
+    }
+}
diff --git a/components/camel-micrometer/src/test/java/org/apache/camel/component/micrometer/routepolicy/MicrometerRoutePolicyTest.java b/components/camel-micrometer/src/test/java/org/apache/camel/component/micrometer/routepolicy/MicrometerRoutePolicyTest.java
new file mode 100644
index 0000000..e88abcd
--- /dev/null
+++ b/components/camel-micrometer/src/test/java/org/apache/camel/component/micrometer/routepolicy/MicrometerRoutePolicyTest.java
@@ -0,0 +1,61 @@
+/**
+ * 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.Meter;
+import io.micrometer.core.instrument.Timer;
+import org.apache.camel.builder.RouteBuilder;
+import org.junit.Test;
+
+import java.util.List;
+
+public class MicrometerRoutePolicyTest extends AbstractMicrometerRoutePolicyTest {
+
+    @Test
+    public void testMetricsRoutePolicy() throws Exception {
+        getMockEndpoint("mock:result").expectedMessageCount(10);
+
+        for (int i = 0; i < 10; i++) {
+            if (i % 2 == 0) {
+                template.sendBody("seda:foo", "Hello " + i);
+            } else {
+                template.sendBody("seda:bar", "Hello " + i);
+            }
+        }
+
+        assertMockEndpointsSatisfied();
+
+        // there should be 2 names
+        List<Meter> meters = registry.getMeters();
+        assertEquals(2, meters.size());
+        meters.forEach(meter -> assertTrue(meter instanceof Timer));
+    }
+
+    @Override
+    protected RouteBuilder createRouteBuilder() throws Exception {
+        return new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                from("seda:foo").routeId("foo")
+                    .to("mock:result");
+
+                from("seda:bar").routeId("bar")
+                    .to("mock:result");
+            }
+        };
+    }
+}
diff --git a/components/camel-micrometer/src/test/java/org/apache/camel/component/micrometer/spi/InstrumentedThreadPoolFactoryTest.java b/components/camel-micrometer/src/test/java/org/apache/camel/component/micrometer/spi/InstrumentedThreadPoolFactoryTest.java
new file mode 100644
index 0000000..5335657
--- /dev/null
+++ b/components/camel-micrometer/src/test/java/org/apache/camel/component/micrometer/spi/InstrumentedThreadPoolFactoryTest.java
@@ -0,0 +1,113 @@
+/**
+ * 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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.spi;
+
+import io.micrometer.core.instrument.MeterRegistry;
+import io.micrometer.core.instrument.internal.TimedExecutorService;
+import org.apache.camel.ThreadPoolRejectedPolicy;
+import org.apache.camel.spi.ThreadPoolFactory;
+import org.apache.camel.spi.ThreadPoolProfile;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.InOrder;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.junit.MockitoJUnitRunner;
+
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.TimeUnit;
+
+import static org.hamcrest.Matchers.*;
+import static org.junit.Assert.assertThat;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.times;
+
+@RunWith(MockitoJUnitRunner.class)
+public class InstrumentedThreadPoolFactoryTest {
+
+    private static final String METRICS_NAME = "metrics.name";
+
+    @Mock
+    private MeterRegistry registry;
+
+    @Mock
+    private ThreadPoolFactory threadPoolFactory;
+
+    @Mock
+    private ThreadFactory threadFactory;
+
+    private ThreadPoolProfile profile;
+
+    private InstrumentedThreadPoolFactory instrumentedThreadPoolFactory;
+
+    private InOrder inOrder;
+
+    @Before
+    public void setUp() throws Exception {
+        profile = new ThreadPoolProfile(METRICS_NAME);
+        profile.setDefaultProfile(false);
+        profile.setMaxPoolSize(10);
+        profile.setMaxQueueSize(1000);
+        profile.setPoolSize(5);
+        profile.setKeepAliveTime(5L);
+        profile.setTimeUnit(TimeUnit.SECONDS);
+        profile.setRejectedPolicy(ThreadPoolRejectedPolicy.CallerRuns);
+
+        instrumentedThreadPoolFactory = new InstrumentedThreadPoolFactory(registry, threadPoolFactory);
+
+        inOrder = Mockito.inOrder(registry);
+    }
+
+    @Test
+    public void testNewCacheThreadPool() throws Exception {
+        final ExecutorService executorService = instrumentedThreadPoolFactory.newCachedThreadPool(threadFactory);
+        assertThat(executorService, is(notNullValue()));
+        assertThat(executorService, is(instanceOf(TimedExecutorService.class)));
+
+        inOrder.verify(registry, times(1)).counter(anyString());
+        inOrder.verify(registry, times(1)).timer(anyString());
+        inOrder.verifyNoMoreInteractions();
+    }
+
+    @Test
+    public void testNewThreadPool() throws Exception {
+        final ExecutorService executorService = instrumentedThreadPoolFactory.newThreadPool(profile, threadFactory);
+        assertThat(executorService, is(notNullValue()));
+        assertThat(executorService, is(instanceOf(TimedExecutorService.class)));
+
+        inOrder.verify(registry, times(1)).timer("executor");
+        inOrder.verifyNoMoreInteractions();
+    }
+
+    @Test
+    public void testNewScheduledThreadPool() throws Exception {
+        final ScheduledExecutorService scheduledExecutorService = instrumentedThreadPoolFactory.newScheduledThreadPool(profile, threadFactory);
+
+        assertThat(scheduledExecutorService, is(notNullValue()));
+        assertThat(scheduledExecutorService, is(instanceOf(TimedExecutorService.class)));
+
+        inOrder.verify(registry, times(1)).counter("running");
+        inOrder.verify(registry, times(1)).timer("duration");
+        inOrder.verify(registry, times(1)).counter("scheduled.overrun");
+        inOrder.verify(registry, times(1)).summary("scheduled.percent-of-period");
+        inOrder.verifyNoMoreInteractions();
+    }
+
+}
diff --git a/components/camel-micrometer/src/test/resources/log4j2.properties b/components/camel-micrometer/src/test/resources/log4j2.properties
new file mode 100644
index 0000000..821d95a
--- /dev/null
+++ b/components/camel-micrometer/src/test/resources/log4j2.properties
@@ -0,0 +1,28 @@
+## ---------------------------------------------------------------------------
+## 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.
+## ---------------------------------------------------------------------------
+
+appender.file.type = File
+appender.file.name = file
+appender.file.fileName = target/camel-test-metrics.log
+appender.file.layout.type = PatternLayout
+appender.file.layout.pattern = %d [%-15.15t] %-5p %-30.30c{1} - %m%n
+appender.out.type = Console
+appender.out.name = out
+appender.out.layout.type = PatternLayout
+appender.out.layout.pattern = %d [%-15.15t] %-5p %-30.30c{1} - %m%n
+rootLogger.level = INFO
+rootLogger.appenderRef.file.ref = file
diff --git a/components/camel-micrometer/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker b/components/camel-micrometer/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker
new file mode 100644
index 0000000..ca6ee9c
--- /dev/null
+++ b/components/camel-micrometer/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker
@@ -0,0 +1 @@
+mock-maker-inline
\ No newline at end of file
diff --git a/components/camel-micrometer/src/test/resources/org/apache/camel/component/micrometer/messagehistory/SpringMetricsMessageHistoryTest.xml b/components/camel-micrometer/src/test/resources/org/apache/camel/component/micrometer/messagehistory/SpringMetricsMessageHistoryTest.xml
new file mode 100644
index 0000000..e3abf71
--- /dev/null
+++ b/components/camel-micrometer/src/test/resources/org/apache/camel/component/micrometer/messagehistory/SpringMetricsMessageHistoryTest.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    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.
+
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xmlns:context="http://www.springframework.org/schema/context"
+       xmlns:aop="http://www.springframework.org/schema/aop"
+       xsi:schemaLocation="
+       http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
+       http://camel.apache.org/schema/spring http://camel.apache.org/schema/spring/camel-spring.xsd
+    ">
+
+  <bean id="registry" class="io.micrometer.core.instrument.simple.SimpleMeterRegistry"/>
+
+  <bean id="history" class="org.apache.camel.component.micrometer.messagehistory.MicrometerMessageHistoryFactory">
+    <property name="meterRegistry" ref="registry"/>
+  </bean>
+
+  <camelContext xmlns="http://camel.apache.org/schema/spring">
+    <route>
+      <from uri="seda:foo"/>
+      <to uri="mock:foo" id="foo"/>
+    </route>
+    <route>
+      <from uri="seda:bar"/>
+      <to uri="mock:bar" id="bar"/>
+      <to uri="mock:baz" id="baz"/>
+    </route>
+  </camelContext>
+
+</beans>
diff --git a/components/pom.xml b/components/pom.xml
index 485a8d8..1c46e22 100644
--- a/components/pom.xml
+++ b/components/pom.xml
@@ -206,6 +206,7 @@
     <module>camel-lzf</module>
     <module>camel-mail</module>
     <module>camel-metrics</module>
+	<module>camel-micrometer</module>
     <module>camel-milo</module>
     <module>camel-mina</module>
     <module>camel-mina2</module>
diff --git a/parent/pom.xml b/parent/pom.xml
index 7c2dd0a..b940e4e 100644
--- a/parent/pom.xml
+++ b/parent/pom.xml
@@ -494,6 +494,7 @@
     <maven-war-plugin-version>3.0.0</maven-war-plugin-version>
     <mchange-commons-java-version>0.2.11</mchange-commons-java-version>
     <metrics-version>3.2.6</metrics-version>
+	<micrometer-version>1.0.2</micrometer-version>
     <milo-version>0.2.1</milo-version>
     <mina-bundle-version>1.1.7_6</mina-bundle-version>
     <mina-version>1.1.7</mina-version>
@@ -1621,6 +1622,11 @@
       </dependency>
       <dependency>
         <groupId>org.apache.camel</groupId>
+        <artifactId>camel-micrometer</artifactId>
+        <version>${project.version}</version>
+      </dependency>	  
+      <dependency>
+        <groupId>org.apache.camel</groupId>
         <artifactId>camel-milo</artifactId>
         <version>${project.version}</version>
       </dependency>

-- 
To stop receiving notification emails like this one, please contact
acosentino@apache.org.