diff --git a/jmx-scraper/src/integrationTest/java/io/opentelemetry/contrib/jmxscraper/JmxScraperContainer.java b/jmx-scraper/src/integrationTest/java/io/opentelemetry/contrib/jmxscraper/JmxScraperContainer.java index 691c2fa08..e3b95e1ec 100644 --- a/jmx-scraper/src/integrationTest/java/io/opentelemetry/contrib/jmxscraper/JmxScraperContainer.java +++ b/jmx-scraper/src/integrationTest/java/io/opentelemetry/contrib/jmxscraper/JmxScraperContainer.java @@ -100,6 +100,12 @@ public JmxScraperContainer withExtraJar(String jarPath) { return this; } + /** + * Adds custom metrics yaml from classpath resource + * + * @param yamlPath path to resource in classpath + * @return this + */ @CanIgnoreReturnValue public JmxScraperContainer withCustomYaml(String yamlPath) { this.customYamlFiles.add(yamlPath); diff --git a/jmx-scraper/src/integrationTest/java/io/opentelemetry/contrib/jmxscraper/target_systems/CustomIntegrationTest.java b/jmx-scraper/src/integrationTest/java/io/opentelemetry/contrib/jmxscraper/target_systems/CustomIntegrationTest.java new file mode 100644 index 000000000..dcecc8f5f --- /dev/null +++ b/jmx-scraper/src/integrationTest/java/io/opentelemetry/contrib/jmxscraper/target_systems/CustomIntegrationTest.java @@ -0,0 +1,46 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.contrib.jmxscraper.target_systems; + +import io.opentelemetry.contrib.jmxscraper.JmxScraperContainer; +import io.opentelemetry.contrib.jmxscraper.TestAppContainer; +import java.nio.file.Path; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.containers.wait.strategy.Wait; + +public class CustomIntegrationTest extends TargetSystemIntegrationTest { + + @Override + protected GenericContainer createTargetContainer(int jmxPort) { + // reusing test application for custom yaml + //noinspection resource + return new TestAppContainer() + .withJmxPort(jmxPort) + .withExposedPorts(jmxPort) + .waitingFor(Wait.forListeningPorts(jmxPort)); + } + + @Override + protected JmxScraperContainer customizeScraperContainer( + JmxScraperContainer scraper, GenericContainer target, Path tempDir) { + // only testing custom yaml + return scraper.withCustomYaml("custom-metrics.yaml"); + } + + @Override + protected MetricsVerifier createMetricsVerifier() { + return MetricsVerifier.create() + // custom metric in custom-metrics.yaml + .add( + "custom.jvm.uptime", + metric -> + metric + .hasDescription("JVM uptime in milliseconds") + .hasUnit("ms") + .isCounter() + .hasDataPointsWithoutAttributes()); + } +} diff --git a/jmx-scraper/src/integrationTest/java/io/opentelemetry/contrib/jmxscraper/target_systems/JvmIntegrationTest.java b/jmx-scraper/src/integrationTest/java/io/opentelemetry/contrib/jmxscraper/target_systems/JvmIntegrationTest.java index 3e9b73367..e29021a93 100644 --- a/jmx-scraper/src/integrationTest/java/io/opentelemetry/contrib/jmxscraper/target_systems/JvmIntegrationTest.java +++ b/jmx-scraper/src/integrationTest/java/io/opentelemetry/contrib/jmxscraper/target_systems/JvmIntegrationTest.java @@ -30,7 +30,10 @@ protected GenericContainer createTargetContainer(int jmxPort) { @Override protected JmxScraperContainer customizeScraperContainer( JmxScraperContainer scraper, GenericContainer target, Path tempDir) { - return scraper.withTargetSystem("jvm"); + return scraper + .withTargetSystem("jvm") + // also testing custom yaml + .withCustomYaml("custom-metrics.yaml"); } @Override @@ -48,6 +51,16 @@ protected MetricsVerifier createMetricsVerifier() { nameAttributeMatchers("PS MarkSweep", "PS Scavenge"); return MetricsVerifier.create() + // custom metric in custom-metrics.yaml + .add( + "custom.jvm.uptime", + metric -> + metric + .hasDescription("JVM uptime in milliseconds") + .hasUnit("ms") + .isCounter() + .hasDataPointsWithoutAttributes()) + // metrics for 'jvm' target system .add( "jvm.classes.loaded", metric -> diff --git a/jmx-scraper/src/integrationTest/resources/custom-metrics.yaml b/jmx-scraper/src/integrationTest/resources/custom-metrics.yaml new file mode 100644 index 000000000..a002d71de --- /dev/null +++ b/jmx-scraper/src/integrationTest/resources/custom-metrics.yaml @@ -0,0 +1,11 @@ +--- + +rules: + + - bean: java.lang:type=Runtime + mapping: + Uptime: + metric: custom.jvm.uptime + type: counter + unit: ms + desc: JVM uptime in milliseconds diff --git a/jmx-scraper/src/main/java/io/opentelemetry/contrib/jmxscraper/JmxScraper.java b/jmx-scraper/src/main/java/io/opentelemetry/contrib/jmxscraper/JmxScraper.java index 656b05eb9..d484d2ba9 100644 --- a/jmx-scraper/src/main/java/io/opentelemetry/contrib/jmxscraper/JmxScraper.java +++ b/jmx-scraper/src/main/java/io/opentelemetry/contrib/jmxscraper/JmxScraper.java @@ -18,6 +18,7 @@ import java.io.IOException; import java.io.InputStream; import java.nio.file.Files; +import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; import java.util.Arrays; @@ -225,7 +226,9 @@ private static MetricConfiguration getMetricConfig(JmxScraperConfig scraperConfi for (String system : scraperConfig.getTargetSystems()) { addRulesForSystem(system, config); } - // TODO : add ability for user to provide custom yaml configurations + for (String file : scraperConfig.getJmxConfig()) { + addRulesFromFile(file, config); + } return config; } @@ -234,8 +237,7 @@ private static void addRulesForSystem(String system, MetricConfiguration conf) { try (InputStream inputStream = JmxScraper.class.getClassLoader().getResourceAsStream(yamlResource)) { if (inputStream != null) { - RuleParser parserInstance = RuleParser.get(); - parserInstance.addMetricDefsTo(conf, inputStream, system); + RuleParser.get().addMetricDefsTo(conf, inputStream, system); } else { throw new IllegalArgumentException("No support for system " + system); } @@ -243,4 +245,17 @@ private static void addRulesForSystem(String system, MetricConfiguration conf) { throw new IllegalStateException("Error while loading rules for system " + system, e); } } + + private static void addRulesFromFile(String file, MetricConfiguration conf) { + Path path = Paths.get(file); + if (!Files.isReadable(path)) { + throw new IllegalArgumentException("Unable to read file: " + path); + } + + try (InputStream inputStream = Files.newInputStream(path)) { + RuleParser.get().addMetricDefsTo(conf, inputStream, file); + } catch (IOException e) { + throw new IllegalArgumentException("Error while loading rules from file: " + file, e); + } + } }