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.