This is an automated email from the ASF dual-hosted git repository. davsclaus pushed a commit to branch mm in repository https://gitbox.apache.org/repos/asf/camel.git
commit 222300e9087af3b833d539774c67c443bbef288d Author: Claus Ibsen <claus.ib...@gmail.com> AuthorDate: Thu Nov 16 15:05:13 2023 +0100 CAMEL-20105: camel-micromemter - Make it easier to configure for camel-main --- components/camel-micrometer-prometheus/pom.xml | 44 +++++++++ .../micrometer/prometheus/JandexHelper.java | 44 +++++++++ .../prometheus/MicrometerPrometheusConfigurer.java | 6 ++ .../prometheus/MicrometerPrometheus.java | 102 ++++++++++++++++++++- .../MetricsConfigurationPropertiesConfigurer.java | 6 ++ .../camel-main-configuration-metadata.json | 1 + core/camel-main/src/main/docs/main.adoc | 3 +- .../camel/main/MetricsConfigurationProperties.java | 22 +++++ 8 files changed, 222 insertions(+), 6 deletions(-) diff --git a/components/camel-micrometer-prometheus/pom.xml b/components/camel-micrometer-prometheus/pom.xml index 72cb76f0fa3..d6e883369ec 100644 --- a/components/camel-micrometer-prometheus/pom.xml +++ b/components/camel-micrometer-prometheus/pom.xml @@ -46,10 +46,20 @@ <artifactId>camel-platform-http-main</artifactId> </dependency> + <dependency> + <groupId>io.micrometer</groupId> + <artifactId>micrometer-core</artifactId> + </dependency> <dependency> <groupId>io.micrometer</groupId> <artifactId>micrometer-registry-prometheus</artifactId> </dependency> + <!-- to load micrometer binders --> + <dependency> + <groupId>io.smallrye</groupId> + <artifactId>jandex</artifactId> + <version>${jandex-version}</version> + </dependency> <!-- testing --> <dependency> @@ -100,4 +110,38 @@ </dependencies> + <build> + <plugins> + + <!-- generate jandex index for all the micrometer binders that can be enabled --> + <plugin> + <groupId>io.smallrye</groupId> + <artifactId>jandex-maven-plugin</artifactId> + <version>${jandex-version}</version> + <executions> + <execution> + <id>make-index</id> + <goals> + <goal>jandex</goal> + </goals> + <configuration> + <indexName>micrometer-binder-index.dat</indexName> + <fileSets> + <fileSet> + <dependency> + <groupId>io.micrometer</groupId> + <artifactId>micrometer-core</artifactId> + </dependency> + <includes> + <include>io/micrometer/core/instrument/binder/**/*.class</include> + </includes> + </fileSet> + </fileSets> + </configuration> + </execution> + </executions> + </plugin> + </plugins> + </build> + </project> diff --git a/components/camel-micrometer-prometheus/src/generated/java/org/apache/camel/component/micrometer/prometheus/JandexHelper.java b/components/camel-micrometer-prometheus/src/generated/java/org/apache/camel/component/micrometer/prometheus/JandexHelper.java new file mode 100644 index 00000000000..93d05bbe4a4 --- /dev/null +++ b/components/camel-micrometer-prometheus/src/generated/java/org/apache/camel/component/micrometer/prometheus/JandexHelper.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.component.micrometer.prometheus; + +import org.apache.camel.CamelContext; +import org.apache.camel.util.IOHelper; +import org.jboss.jandex.Index; +import org.jboss.jandex.IndexReader; + +import java.io.IOException; +import java.io.InputStream; + +public final class JandexHelper { + + private JandexHelper() { + } + + public static Index readJandexIndex(CamelContext camelContext) throws IOException { + InputStream is = camelContext.getClassResolver().loadResourceAsStream("META-INF/micrometer-binder-index.dat"); + try { + if (is != null) { + IndexReader reader = new IndexReader(is); + return reader.read(); + } + } finally { + IOHelper.close(is); + } + return null; + } +} diff --git a/components/camel-micrometer-prometheus/src/generated/java/org/apache/camel/component/micrometer/prometheus/MicrometerPrometheusConfigurer.java b/components/camel-micrometer-prometheus/src/generated/java/org/apache/camel/component/micrometer/prometheus/MicrometerPrometheusConfigurer.java index abad014040c..b18ea39de08 100644 --- a/components/camel-micrometer-prometheus/src/generated/java/org/apache/camel/component/micrometer/prometheus/MicrometerPrometheusConfigurer.java +++ b/components/camel-micrometer-prometheus/src/generated/java/org/apache/camel/component/micrometer/prometheus/MicrometerPrometheusConfigurer.java @@ -21,6 +21,8 @@ public class MicrometerPrometheusConfigurer extends org.apache.camel.support.com public boolean configure(CamelContext camelContext, Object obj, String name, Object value, boolean ignoreCase) { org.apache.camel.component.micrometer.prometheus.MicrometerPrometheus target = (org.apache.camel.component.micrometer.prometheus.MicrometerPrometheus) obj; switch (ignoreCase ? name.toLowerCase() : name) { + case "binders": + case "Binders": target.setBinders(property(camelContext, java.lang.String.class, value)); return true; case "camelcontext": case "CamelContext": target.setCamelContext(property(camelContext, org.apache.camel.CamelContext.class, value)); return true; case "enableexchangeeventnotifier": @@ -40,6 +42,8 @@ public class MicrometerPrometheusConfigurer extends org.apache.camel.support.com @Override public Class<?> getOptionType(String name, boolean ignoreCase) { switch (ignoreCase ? name.toLowerCase() : name) { + case "binders": + case "Binders": return java.lang.String.class; case "camelcontext": case "CamelContext": return org.apache.camel.CamelContext.class; case "enableexchangeeventnotifier": @@ -60,6 +64,8 @@ public class MicrometerPrometheusConfigurer extends org.apache.camel.support.com public Object getOptionValue(Object obj, String name, boolean ignoreCase) { org.apache.camel.component.micrometer.prometheus.MicrometerPrometheus target = (org.apache.camel.component.micrometer.prometheus.MicrometerPrometheus) obj; switch (ignoreCase ? name.toLowerCase() : name) { + case "binders": + case "Binders": return target.getBinders(); case "camelcontext": case "CamelContext": return target.getCamelContext(); case "enableexchangeeventnotifier": diff --git a/components/camel-micrometer-prometheus/src/main/java/org/apache/camel/component/micrometer/prometheus/MicrometerPrometheus.java b/components/camel-micrometer-prometheus/src/main/java/org/apache/camel/component/micrometer/prometheus/MicrometerPrometheus.java index 1f20b3e3841..cb04572ad60 100644 --- a/components/camel-micrometer-prometheus/src/main/java/org/apache/camel/component/micrometer/prometheus/MicrometerPrometheus.java +++ b/components/camel-micrometer-prometheus/src/main/java/org/apache/camel/component/micrometer/prometheus/MicrometerPrometheus.java @@ -16,6 +16,15 @@ */ package org.apache.camel.component.micrometer.prometheus; +import java.io.Closeable; +import java.io.IOException; +import java.util.HashSet; +import java.util.List; +import java.util.Optional; +import java.util.Set; +import java.util.StringJoiner; + +import io.micrometer.core.instrument.binder.MeterBinder; import io.micrometer.prometheus.PrometheusConfig; import io.micrometer.prometheus.PrometheusMeterRegistry; import io.prometheus.client.exporter.common.TextFormat; @@ -40,6 +49,11 @@ import org.apache.camel.spi.ManagementStrategy; import org.apache.camel.spi.Metadata; import org.apache.camel.spi.annotations.JdkService; import org.apache.camel.support.service.ServiceSupport; +import org.apache.camel.util.IOHelper; +import org.apache.camel.util.StringHelper; +import org.jboss.jandex.ClassInfo; +import org.jboss.jandex.DotName; +import org.jboss.jandex.Index; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -50,17 +64,18 @@ public class MicrometerPrometheus extends ServiceSupport implements CamelMetrics private static final Logger LOG = LoggerFactory.getLogger(MicrometerPrometheus.class); + private static final String JANDEX_INDEX = "META-INF/micrometer-binder-index.dat"; + private MainHttpServer server; private VertxPlatformHttpRouter router; private PlatformHttpComponent platformHttpComponent; - // TODO: option include JVM metrics - // TODO: option include platform-http metrics // TODO: include easily with jbang // TODO: docs private CamelContext camelContext; private final PrometheusMeterRegistry meterRegistry = new PrometheusMeterRegistry(PrometheusConfig.DEFAULT); + private final Set<MeterBinder> createdBinders = new HashSet<>(); @Metadata(defaultValue = "true") private boolean enableRoutePolicy = true; @@ -72,6 +87,8 @@ public class MicrometerPrometheus extends ServiceSupport implements CamelMetrics private boolean enableRouteEventNotifier = true; @Metadata(defaultValue = "0.0.4", enums = "0.0.4,1.0.0") private String textFormatVersion = "0.0.4"; + @Metadata + private String binders; @Override public CamelContext getCamelContext() { @@ -139,17 +156,34 @@ public class MicrometerPrometheus extends ServiceSupport implements CamelMetrics /** * The text-format version to use with Prometheus scraping. * - * 0.0.4 = text/plain; version=0.0.4; charset=utf-8 - * 1.0.0 = application/openmetrics-text; version=1.0.0; charset=utf-8 + * 0.0.4 = text/plain; version=0.0.4; charset=utf-8 1.0.0 = application/openmetrics-text; version=1.0.0; + * charset=utf-8 */ public void setTextFormatVersion(String textFormatVersion) { this.textFormatVersion = textFormatVersion; } + public String getBinders() { + return binders; + } + + /** + * Additional Micrometer binders to include such as jvm-memory, processor, jvm-thread, and so forth. Multiple + * binders can be separated by comma. + */ + public void setBinders(String binders) { + this.binders = binders; + } + @Override protected void doStart() throws Exception { super.doStart(); + if (binders != null) { + // load binders from micrometer + initBinders(); + } + if (isEnableRoutePolicy()) { MicrometerRoutePolicyFactory factory = new MicrometerRoutePolicyFactory(); factory.setMeterRegistry(meterRegistry); @@ -190,11 +224,69 @@ public class MicrometerPrometheus extends ServiceSupport implements CamelMetrics } } + private void initBinders() throws IOException { + LOG.debug("Loading {}", JANDEX_INDEX); + Index index = JandexHelper.readJandexIndex(camelContext); + if (index == null) { + LOG.warn("Cannot read {} with list of known MeterBinder classes}", JANDEX_INDEX); + } else { + DotName dn = DotName.createSimple(MeterBinder.class); + List<ClassInfo> classes = index.getKnownDirectImplementors(dn); + LOG.debug("Found {} MeterBinder classes from {}", classes.size(), JANDEX_INDEX); + + StringJoiner sj = new StringJoiner(", "); + for (String binder : binders.split(",")) { + binder = binder.trim(); + binder = StringHelper.dashToCamelCase(binder); + binder = binder.toLowerCase(); + + final String target = binder; + Optional<ClassInfo> found = classes.stream() + // use naming convention with and without metrics + .filter(c -> c.name().local().toLowerCase().equals(target) + || c.name().local().toLowerCase().equals(target + "metrics")) + .findFirst(); + + if (found.isPresent()) { + String fqn = found.get().name().toString(); + LOG.debug("Creating MeterBinder: {}", fqn); + try { + Class<MeterBinder> clazz = camelContext.getClassResolver().resolveClass(fqn, MeterBinder.class); + MeterBinder mb = camelContext.getInjector().newInstance(clazz); + if (mb != null) { + mb.bindTo(meterRegistry); + createdBinders.add(mb); + sj.add(mb.getClass().getSimpleName()); + } + } catch (Exception e) { + LOG.warn("Error creating MeterBinder: {} due to: {}", fqn, e.getMessage(), e); + } + } + } + if (!createdBinders.isEmpty()) { + LOG.info("Registered {} MeterBinders: {}", createdBinders.size(), sj); + } + } + } + + @Override + protected void doShutdown() throws Exception { + super.doShutdown(); + + for (MeterBinder mb : createdBinders) { + if (mb instanceof Closeable ac) { + IOHelper.close(ac); + } + } + createdBinders.clear(); + } + protected void setupHttpScraper() { Route metrics = router.route("/q/metrics"); metrics.method(HttpMethod.GET); - final String format = "0.0.4".equals(textFormatVersion) ? TextFormat.CONTENT_TYPE_004 : TextFormat.CONTENT_TYPE_OPENMETRICS_100; + final String format + = "0.0.4".equals(textFormatVersion) ? TextFormat.CONTENT_TYPE_004 : TextFormat.CONTENT_TYPE_OPENMETRICS_100; metrics.produces(format); Handler<RoutingContext> handler = new Handler<RoutingContext>() { diff --git a/core/camel-main/src/generated/java/org/apache/camel/main/MetricsConfigurationPropertiesConfigurer.java b/core/camel-main/src/generated/java/org/apache/camel/main/MetricsConfigurationPropertiesConfigurer.java index ed323ad35fd..5ff6c35f698 100644 --- a/core/camel-main/src/generated/java/org/apache/camel/main/MetricsConfigurationPropertiesConfigurer.java +++ b/core/camel-main/src/generated/java/org/apache/camel/main/MetricsConfigurationPropertiesConfigurer.java @@ -21,6 +21,8 @@ public class MetricsConfigurationPropertiesConfigurer extends org.apache.camel.s public boolean configure(CamelContext camelContext, Object obj, String name, Object value, boolean ignoreCase) { org.apache.camel.main.MetricsConfigurationProperties target = (org.apache.camel.main.MetricsConfigurationProperties) obj; switch (ignoreCase ? name.toLowerCase() : name) { + case "binders": + case "Binders": target.setBinders(property(camelContext, java.lang.String.class, value)); return true; case "enableexchangeeventnotifier": case "EnableExchangeEventNotifier": target.setEnableExchangeEventNotifier(property(camelContext, boolean.class, value)); return true; case "enablemessagehistory": @@ -40,6 +42,8 @@ public class MetricsConfigurationPropertiesConfigurer extends org.apache.camel.s @Override public Class<?> getOptionType(String name, boolean ignoreCase) { switch (ignoreCase ? name.toLowerCase() : name) { + case "binders": + case "Binders": return java.lang.String.class; case "enableexchangeeventnotifier": case "EnableExchangeEventNotifier": return boolean.class; case "enablemessagehistory": @@ -60,6 +64,8 @@ public class MetricsConfigurationPropertiesConfigurer extends org.apache.camel.s public Object getOptionValue(Object obj, String name, boolean ignoreCase) { org.apache.camel.main.MetricsConfigurationProperties target = (org.apache.camel.main.MetricsConfigurationProperties) obj; switch (ignoreCase ? name.toLowerCase() : name) { + case "binders": + case "Binders": return target.getBinders(); case "enableexchangeeventnotifier": case "EnableExchangeEventNotifier": return target.isEnableExchangeEventNotifier(); case "enablemessagehistory": diff --git a/core/camel-main/src/generated/resources/META-INF/camel-main-configuration-metadata.json b/core/camel-main/src/generated/resources/META-INF/camel-main-configuration-metadata.json index 22167f65ed6..b394013bf78 100644 --- a/core/camel-main/src/generated/resources/META-INF/camel-main-configuration-metadata.json +++ b/core/camel-main/src/generated/resources/META-INF/camel-main-configuration-metadata.json @@ -180,6 +180,7 @@ { "name": "camel.lra.enabled", "description": "To enable Saga LRA", "sourceType": "org.apache.camel.main.LraConfigurationProperties", "type": "boolean", "javaType": "boolean", "defaultValue": false }, { "name": "camel.lra.localParticipantContextPath", "description": "The context-path for the local participant. Is default \/lra-participant", "sourceType": "org.apache.camel.main.LraConfigurationProperties", "type": "string", "javaType": "java.lang.String", "defaultValue": "\/lra-participant" }, { "name": "camel.lra.localParticipantUrl", "description": "The URL for the local participant", "sourceType": "org.apache.camel.main.LraConfigurationProperties", "type": "string", "javaType": "java.lang.String" }, + { "name": "camel.metrics.binders", "description": "Additional Micrometer binders to include such as jvm-memory, processor, jvm-thread, and so forth. Multiple binders can be separated by comma.", "sourceType": "org.apache.camel.main.MetricsConfigurationProperties", "type": "string", "javaType": "java.lang.String" }, { "name": "camel.metrics.enabled", "description": "To enable Micrometer metrics.", "sourceType": "org.apache.camel.main.MetricsConfigurationProperties", "type": "boolean", "javaType": "boolean", "defaultValue": "false" }, { "name": "camel.metrics.enableExchangeEventNotifier", "description": "Set whether to enable the MicrometerExchangeEventNotifier for capturing metrics on exchange processing times.", "sourceType": "org.apache.camel.main.MetricsConfigurationProperties", "type": "boolean", "javaType": "boolean", "defaultValue": true }, { "name": "camel.metrics.enableMessageHistory", "description": "Set whether to enable the MicrometerMessageHistoryFactory for capturing metrics on individual route node processing times. Depending on the number of configured route nodes, there is the potential to create a large volume of metrics. Therefore, this option is disabled by default.", "sourceType": "org.apache.camel.main.MetricsConfigurationProperties", "type": "boolean", "javaType": "boolean", "defaultValue": "false" }, diff --git a/core/camel-main/src/main/docs/main.adoc b/core/camel-main/src/main/docs/main.adoc index 426dff178b1..14021ec8ebf 100644 --- a/core/camel-main/src/main/docs/main.adoc +++ b/core/camel-main/src/main/docs/main.adoc @@ -347,11 +347,12 @@ The camel.opentelemetry supports 4 options, which are listed below. === Camel Micrometer Metrics configurations -The camel.metrics supports 6 options, which are listed below. +The camel.metrics supports 7 options, which are listed below. [width="100%",cols="2,5,^1,2",options="header"] |=== | Name | Description | Default | Type +| *camel.metrics.binders* | Additional Micrometer binders to include such as jvm-memory, processor, jvm-thread, and so forth. Multiple binders can be separated by comma. | | String | *camel.metrics.enabled* | To enable Micrometer metrics. | false | boolean | *camel.metrics.enableExchange{zwsp}EventNotifier* | Set whether to enable the MicrometerExchangeEventNotifier for capturing metrics on exchange processing times. | true | boolean | *camel.metrics.enableMessage{zwsp}History* | Set whether to enable the MicrometerMessageHistoryFactory for capturing metrics on individual route node processing times. Depending on the number of configured route nodes, there is the potential to create a large volume of metrics. Therefore, this option is disabled by default. | false | boolean diff --git a/core/camel-main/src/main/java/org/apache/camel/main/MetricsConfigurationProperties.java b/core/camel-main/src/main/java/org/apache/camel/main/MetricsConfigurationProperties.java index dd55c2951c3..badc1b27506 100644 --- a/core/camel-main/src/main/java/org/apache/camel/main/MetricsConfigurationProperties.java +++ b/core/camel-main/src/main/java/org/apache/camel/main/MetricsConfigurationProperties.java @@ -38,6 +38,8 @@ public class MetricsConfigurationProperties implements BootstrapCloseable { private boolean enableRouteEventNotifier = true; @Metadata(defaultValue = "0.0.4", enums = "0.0.4,1.0.0") private String textFormatVersion = "0.0.4"; + @Metadata + private String binders; public MetricsConfigurationProperties(MainConfigurationProperties parent) { this.parent = parent; @@ -121,6 +123,18 @@ public class MetricsConfigurationProperties implements BootstrapCloseable { this.textFormatVersion = textFormatVersion; } + public String getBinders() { + return binders; + } + + /** + * Additional Micrometer binders to include such as jvm-memory, processor, jvm-thread, and so forth. Multiple + * binders can be separated by comma. + */ + public void setBinders(String binders) { + this.binders = binders; + } + @Override public void close() { parent = null; @@ -182,5 +196,13 @@ public class MetricsConfigurationProperties implements BootstrapCloseable { return this; } + /** + * Additional Micrometer binders to include such as jvm-memory, processor, jvm-thread, and so forth. Multiple + * binders can be separated by comma. + */ + public MetricsConfigurationProperties withbinders(String binders) { + this.binders = binders; + return this; + } }