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;
+    }
 
 }

Reply via email to