diff --git a/collector/src/main/java/io/prometheus/jmx/JmxCollector.java b/collector/src/main/java/io/prometheus/jmx/JmxCollector.java index 304a531c..d13f2cdd 100644 --- a/collector/src/main/java/io/prometheus/jmx/JmxCollector.java +++ b/collector/src/main/java/io/prometheus/jmx/JmxCollector.java @@ -77,6 +77,16 @@ static class Rule { ArrayList labelValues; } + public static class MetricCustomizer { + MBeanFilter mbeanFilter; + List attributesAsLabels; + } + + public static class MBeanFilter { + String domain; + Map properties; + } + private static class Config { Integer startDelaySeconds = 0; String jmxUrl = ""; @@ -90,7 +100,7 @@ private static class Config { ObjectNameAttributeFilter objectNameAttributeFilter; final List rules = new ArrayList<>(); long lastUpdate = 0L; - + List metricCustomizers = new ArrayList<>(); MatchedRulesCache rulesCache; } @@ -326,6 +336,42 @@ private Config loadConfig(Map yamlConfig) throws MalformedObject } } + if (yamlConfig.containsKey("metricCustomizers")) { + List> metricCustomizersYaml = + (List>) yamlConfig.get("metricCustomizers"); + if (metricCustomizersYaml != null) { + for (Map metricCustomizerYaml : metricCustomizersYaml) { + Map mbeanFilterYaml = + (Map) metricCustomizerYaml.get("mbeanFilter"); + if (mbeanFilterYaml == null) { + throw new IllegalArgumentException( + "Must provide mbeanFilter, if metricCustomizers is given: " + metricCustomizersYaml); + } + MBeanFilter mbeanFilter = new MBeanFilter(); + mbeanFilter.domain = (String) mbeanFilterYaml.get("domain"); + if (mbeanFilter.domain == null) { + throw new IllegalArgumentException( + "Must provide domain, if metricCustomizers is given: " + metricCustomizersYaml); + } + mbeanFilter.properties = (Map) mbeanFilterYaml.getOrDefault("properties", new HashMap<>()); + + List attributesAsLabels = + (List) metricCustomizerYaml.get("attributesAsLabels"); + if (attributesAsLabels == null) { + throw new IllegalArgumentException( + "Must provide attributesAsLabels, if metricCustomizers is given: " + metricCustomizersYaml); + } + MetricCustomizer metricCustomizer = new MetricCustomizer(); + metricCustomizer.mbeanFilter = mbeanFilter; + metricCustomizer.attributesAsLabels = attributesAsLabels; + cfg.metricCustomizers.add(metricCustomizer); + } + } else { + throw new IllegalArgumentException( + "Must provide mbeanFilter, if metricCustomizers is given "); + } + } + if (yamlConfig.containsKey("rules")) { List> configRules = (List>) yamlConfig.get("rules"); @@ -498,7 +544,8 @@ private MatchedRule defaultExport( String help, Double value, double valueFactor, - String type) { + String type, + Map attributesAsLabelsWithValues) { StringBuilder name = new StringBuilder(); name.append(domain); if (!beanProperties.isEmpty()) { @@ -533,6 +580,7 @@ private MatchedRule defaultExport( labelValues.add(entry.getValue()); } } + addAttributesAsLabelsWithValuesToLabels(config, attributesAsLabelsWithValues, labelNames, labelValues); return new MatchedRule( fullname, matchName, type, help, labelNames, labelValues, value, valueFactor); @@ -541,6 +589,7 @@ private MatchedRule defaultExport( public void recordBean( String domain, LinkedHashMap beanProperties, + Map attributesAsLabelsWithValues, LinkedList attrKeys, String attrName, String attrType, @@ -638,7 +687,8 @@ public void recordBean( help, value, rule.valueFactor, - rule.type); + rule.type, + attributesAsLabelsWithValues); addToCache(rule, matchName, matchedRule); break; } @@ -660,6 +710,7 @@ public void recordBean( // Set the labels. ArrayList labelNames = new ArrayList<>(); ArrayList labelValues = new ArrayList<>(); + addAttributesAsLabelsWithValuesToLabels(config, attributesAsLabelsWithValues, labelNames, labelValues); if (rule.labelNames != null) { for (int i = 0; i < rule.labelNames.size(); i++) { final String unsafeLabelName = rule.labelNames.get(i); @@ -734,6 +785,18 @@ public void recordBean( } } + private static void addAttributesAsLabelsWithValuesToLabels(Config config, Map attributesAsLabelsWithValues, List labelNames, List labelValues) { + attributesAsLabelsWithValues.forEach( + (attributeAsLabelName, attributeValue) -> { + String labelName = safeName(attributeAsLabelName); + if (config.lowercaseOutputLabelNames) { + labelName = labelName.toLowerCase(); + } + labelNames.add(labelName); + labelValues.add(attributeValue); + }); + } + @Override public MetricSnapshots collect() { // Take a reference to the current config and collect with this one @@ -754,6 +817,7 @@ public MetricSnapshots collect() { config.includeObjectNames, config.excludeObjectNames, config.objectNameAttributeFilter, + config.metricCustomizers, receiver, jmxMBeanPropertyCache); diff --git a/collector/src/main/java/io/prometheus/jmx/JmxScraper.java b/collector/src/main/java/io/prometheus/jmx/JmxScraper.java index 54e40af6..695da9d7 100644 --- a/collector/src/main/java/io/prometheus/jmx/JmxScraper.java +++ b/collector/src/main/java/io/prometheus/jmx/JmxScraper.java @@ -31,6 +31,8 @@ import java.util.Optional; import java.util.Set; import java.util.TreeSet; +import java.util.Collections; +import java.util.stream.Collectors; import javax.management.Attribute; import javax.management.AttributeList; import javax.management.JMException; @@ -72,6 +74,7 @@ public interface MBeanReceiver { void recordBean( String domain, LinkedHashMap beanProperties, + Map attributesAsLabelsWithValues, LinkedList attrKeys, String attrName, String attrType, @@ -85,6 +88,7 @@ void recordBean( private final String password; private final boolean ssl; private final List includeObjectNames, excludeObjectNames; + private final List metricCustomizers; private final ObjectNameAttributeFilter objectNameAttributeFilter; private final JmxMBeanPropertyCache jmxMBeanPropertyCache; @@ -109,6 +113,7 @@ public JmxScraper( List includeObjectNames, List excludeObjectNames, ObjectNameAttributeFilter objectNameAttributeFilter, + List metricCustomizers, MBeanReceiver receiver, JmxMBeanPropertyCache jmxMBeanPropertyCache) { this.jmxUrl = jmxUrl; @@ -118,6 +123,7 @@ public JmxScraper( this.ssl = ssl; this.includeObjectNames = includeObjectNames; this.excludeObjectNames = excludeObjectNames; + this.metricCustomizers = metricCustomizers; this.objectNameAttributeFilter = objectNameAttributeFilter; this.jmxMBeanPropertyCache = jmxMBeanPropertyCache; } @@ -253,6 +259,12 @@ private void scrapeBean(MBeanServerConnection beanConn, ObjectName mBeanName) { final String mBeanNameString = mBeanName.toString(); final String mBeanDomain = mBeanName.getDomain(); + JmxCollector.MetricCustomizer metricCustomizer = getMetricCustomizer(mBeanName); + Map attributesAsLabelsWithValues = Collections.emptyMap(); + if (metricCustomizer != null) { + attributesAsLabelsWithValues = + getAttributesAsLabelsWithValues(metricCustomizer, attributes); + } for (Object object : attributes) { // The contents of an AttributeList should all be Attribute instances, but we'll verify @@ -280,6 +292,7 @@ private void scrapeBean(MBeanServerConnection beanConn, ObjectName mBeanName) { mBeanName, mBeanDomain, jmxMBeanPropertyCache.getKeyPropertyList(mBeanName), + attributesAsLabelsWithValues, new LinkedList<>(), mBeanAttributeInfo.getName(), mBeanAttributeInfo.getType(), @@ -300,6 +313,35 @@ private void scrapeBean(MBeanServerConnection beanConn, ObjectName mBeanName) { } } + private Map getAttributesAsLabelsWithValues(JmxCollector.MetricCustomizer metricCustomizer, AttributeList attributes) { + Map attributeMap = attributes.asList().stream() + .collect(Collectors.toMap(Attribute::getName, Attribute::getValue)); + Map attributesAsLabelsWithValues = new HashMap<>(); + for (String attributeAsLabel : metricCustomizer.attributesAsLabels) { + Object attrValue = attributeMap.get(attributeAsLabel); + if (attrValue != null) { + attributesAsLabelsWithValues.put(attributeAsLabel, attrValue.toString()); + } + } + return attributesAsLabelsWithValues; + } + + private JmxCollector.MetricCustomizer getMetricCustomizer(ObjectName mBeanName) { + if (!metricCustomizers.isEmpty()) { + for (JmxCollector.MetricCustomizer metricCustomizer : metricCustomizers) { + if (filterMbeanByDomainAndProperties(mBeanName, metricCustomizer)) { + return metricCustomizer; + } + } + } + return null; + } + + private boolean filterMbeanByDomainAndProperties(ObjectName mBeanName, JmxCollector.MetricCustomizer metricCustomizer) { + return metricCustomizer.mbeanFilter.domain.equals(mBeanName.getDomain()) && + mBeanName.getKeyPropertyList().entrySet().containsAll(metricCustomizer.mbeanFilter.properties.entrySet()); + } + private void processAttributesOneByOne( MBeanServerConnection beanConn, ObjectName mbeanName, @@ -318,6 +360,7 @@ private void processAttributesOneByOne( mbeanName, mbeanName.getDomain(), jmxMBeanPropertyCache.getKeyPropertyList(mbeanName), + new HashMap<>(), new LinkedList<>(), attr.getName(), attr.getType(), @@ -335,6 +378,7 @@ private void processBeanValue( ObjectName objectName, String domain, LinkedHashMap beanProperties, + Map attributesAsLabelsWithValues, LinkedList attrKeys, String attrName, String attrType, @@ -352,7 +396,7 @@ private void processBeanValue( } LOGGER.log(FINE, "%s%s%s scrape: %s", domain, beanProperties, attrName, value); this.receiver.recordBean( - domain, beanProperties, attrKeys, attrName, attrType, attrDescription, value); + domain, beanProperties, attributesAsLabelsWithValues, attrKeys, attrName, attrType, attrDescription, value); } else if (value instanceof CompositeData) { LOGGER.log(FINE, "%s%s%s scrape: compositedata", domain, beanProperties, attrName); CompositeData composite = (CompositeData) value; @@ -366,6 +410,7 @@ private void processBeanValue( objectName, domain, beanProperties, + attributesAsLabelsWithValues, attrKeys, key, typ, @@ -432,6 +477,7 @@ private void processBeanValue( objectName, domain, l2s, + attributesAsLabelsWithValues, attrNames, name, typ, @@ -452,6 +498,7 @@ private void processBeanValue( objectName, domain, beanProperties, + attributesAsLabelsWithValues, attrKeys, attrName, attrType, @@ -464,6 +511,7 @@ private void processBeanValue( objectName, domain, beanProperties, + attributesAsLabelsWithValues, attrKeys, attrName, attrType, @@ -479,6 +527,7 @@ private static class StdoutWriter implements MBeanReceiver { public void recordBean( String domain, LinkedHashMap beanProperties, + Map attributesAsLabelsWithValues, LinkedList attrKeys, String attrName, String attrType, @@ -503,6 +552,7 @@ public static void main(String[] args) throws Exception { objectNames, new LinkedList<>(), objectNameAttributeFilter, + new LinkedList<>(), new StdoutWriter(), new JmxMBeanPropertyCache()) .doScrape(); @@ -515,6 +565,7 @@ public static void main(String[] args) throws Exception { objectNames, new LinkedList<>(), objectNameAttributeFilter, + new LinkedList<>(), new StdoutWriter(), new JmxMBeanPropertyCache()) .doScrape(); @@ -527,6 +578,7 @@ public static void main(String[] args) throws Exception { objectNames, new LinkedList<>(), objectNameAttributeFilter, + new LinkedList<>(), new StdoutWriter(), new JmxMBeanPropertyCache()) .doScrape(); diff --git a/collector/src/test/java/io/prometheus/jmx/CustomValueMBean.java b/collector/src/test/java/io/prometheus/jmx/CustomValueMBean.java new file mode 100644 index 00000000..0603c583 --- /dev/null +++ b/collector/src/test/java/io/prometheus/jmx/CustomValueMBean.java @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2023-present The Prometheus jmx_exporter Authors + * + * 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. + */ +package io.prometheus.jmx; + +import javax.management.MBeanServer; +import javax.management.ObjectName; + +/** Class to implement CustomValueMBean */ +public interface CustomValueMBean { + + /** + * Method to get the value + * + * @return value + */ + Integer getValue(); + + /** + * Method to get the text + * + * @return text + */ + String getText(); +} + +/** Class to implement CustomValue */ +class CustomValue implements CustomValueMBean { + + @Override + public Integer getValue() { + return 345; + } + + @Override + public String getText() { + return "value"; + } + + public static void registerBean(MBeanServer mbs) throws javax.management.JMException { + ObjectName mbeanName = + new ObjectName("io.prometheus.jmx:type=customValue"); + CustomValueMBean mbean = new CustomValue(); + mbs.registerMBean(mbean, mbeanName); + } +} diff --git a/collector/src/test/java/io/prometheus/jmx/JmxCollectorTest.java b/collector/src/test/java/io/prometheus/jmx/JmxCollectorTest.java index 830bd15e..fe5adc1c 100644 --- a/collector/src/test/java/io/prometheus/jmx/JmxCollectorTest.java +++ b/collector/src/test/java/io/prometheus/jmx/JmxCollectorTest.java @@ -56,6 +56,7 @@ public static void classSetUp() throws Exception { TomcatServlet.registerBean(mbs); Bool.registerBean(mbs); Camel.registerBean(mbs); + CustomValue.registerBean(mbs); } @Before @@ -163,6 +164,21 @@ public void testNameAndLabelSanitized() throws Exception { .001); } + @Test + public void testMetricCustomizers() throws Exception { + new JmxCollector( + "\n---\nincludeObjectNames: [`io.prometheus.jmx:type=customValue`]\nmetricCustomizers:\n - mbeanFilter:\n domain: io.prometheus.jmx\n properties:\n type: customValue\n attributesAsLabels:\n - Text" + .replace('`', '"')) + .register(prometheusRegistry); + assertEquals( + 345, + getSampleValue( + "io_prometheus_jmx_customValue_Value", + new String[] {"Text"}, + new String[] {"value"}), + .001); + } + /* @Test public void testHelpFromPattern() throws Exception { diff --git a/docs/content/1.1.0/http-mode/rules.md b/docs/content/1.1.0/http-mode/rules.md index 31ece10a..8cf6c89e 100644 --- a/docs/content/1.1.0/http-mode/rules.md +++ b/docs/content/1.1.0/http-mode/rules.md @@ -25,6 +25,11 @@ labels | A map of label name to label value pairs. Capture groups fro help | Help text for the metric. Capture groups from `pattern` can be used. `name` must be set to use this. Defaults to the mBean attribute description, domain, and name of the attribute. cache | Whether to cache bean name expressions to rule computation (match and mismatch). Not recommended for rules matching on bean value, as only the value from the first scrape will be cached and re-used. This can increase performance when collecting a lot of mbeans. Defaults to `false`. type | The type of the metric, can be `GAUGE`, `COUNTER` or `UNTYPED`. `name` must be set to use this. Defaults to `UNTYPED`. +metricCustomizers | A list of objects that contain `mbeanFilter` and `attributesAsLabels`. For those mBeans that match the filter, the items in the `attributesAsLabels` list will be added as attributes to the existing metrics. +mbeanFilter | A map of the criteria by which mBeans are filtered. It contains `domain` and `properties`. +domain | Domain of an mBean. Mandatory if `metricCustomizers` defined. +properties | Properties of an mBean. Optional +attributesAsLabels | List of elements to be added as attributes to existing metrics. Mandatory if `metricCustomizers` defined. Metric names and label names are sanitized. All characters other than `[a-zA-Z0-9:_]` are replaced with underscores, and adjacent underscores are collapsed. There's no limitations on label values or the help text. diff --git a/integration_test_suite/integration_tests/src/test/java/io/prometheus/jmx/test/core/MetricCustomizersTest.java b/integration_test_suite/integration_tests/src/test/java/io/prometheus/jmx/test/core/MetricCustomizersTest.java new file mode 100644 index 00000000..84879a14 --- /dev/null +++ b/integration_test_suite/integration_tests/src/test/java/io/prometheus/jmx/test/core/MetricCustomizersTest.java @@ -0,0 +1,162 @@ +/* + * Copyright (C) 2023-present The Prometheus jmx_exporter Authors + * + * 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. + */ + +package io.prometheus.jmx.test.core; + +import static io.prometheus.jmx.test.support.Assertions.assertCommonMetricsResponse; +import static io.prometheus.jmx.test.support.Assertions.assertHealthyResponse; +import static org.assertj.core.api.Assertions.assertThat; + +import io.prometheus.jmx.test.support.ExporterPath; +import io.prometheus.jmx.test.support.ExporterTestEnvironment; +import io.prometheus.jmx.test.support.TestSupport; +import io.prometheus.jmx.test.support.http.HttpClient; +import io.prometheus.jmx.test.support.http.HttpHeader; +import io.prometheus.jmx.test.support.http.HttpResponse; +import io.prometheus.jmx.test.support.metrics.Metric; +import io.prometheus.jmx.test.support.metrics.MetricsContentType; +import io.prometheus.jmx.test.support.metrics.MetricsParser; +import java.io.IOException; +import java.util.*; +import java.util.stream.Stream; +import org.testcontainers.containers.Network; +import org.verifyica.api.ArgumentContext; +import org.verifyica.api.ClassContext; +import org.verifyica.api.Trap; +import org.verifyica.api.Verifyica; + +public class MetricCustomizersTest { + + @Verifyica.ArgumentSupplier(parallelism = Integer.MAX_VALUE) + public static Stream arguments() { + return ExporterTestEnvironment.createExporterTestEnvironments(); + } + + @Verifyica.Prepare + public static void prepare(ClassContext classContext) { + TestSupport.getOrCreateNetwork(classContext); + } + + @Verifyica.BeforeAll + public void beforeAll(ArgumentContext argumentContext) { + Class testClass = argumentContext.classContext().testClass(); + Network network = TestSupport.getOrCreateNetwork(argumentContext); + TestSupport.initializeExporterTestEnvironment(argumentContext, network, testClass); + } + + @Verifyica.Test + @Verifyica.Order(1) + public void testHealthy(ExporterTestEnvironment exporterTestEnvironment) throws IOException { + String url = exporterTestEnvironment.getUrl(ExporterPath.HEALTHY); + + HttpResponse httpResponse = HttpClient.sendRequest(url); + + assertHealthyResponse(httpResponse); + } + + @Verifyica.Test + public void testDefaultTextMetrics(ExporterTestEnvironment exporterTestEnvironment) + throws IOException { + String url = exporterTestEnvironment.getUrl(ExporterPath.METRICS); + + HttpResponse httpResponse = HttpClient.sendRequest(url); + + assertMetricsResponse(exporterTestEnvironment, httpResponse, MetricsContentType.DEFAULT); + } + + @Verifyica.Test + public void testOpenMetricsTextMetrics(ExporterTestEnvironment exporterTestEnvironment) + throws IOException { + String url = exporterTestEnvironment.getUrl(ExporterPath.METRICS); + + HttpResponse httpResponse = + HttpClient.sendRequest( + url, + HttpHeader.ACCEPT, + MetricsContentType.OPEN_METRICS_TEXT_METRICS.toString()); + + assertMetricsResponse( + exporterTestEnvironment, + httpResponse, + MetricsContentType.OPEN_METRICS_TEXT_METRICS); + } + + @Verifyica.Test + public void testPrometheusTextMetrics(ExporterTestEnvironment exporterTestEnvironment) + throws IOException { + String url = exporterTestEnvironment.getUrl(ExporterPath.METRICS); + + HttpResponse httpResponse = + HttpClient.sendRequest( + url, + HttpHeader.ACCEPT, + MetricsContentType.PROMETHEUS_TEXT_METRICS.toString()); + + assertMetricsResponse( + exporterTestEnvironment, httpResponse, MetricsContentType.PROMETHEUS_TEXT_METRICS); + } + + @Verifyica.Test + public void testPrometheusProtobufMetrics(ExporterTestEnvironment exporterTestEnvironment) + throws IOException { + String url = exporterTestEnvironment.getUrl(ExporterPath.METRICS); + + HttpResponse httpResponse = + HttpClient.sendRequest( + url, + HttpHeader.ACCEPT, + MetricsContentType.PROMETHEUS_PROTOBUF_METRICS.toString()); + + assertMetricsResponse( + exporterTestEnvironment, + httpResponse, + MetricsContentType.PROMETHEUS_PROTOBUF_METRICS); + } + + @Verifyica.AfterAll + public void afterAll(ArgumentContext argumentContext) throws Throwable { + List traps = new ArrayList<>(); + + traps.add(new Trap(() -> TestSupport.destroyExporterTestEnvironment(argumentContext))); + traps.add(new Trap(() -> TestSupport.destroyNetwork(argumentContext))); + + Trap.assertEmpty(traps); + } + + @Verifyica.Conclude + public static void conclude(ClassContext classContext) throws Throwable { + new Trap(() -> TestSupport.destroyNetwork(classContext)).assertEmpty(); + } + + private void assertMetricsResponse( + ExporterTestEnvironment exporterTestEnvironment, + HttpResponse httpResponse, + MetricsContentType metricsContentType) { + assertCommonMetricsResponse(httpResponse, metricsContentType); + + Collection metrics = MetricsParser.parseCollection(httpResponse); + + /* + * Assert that a certain metric has specific labels + */ + metrics.stream() + .filter(metric -> metric.name().contains("io_prometheus_jmx_customValue_Value")) + .forEach( + metric -> + assertThat(metric.labels()) + .containsEntry("Text", "value")); + } +} diff --git a/integration_test_suite/integration_tests/src/test/resources/io/prometheus/jmx/test/core/MetricCustomizersTest/JavaAgent/application.sh b/integration_test_suite/integration_tests/src/test/resources/io/prometheus/jmx/test/core/MetricCustomizersTest/JavaAgent/application.sh new file mode 100644 index 00000000..9e5717d6 --- /dev/null +++ b/integration_test_suite/integration_tests/src/test/resources/io/prometheus/jmx/test/core/MetricCustomizersTest/JavaAgent/application.sh @@ -0,0 +1,6 @@ +#!/bin/bash + +java \ + -Xmx512M \ + -javaagent:jmx_prometheus_javaagent.jar=8888:exporter.yaml \ + -jar jmx_example_application.jar \ No newline at end of file diff --git a/integration_test_suite/integration_tests/src/test/resources/io/prometheus/jmx/test/core/MetricCustomizersTest/JavaAgent/exporter.yaml b/integration_test_suite/integration_tests/src/test/resources/io/prometheus/jmx/test/core/MetricCustomizersTest/JavaAgent/exporter.yaml new file mode 100644 index 00000000..0416d69a --- /dev/null +++ b/integration_test_suite/integration_tests/src/test/resources/io/prometheus/jmx/test/core/MetricCustomizersTest/JavaAgent/exporter.yaml @@ -0,0 +1,8 @@ +includeObjectNames: ["io.prometheus.jmx:type=customValue"] +metricCustomizers: + - mbeanFilter: + domain: io.prometheus.jmx + properties: + type: customValue + attributesAsLabels: + - Text \ No newline at end of file diff --git a/integration_test_suite/integration_tests/src/test/resources/io/prometheus/jmx/test/core/MetricCustomizersTest/Standalone/application.sh b/integration_test_suite/integration_tests/src/test/resources/io/prometheus/jmx/test/core/MetricCustomizersTest/Standalone/application.sh new file mode 100644 index 00000000..9efc7364 --- /dev/null +++ b/integration_test_suite/integration_tests/src/test/resources/io/prometheus/jmx/test/core/MetricCustomizersTest/Standalone/application.sh @@ -0,0 +1,13 @@ +#!/bin/bash + +java \ + -Xmx512M \ + -Dcom.sun.management.jmxremote=true \ + -Dcom.sun.management.jmxremote.authenticate=false \ + -Dcom.sun.management.jmxremote.local.only=false \ + -Dcom.sun.management.jmxremote.port=9999 \ + -Dcom.sun.management.jmxremote.registry.ssl=false \ + -Dcom.sun.management.jmxremote.rmi.port=9999 \ + -Dcom.sun.management.jmxremote.ssl.need.client.auth=false \ + -Dcom.sun.management.jmxremote.ssl=false \ + -jar jmx_example_application.jar diff --git a/integration_test_suite/integration_tests/src/test/resources/io/prometheus/jmx/test/core/MetricCustomizersTest/Standalone/exporter.sh b/integration_test_suite/integration_tests/src/test/resources/io/prometheus/jmx/test/core/MetricCustomizersTest/Standalone/exporter.sh new file mode 100644 index 00000000..3de46adb --- /dev/null +++ b/integration_test_suite/integration_tests/src/test/resources/io/prometheus/jmx/test/core/MetricCustomizersTest/Standalone/exporter.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +java \ + -Xmx512M \ + -jar jmx_prometheus_standalone.jar 8888 exporter.yaml diff --git a/integration_test_suite/integration_tests/src/test/resources/io/prometheus/jmx/test/core/MetricCustomizersTest/Standalone/exporter.yaml b/integration_test_suite/integration_tests/src/test/resources/io/prometheus/jmx/test/core/MetricCustomizersTest/Standalone/exporter.yaml new file mode 100644 index 00000000..d31460ee --- /dev/null +++ b/integration_test_suite/integration_tests/src/test/resources/io/prometheus/jmx/test/core/MetricCustomizersTest/Standalone/exporter.yaml @@ -0,0 +1,9 @@ +hostPort: application:9999 +includeObjectNames: ["io.prometheus.jmx:type=customValue"] +metricCustomizers: + - mbeanFilter: + domain: io.prometheus.jmx + properties: + type: customValue + attributesAsLabels: + - Text \ No newline at end of file diff --git a/integration_test_suite/jmx_example_application/src/main/java/io/prometheus/jmx/CustomValueMBean.java b/integration_test_suite/jmx_example_application/src/main/java/io/prometheus/jmx/CustomValueMBean.java new file mode 100644 index 00000000..814a7739 --- /dev/null +++ b/integration_test_suite/jmx_example_application/src/main/java/io/prometheus/jmx/CustomValueMBean.java @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2023-present The Prometheus jmx_exporter Authors + * + * 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. + */ +package io.prometheus.jmx; + +/** Class to implement CustomValueMBean */ +public interface CustomValueMBean { + + /** + * Method to get the value + * + * @return value + */ + Integer getValue(); + + /** + * Method to get the text + * + * @return text + */ + String getText(); +} + +/** Class to implement CustomValue */ +class CustomValue implements CustomValueMBean { + + /** Constructor */ + public CustomValue() { + // INTENTIONALLY BLANK + } + + @Override + public Integer getValue() { + return 345; + } + + @Override + public String getText() { + return "value"; + } +} diff --git a/integration_test_suite/jmx_example_application/src/main/java/io/prometheus/jmx/JmxExampleApplication.java b/integration_test_suite/jmx_example_application/src/main/java/io/prometheus/jmx/JmxExampleApplication.java index c7796cb3..48b46894 100644 --- a/integration_test_suite/jmx_example_application/src/main/java/io/prometheus/jmx/JmxExampleApplication.java +++ b/integration_test_suite/jmx_example_application/src/main/java/io/prometheus/jmx/JmxExampleApplication.java @@ -59,6 +59,9 @@ public static void main(String[] args) throws Exception { new PerformanceMetrics(), new ObjectName("io.prometheus.jmx.test:name=PerformanceMetricsMBean")); + mBeanServer.registerMBean( + new CustomValue(), new ObjectName("io.prometheus.jmx:type=customValue")); + System.out.printf( "%s | %s | INFO | %s | %s%n", LocalDateTime.now().format(DATE_TIME_FORMATTER),