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 3871572a58678e0f74c43359231a7229f4a0b361 Author: Christian Ohr <christian....@gmail.com> AuthorDate: Tue May 15 14:44:06 2018 +0200 CAMEL-11600:added micrometer event notifiers --- components/camel-micrometer/pom.xml | 184 ++++++++++--------- .../src/main/docs/micrometer-component.adoc | 66 +++++-- .../micrometer/AbstractMicrometerProducer.java | 27 +-- .../DistributionStatisticConfigFilter.java | 8 +- .../component/micrometer/MicrometerComponent.java | 32 +--- .../component/micrometer/MicrometerConstants.java | 9 + .../component/micrometer/MicrometerUtils.java | 59 ++++++ .../AbstractMicrometerEventNotifier.java | 117 ++++++++++++ .../MicrometerEventNotifierMBean.java} | 21 +-- .../MicrometerEventNotifierService.java} | 11 +- .../MicrometerExchangeEventNotifier.java | 115 ++++++++++++ ...ometerExchangeEventNotifierNamingStrategy.java} | 21 +-- .../MicrometerRouteEventNotifier.java | 70 +++++++ .../messagehistory/MicrometerMessageHistory.java | 13 +- .../MicrometerMessageHistoryFactory.java | 37 ++-- .../MicrometerMessageHistoryNamingStrategy.java} | 20 +- .../routepolicy/MicrometerRoutePolicy.java | 56 ++++-- .../routepolicy/MicrometerRoutePolicyFactory.java | 10 +- ...va => MicrometerRoutePolicyNamingStrategy.java} | 17 +- .../routepolicy/MicrometerRoutePolicyService.java | 2 +- .../micrometer/MicrometerComponentTest.java | 10 +- .../AbstractMicrometerEventNotifierTest.java} | 26 +-- .../MicrometerExchangeEventNotifierTest.java | 87 +++++++++ .../MicrometerRouteEventNotifierTest.java | 66 +++++++ .../messagehistory/ManagedMessageHistoryTest.java | 14 +- .../AbstractMicrometerRoutePolicyTest.java | 4 +- .../routepolicy/MicrometerRoutePolicyTest.java | 12 +- examples/camel-example-micrometer/README.md | 66 +++++++ .../camel-example-micrometer}/pom.xml | 106 +++++------ .../camel-example-micrometer/src/data/message1.xml | 24 +++ .../camel-example-micrometer/src/data/message2.xml | 24 +++ .../example/micrometer/CamelPrometheusExample.java | 84 +++++++++ .../example/micrometer/ScheduledRouteBuilder.java | 44 +++++ .../example/micrometer/ScrapeRouteBuilder.java | 43 +++++ .../src/main/resources/META-INF/LICENSE.txt | 203 +++++++++++++++++++++ .../src/main/resources/META-INF/NOTICE.txt | 11 ++ .../src/main/resources/log4j2.properties | 23 +++ .../camel/example/micrometer/IntegrationTest.java | 27 +-- examples/pom.xml | 1 + 39 files changed, 1433 insertions(+), 337 deletions(-) diff --git a/components/camel-micrometer/pom.xml b/components/camel-micrometer/pom.xml index 273f497..9dcc3d4 100644 --- a/components/camel-micrometer/pom.xml +++ b/components/camel-micrometer/pom.xml @@ -17,103 +17,107 @@ 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> +<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> + <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> + <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> + <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> + <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>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> + <dependency> + <groupId>io.micrometer</groupId> + <artifactId>micrometer-registry-jmx</artifactId> + <version>${micrometer-version}</version> + <optional>true</optional> + </dependency> - <dependency> - <groupId>io.micrometer</groupId> - <artifactId>micrometer-core</artifactId> - <version>${micrometer-version}</version> - </dependency> - <dependency> - <groupId>io.micrometer</groupId> - <artifactId>micrometer-registry-jmx</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> + <!-- 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> - <!-- 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> + <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> - </dependencies> + <!-- 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 index 49320c3..17d8293 100644 --- a/components/camel-micrometer/src/main/docs/micrometer-component.adoc +++ b/components/camel-micrometer/src/main/docs/micrometer-component.adoc @@ -18,11 +18,14 @@ are link:#MicrometerComponent-counter[counter], link:#MicrometerComponent-dist and link:#MicrometerComponent-timer[timer]. http://micrometer.io/[Micrometer] provides simple way to measure behaviour of application. Configurable reporting backends (via Micrometer registries) is enabling different integration options for -collecting and visualizing statistics. The component also provides +collecting and visualizing statistics. + +The component also provides a `MicrometerRoutePolicyFactory` which allows to expose route statistics -using Micrometer, see bottom of page for details. +using Micrometer as well as `EventNotifier` implementations for counting +routes and timing Exchanges. -Maven users will need to add the following dependency to their `pom.xml` +Maven users need to add the following dependency to their `pom.xml` for this component: [source,xml] @@ -45,20 +48,21 @@ micrometer:[ counter | summary | timer ]:metricname[?options] ### Options // component options: START -The Micrometer component supports the following options: +The Micrometer 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. | | MeterRegistry +| *metricsRegistry* (advanced) | To use a custom configured MetricRegistry. | | MeterRegistry | *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: +The Micrometer endpoint is configured using URI syntax: ---- micrometer:metricsType:meterName @@ -66,28 +70,29 @@ micrometer:metricsType:meterName with the following path and query parameters: -==== Path Parameters: +==== Path Parameters (3 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 +| *metricsName* | *Required* Name of metrics | | String +| *tags* | Tags of metrics | | Tag> |=== -==== Query Parameters: +==== Query Parameters (5 parameters): [width="100%",cols="2,5,^1,2",options="header"] |=== | Name | Description | Default | Type -| *tags* (producer) | Tags that accompany the metric. Comma-separated list of key=value pairs | | String -| *action* (producer) | Action when using timer type | | MetricsTimerAction -| *decrement* (producer) | Decrement value when using counter type | | Double -| *increment* (producer) | Increment value when using counter type | | Double -| *value* (producer) | Value value when using histogram type | | Double +| *action* (producer) | Action expression when using timer type | | String +| *decrement* (producer) | Decrement value expression when using counter type | | String +| *increment* (producer) | Increment value expression when using counter type | | String +| *value* (producer) | Value expression when using histogram type | | String +| *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 @@ -95,8 +100,8 @@ with the following path and query parameters: ### [[MicrometerComponent-registry]]Meter Registry -By default the Camel Micrometer component uses a `SimpleMeterRegistry` instance. -This default registry should be replaced with a custom one by providing +By default the Camel Micrometer component creates a `SimpleMeterRegistry` instance. +This default registry, however, should be replaced with a custom one by providing a `MeterRegistry` bean. MeterRegistries primarily determine the backend monitoring system in use. A CompositeMeterRegistry can be used to address more than one monitoring target. @@ -488,6 +493,35 @@ String json = service.dumpStatisticsAsJson(); If JMX is enabled in the CamelContext, the MBean is registered in the `type=services` tree with `name=MicrometerMessageHistory`. + +### MicrometerEventNotifiers + +There is a `MicrometerRouteEventNotifier` (counting added and running routes) and a +`MicrometerExchangeEventNotifier` (timing exchanges from their creation to their completion). + +EventNotifiers can be added to the CamelContext, e.g.: + +[source,java] +---- +camelContext.getManagementStrategy().addEventNotifier(new MicrometerExchangeEventNotifier()) +---- + +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] +---- +MicrometerEventNotifierService service = context.hasService(MicrometerEventNotifierService.class); +String json = service.dumpStatisticsAsJson(); +---- + +If JMX is enabled in the CamelContext, the MBean is registered in the `type=services` tree +with `name=MicrometerEventNotifier`. + + ### InstrumentedThreadPoolFactory This factory allows you to gather performance information about Camel Thread Pools by injecting a `InstrumentedThreadPoolFactory` 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 index 0050de8..e21beb3 100644 --- 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 @@ -16,10 +16,7 @@ */ package org.apache.camel.component.micrometer; -import java.util.function.BiFunction; -import java.util.function.BinaryOperator; import java.util.function.Function; -import java.util.stream.Collectors; import io.micrometer.core.instrument.Meter; import io.micrometer.core.instrument.MeterRegistry; import io.micrometer.core.instrument.Tag; @@ -31,11 +28,14 @@ import org.apache.camel.impl.DefaultProducer; import org.apache.camel.language.simple.SimpleLanguage; import org.apache.camel.util.ObjectHelper; import static org.apache.camel.component.micrometer.MicrometerConstants.CAMEL_CONTEXT_TAG; +import static org.apache.camel.component.micrometer.MicrometerConstants.HEADER_METRIC_NAME; +import static org.apache.camel.component.micrometer.MicrometerConstants.HEADER_METRIC_TAGS; +import static org.apache.camel.component.micrometer.MicrometerConstants.HEADER_PREFIX; public abstract class AbstractMicrometerProducer<T extends Meter> extends DefaultProducer { - public static final String HEADER_PATTERN = MicrometerConstants.HEADER_PREFIX + "*"; + private static final String HEADER_PATTERN = HEADER_PREFIX + "*"; public AbstractMicrometerProducer(MicrometerEndpoint endpoint) { super(endpoint); @@ -51,9 +51,10 @@ public abstract class AbstractMicrometerProducer<T extends Meter> extends Defaul public void process(Exchange exchange) { Message in = exchange.getIn(); String defaultMetricsName = simple(exchange, getEndpoint().getMetricsName(), String.class); - String finalMetricsName = getMetricsName(in, defaultMetricsName); + String finalMetricsName = getStringHeader(in, HEADER_METRIC_NAME, defaultMetricsName); Iterable<Tag> defaultTags = getEndpoint().getTags(); - Iterable<Tag> finalTags = Tags.concat(defaultTags, getTags(in)).stream() + Iterable<Tag> headerTags = getTagHeader(in, HEADER_METRIC_TAGS, Tags.empty()); + Iterable<Tag> finalTags = Tags.concat(defaultTags, headerTags).stream() .map(tag -> Tag.of( simple(exchange, tag.getKey(), String.class), simple(exchange, tag.getValue(), String.class))) @@ -91,24 +92,16 @@ public abstract class AbstractMicrometerProducer<T extends Meter> extends Defaul return getEndpoint().getCamelContext().getTypeConverter().convertTo(clazz, expression); } - public String getMetricsName(Message in, String defaultValue) { - return getStringHeader(in, MicrometerConstants.HEADER_METRIC_NAME, defaultValue); - } - - public Iterable<Tag> getTags(Message in) { - return getTagHeader(in, MicrometerConstants.HEADER_METRIC_TAGS, Tags.empty()); - } - - public String getStringHeader(Message in, String header, String defaultValue) { + protected String getStringHeader(Message in, String header, String defaultValue) { String headerValue = in.getHeader(header, String.class); return ObjectHelper.isNotEmpty(headerValue) ? headerValue : defaultValue; } - public Double getDoubleHeader(Message in, String header, Double defaultValue) { + protected Double getDoubleHeader(Message in, String header, Double defaultValue) { return in.getHeader(header, defaultValue, Double.class); } - public Iterable<Tag> getTagHeader(Message in, String header, Iterable<Tag> defaultTags) { + protected Iterable<Tag> getTagHeader(Message in, String header, Iterable<Tag> defaultTags) { return (Iterable<Tag>) in.getHeader(header, defaultTags, Iterable.class); } 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 index 2bb9830..4d5f9da 100644 --- 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 @@ -25,7 +25,13 @@ import io.micrometer.core.instrument.distribution.DistributionStatisticConfig; * Example filter for adding common distribution statistics for all Timers and Distribution * Summaries. * Configure and add this to the {@link io.micrometer.core.instrument.MeterRegistry} - * if desired. + * if desired: + * + * <pre> + * DistributionStatisticConfigFilter filter = new DistributionStatisticConfigFilter() + * // filter.set... + * meterRegistry.config().meterFilter(filter) + * </pre> */ public class DistributionStatisticConfigFilter implements MeterFilter { 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 index 9acb975..0138e5b 100644 --- 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 @@ -33,7 +33,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** - * Represents the component that manages metrics endpoints. + * Represents the component that manages Micrometer endpoints. */ public class MicrometerComponent extends UriEndpointComponent { @@ -52,7 +52,7 @@ public class MicrometerComponent extends UriEndpointComponent { protected Endpoint createEndpoint(String uri, String remaining, Map<String, Object> parameters) throws Exception { if (metricsRegistry == null) { Registry camelRegistry = getCamelContext().getRegistry(); - metricsRegistry = getOrCreateMeterRegistry(camelRegistry, MicrometerConstants.METRICS_REGISTRY_NAME); + metricsRegistry = MicrometerUtils.getOrCreateMeterRegistry(camelRegistry, MicrometerConstants.METRICS_REGISTRY_NAME); } String metricsName = getMetricsName(remaining); MetricsType metricsType = getMetricsType(remaining); @@ -94,34 +94,6 @@ public class MicrometerComponent extends UriEndpointComponent { return Tags.empty(); } - 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; } diff --git a/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/MicrometerConstants.java b/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/MicrometerConstants.java index 55a2957..845cb44 100644 --- a/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/MicrometerConstants.java +++ b/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/MicrometerConstants.java @@ -28,12 +28,21 @@ public final class MicrometerConstants { public static final String DEFAULT_CAMEL_MESSAGE_HISTORY_METER_NAME = "CamelMessageHistory"; public static final String DEFAULT_CAMEL_ROUTE_POLICY_METER_NAME = "CamelRoutePolicy"; + public static final String DEFAULT_CAMEL_EXCHANGE_EVENT_METER_NAME = "CamelExchangeEventNotifier"; + public static final String DEFAULT_CAMEL_ROUTES_ADDED = "CamelRoutesAdded"; + public static final String DEFAULT_CAMEL_ROUTES_RUNNING = "CamelRoutesRunning"; public static final String ROUTE_ID_TAG = "routeId"; public static final String NODE_ID_TAG = "nodeId"; + public static final String FAILED_TAG = "failed"; public static final String CAMEL_CONTEXT_TAG = "camelContext"; + public static final String EVENT_TYPE_TAG = "eventType"; public static final String METRICS_REGISTRY_NAME = "metricsRegistry"; + public static final String SERVICE_NAME = "serviceName"; + public static final String ENDPOINT_NAME = "endpointName"; + + private MicrometerConstants() { } diff --git a/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/MicrometerUtils.java b/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/MicrometerUtils.java new file mode 100644 index 0000000..ccb129d3 --- /dev/null +++ b/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/MicrometerUtils.java @@ -0,0 +1,59 @@ +/* + * 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.Set; +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.simple.SimpleMeterRegistry; +import org.apache.camel.spi.Registry; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public abstract class MicrometerUtils { + + private static final Logger LOG = LoggerFactory.getLogger(MicrometerUtils.class); + + public static 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("MeterRegistry not found from Camel Registry for name \"{}\"", registryName); + LOG.info("Creating new default MeterRegistry"); + result = createMeterRegistry(); + } + return result; + } + + public static 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; + } + + public static MeterRegistry createMeterRegistry() { + return new SimpleMeterRegistry(); + } +} diff --git a/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/eventnotifier/AbstractMicrometerEventNotifier.java b/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/eventnotifier/AbstractMicrometerEventNotifier.java new file mode 100644 index 0000000..fcb89bb --- /dev/null +++ b/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/eventnotifier/AbstractMicrometerEventNotifier.java @@ -0,0 +1,117 @@ +/** + * 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.eventnotifier; + +import java.util.EventObject; +import java.util.concurrent.TimeUnit; +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.Tags; +import org.apache.camel.CamelContext; +import org.apache.camel.CamelContextAware; +import org.apache.camel.component.micrometer.MicrometerUtils; +import org.apache.camel.support.EventNotifierSupport; +import org.apache.camel.util.ObjectHelper; +import org.apache.camel.util.ServiceHelper; +import static org.apache.camel.component.micrometer.MicrometerConstants.METRICS_REGISTRY_NAME; +import static org.apache.camel.component.micrometer.MicrometerConstants.SERVICE_NAME; + +public abstract class AbstractMicrometerEventNotifier<T extends EventObject> extends EventNotifierSupport implements CamelContextAware { + + private final Class<T> eventType; + + private CamelContext camelContext; + private MeterRegistry meterRegistry; + private boolean prettyPrint; + private TimeUnit durationUnit = TimeUnit.MILLISECONDS; + + public AbstractMicrometerEventNotifier(Class<T> eventType) { + super(); + this.eventType = eventType; + } + + @Override + public void setCamelContext(CamelContext camelContext) { + this.camelContext = camelContext; + } + + @Override + public CamelContext getCamelContext() { + return camelContext; + } + + 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; + } + + @Override + public boolean isEnabled(EventObject eventObject) { + return eventType.isAssignableFrom(eventObject.getClass()); + } + + @Override + protected void doStart() throws Exception { + super.doStart(); + + if (meterRegistry == null) { + meterRegistry = MicrometerUtils.getOrCreateMeterRegistry(camelContext.getRegistry(), METRICS_REGISTRY_NAME); + } + + try { + MicrometerEventNotifierService registryService = camelContext.hasService(MicrometerEventNotifierService.class); + if (registryService == null) { + registryService = new MicrometerEventNotifierService(); + registryService.setMeterRegistry(getMeterRegistry()); + registryService.setPrettyPrint(isPrettyPrint()); + registryService.setDurationUnit(getDurationUnit()); + registryService.setMatchingTags(Tags.of(SERVICE_NAME, registryService.getClass().getSimpleName())); + camelContext.addService(registryService); + // ensure registry service is started + ServiceHelper.startService(registryService); + } + } catch (Exception e) { + throw ObjectHelper.wrapRuntimeCamelException(e); + } + + + try { + + } catch (Exception e) { + throw ObjectHelper.wrapRuntimeCamelException(e); + } + } + +} diff --git a/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/routepolicy/MicrometerRoutePolicyService.java b/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/eventnotifier/MicrometerEventNotifierMBean.java similarity index 56% copy from components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/routepolicy/MicrometerRoutePolicyService.java copy to components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/eventnotifier/MicrometerEventNotifierMBean.java index 91dd4b0..59bd089 100644 --- a/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/routepolicy/MicrometerRoutePolicyService.java +++ b/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/eventnotifier/MicrometerEventNotifierMBean.java @@ -14,17 +14,16 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.camel.component.micrometer.routepolicy; +package org.apache.camel.component.micrometer.eventnotifier; -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; +import org.apache.camel.api.management.ManagedOperation; + +public interface MicrometerEventNotifierMBean { + + @ManagedOperation(description = "Dumps the statistics as json") + String dumpStatisticsAsJson(); + + @ManagedOperation(description = "Dumps the statistics as json using seconds for time units") + String dumpStatisticsAsJsonTimeUnitSeconds(); -/** - * Service holding the {@link MeterRegistry} which registers all metrics. - */ -@ManagedResource(description = "MicrometerRegistry") -public final class MicrometerRoutePolicyService extends AbstractMicrometerService implements CamelContextAware, StaticService, MicrometerRoutePolicyMBean { } diff --git a/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/routepolicy/MicrometerRoutePolicyService.java b/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/eventnotifier/MicrometerEventNotifierService.java similarity index 71% copy from components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/routepolicy/MicrometerRoutePolicyService.java copy to components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/eventnotifier/MicrometerEventNotifierService.java index 91dd4b0..d920b57 100644 --- a/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/routepolicy/MicrometerRoutePolicyService.java +++ b/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/eventnotifier/MicrometerEventNotifierService.java @@ -14,17 +14,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.camel.component.micrometer.routepolicy; +package org.apache.camel.component.micrometer.eventnotifier; -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; +import org.apache.camel.component.micrometer.routepolicy.MicrometerRoutePolicyMBean; -/** - * Service holding the {@link MeterRegistry} which registers all metrics. - */ -@ManagedResource(description = "MicrometerRegistry") -public final class MicrometerRoutePolicyService extends AbstractMicrometerService implements CamelContextAware, StaticService, MicrometerRoutePolicyMBean { +@ManagedResource(description = "MicrometerEventNotifier") +public final class MicrometerEventNotifierService extends AbstractMicrometerService implements CamelContextAware, StaticService, MicrometerRoutePolicyMBean { } diff --git a/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/eventnotifier/MicrometerExchangeEventNotifier.java b/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/eventnotifier/MicrometerExchangeEventNotifier.java new file mode 100644 index 0000000..1f75221 --- /dev/null +++ b/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/eventnotifier/MicrometerExchangeEventNotifier.java @@ -0,0 +1,115 @@ +/** + * 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.eventnotifier; + +import java.util.EventObject; +import java.util.concurrent.TimeUnit; +import java.util.function.Predicate; +import io.micrometer.core.instrument.Timer; +import org.apache.camel.Exchange; +import org.apache.camel.management.event.AbstractExchangeEvent; +import org.apache.camel.management.event.ExchangeCompletedEvent; +import org.apache.camel.management.event.ExchangeCreatedEvent; +import org.apache.camel.management.event.ExchangeFailedEvent; +import org.apache.camel.management.event.ExchangeSentEvent; +import static org.apache.camel.component.micrometer.MicrometerConstants.CAMEL_CONTEXT_TAG; +import static org.apache.camel.component.micrometer.MicrometerConstants.DEFAULT_CAMEL_EXCHANGE_EVENT_METER_NAME; +import static org.apache.camel.component.micrometer.MicrometerConstants.ENDPOINT_NAME; +import static org.apache.camel.component.micrometer.MicrometerConstants.EVENT_TYPE_TAG; +import static org.apache.camel.component.micrometer.MicrometerConstants.FAILED_TAG; +import static org.apache.camel.component.micrometer.MicrometerConstants.SERVICE_NAME; + +public class MicrometerExchangeEventNotifier extends AbstractMicrometerEventNotifier<AbstractExchangeEvent> { + + private Predicate<Exchange> ignoreExchanges = exchange -> false; + private MicrometerExchangeEventNotifierNamingStrategy namingStrategy = (exchange, endpoint) -> DEFAULT_CAMEL_EXCHANGE_EVENT_METER_NAME; + + public MicrometerExchangeEventNotifier() { + super(AbstractExchangeEvent.class); + } + + public void setIgnoreExchanges(Predicate<Exchange> ignoreExchanges) { + this.ignoreExchanges = ignoreExchanges; + } + + public Predicate<Exchange> getIgnoreExchanges() { + return ignoreExchanges; + } + + public MicrometerExchangeEventNotifierNamingStrategy getNamingStrategy() { + return namingStrategy; + } + + public void setNamingStrategy(MicrometerExchangeEventNotifierNamingStrategy namingStrategy) { + this.namingStrategy = namingStrategy; + } + + @Override + public boolean isEnabled(EventObject eventObject) { + return super.isEnabled(eventObject) && !ignoreExchanges.test(((AbstractExchangeEvent) eventObject).getExchange()); + } + + @Override + protected void doStart() throws Exception { + super.doStart(); + } + + @Override + public void notify(EventObject eventObject) { + if (eventObject instanceof ExchangeSentEvent) { + handleSentEvent((ExchangeSentEvent) eventObject); + } else if (eventObject instanceof ExchangeCreatedEvent) { + handleCreatedEvent((ExchangeCreatedEvent) eventObject); + } else if (eventObject instanceof ExchangeCompletedEvent || eventObject instanceof ExchangeFailedEvent) { + handleDoneEvent((AbstractExchangeEvent) eventObject); + } + } + + protected void handleSentEvent(ExchangeSentEvent sentEvent) { + Timer.builder(namingStrategy.getName(sentEvent.getExchange(), sentEvent.getEndpoint())) + .tag(CAMEL_CONTEXT_TAG, getCamelContext().getName()) + .tag(SERVICE_NAME, MicrometerEventNotifierService.class.getSimpleName()) + .tag(ENDPOINT_NAME, sentEvent.getEndpoint().getEndpointUri()) + .tag(FAILED_TAG, Boolean.toString(sentEvent.getExchange().isFailed())) + .tag(EVENT_TYPE_TAG, sentEvent.getClass().getSimpleName()) + .register(getMeterRegistry()) + .record(sentEvent.getTimeTaken(), TimeUnit.MILLISECONDS); + } + + protected void handleCreatedEvent(ExchangeCreatedEvent createdEvent) { + String name = namingStrategy.getName(createdEvent.getExchange(), createdEvent.getExchange().getFromEndpoint()); + createdEvent.getExchange().setProperty("eventTimer:" + name, Timer.start(getMeterRegistry())); + } + + + protected void handleDoneEvent(AbstractExchangeEvent doneEvent) { + String name = namingStrategy.getName(doneEvent.getExchange(), doneEvent.getExchange().getFromEndpoint()); + // Would have preferred LongTaskTimer, but you cannot set the FAILED_TAG once it is registered + Timer.Sample sample = (Timer.Sample) doneEvent.getExchange().removeProperty("eventTimer:" + name); + if (sample != null) { + sample.stop(Timer.builder(name) + .tag(CAMEL_CONTEXT_TAG, getCamelContext().getName()) + .tag(SERVICE_NAME, MicrometerEventNotifierService.class.getSimpleName()) + .tag(ENDPOINT_NAME, doneEvent.getExchange().getFromEndpoint().getEndpointUri()) + .tag(FAILED_TAG, Boolean.toString(doneEvent.getExchange().isFailed())) + .tag(EVENT_TYPE_TAG, doneEvent.getClass().getSimpleName()) + .register(getMeterRegistry())); + } + } + + +} diff --git a/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/routepolicy/MicrometerRoutePolicyService.java b/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/eventnotifier/MicrometerExchangeEventNotifierNamingStrategy.java similarity index 53% copy from components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/routepolicy/MicrometerRoutePolicyService.java copy to components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/eventnotifier/MicrometerExchangeEventNotifierNamingStrategy.java index 91dd4b0..274bbaa 100644 --- a/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/routepolicy/MicrometerRoutePolicyService.java +++ b/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/eventnotifier/MicrometerExchangeEventNotifierNamingStrategy.java @@ -6,25 +6,22 @@ * (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 + * 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; +package org.apache.camel.component.micrometer.eventnotifier; -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; +import org.apache.camel.Endpoint; +import org.apache.camel.Exchange; + +public interface MicrometerExchangeEventNotifierNamingStrategy { + + String getName(Exchange exchange, Endpoint endpoint); -/** - * Service holding the {@link MeterRegistry} which registers all metrics. - */ -@ManagedResource(description = "MicrometerRegistry") -public final class MicrometerRoutePolicyService extends AbstractMicrometerService implements CamelContextAware, StaticService, MicrometerRoutePolicyMBean { } diff --git a/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/eventnotifier/MicrometerRouteEventNotifier.java b/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/eventnotifier/MicrometerRouteEventNotifier.java new file mode 100644 index 0000000..d31ff21 --- /dev/null +++ b/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/eventnotifier/MicrometerRouteEventNotifier.java @@ -0,0 +1,70 @@ +/** + * 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.eventnotifier; + +import java.util.EventObject; +import java.util.concurrent.atomic.AtomicLong; +import io.micrometer.core.instrument.Gauge; +import org.apache.camel.management.event.AbstractRouteEvent; +import org.apache.camel.management.event.RouteAddedEvent; +import org.apache.camel.management.event.RouteRemovedEvent; +import org.apache.camel.management.event.RouteStartedEvent; +import org.apache.camel.management.event.RouteStoppedEvent; +import static org.apache.camel.component.micrometer.MicrometerConstants.CAMEL_CONTEXT_TAG; +import static org.apache.camel.component.micrometer.MicrometerConstants.DEFAULT_CAMEL_ROUTES_ADDED; +import static org.apache.camel.component.micrometer.MicrometerConstants.DEFAULT_CAMEL_ROUTES_RUNNING; +import static org.apache.camel.component.micrometer.MicrometerConstants.EVENT_TYPE_TAG; +import static org.apache.camel.component.micrometer.MicrometerConstants.SERVICE_NAME; + + +public class MicrometerRouteEventNotifier extends AbstractMicrometerEventNotifier<AbstractRouteEvent> { + + private final AtomicLong routesAdded = new AtomicLong(); + private final AtomicLong routesRunning = new AtomicLong(); + + public MicrometerRouteEventNotifier() { + super(AbstractRouteEvent.class); + } + + @Override + protected void doStart() throws Exception { + super.doStart(); + Gauge.builder(DEFAULT_CAMEL_ROUTES_ADDED, routesAdded, value -> Long.valueOf(value.get()).doubleValue()) + .tag(SERVICE_NAME, MicrometerEventNotifierService.class.getSimpleName()) + .tag(CAMEL_CONTEXT_TAG, getCamelContext().getName()) + .tag(EVENT_TYPE_TAG, AbstractRouteEvent.class.getSimpleName()) + .register(getMeterRegistry()); + Gauge.builder(DEFAULT_CAMEL_ROUTES_RUNNING, routesRunning, value -> Long.valueOf(value.get()).doubleValue()) + .tag(SERVICE_NAME, MicrometerEventNotifierService.class.getSimpleName()) + .tag(CAMEL_CONTEXT_TAG, getCamelContext().getName()) + .tag(EVENT_TYPE_TAG, AbstractRouteEvent.class.getSimpleName()) + .register(getMeterRegistry()); + } + + @Override + public void notify(EventObject eventObject) { + if (eventObject instanceof RouteAddedEvent) { + routesAdded.incrementAndGet(); + } else if (eventObject instanceof RouteRemovedEvent) { + routesAdded.decrementAndGet(); + } else if (eventObject instanceof RouteStartedEvent) { + routesRunning.incrementAndGet(); + } else if (eventObject instanceof RouteStoppedEvent) { + routesRunning.decrementAndGet(); + } + } +} 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 index aef0908..2d45b70 100644 --- 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 @@ -23,31 +23,36 @@ import org.apache.camel.NamedNode; import org.apache.camel.Route; import org.apache.camel.impl.DefaultMessageHistory; import static org.apache.camel.component.micrometer.MicrometerConstants.CAMEL_CONTEXT_TAG; -import static org.apache.camel.component.micrometer.MicrometerConstants.DEFAULT_CAMEL_MESSAGE_HISTORY_METER_NAME; import static org.apache.camel.component.micrometer.MicrometerConstants.NODE_ID_TAG; import static org.apache.camel.component.micrometer.MicrometerConstants.ROUTE_ID_TAG; +import static org.apache.camel.component.micrometer.MicrometerConstants.SERVICE_NAME; /** - * A micrometer metrics based {@link MessageHistory} + * A micrometer metrics based {@link MessageHistory}. This could also use {@link #elapsed} + * provided by the super class, but Micrometer can potentially use other {@link io.micrometer.core.instrument.Clock clocks} + * and measures in nano-second precision. */ public class MicrometerMessageHistory extends DefaultMessageHistory { private final Route route; private final Timer.Sample sample; private final MeterRegistry meterRegistry; + private final MicrometerMessageHistoryNamingStrategy namingStrategy; - public MicrometerMessageHistory(MeterRegistry meterRegistry, Route route, NamedNode namedNode, long timestamp) { + public MicrometerMessageHistory(MeterRegistry meterRegistry, Route route, NamedNode namedNode, MicrometerMessageHistoryNamingStrategy namingStrategy, long timestamp) { super(route.getId(), namedNode, timestamp); this.meterRegistry = meterRegistry; this.route = route; + this.namingStrategy = namingStrategy; this.sample = Timer.start(meterRegistry); } @Override public void nodeProcessingDone() { super.nodeProcessingDone(); - Timer timer = Timer.builder(DEFAULT_CAMEL_MESSAGE_HISTORY_METER_NAME) + Timer timer = Timer.builder(namingStrategy.getName(route, getNode())) .tag(CAMEL_CONTEXT_TAG, route.getRouteContext().getCamelContext().getName()) + .tag(SERVICE_NAME, MicrometerMessageHistoryService.class.getSimpleName()) .tag(ROUTE_ID_TAG, getRouteId()) .tag(NODE_ID_TAG, getNode().getId()) .register(meterRegistry); 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 index c427712..2e6ac67 100644 --- 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 @@ -19,16 +19,19 @@ package org.apache.camel.component.micrometer.messagehistory; import java.util.Date; import java.util.concurrent.TimeUnit; import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.Tags; import org.apache.camel.CamelContext; import org.apache.camel.CamelContextAware; import org.apache.camel.MessageHistory; import org.apache.camel.NamedNode; import org.apache.camel.NonManagedService; import org.apache.camel.StaticService; +import org.apache.camel.component.micrometer.MicrometerUtils; import org.apache.camel.spi.MessageHistoryFactory; import org.apache.camel.support.ServiceSupport; import org.apache.camel.util.ObjectHelper; -import static org.apache.camel.component.micrometer.MicrometerConstants.DEFAULT_CAMEL_MESSAGE_HISTORY_METER_NAME; +import static org.apache.camel.component.micrometer.MicrometerConstants.METRICS_REGISTRY_NAME; +import static org.apache.camel.component.micrometer.MicrometerConstants.SERVICE_NAME; /** * A factory to setup and use {@link MicrometerMessageHistory} as message history implementation. @@ -39,6 +42,7 @@ public class MicrometerMessageHistoryFactory extends ServiceSupport implements C private MeterRegistry meterRegistry; private boolean prettyPrint = true; private TimeUnit durationUnit = TimeUnit.MILLISECONDS; + private MicrometerMessageHistoryNamingStrategy namingStrategy = MicrometerMessageHistoryNamingStrategy.DEFAULT; @Override public CamelContext getCamelContext() { @@ -85,6 +89,17 @@ public class MicrometerMessageHistoryFactory extends ServiceSupport implements C this.durationUnit = durationUnit; } + public MicrometerMessageHistoryNamingStrategy getNamingStrategy() { + return namingStrategy; + } + + /** + * Sets the naming strategy for message history meter names + */ + public void setNamingStrategy(MicrometerMessageHistoryNamingStrategy namingStrategy) { + this.namingStrategy = namingStrategy; + } + @Override @Deprecated public MessageHistory newMessageHistory(String routeId, NamedNode namedNode, Date date) { @@ -93,32 +108,30 @@ public class MicrometerMessageHistoryFactory extends ServiceSupport implements C @Override public MessageHistory newMessageHistory(String routeId, NamedNode namedNode, long timestamp) { - return new MicrometerMessageHistory(meterRegistry, camelContext.getRoute(routeId), namedNode, timestamp); + return new MicrometerMessageHistory(getMeterRegistry(), camelContext.getRoute(routeId), namedNode, getNamingStrategy(), timestamp); } @Override - protected void doStart() { - MicrometerMessageHistoryService messageHistoryService; + protected void doStart() throws Exception { + + if (meterRegistry == null) { + meterRegistry = MicrometerUtils.getOrCreateMeterRegistry(camelContext.getRegistry(), METRICS_REGISTRY_NAME); + } + try { - messageHistoryService = camelContext.hasService(MicrometerMessageHistoryService.class); + MicrometerMessageHistoryService messageHistoryService = camelContext.hasService(MicrometerMessageHistoryService.class); if (messageHistoryService == null) { messageHistoryService = new MicrometerMessageHistoryService(); messageHistoryService.setMeterRegistry(getMeterRegistry()); messageHistoryService.setPrettyPrint(isPrettyPrint()); messageHistoryService.setDurationUnit(getDurationUnit()); - messageHistoryService.setMatchingNames(name -> name.equals(DEFAULT_CAMEL_MESSAGE_HISTORY_METER_NAME)); + messageHistoryService.setMatchingTags(Tags.of(SERVICE_NAME, MicrometerMessageHistoryService.class.getSimpleName())); 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 diff --git a/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/routepolicy/MicrometerRoutePolicyService.java b/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/messagehistory/MicrometerMessageHistoryNamingStrategy.java similarity index 57% copy from components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/routepolicy/MicrometerRoutePolicyService.java copy to components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/messagehistory/MicrometerMessageHistoryNamingStrategy.java index 91dd4b0..bada69c 100644 --- a/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/routepolicy/MicrometerRoutePolicyService.java +++ b/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/messagehistory/MicrometerMessageHistoryNamingStrategy.java @@ -14,17 +14,19 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.camel.component.micrometer.routepolicy; +package org.apache.camel.component.micrometer.messagehistory; -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; +import org.apache.camel.NamedNode; +import org.apache.camel.Route; +import static org.apache.camel.component.micrometer.MicrometerConstants.DEFAULT_CAMEL_MESSAGE_HISTORY_METER_NAME; /** - * Service holding the {@link MeterRegistry} which registers all metrics. + * Provides a strategy to derive a meter name from the route and node */ -@ManagedResource(description = "MicrometerRegistry") -public final class MicrometerRoutePolicyService extends AbstractMicrometerService implements CamelContextAware, StaticService, MicrometerRoutePolicyMBean { +public interface MicrometerMessageHistoryNamingStrategy { + + MicrometerMessageHistoryNamingStrategy DEFAULT = (route, node) -> DEFAULT_CAMEL_MESSAGE_HISTORY_METER_NAME; + + String getName(Route route, NamedNode node); + } diff --git a/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/routepolicy/MicrometerRoutePolicy.java b/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/routepolicy/MicrometerRoutePolicy.java index 218a849..4d3b012 100644 --- a/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/routepolicy/MicrometerRoutePolicy.java +++ b/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/routepolicy/MicrometerRoutePolicy.java @@ -18,16 +18,21 @@ package org.apache.camel.component.micrometer.routepolicy; import java.util.concurrent.TimeUnit; import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.Tags; import io.micrometer.core.instrument.Timer; import org.apache.camel.Exchange; import org.apache.camel.NonManagedService; import org.apache.camel.Route; +import org.apache.camel.component.micrometer.MicrometerUtils; import org.apache.camel.support.RoutePolicySupport; import org.apache.camel.util.ObjectHelper; import org.apache.camel.util.ServiceHelper; import static org.apache.camel.component.micrometer.MicrometerConstants.CAMEL_CONTEXT_TAG; import static org.apache.camel.component.micrometer.MicrometerConstants.DEFAULT_CAMEL_ROUTE_POLICY_METER_NAME; +import static org.apache.camel.component.micrometer.MicrometerConstants.FAILED_TAG; +import static org.apache.camel.component.micrometer.MicrometerConstants.METRICS_REGISTRY_NAME; import static org.apache.camel.component.micrometer.MicrometerConstants.ROUTE_ID_TAG; +import static org.apache.camel.component.micrometer.MicrometerConstants.SERVICE_NAME; /** * A {@link org.apache.camel.spi.RoutePolicy} which gathers statistics and reports them using {@link MeterRegistry}. @@ -40,35 +45,42 @@ public class MicrometerRoutePolicy extends RoutePolicySupport implements NonMana private boolean prettyPrint; private TimeUnit durationUnit = TimeUnit.MILLISECONDS; private MetricsStatistics statistics; + private MicrometerRoutePolicyNamingStrategy namingStrategy = MicrometerRoutePolicyNamingStrategy.DEFAULT; private static final class MetricsStatistics { - private static final String MICROMETER_ROUTE_POLICY = "MicrometerRoutePolicy-"; private final MeterRegistry meterRegistry; private final Route route; + private final MicrometerRoutePolicyNamingStrategy namingStrategy; - private MetricsStatistics(MeterRegistry meterRegistry, Route route) { - this.meterRegistry = meterRegistry; + private MetricsStatistics(MeterRegistry meterRegistry, Route route, MicrometerRoutePolicyNamingStrategy namingStrategy) { + this.meterRegistry = ObjectHelper.notNull(meterRegistry, "MeterRegistry", this); + this.namingStrategy = ObjectHelper.notNull(namingStrategy, "MicrometerRoutePolicyNamingStrategy", this); this.route = route; } public void onExchangeBegin(Exchange exchange) { Timer.Sample sample = Timer.start(meterRegistry); - exchange.setProperty(MICROMETER_ROUTE_POLICY + route.getId(), sample); + exchange.setProperty(propertyName(exchange), sample); } public void onExchangeDone(Exchange exchange) { - Timer.Sample sample = (Timer.Sample) exchange.removeProperty(MICROMETER_ROUTE_POLICY + route.getId()); + Timer.Sample sample = (Timer.Sample) exchange.removeProperty(propertyName(exchange)); if (sample != null) { - Timer timer = Timer.builder(DEFAULT_CAMEL_ROUTE_POLICY_METER_NAME) + Timer timer = Timer.builder(namingStrategy.getName(route)) .description(route.getDescription()) .tag(CAMEL_CONTEXT_TAG, route.getRouteContext().getCamelContext().getName()) + .tag(SERVICE_NAME, MicrometerRoutePolicyService.class.getSimpleName()) .tag(ROUTE_ID_TAG, route.getId()) - .tag("failed", Boolean.toString(exchange.isFailed())) + .tag(FAILED_TAG, Boolean.toString(exchange.isFailed())) .register(meterRegistry); sample.stop(timer); } } + + private String propertyName(Exchange exchange) { + return String.format("%s-%s-%s", DEFAULT_CAMEL_ROUTE_POLICY_METER_NAME, route.getId(), exchange.getExchangeId()); + } } @@ -96,36 +108,42 @@ public class MicrometerRoutePolicy extends RoutePolicySupport implements NonMana this.durationUnit = durationUnit; } + public MicrometerRoutePolicyNamingStrategy getNamingStrategy() { + return namingStrategy; + } + + public void setNamingStrategy(MicrometerRoutePolicyNamingStrategy namingStrategy) { + this.namingStrategy = namingStrategy; + } + @Override public void onInit(Route route) { super.onInit(route); - MicrometerRoutePolicyService registryService; + if (getMeterRegistry() == null) { + setMeterRegistry(MicrometerUtils.getOrCreateMeterRegistry( + route.getRouteContext().getCamelContext().getRegistry(), METRICS_REGISTRY_NAME)); + } + try { - registryService = route.getRouteContext().getCamelContext().hasService(MicrometerRoutePolicyService.class); + MicrometerRoutePolicyService registryService = route.getRouteContext().getCamelContext().hasService(MicrometerRoutePolicyService.class); if (registryService == null) { registryService = new MicrometerRoutePolicyService(); registryService.setMeterRegistry(getMeterRegistry()); registryService.setPrettyPrint(isPrettyPrint()); registryService.setDurationUnit(getDurationUnit()); - registryService.setMatchingNames(name -> name.equals(DEFAULT_CAMEL_ROUTE_POLICY_METER_NAME)); + registryService.setMatchingTags(Tags.of(SERVICE_NAME, MicrometerRoutePolicyService.class.getSimpleName())); route.getRouteContext().getCamelContext().addService(registryService); + ServiceHelper.startService(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) + // for now 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); + statistics = new MetricsStatistics(getMeterRegistry(), route, getNamingStrategy()); } diff --git a/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/routepolicy/MicrometerRoutePolicyFactory.java b/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/routepolicy/MicrometerRoutePolicyFactory.java index 8ba8b1f..ae3cb4e 100644 --- a/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/routepolicy/MicrometerRoutePolicyFactory.java +++ b/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/routepolicy/MicrometerRoutePolicyFactory.java @@ -31,7 +31,7 @@ public class MicrometerRoutePolicyFactory implements RoutePolicyFactory { private MeterRegistry meterRegistry; private boolean prettyPrint = true; private TimeUnit durationUnit = TimeUnit.MILLISECONDS; - private String name; + private MicrometerRoutePolicyNamingStrategy namingStrategy = MicrometerRoutePolicyNamingStrategy.DEFAULT; /** * To use a specific {@link io.micrometer.core.instrument.MeterRegistry} instance. @@ -71,6 +71,13 @@ public class MicrometerRoutePolicyFactory implements RoutePolicyFactory { this.durationUnit = durationUnit; } + public MicrometerRoutePolicyNamingStrategy getNamingStrategy() { + return namingStrategy; + } + + public void setNamingStrategy(MicrometerRoutePolicyNamingStrategy namingStrategy) { + this.namingStrategy = namingStrategy; + } @Override public RoutePolicy createRoutePolicy(CamelContext camelContext, String routeId, RouteDefinition routeDefinition) { @@ -78,6 +85,7 @@ public class MicrometerRoutePolicyFactory implements RoutePolicyFactory { answer.setMeterRegistry(getMeterRegistry()); answer.setPrettyPrint(isPrettyPrint()); answer.setDurationUnit(getDurationUnit()); + answer.setNamingStrategy(getNamingStrategy()); return answer; } diff --git a/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/routepolicy/MicrometerRoutePolicyService.java b/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/routepolicy/MicrometerRoutePolicyNamingStrategy.java similarity index 61% copy from components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/routepolicy/MicrometerRoutePolicyService.java copy to components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/routepolicy/MicrometerRoutePolicyNamingStrategy.java index 91dd4b0..83e76c7 100644 --- a/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/routepolicy/MicrometerRoutePolicyService.java +++ b/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/routepolicy/MicrometerRoutePolicyNamingStrategy.java @@ -16,15 +16,16 @@ */ 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; +import org.apache.camel.Route; +import static org.apache.camel.component.micrometer.MicrometerConstants.DEFAULT_CAMEL_ROUTE_POLICY_METER_NAME; /** - * Service holding the {@link MeterRegistry} which registers all metrics. + * Provides a strategy to derive a meter name from the route */ -@ManagedResource(description = "MicrometerRegistry") -public final class MicrometerRoutePolicyService extends AbstractMicrometerService implements CamelContextAware, StaticService, MicrometerRoutePolicyMBean { +public interface MicrometerRoutePolicyNamingStrategy { + + MicrometerRoutePolicyNamingStrategy DEFAULT = route -> DEFAULT_CAMEL_ROUTE_POLICY_METER_NAME; + + String getName(Route route); + } diff --git a/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/routepolicy/MicrometerRoutePolicyService.java b/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/routepolicy/MicrometerRoutePolicyService.java index 91dd4b0..baed6df 100644 --- a/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/routepolicy/MicrometerRoutePolicyService.java +++ b/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/routepolicy/MicrometerRoutePolicyService.java @@ -25,6 +25,6 @@ import org.apache.camel.component.micrometer.json.AbstractMicrometerService; /** * Service holding the {@link MeterRegistry} which registers all metrics. */ -@ManagedResource(description = "MicrometerRegistry") +@ManagedResource(description = "MicrometerRoutePolicy") public final class MicrometerRoutePolicyService extends AbstractMicrometerService implements CamelContextAware, StaticService, MicrometerRoutePolicyMBean { } 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 index eb36feb..bbb2c17 100644 --- 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 @@ -139,7 +139,7 @@ public class MicrometerComponentTest { @Test public void testGetOrCreateMetricRegistryFoundInCamelRegistry() { when(camelRegistry.lookupByNameAndType("name", MeterRegistry.class)).thenReturn(metricRegistry); - MeterRegistry result = component.getOrCreateMeterRegistry(camelRegistry, "name"); + MeterRegistry result = MicrometerUtils.getOrCreateMeterRegistry(camelRegistry, "name"); assertThat(result, is(metricRegistry)); inOrder.verify(camelRegistry, times(1)).lookupByNameAndType("name", MeterRegistry.class); inOrder.verifyNoMoreInteractions(); @@ -149,7 +149,7 @@ public class MicrometerComponentTest { public void testGetOrCreateMetricRegistryFoundInCamelRegistryByType() { when(camelRegistry.lookupByNameAndType("name", MeterRegistry.class)).thenReturn(null); when(camelRegistry.findByType(MeterRegistry.class)).thenReturn(Collections.singleton(metricRegistry)); - MeterRegistry result = component.getOrCreateMeterRegistry(camelRegistry, "name"); + MeterRegistry result = MicrometerUtils.getOrCreateMeterRegistry(camelRegistry, "name"); assertThat(result, is(metricRegistry)); inOrder.verify(camelRegistry, times(1)).lookupByNameAndType("name", MeterRegistry.class); inOrder.verify(camelRegistry, times(1)).findByType(MeterRegistry.class); @@ -160,7 +160,7 @@ public class MicrometerComponentTest { public void testGetOrCreateMetricRegistryNotFoundInCamelRegistry() { when(camelRegistry.lookupByNameAndType("name", MeterRegistry.class)).thenReturn(null); when(camelRegistry.findByType(MeterRegistry.class)).thenReturn(Collections.emptySet()); - MeterRegistry result = component.getOrCreateMeterRegistry(camelRegistry, "name"); + MeterRegistry result = MicrometerUtils.getOrCreateMeterRegistry(camelRegistry, "name"); assertThat(result, is(notNullValue())); assertThat(result, is(not(metricRegistry))); inOrder.verify(camelRegistry, times(1)).lookupByNameAndType("name", MeterRegistry.class); @@ -171,7 +171,7 @@ public class MicrometerComponentTest { @Test public void testGetMetricRegistryFromCamelRegistry() { when(camelRegistry.lookupByNameAndType("name", MeterRegistry.class)).thenReturn(metricRegistry); - MeterRegistry result = component.getMeterRegistryFromCamelRegistry(camelRegistry, "name"); + MeterRegistry result = MicrometerUtils.getMeterRegistryFromCamelRegistry(camelRegistry, "name"); assertThat(result, is(metricRegistry)); inOrder.verify(camelRegistry, times(1)).lookupByNameAndType("name", MeterRegistry.class); inOrder.verifyNoMoreInteractions(); @@ -179,7 +179,7 @@ public class MicrometerComponentTest { @Test public void testCreateMetricRegistry() { - MeterRegistry registry = component.createMeterRegistry(); + MeterRegistry registry = MicrometerUtils.createMeterRegistry(); assertThat(registry, is(notNullValue())); } } 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/eventNotifier/AbstractMicrometerEventNotifierTest.java similarity index 67% copy from components/camel-micrometer/src/test/java/org/apache/camel/component/micrometer/routepolicy/AbstractMicrometerRoutePolicyTest.java copy to components/camel-micrometer/src/test/java/org/apache/camel/component/micrometer/eventNotifier/AbstractMicrometerEventNotifierTest.java index 06ea46f..37cd628 100644 --- a/components/camel-micrometer/src/test/java/org/apache/camel/component/micrometer/routepolicy/AbstractMicrometerRoutePolicyTest.java +++ b/components/camel-micrometer/src/test/java/org/apache/camel/component/micrometer/eventNotifier/AbstractMicrometerEventNotifierTest.java @@ -6,17 +6,18 @@ * (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 + * 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; +package org.apache.camel.component.micrometer.eventNotifier; -import io.micrometer.core.instrument.MockClock; +import io.micrometer.core.instrument.Clock; import io.micrometer.core.instrument.composite.CompositeMeterRegistry; import io.micrometer.core.instrument.simple.SimpleMeterRegistry; import io.micrometer.core.instrument.util.HierarchicalNameMapper; @@ -24,10 +25,11 @@ import io.micrometer.jmx.JmxMeterRegistry; import org.apache.camel.CamelContext; import org.apache.camel.component.micrometer.CamelJmxConfig; import org.apache.camel.component.micrometer.MicrometerConstants; +import org.apache.camel.component.micrometer.eventnotifier.AbstractMicrometerEventNotifier; import org.apache.camel.impl.JndiRegistry; import org.apache.camel.test.junit4.CamelTestSupport; -public class AbstractMicrometerRoutePolicyTest extends CamelTestSupport { +abstract class AbstractMicrometerEventNotifierTest extends CamelTestSupport { protected CompositeMeterRegistry meterRegistry; @@ -41,19 +43,21 @@ public class AbstractMicrometerRoutePolicyTest extends CamelTestSupport { JndiRegistry registry = super.createRegistry(); meterRegistry = new CompositeMeterRegistry(); meterRegistry.add(new SimpleMeterRegistry()); - meterRegistry.add(new JmxMeterRegistry(new CamelJmxConfig(), new MockClock(), HierarchicalNameMapper.DEFAULT)); + meterRegistry.add(new JmxMeterRegistry(CamelJmxConfig.DEFAULT, Clock.SYSTEM, HierarchicalNameMapper.DEFAULT)); registry.bind(MicrometerConstants.METRICS_REGISTRY_NAME, meterRegistry); return registry; } @Override protected CamelContext createCamelContext() throws Exception { - CamelContext context = super.createCamelContext(); - MicrometerRoutePolicyFactory factory = new MicrometerRoutePolicyFactory(); - factory.setMeterRegistry(meterRegistry); - context.addRoutePolicyFactory(factory); - - return context; + CamelContext camelContext = super.createCamelContext(); + AbstractMicrometerEventNotifier<?> eventNotifier = getEventNotifier(); + eventNotifier.setMeterRegistry(meterRegistry); + eventNotifier.setPrettyPrint(true); + camelContext.getManagementStrategy().addEventNotifier(eventNotifier); + return camelContext; } + protected abstract AbstractMicrometerEventNotifier<?> getEventNotifier(); + } diff --git a/components/camel-micrometer/src/test/java/org/apache/camel/component/micrometer/eventNotifier/MicrometerExchangeEventNotifierTest.java b/components/camel-micrometer/src/test/java/org/apache/camel/component/micrometer/eventNotifier/MicrometerExchangeEventNotifierTest.java new file mode 100644 index 0000000..e983882 --- /dev/null +++ b/components/camel-micrometer/src/test/java/org/apache/camel/component/micrometer/eventNotifier/MicrometerExchangeEventNotifierTest.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.eventNotifier; + +import java.util.concurrent.TimeUnit; +import io.micrometer.core.instrument.Timer; +import org.apache.camel.CamelExchangeException; +import org.apache.camel.CamelExecutionException; +import org.apache.camel.Endpoint; +import org.apache.camel.Exchange; +import org.apache.camel.builder.RouteBuilder; +import org.apache.camel.component.micrometer.eventnotifier.AbstractMicrometerEventNotifier; +import org.apache.camel.component.micrometer.eventnotifier.MicrometerExchangeEventNotifier; +import org.apache.camel.component.micrometer.eventnotifier.MicrometerExchangeEventNotifierNamingStrategy; +import org.apache.camel.component.mock.MockEndpoint; +import org.apache.camel.support.ExpressionAdapter; +import org.junit.Test; +import static org.apache.camel.component.micrometer.MicrometerConstants.DEFAULT_CAMEL_EXCHANGE_EVENT_METER_NAME; + + +public class MicrometerExchangeEventNotifierTest extends AbstractMicrometerEventNotifierTest { + + private static final String ROUTE_ID = "test"; + private static final String MOCK_OUT = "mock://out"; + private static final String DIRECT_IN = "direct://in"; + private static final Long SLEEP = 20L; + + @Override + protected AbstractMicrometerEventNotifier<?> getEventNotifier() { + MicrometerExchangeEventNotifier eventNotifier = new MicrometerExchangeEventNotifier(); + eventNotifier.setNamingStrategy((exchange, endpoint) -> endpoint.getEndpointUri()); + return eventNotifier; + } + + @Test + public void testCamelRouteEvents() throws Exception { + int count = 10; + MockEndpoint mock = getMockEndpoint(MOCK_OUT); + mock.returnReplyBody(new ExpressionAdapter() { + @Override + public Object evaluate(Exchange exchange) { + try { + Thread.sleep(SLEEP); + return exchange.getIn().getBody(); + } catch (InterruptedException e) { + throw new CamelExecutionException(e.getMessage(), exchange, e); + } + + } + }); + mock.expectedMessageCount(count); + + for (int i = 0; i < count; i++) { + template.sendBody(DIRECT_IN, new Object()); + } + Timer timer = meterRegistry.find(MOCK_OUT).timer(); + assertEquals(count, timer.count()); + assertTrue(timer.mean(TimeUnit.MILLISECONDS) > SLEEP.doubleValue()); + } + + @Override + protected RouteBuilder createRouteBuilder() { + return new RouteBuilder() { + @Override + public void configure() { + from(DIRECT_IN).routeId(ROUTE_ID).to(MOCK_OUT); + } + }; + } + +} diff --git a/components/camel-micrometer/src/test/java/org/apache/camel/component/micrometer/eventNotifier/MicrometerRouteEventNotifierTest.java b/components/camel-micrometer/src/test/java/org/apache/camel/component/micrometer/eventNotifier/MicrometerRouteEventNotifierTest.java new file mode 100644 index 0000000..f25fb9c --- /dev/null +++ b/components/camel-micrometer/src/test/java/org/apache/camel/component/micrometer/eventNotifier/MicrometerRouteEventNotifierTest.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.eventNotifier; + +import io.micrometer.core.instrument.Gauge; +import org.apache.camel.builder.RouteBuilder; +import org.apache.camel.component.micrometer.eventnotifier.AbstractMicrometerEventNotifier; +import org.apache.camel.component.micrometer.eventnotifier.MicrometerRouteEventNotifier; +import org.apache.camel.spi.EventNotifier; +import org.junit.Test; +import static org.apache.camel.component.micrometer.MicrometerConstants.DEFAULT_CAMEL_ROUTES_ADDED; +import static org.apache.camel.component.micrometer.MicrometerConstants.DEFAULT_CAMEL_ROUTES_RUNNING; + +/** + * @author Christian Ohr + */ +public class MicrometerRouteEventNotifierTest extends AbstractMicrometerEventNotifierTest { + + private static final String ROUTE_ID = "test"; + + @Override + protected AbstractMicrometerEventNotifier<?> getEventNotifier() { + return new MicrometerRouteEventNotifier(); + } + + @Test + public void testCamelRouteEvents() throws Exception { + Gauge added = meterRegistry.find(DEFAULT_CAMEL_ROUTES_ADDED).gauge(); + Gauge running = meterRegistry.find(DEFAULT_CAMEL_ROUTES_RUNNING).gauge(); + assertEquals(0.0d, added.value(), 0.0001d); + assertEquals(0.0d, running.value(), 0.0001d); + context.addRoutes(new TestRoute()); + assertEquals(1.0d, added.value(), 0.0001d); + assertEquals(1.0d, running.value(), 0.0001d); + context.stopRoute(ROUTE_ID); + assertEquals(1.0d, added.value(), 0.0001d); + assertEquals(0.0d, running.value(), 0.0001d); + context.removeRoute(ROUTE_ID); + assertEquals(0.0d, added.value(), 0.0001d); + assertEquals(0.0d, running.value(), 0.0001d); + } + + + private class TestRoute extends RouteBuilder { + @Override + public void configure() throws Exception { + from("direct:in").routeId(ROUTE_ID).to("mock:out"); + } + } +} diff --git a/components/camel-micrometer/src/test/java/org/apache/camel/component/micrometer/messagehistory/ManagedMessageHistoryTest.java b/components/camel-micrometer/src/test/java/org/apache/camel/component/micrometer/messagehistory/ManagedMessageHistoryTest.java index 47ac488..2e1f59c 100644 --- a/components/camel-micrometer/src/test/java/org/apache/camel/component/micrometer/messagehistory/ManagedMessageHistoryTest.java +++ b/components/camel-micrometer/src/test/java/org/apache/camel/component/micrometer/messagehistory/ManagedMessageHistoryTest.java @@ -20,7 +20,6 @@ import java.util.Set; import javax.management.MBeanServer; import javax.management.ObjectName; import io.micrometer.core.instrument.Clock; -import io.micrometer.core.instrument.MockClock; import io.micrometer.core.instrument.composite.CompositeMeterRegistry; import io.micrometer.core.instrument.simple.SimpleMeterRegistry; import io.micrometer.core.instrument.util.HierarchicalNameMapper; @@ -52,7 +51,7 @@ public class ManagedMessageHistoryTest extends CamelTestSupport { JndiRegistry registry = super.createRegistry(); meterRegistry = new CompositeMeterRegistry(); meterRegistry.add(new SimpleMeterRegistry()); - meterRegistry.add(new JmxMeterRegistry(new CamelJmxConfig(), Clock.SYSTEM, HierarchicalNameMapper.DEFAULT)); + meterRegistry.add(new JmxMeterRegistry(CamelJmxConfig.DEFAULT, Clock.SYSTEM, HierarchicalNameMapper.DEFAULT)); registry.bind(MicrometerConstants.METRICS_REGISTRY_NAME, meterRegistry); return registry; } @@ -91,12 +90,15 @@ public class ManagedMessageHistoryTest extends CamelTestSupport { assertEquals(3, meterRegistry.getMeters().size()); // there should be 3 mbeans - Set<ObjectName> set = getMBeanServer().queryNames(new ObjectName("org.apache.camel.micrometer:*"), null); + Set<ObjectName> set = getMBeanServer().queryNames(new ObjectName("org.apache.camel.micrometer:name=CamelMessageHistory.*"), null); assertEquals(3, set.size()); - String camelContextName = context().getName(); - Long testCount = (Long)getMBeanServer().getAttribute( - new ObjectName("org.apache.camel.micrometer:name=CamelMessageHistory.camelContext." + camelContextName + ".nodeId.foo.routeId.route1"), "Count"); + ObjectName fooMBean = set.stream() + .filter(on -> on.getCanonicalName().contains("foo")) + .findFirst() + .orElseThrow(() -> new AssertionError("Expected MBean with node Id foo")); + + Long testCount = (Long)getMBeanServer().getAttribute(fooMBean, "Count"); assertEquals(count / 2, testCount.longValue()); // get the message history service using JMX diff --git a/components/camel-micrometer/src/test/java/org/apache/camel/component/micrometer/routepolicy/AbstractMicrometerRoutePolicyTest.java b/components/camel-micrometer/src/test/java/org/apache/camel/component/micrometer/routepolicy/AbstractMicrometerRoutePolicyTest.java index 06ea46f..4da939f 100644 --- a/components/camel-micrometer/src/test/java/org/apache/camel/component/micrometer/routepolicy/AbstractMicrometerRoutePolicyTest.java +++ b/components/camel-micrometer/src/test/java/org/apache/camel/component/micrometer/routepolicy/AbstractMicrometerRoutePolicyTest.java @@ -16,7 +16,7 @@ */ package org.apache.camel.component.micrometer.routepolicy; -import io.micrometer.core.instrument.MockClock; +import io.micrometer.core.instrument.Clock; import io.micrometer.core.instrument.composite.CompositeMeterRegistry; import io.micrometer.core.instrument.simple.SimpleMeterRegistry; import io.micrometer.core.instrument.util.HierarchicalNameMapper; @@ -41,7 +41,7 @@ public class AbstractMicrometerRoutePolicyTest extends CamelTestSupport { JndiRegistry registry = super.createRegistry(); meterRegistry = new CompositeMeterRegistry(); meterRegistry.add(new SimpleMeterRegistry()); - meterRegistry.add(new JmxMeterRegistry(new CamelJmxConfig(), new MockClock(), HierarchicalNameMapper.DEFAULT)); + meterRegistry.add(new JmxMeterRegistry(CamelJmxConfig.DEFAULT, Clock.SYSTEM, HierarchicalNameMapper.DEFAULT)); registry.bind(MicrometerConstants.METRICS_REGISTRY_NAME, meterRegistry); return registry; } 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 index ba875c4..2b75ced 100644 --- 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 @@ -19,6 +19,7 @@ package org.apache.camel.component.micrometer.routepolicy; import java.util.concurrent.TimeUnit; import io.micrometer.core.instrument.Timer; import org.apache.camel.builder.RouteBuilder; +import org.apache.camel.component.mock.MockEndpoint; import org.junit.Test; import static org.apache.camel.component.micrometer.MicrometerConstants.DEFAULT_CAMEL_ROUTE_POLICY_METER_NAME; import static org.apache.camel.component.micrometer.MicrometerConstants.ROUTE_ID_TAG; @@ -31,13 +32,14 @@ public class MicrometerRoutePolicyTest extends AbstractMicrometerRoutePolicyTest @Test public void testMetricsRoutePolicy() throws Exception { int count = 10; - getMockEndpoint("mock:result").expectedMessageCount(count); + MockEndpoint mockEndpoint = getMockEndpoint("mock:result"); + mockEndpoint.expectedMessageCount(count); for (int i = 0; i < count; i++) { if (i % 2 == 0) { - template.sendBody("seda:foo", "Hello " + i); + template.sendBody("direct:foo", "Hello " + i); } else { - template.sendBody("seda:bar", "Hello " + i); + template.sendBody("direct:bar", "Hello " + i); } } @@ -62,11 +64,11 @@ public class MicrometerRoutePolicyTest extends AbstractMicrometerRoutePolicyTest return new RouteBuilder() { @Override public void configure() { - from("seda:foo").routeId("foo") + from("direct:foo").routeId("foo") .delay(DELAY_FOO) .to("mock:result"); - from("seda:bar").routeId("bar") + from("direct:bar").routeId("bar") .delay(DELAY_BAR) .to("mock:result"); } diff --git a/examples/camel-example-micrometer/README.md b/examples/camel-example-micrometer/README.md new file mode 100644 index 0000000..1523225 --- /dev/null +++ b/examples/camel-example-micrometer/README.md @@ -0,0 +1,66 @@ +# Spring Java Config Example + +### Introduction +This example shows how to work with Micrometer metrics, using Spring Java Config +to boot up Camel, configure the routes and meters. + +The example triggers an exchange every 10s that runs through a route using a variable delay. +The exchange is measured in various ways: + +* using Micrometer producers (timer and distribution summary) +* using a Route Policy Factory +* using Event Notifiers + + +### Build +You will need to compile this example first: + + mvn compile + + +### Setup of Monitoring backend + +This example uses [Prometheus](https://prometheus.io) as monitoring backend. + +* Download the package for your platform and unpack it on your local host +* Edit the `prometheus.yml` file and append another `scrape_config`: + +``` +... + - job_name: 'camel' + static_configs: + - targets: ['localhost:8088'] +``` + +* Start up Prometheus + +Optionally, you can install a metrics visualizer like [Grafana](https://grafana.com/). + + +### Run +To run the example type + + mvn exec:java + + +* You can access http://localhost:8088/metrics in order to manually obtain the Micrometer output for Prometheus. +* In Prometheus, you can [query](https://prometheus.io/docs/prometheus/latest/querying/examples/) for one of +the metrics related to Camel or the JVM. + +To stop the example hit <kbd>Ctrl</kbd>+<kbd>c</kbd> + +### Configuration +You can see the routing rules by looking at the java code in the +`src/main/java directory` + +### Forum, Help, etc + +If you hit an problems please let us know on the Camel Forums + <http://camel.apache.org/discussion-forums.html> + +Please help us make Apache Camel better - we appreciate any feedback you may +have. Enjoy! + + + +The Camel riders! diff --git a/components/camel-micrometer/pom.xml b/examples/camel-example-micrometer/pom.xml similarity index 50% copy from components/camel-micrometer/pom.xml copy to examples/camel-example-micrometer/pom.xml index 273f497..4f18ec4 100644 --- a/components/camel-micrometer/pom.xml +++ b/examples/camel-example-micrometer/pom.xml @@ -17,103 +17,91 @@ 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"> +<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> + <groupId>org.apache.camel.example</groupId> + <artifactId>examples</artifactId> <version>2.22.0-SNAPSHOT</version> </parent> - <artifactId>camel-micrometer</artifactId> + <artifactId>camel-example-micrometer</artifactId> <packaging>jar</packaging> - <name>Camel :: Micrometer</name> - <description>Camel Micrometer based monitoring component</description> + <name>Camel :: Example :: Micrometer</name> + <description>An example showing how to work with Camel, Spring Java Config and Micrometer monitoring</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> + <category>Advanced</category> + <title>Micrometer</title> </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> - <dependency> - <groupId>io.micrometer</groupId> - <artifactId>micrometer-registry-jmx</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> + <artifactId>camel-micrometer</artifactId> </dependency> - <dependency> - <groupId>org.hamcrest</groupId> - <artifactId>java-hamcrest</artifactId> - <version>${hamcrest-version}</version> - <scope>test</scope> - </dependency> - - <!-- logging --> + <dependency> + <groupId>org.apache.camel</groupId> + <artifactId>camel-netty4-http</artifactId> + </dependency> + <dependency> + <groupId>io.micrometer</groupId> + <artifactId>micrometer-registry-prometheus</artifactId> + <version>${micrometer-version}</version> + </dependency> + <!-- logging --> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-api</artifactId> - <scope>test</scope> + <scope>runtime</scope> </dependency> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-core</artifactId> - <scope>test</scope> + <scope>runtime</scope> </dependency> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-slf4j-impl</artifactId> + <scope>runtime</scope> + </dependency> + + <!-- for testing --> + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> <scope>test</scope> </dependency> </dependencies> + <build> + <plugins> + + <!-- START SNIPPET: example --> + + <!-- Allows the routes to be run via 'mvn exec:java' --> + <plugin> + <groupId>org.codehaus.mojo</groupId> + <artifactId>exec-maven-plugin</artifactId> + <configuration> + <mainClass>org.apache.camel.example.micrometer.CamelPrometheusExample</mainClass> + </configuration> + </plugin> + <!-- END SNIPPET: example --> + </plugins> + </build> + </project> diff --git a/examples/camel-example-micrometer/src/data/message1.xml b/examples/camel-example-micrometer/src/data/message1.xml new file mode 100644 index 0000000..1a85d06 --- /dev/null +++ b/examples/camel-example-micrometer/src/data/message1.xml @@ -0,0 +1,24 @@ +<?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. + +--> +<person user="james"> + <firstName>James</firstName> + <lastName>Strachan</lastName> + <city>London</city> +</person> \ No newline at end of file diff --git a/examples/camel-example-micrometer/src/data/message2.xml b/examples/camel-example-micrometer/src/data/message2.xml new file mode 100644 index 0000000..73c88bf --- /dev/null +++ b/examples/camel-example-micrometer/src/data/message2.xml @@ -0,0 +1,24 @@ +<?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. + +--> +<person user="hiram"> + <firstName>Hiram</firstName> + <lastName>Chirino</lastName> + <city>Tampa</city> +</person> \ No newline at end of file diff --git a/examples/camel-example-micrometer/src/main/java/org/apache/camel/example/micrometer/CamelPrometheusExample.java b/examples/camel-example-micrometer/src/main/java/org/apache/camel/example/micrometer/CamelPrometheusExample.java new file mode 100644 index 0000000..89eee21 --- /dev/null +++ b/examples/camel-example-micrometer/src/main/java/org/apache/camel/example/micrometer/CamelPrometheusExample.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 + * + * 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.example.micrometer; + +import io.micrometer.core.instrument.Tags; +import io.micrometer.core.instrument.binder.jvm.ClassLoaderMetrics; +import io.micrometer.core.instrument.binder.jvm.JvmGcMetrics; +import io.micrometer.core.instrument.binder.jvm.JvmMemoryMetrics; +import io.micrometer.core.instrument.binder.jvm.JvmThreadMetrics; +import io.micrometer.core.instrument.binder.system.ProcessorMetrics; +import io.micrometer.prometheus.PrometheusConfig; +import io.micrometer.prometheus.PrometheusMeterRegistry; +import org.apache.camel.CamelContext; +import org.apache.camel.component.micrometer.DistributionStatisticConfigFilter; +import org.apache.camel.component.micrometer.MicrometerConstants; +import org.apache.camel.component.micrometer.eventnotifier.MicrometerExchangeEventNotifier; +import org.apache.camel.component.micrometer.eventnotifier.MicrometerRouteEventNotifier; +import org.apache.camel.component.micrometer.routepolicy.MicrometerRoutePolicyFactory; +import org.apache.camel.spring.javaconfig.CamelConfiguration; +import org.apache.camel.spring.javaconfig.Main; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; + +//START SNIPPET: RouteConfig + +@Configuration +@ComponentScan +public class CamelPrometheusExample extends CamelConfiguration { + + /** + * Allow this route to be run as an application + */ + public static void main(String[] args) throws Exception { + Main main = new Main(); + main.setConfigClass(CamelPrometheusExample.class); + main.run(); + } + + @Override + protected void setupCamelContext(CamelContext camelContext) { + camelContext.addRoutePolicyFactory(new MicrometerRoutePolicyFactory()); + camelContext.getManagementStrategy().addEventNotifier(new MicrometerRouteEventNotifier()); + camelContext.getManagementStrategy().addEventNotifier(new MicrometerExchangeEventNotifier()); + } + + @Bean(name = MicrometerConstants.METRICS_REGISTRY_NAME) + public PrometheusMeterRegistry meterRegistry() { + + // Register the meter registry and some standard meters + PrometheusMeterRegistry meterRegistry = new PrometheusMeterRegistry(PrometheusConfig.DEFAULT); + + // Configure meter registry to expose + meterRegistry.config() + .commonTags(Tags.of("application", "CamelPrometheusExample")) + .meterFilter(new DistributionStatisticConfigFilter()); + + new ClassLoaderMetrics().bindTo(meterRegistry); + new JvmMemoryMetrics().bindTo(meterRegistry); + new JvmGcMetrics().bindTo(meterRegistry); + new ProcessorMetrics().bindTo(meterRegistry); + new JvmThreadMetrics().bindTo(meterRegistry); + + return meterRegistry; + } + +} +//END SNIPPET: RouteConfig + diff --git a/examples/camel-example-micrometer/src/main/java/org/apache/camel/example/micrometer/ScheduledRouteBuilder.java b/examples/camel-example-micrometer/src/main/java/org/apache/camel/example/micrometer/ScheduledRouteBuilder.java new file mode 100644 index 0000000..2bfa68a --- /dev/null +++ b/examples/camel-example-micrometer/src/main/java/org/apache/camel/example/micrometer/ScheduledRouteBuilder.java @@ -0,0 +1,44 @@ +/** + * 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.example.micrometer; + +import java.util.Random; +import org.apache.camel.builder.RouteBuilder; +import org.springframework.stereotype.Component; + +@Component +public class ScheduledRouteBuilder extends RouteBuilder { + + private Random random = new Random(System.currentTimeMillis()); + + @Override + public void configure() throws Exception { + from("timer:foo?period=10s&fixedRate=true") + .routeId("timer:foo") + .setHeader("random").exchange(exchange -> random.nextInt(100)) + .to("direct:bar"); + + from("direct:bar") + .routeId("direct:bar") + .to("micrometer:summary:histo?value=${header.random}") + .to("micrometer:timer:timer?action=start") + .delay().exchange(exchange -> random.nextInt(100)) + .to("micrometer:timer:timer?action=stop"); + } +} diff --git a/examples/camel-example-micrometer/src/main/java/org/apache/camel/example/micrometer/ScrapeRouteBuilder.java b/examples/camel-example-micrometer/src/main/java/org/apache/camel/example/micrometer/ScrapeRouteBuilder.java new file mode 100644 index 0000000..a932a47 --- /dev/null +++ b/examples/camel-example-micrometer/src/main/java/org/apache/camel/example/micrometer/ScrapeRouteBuilder.java @@ -0,0 +1,43 @@ +/** + * 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.example.micrometer; + +import io.micrometer.prometheus.PrometheusMeterRegistry; +import org.apache.camel.builder.RouteBuilder; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +@Component +public class ScrapeRouteBuilder extends RouteBuilder { + + private final PrometheusMeterRegistry prometheusMeterRegistry; + + @Autowired + public ScrapeRouteBuilder(PrometheusMeterRegistry prometheusMeterRegistry) { + this.prometheusMeterRegistry = prometheusMeterRegistry; + } + + @Override + public void configure() throws Exception { + from("netty4-http:http://0.0.0.0:8088/metrics") + .routeId("netty4-http-scrape") + .transform().method(prometheusMeterRegistry, "scrape"); + + } +} diff --git a/examples/camel-example-micrometer/src/main/resources/META-INF/LICENSE.txt b/examples/camel-example-micrometer/src/main/resources/META-INF/LICENSE.txt new file mode 100644 index 0000000..6b0b127 --- /dev/null +++ b/examples/camel-example-micrometer/src/main/resources/META-INF/LICENSE.txt @@ -0,0 +1,203 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed 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. + diff --git a/examples/camel-example-micrometer/src/main/resources/META-INF/NOTICE.txt b/examples/camel-example-micrometer/src/main/resources/META-INF/NOTICE.txt new file mode 100644 index 0000000..2e215bf --- /dev/null +++ b/examples/camel-example-micrometer/src/main/resources/META-INF/NOTICE.txt @@ -0,0 +1,11 @@ + ========================================================================= + == NOTICE file corresponding to the section 4 d of == + == the Apache License, Version 2.0, == + == in this case for the Apache Camel distribution. == + ========================================================================= + + This product includes software developed by + The Apache Software Foundation (http://www.apache.org/). + + Please read the different LICENSE files present in the licenses directory of + this distribution. diff --git a/examples/camel-example-micrometer/src/main/resources/log4j2.properties b/examples/camel-example-micrometer/src/main/resources/log4j2.properties new file mode 100644 index 0000000..f02e7ae --- /dev/null +++ b/examples/camel-example-micrometer/src/main/resources/log4j2.properties @@ -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. +## --------------------------------------------------------------------------- + +appender.stdout.type = Console +appender.stdout.name = stdout +appender.stdout.layout.type = PatternLayout +appender.stdout.layout.pattern = %d [%-15.15t] %-5p %-30.30c{1} - %m%n +rootLogger.level = INFO +rootLogger.appenderRef.stdout.ref = stdout diff --git a/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/routepolicy/MicrometerRoutePolicyService.java b/examples/camel-example-micrometer/src/test/java/org/apache/camel/example/micrometer/IntegrationTest.java similarity index 53% copy from components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/routepolicy/MicrometerRoutePolicyService.java copy to examples/camel-example-micrometer/src/test/java/org/apache/camel/example/micrometer/IntegrationTest.java index 91dd4b0..d4e88e9 100644 --- a/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/routepolicy/MicrometerRoutePolicyService.java +++ b/examples/camel-example-micrometer/src/test/java/org/apache/camel/example/micrometer/IntegrationTest.java @@ -1,4 +1,4 @@ -/** +/* * 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. @@ -6,25 +6,30 @@ * (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 + * 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; +package org.apache.camel.example.micrometer; -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; +import org.apache.camel.spring.javaconfig.Main; +import org.junit.Assert; +import org.junit.Test; /** - * Service holding the {@link MeterRegistry} which registers all metrics. + * @version */ -@ManagedResource(description = "MicrometerRegistry") -public final class MicrometerRoutePolicyService extends AbstractMicrometerService implements CamelContextAware, StaticService, MicrometerRoutePolicyMBean { +public class IntegrationTest extends Assert { + + @Test + public void testCamelRulesDeployCorrectlyInSpring() throws Exception { + // let's boot up the Spring application context for 2 seconds to check that it works OK + Main.main("-duration", "2s", "-cc", "CamelPrometheusExample", "-bp", "org.apache.camel.example.micrometer"); + } + } diff --git a/examples/pom.xml b/examples/pom.xml index 1769a97..cc9b066 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -76,6 +76,7 @@ <module>camel-example-loan-broker-cxf</module> <module>camel-example-loan-broker-jms</module> <module>camel-example-management</module> + <module>camel-example-micrometer</module> <module>camel-example-mybatis</module> <module>camel-example-netty-custom-correlation</module> <module>camel-example-netty-http</module> -- To stop receiving notification emails like this one, please contact acosent...@apache.org.