diff --git a/jmx-metrics/src/integrationTest/java/io/opentelemetry/contrib/jmxmetrics/target_systems/WildflyIntegrationTest.java b/jmx-metrics/src/integrationTest/java/io/opentelemetry/contrib/jmxmetrics/target_systems/WildflyIntegrationTest.java index 0aeaf0973..c5e208f78 100644 --- a/jmx-metrics/src/integrationTest/java/io/opentelemetry/contrib/jmxmetrics/target_systems/WildflyIntegrationTest.java +++ b/jmx-metrics/src/integrationTest/java/io/opentelemetry/contrib/jmxmetrics/target_systems/WildflyIntegrationTest.java @@ -54,7 +54,7 @@ void endToEnd() { metric, "wildfly.request.count", "The number of requests received.", - "{requests}", + "{request}", attrs -> attrs.containsOnly( entry("server", "default-server"), entry("listener", "default"))), @@ -72,7 +72,7 @@ void endToEnd() { metric, "wildfly.request.server_error", "The number of requests that have resulted in a 5xx response.", - "{requests}", + "{request}", attrs -> attrs.containsOnly( entry("server", "default-server"), entry("listener", "default"))), @@ -81,7 +81,7 @@ void endToEnd() { metric, "wildfly.network.io", "The number of bytes transmitted.", - "by", + "By", attrs -> attrs.containsOnly( entry("server", "default-server"), @@ -97,7 +97,7 @@ void endToEnd() { metric, "wildfly.jdbc.connection.open", "The number of open jdbc connections.", - "{connections}", + "{connection}", attrs -> attrs.containsOnly(entry("data_source", "ExampleDS"), entry("state", "active")), attrs -> @@ -107,20 +107,20 @@ void endToEnd() { metric, "wildfly.jdbc.request.wait", "The number of jdbc connections that had to wait before opening.", - "{requests}", + "{request}", attrs -> attrs.containsOnly(entry("data_source", "ExampleDS"))), metric -> assertSum( metric, "wildfly.jdbc.transaction.count", "The number of transactions created.", - "{transactions}"), + "{transaction}"), metric -> assertSumWithAttributes( metric, "wildfly.jdbc.rollback.count", "The number of transactions rolled back.", - "{transactions}", + "{transaction}", attrs -> attrs.containsOnly(entry("cause", "system")), attrs -> attrs.containsOnly(entry("cause", "resource")), attrs -> attrs.containsOnly(entry("cause", "application")))); diff --git a/jmx-metrics/src/main/resources/target-systems/wildfly.groovy b/jmx-metrics/src/main/resources/target-systems/wildfly.groovy index feaf4f22e..4e5519e1f 100644 --- a/jmx-metrics/src/main/resources/target-systems/wildfly.groovy +++ b/jmx-metrics/src/main/resources/target-systems/wildfly.groovy @@ -15,46 +15,50 @@ */ def beanWildflyDeployment = otel.mbeans("jboss.as:deployment=*,subsystem=undertow") -otel.instrument(beanWildflyDeployment, "wildfly.session.count", "The number of sessions created.", "{sessions}", +// no test covers sessions +otel.instrument(beanWildflyDeployment, "wildfly.session.count", "The number of sessions created.", "{session}", ["deployment": { mbean -> mbean.name().getKeyProperty("deployment")}], "sessionsCreated", otel.&longCounterCallback) -otel.instrument(beanWildflyDeployment, "wildfly.session.active", "The number of currently active sessions.", "{sessions}", + +otel.instrument(beanWildflyDeployment, "wildfly.session.active", "The number of currently active sessions.", "{session}", ["deployment": { mbean -> mbean.name().getKeyProperty("deployment")}], "activeSessions", otel.&longUpDownCounterCallback) -otel.instrument(beanWildflyDeployment, "wildfly.session.expired", "The number of sessions that have expired.", "{sessions}", +otel.instrument(beanWildflyDeployment, "wildfly.session.expired", "The number of sessions that have expired.", "{session}", ["deployment": { mbean -> mbean.name().getKeyProperty("deployment")}], "expiredSessions", otel.&longCounterCallback) -otel.instrument(beanWildflyDeployment, "wildfly.session.rejected", "The number of sessions that have been rejected.", "{sessions}", +otel.instrument(beanWildflyDeployment, "wildfly.session.rejected", "The number of sessions that have been rejected.", "{session}", ["deployment": { mbean -> mbean.name().getKeyProperty("deployment")}], "rejectedSessions", otel.&longCounterCallback) + + def beanWildflyHttpListener = otel.mbeans("jboss.as:subsystem=undertow,server=*,http-listener=*") -otel.instrument(beanWildflyHttpListener, "wildfly.request.count", "The number of requests received.", "{requests}", +otel.instrument(beanWildflyHttpListener, "wildfly.request.count", "The number of requests received.", "{request}", ["server": { mbean -> mbean.name().getKeyProperty("server")}, "listener": { mbean -> mbean.name().getKeyProperty("http-listener")}], "requestCount", otel.&longCounterCallback) otel.instrument(beanWildflyHttpListener, "wildfly.request.time", "The total amount of time spent on requests.", "ns", ["server": { mbean -> mbean.name().getKeyProperty("server")}, "listener": { mbean -> mbean.name().getKeyProperty("http-listener")}], "processingTime", otel.&longCounterCallback) -otel.instrument(beanWildflyHttpListener, "wildfly.request.server_error", "The number of requests that have resulted in a 5xx response.", "{requests}", +otel.instrument(beanWildflyHttpListener, "wildfly.request.server_error", "The number of requests that have resulted in a 5xx response.", "{request}", ["server": { mbean -> mbean.name().getKeyProperty("server")}, "listener": { mbean -> mbean.name().getKeyProperty("http-listener")}], "errorCount", otel.&longCounterCallback) -otel.instrument(beanWildflyHttpListener, "wildfly.network.io", "The number of bytes transmitted.", "by", +otel.instrument(beanWildflyHttpListener, "wildfly.network.io", "The number of bytes transmitted.", "By", ["server": { mbean -> mbean.name().getKeyProperty("server")}, "listener": { mbean -> mbean.name().getKeyProperty("http-listener")}], ["bytesSent":["state":{"out"}], "bytesReceived":["state":{"in"}]], otel.&longCounterCallback) def beanWildflyDataSource = otel.mbeans("jboss.as:subsystem=datasources,data-source=*,statistics=pool") -otel.instrument(beanWildflyDataSource, "wildfly.jdbc.connection.open", "The number of open jdbc connections.", "{connections}", +otel.instrument(beanWildflyDataSource, "wildfly.jdbc.connection.open", "The number of open jdbc connections.", "{connection}", ["data_source": { mbean -> mbean.name().getKeyProperty("data-source")}], ["ActiveCount":["state":{"active"}], "IdleCount":["state":{"idle"}]], otel.&longUpDownCounterCallback) -otel.instrument(beanWildflyDataSource, "wildfly.jdbc.request.wait", "The number of jdbc connections that had to wait before opening.", "{requests}", +otel.instrument(beanWildflyDataSource, "wildfly.jdbc.request.wait", "The number of jdbc connections that had to wait before opening.", "{request}", ["data_source": { mbean -> mbean.name().getKeyProperty("data-source")}], "WaitCount", otel.&longCounterCallback) def beanWildflyTransaction = otel.mbean("jboss.as:subsystem=transactions") -otel.instrument(beanWildflyTransaction, "wildfly.jdbc.transaction.count", "The number of transactions created.", "{transactions}", +otel.instrument(beanWildflyTransaction, "wildfly.jdbc.transaction.count", "The number of transactions created.", "{transaction}", "numberOfTransactions", otel.&longCounterCallback) -otel.instrument(beanWildflyTransaction, "wildfly.jdbc.rollback.count", "The number of transactions rolled back.", "{transactions}", +otel.instrument(beanWildflyTransaction, "wildfly.jdbc.rollback.count", "The number of transactions rolled back.", "{transaction}", ["numberOfSystemRollbacks":["cause":{"system"}], "numberOfResourceRollbacks":["cause":{"resource"}], "numberOfApplicationRollbacks":["cause":{"application"}]], otel.&longCounterCallback) diff --git a/jmx-scraper/README.md b/jmx-scraper/README.md index 38d3a3d44..b57c508c1 100644 --- a/jmx-scraper/README.md +++ b/jmx-scraper/README.md @@ -7,6 +7,48 @@ The JMX MBeans and their metric mappings are defined in YAML and reuse implement This is currently a work-in-progress component not ready to be used in production. The end goal is to provide an alternative to the [JMX Gatherer](../jmx-metrics/README.md) utility. +## Usage + +The general command to invoke JMX scraper is `java -jar scraper.jar `, where `scraper.jar` +is the `build/libs/opentelemetry-jmx-scraper-.jar` packaged binary when building this module. + +Minimal configuration required + +- `otel.jmx.service.url` for example `service:jmx:rmi:///jndi/rmi://server:9999/jmxrmi` for `server` + host on port `9999` with RMI JMX connector. +- `otel.jmx.target.system` or `otel.jmx.custom.scraping.config` + +Configuration can be provided through: + +- command line arguments: + `java -jar scraper.jar --config otel.jmx.service.url=service:jmx:rmi:///jndi/rmi://tomcat:9010/jmxrmi otel.jmx.target.system=tomcat`. +- command line arguments JVM system properties: + `java -Dotel.jmx.service.url=service:jmx:rmi:///jndi/rmi://tomcat:9010/jmxrmi -Dotel.jmx.target.system=tomcat -jar scraper.jar`. +- java properties file: `java -jar scraper.jar -config config.properties`. +- stdin: `java -jar scraper.jar -config -` where `otel.jmx.target.system=tomcat` and + `otel.jmx.service.url=service:jmx:rmi:///jndi/rmi://tomcat:9010/jmxrmi` is written to stdin. + +TODO: update this once autoconfiguration is supported + +### Configuration reference + +TODO + +### Extra libraries in classpath + +By default, only the RMI JMX connector is provided by the JVM, so it might be required to add extra +libraries in the classpath when connecting to remote JVMs that are not directly accessible with RMI. + +One known example of this is the Wildfly/Jboss HTTP management interface for which the `jboss-client.jar` +needs to be used to support `otel.jmx.service.url` = `service:jmx:remote+http://server:9999`. + +When doing so, the `java -jar` command canĀ“t be used, we have to provide the classpath with +`-cp`/`--class-path`/`-classpath` option and provide the main class file name: + +``` +java -cp scraper.jar:jboss-client.jar io.opentelemetry.contrib.jmxscraper.JmxScraper +``` + ## Component owners - [Jason Plumb](https://github.com/breedx-splk), Splunk diff --git a/jmx-scraper/build.gradle.kts b/jmx-scraper/build.gradle.kts index c2e7eb387..964fc79fb 100644 --- a/jmx-scraper/build.gradle.kts +++ b/jmx-scraper/build.gradle.kts @@ -60,9 +60,16 @@ tasks { withType().configureEach { dependsOn(shadowJar) - dependsOn(named("appJar")) systemProperty("shadow.jar.path", shadowJar.get().archiveFile.get().asFile.absolutePath) - systemProperty("app.jar.path", named("appJar").get().archiveFile.get().asFile.absolutePath) + + val testAppTask = project("test-app").tasks.named("jar") + dependsOn(testAppTask) + systemProperty("app.jar.path", testAppTask.get().archiveFile.get().asFile.absolutePath) + + val testWarTask = project("test-webapp").tasks.named("war") + dependsOn(testWarTask) + systemProperty("app.war.path", testWarTask.get().archiveFile.get().asFile.absolutePath) + systemProperty("gradle.project.version", "${project.version}") } @@ -74,14 +81,6 @@ tasks { } } -tasks.register("appJar") { - from(sourceSets.get("integrationTest").output) - archiveClassifier.set("app") - manifest { - attributes["Main-Class"] = "io.opentelemetry.contrib.jmxscraper.TestApp" - } -} - // Don't publish non-shadowed jar (shadowJar is in shadowRuntimeElements) with(components["java"] as AdhocComponentWithVariants) { configurations.forEach { diff --git a/jmx-scraper/src/integrationTest/java/io/opentelemetry/contrib/jmxscraper/JmxConnectorBuilderTest.java b/jmx-scraper/src/integrationTest/java/io/opentelemetry/contrib/jmxscraper/JmxConnectorBuilderTest.java index 5766cd890..f3f931e58 100644 --- a/jmx-scraper/src/integrationTest/java/io/opentelemetry/contrib/jmxscraper/JmxConnectorBuilderTest.java +++ b/jmx-scraper/src/integrationTest/java/io/opentelemetry/contrib/jmxscraper/JmxConnectorBuilderTest.java @@ -50,7 +50,8 @@ void loginPwdAuth() { testConnector( () -> JmxConnectorBuilder.createNew(app.getHost(), app.getMappedPort(port)) - .userCredentials(login, pwd) + .withUser(login) + .withPassword(pwd) .build()); } } @@ -75,7 +76,7 @@ private static void testConnector(ConnectorSupplier connectorSupplier) { .satisfies( connection -> { try { - ObjectName name = new ObjectName(TestApp.OBJECT_NAME); + ObjectName name = new ObjectName("io.opentelemetry.test:name=TestApp"); Object value = connection.getAttribute(name, "IntValue"); assertThat(value).isEqualTo(42); } catch (Exception e) { 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 125b56a97..cbb06a784 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 @@ -26,9 +26,12 @@ public class JmxScraperContainer extends GenericContainer { private String serviceUrl; private int intervalMillis; private final Set customYamlFiles; + private String user; + private String password; + private final List extraJars; - public JmxScraperContainer(String otlpEndpoint) { - super("openjdk:8u342-jre-slim"); + public JmxScraperContainer(String otlpEndpoint, String baseImage) { + super(baseImage); String scraperJarPath = System.getProperty("shadow.jar.path"); assertThat(scraperJarPath).isNotNull(); @@ -42,6 +45,7 @@ public JmxScraperContainer(String otlpEndpoint) { this.targetSystems = new HashSet<>(); this.customYamlFiles = new HashSet<>(); this.intervalMillis = 1000; + this.extraJars = new ArrayList<>(); } @CanIgnoreReturnValue @@ -57,11 +61,52 @@ public JmxScraperContainer withIntervalMillis(int intervalMillis) { } @CanIgnoreReturnValue - public JmxScraperContainer withService(String host, int port) { + public JmxScraperContainer withRmiServiceUrl(String host, int port) { // TODO: adding a way to provide 'host:port' syntax would make this easier for end users - this.serviceUrl = + return withServiceUrl( String.format( - Locale.getDefault(), "service:jmx:rmi:///jndi/rmi://%s:%d/jmxrmi", host, port); + Locale.getDefault(), "service:jmx:rmi:///jndi/rmi://%s:%d/jmxrmi", host, port)); + } + + @CanIgnoreReturnValue + public JmxScraperContainer withServiceUrl(String serviceUrl) { + this.serviceUrl = serviceUrl; + return this; + } + + /** + * Sets JMX user login + * + * @param user user login + * @return this + */ + @CanIgnoreReturnValue + public JmxScraperContainer withUser(String user) { + this.user = user; + return this; + } + + /** + * Sets JMX password + * + * @param password user password + * @return this + */ + @CanIgnoreReturnValue + public JmxScraperContainer withPassword(String password) { + this.password = password; + return this; + } + + /** + * Adds path to an extra jar for classpath + * + * @param jarPath path to an extra jar that should be added to jmx scraper classpath + * @return this + */ + @CanIgnoreReturnValue + public JmxScraperContainer withExtraJar(String jarPath) { + this.extraJars.add(jarPath); return this; } @@ -89,6 +134,13 @@ public void start() { arguments.add("-Dotel.jmx.service.url=" + serviceUrl); arguments.add("-Dotel.jmx.interval.milliseconds=" + intervalMillis); + if (user != null) { + arguments.add("-Dotel.jmx.username=" + user); + } + if (password != null) { + arguments.add("-Dotel.jmx.password=" + password); + } + if (!customYamlFiles.isEmpty()) { for (String yaml : customYamlFiles) { this.withCopyFileToContainer(MountableFile.forClasspathResource(yaml), yaml); @@ -96,8 +148,16 @@ public void start() { arguments.add("-Dotel.jmx.config=" + String.join(",", customYamlFiles)); } - arguments.add("-jar"); - arguments.add("/scraper.jar"); + if (extraJars.isEmpty()) { + // using "java -jar" to start + arguments.add("-jar"); + arguments.add("/scraper.jar"); + } else { + // using "java -cp" to start + arguments.add("-cp"); + arguments.add("/scraper.jar:" + String.join(":", extraJars)); + arguments.add("io.opentelemetry.contrib.jmxscraper.JmxScraper"); + } this.withCommand(arguments.toArray(new String[0])); diff --git a/jmx-scraper/src/integrationTest/java/io/opentelemetry/contrib/jmxscraper/TestAppContainer.java b/jmx-scraper/src/integrationTest/java/io/opentelemetry/contrib/jmxscraper/TestAppContainer.java index 990a316c9..649153a41 100644 --- a/jmx-scraper/src/integrationTest/java/io/opentelemetry/contrib/jmxscraper/TestAppContainer.java +++ b/jmx-scraper/src/integrationTest/java/io/opentelemetry/contrib/jmxscraper/TestAppContainer.java @@ -21,7 +21,7 @@ import org.testcontainers.shaded.com.google.errorprone.annotations.CanIgnoreReturnValue; import org.testcontainers.utility.MountableFile; -/** Test container that allows to execute {@link TestApp} in an isolated container */ +/** Test container that allows to execute TestApp in an isolated container */ public class TestAppContainer extends GenericContainer { private final Map properties; @@ -38,8 +38,7 @@ public TestAppContainer() { this.withCopyFileToContainer(MountableFile.forHostPath(appJar), "/app.jar") .waitingFor( - Wait.forLogMessage(TestApp.APP_STARTED_MSG + "\\n", 1) - .withStartupTimeout(Duration.ofSeconds(5))) + Wait.forLogMessage("app started\\n", 1).withStartupTimeout(Duration.ofSeconds(5))) .withCommand("java", "-jar", "/app.jar"); } diff --git a/jmx-scraper/src/integrationTest/java/io/opentelemetry/contrib/jmxscraper/target_systems/ActiveMqIntegrationTest.java b/jmx-scraper/src/integrationTest/java/io/opentelemetry/contrib/jmxscraper/target_systems/ActiveMqIntegrationTest.java index 675c5d39d..5cd1ff608 100644 --- a/jmx-scraper/src/integrationTest/java/io/opentelemetry/contrib/jmxscraper/target_systems/ActiveMqIntegrationTest.java +++ b/jmx-scraper/src/integrationTest/java/io/opentelemetry/contrib/jmxscraper/target_systems/ActiveMqIntegrationTest.java @@ -10,6 +10,7 @@ import static org.assertj.core.api.Assertions.entry; import io.opentelemetry.contrib.jmxscraper.JmxScraperContainer; +import java.nio.file.Path; import java.time.Duration; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.wait.strategy.Wait; @@ -17,6 +18,8 @@ public class ActiveMqIntegrationTest extends TargetSystemIntegrationTest { + private static final int ACTIVEMQ_PORT = 61616; + @Override protected GenericContainer createTargetContainer(int jmxPort) { return new GenericContainer<>( @@ -25,11 +28,13 @@ protected GenericContainer createTargetContainer(int jmxPort) { builder -> builder.from("apache/activemq-classic:5.18.6").build())) .withEnv("JAVA_TOOL_OPTIONS", genericJmxJvmArguments(jmxPort)) .withStartupTimeout(Duration.ofMinutes(2)) - .waitingFor(Wait.forListeningPort()); + .withExposedPorts(ACTIVEMQ_PORT, jmxPort) + .waitingFor(Wait.forListeningPorts(ACTIVEMQ_PORT, jmxPort)); } @Override - protected JmxScraperContainer customizeScraperContainer(JmxScraperContainer scraper) { + protected JmxScraperContainer customizeScraperContainer( + JmxScraperContainer scraper, GenericContainer target, Path tempDir) { return scraper.withTargetSystem("activemq"); } diff --git a/jmx-scraper/src/integrationTest/java/io/opentelemetry/contrib/jmxscraper/target_systems/CassandraIntegrationTest.java b/jmx-scraper/src/integrationTest/java/io/opentelemetry/contrib/jmxscraper/target_systems/CassandraIntegrationTest.java index 3a6608ac2..b909e62f7 100644 --- a/jmx-scraper/src/integrationTest/java/io/opentelemetry/contrib/jmxscraper/target_systems/CassandraIntegrationTest.java +++ b/jmx-scraper/src/integrationTest/java/io/opentelemetry/contrib/jmxscraper/target_systems/CassandraIntegrationTest.java @@ -11,6 +11,7 @@ import static org.assertj.core.api.Assertions.entry; import io.opentelemetry.contrib.jmxscraper.JmxScraperContainer; +import java.nio.file.Path; import java.time.Duration; import java.util.Arrays; import java.util.List; @@ -21,6 +22,8 @@ public class CassandraIntegrationTest extends TargetSystemIntegrationTest { + private static final int CASSANDRA_PORT = 9042; + @Override protected GenericContainer createTargetContainer(int jmxPort) { return new GenericContainer<>("cassandra:5.0.2") @@ -30,11 +33,13 @@ protected GenericContainer createTargetContainer(int jmxPort) { // making cassandra startup faster for single node, from ~1min to ~15s + " -Dcassandra.skip_wait_for_gossip_to_settle=0 -Dcassandra.initial_token=0") .withStartupTimeout(Duration.ofMinutes(2)) - .waitingFor(Wait.forLogMessage(".*Startup complete.*", 1)); + .withExposedPorts(CASSANDRA_PORT, jmxPort) + .waitingFor(Wait.forListeningPorts(CASSANDRA_PORT, jmxPort)); } @Override - protected JmxScraperContainer customizeScraperContainer(JmxScraperContainer scraper) { + protected JmxScraperContainer customizeScraperContainer( + JmxScraperContainer scraper, GenericContainer target, Path tempDir) { return scraper.withTargetSystem("cassandra"); } diff --git a/jmx-scraper/src/integrationTest/java/io/opentelemetry/contrib/jmxscraper/target_systems/JettyIntegrationTest.java b/jmx-scraper/src/integrationTest/java/io/opentelemetry/contrib/jmxscraper/target_systems/JettyIntegrationTest.java index d6d8502b2..fb1f489b1 100644 --- a/jmx-scraper/src/integrationTest/java/io/opentelemetry/contrib/jmxscraper/target_systems/JettyIntegrationTest.java +++ b/jmx-scraper/src/integrationTest/java/io/opentelemetry/contrib/jmxscraper/target_systems/JettyIntegrationTest.java @@ -11,6 +11,7 @@ import static io.opentelemetry.contrib.jmxscraper.target_systems.MetricAssertions.assertSumWithAttributesMultiplePoints; import io.opentelemetry.contrib.jmxscraper.JmxScraperContainer; +import java.nio.file.Path; import java.time.Duration; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.wait.strategy.Wait; @@ -18,6 +19,8 @@ public class JettyIntegrationTest extends TargetSystemIntegrationTest { + private static final int JETTY_PORT = 8080; + @Override protected GenericContainer createTargetContainer(int jmxPort) { GenericContainer container = @@ -39,13 +42,15 @@ protected GenericContainer createTargetContainer(int jmxPort) { container .withEnv("JAVA_OPTIONS", genericJmxJvmArguments(jmxPort)) .withStartupTimeout(Duration.ofMinutes(2)) - .waitingFor(Wait.forLogMessage(".*Started Server.*", 1)); + .withExposedPorts(JETTY_PORT, jmxPort) + .waitingFor(Wait.forListeningPorts(JETTY_PORT, jmxPort)); return container; } @Override - protected JmxScraperContainer customizeScraperContainer(JmxScraperContainer scraper) { + protected JmxScraperContainer customizeScraperContainer( + JmxScraperContainer scraper, GenericContainer target, Path tempDir) { return scraper.withTargetSystem("jetty"); } 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 2b89914f3..d22f27496 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 @@ -11,20 +11,27 @@ import io.opentelemetry.contrib.jmxscraper.JmxScraperContainer; import io.opentelemetry.contrib.jmxscraper.TestAppContainer; +import java.nio.file.Path; import java.util.Arrays; import java.util.List; import org.testcontainers.containers.GenericContainer; +import org.testcontainers.containers.wait.strategy.Wait; public class JvmIntegrationTest extends TargetSystemIntegrationTest { @Override protected GenericContainer createTargetContainer(int jmxPort) { // reusing test application for JVM metrics and custom yaml - return new TestAppContainer().withJmxPort(jmxPort); + //noinspection resource + return new TestAppContainer() + .withJmxPort(jmxPort) + .withExposedPorts(jmxPort) + .waitingFor(Wait.forListeningPorts(jmxPort)); } @Override - protected JmxScraperContainer customizeScraperContainer(JmxScraperContainer scraper) { + protected JmxScraperContainer customizeScraperContainer( + JmxScraperContainer scraper, GenericContainer target, Path tempDir) { return scraper.withTargetSystem("jvm"); } diff --git a/jmx-scraper/src/integrationTest/java/io/opentelemetry/contrib/jmxscraper/target_systems/MetricAssertions.java b/jmx-scraper/src/integrationTest/java/io/opentelemetry/contrib/jmxscraper/target_systems/MetricAssertions.java index 713d2f21b..881ccbf07 100644 --- a/jmx-scraper/src/integrationTest/java/io/opentelemetry/contrib/jmxscraper/target_systems/MetricAssertions.java +++ b/jmx-scraper/src/integrationTest/java/io/opentelemetry/contrib/jmxscraper/target_systems/MetricAssertions.java @@ -87,7 +87,7 @@ static void assertSumWithAttributes( assertThat(metric.getName()).isEqualTo(name); assertThat(metric.getDescription()).isEqualTo(description); assertThat(metric.getUnit()).isEqualTo(unit); - assertThat(metric.hasSum()).isTrue(); + assertThat(metric.hasSum()).describedAs("sum expected").isTrue(); assertThat(metric.getSum().getIsMonotonic()).isEqualTo(isMonotonic); assertAttributedPoints(metric.getSum().getDataPointsList(), attributeGroupAssertions); } diff --git a/jmx-scraper/src/integrationTest/java/io/opentelemetry/contrib/jmxscraper/target_systems/TargetSystemIntegrationTest.java b/jmx-scraper/src/integrationTest/java/io/opentelemetry/contrib/jmxscraper/target_systems/TargetSystemIntegrationTest.java index 4d4ca988b..064e4f021 100644 --- a/jmx-scraper/src/integrationTest/java/io/opentelemetry/contrib/jmxscraper/target_systems/TargetSystemIntegrationTest.java +++ b/jmx-scraper/src/integrationTest/java/io/opentelemetry/contrib/jmxscraper/target_systems/TargetSystemIntegrationTest.java @@ -18,6 +18,7 @@ import io.opentelemetry.proto.collector.metrics.v1.MetricsServiceGrpc; import io.opentelemetry.proto.metrics.v1.Metric; import io.opentelemetry.proto.metrics.v1.ResourceMetrics; +import java.nio.file.Path; import java.time.Duration; import java.util.ArrayList; import java.util.Arrays; @@ -32,6 +33,7 @@ import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.testcontainers.Testcontainers; @@ -97,8 +99,12 @@ void afterEach() { } } + protected String scraperBaseImage() { + return "openjdk:8u342-jre-slim"; + } + @Test - void endToEndTest() { + void endToEndTest(@TempDir Path tmpDir) { target = createTargetContainer(JMX_PORT) @@ -108,12 +114,12 @@ void endToEndTest() { target.start(); scraper = - new JmxScraperContainer(otlpEndpoint) + new JmxScraperContainer(otlpEndpoint, scraperBaseImage()) .withLogConsumer(new Slf4jLogConsumer(jmxScraperLogger)) .withNetwork(network) - .withService(TARGET_SYSTEM_NETWORK_ALIAS, JMX_PORT); + .withRmiServiceUrl(TARGET_SYSTEM_NETWORK_ALIAS, JMX_PORT); - scraper = customizeScraperContainer(scraper); + scraper = customizeScraperContainer(scraper, target, tmpDir); scraper.start(); verifyMetrics(); @@ -156,7 +162,8 @@ protected final void waitAndAssertMetrics(Consumer... assertions) { protected abstract void verifyMetrics(); - protected JmxScraperContainer customizeScraperContainer(JmxScraperContainer scraper) { + protected JmxScraperContainer customizeScraperContainer( + JmxScraperContainer scraper, GenericContainer target, Path tempDir) { return scraper; } diff --git a/jmx-scraper/src/integrationTest/java/io/opentelemetry/contrib/jmxscraper/target_systems/TomcatIntegrationTest.java b/jmx-scraper/src/integrationTest/java/io/opentelemetry/contrib/jmxscraper/target_systems/TomcatIntegrationTest.java index 7b1313a1b..66fb3ec86 100644 --- a/jmx-scraper/src/integrationTest/java/io/opentelemetry/contrib/jmxscraper/target_systems/TomcatIntegrationTest.java +++ b/jmx-scraper/src/integrationTest/java/io/opentelemetry/contrib/jmxscraper/target_systems/TomcatIntegrationTest.java @@ -10,6 +10,7 @@ import static org.assertj.core.api.Assertions.entry; import io.opentelemetry.contrib.jmxscraper.JmxScraperContainer; +import java.nio.file.Path; import java.time.Duration; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.wait.strategy.Wait; @@ -17,6 +18,8 @@ public class TomcatIntegrationTest extends TargetSystemIntegrationTest { + private static final int TOMCAT_PORT = 8080; + @Override protected GenericContainer createTargetContainer(int jmxPort) { return new GenericContainer<>( @@ -32,11 +35,13 @@ protected GenericContainer createTargetContainer(int jmxPort) { .build())) .withEnv("CATALINA_OPTS", genericJmxJvmArguments(jmxPort)) .withStartupTimeout(Duration.ofMinutes(2)) - .waitingFor(Wait.forListeningPort()); + .withExposedPorts(TOMCAT_PORT, jmxPort) + .waitingFor(Wait.forListeningPorts(TOMCAT_PORT, jmxPort)); } @Override - protected JmxScraperContainer customizeScraperContainer(JmxScraperContainer scraper) { + protected JmxScraperContainer customizeScraperContainer( + JmxScraperContainer scraper, GenericContainer target, Path tempDir) { return scraper.withTargetSystem("tomcat"); } diff --git a/jmx-scraper/src/integrationTest/java/io/opentelemetry/contrib/jmxscraper/target_systems/WildflyIntegrationTest.java b/jmx-scraper/src/integrationTest/java/io/opentelemetry/contrib/jmxscraper/target_systems/WildflyIntegrationTest.java new file mode 100644 index 000000000..503622bf8 --- /dev/null +++ b/jmx-scraper/src/integrationTest/java/io/opentelemetry/contrib/jmxscraper/target_systems/WildflyIntegrationTest.java @@ -0,0 +1,200 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.contrib.jmxscraper.target_systems; + +import static io.opentelemetry.contrib.jmxscraper.target_systems.MetricAssertions.assertSum; +import static io.opentelemetry.contrib.jmxscraper.target_systems.MetricAssertions.assertSumWithAttributes; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.entry; + +import io.opentelemetry.contrib.jmxscraper.JmxScraperContainer; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.time.Duration; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.containers.wait.strategy.Wait; +import org.testcontainers.images.builder.ImageFromDockerfile; +import org.testcontainers.utility.MountableFile; + +public class WildflyIntegrationTest extends TargetSystemIntegrationTest { + + private static final int WILDFLY_SERVICE_PORT = 8080; + private static final int WILDFLY_MANAGEMENT_PORT = 9990; + + @Override + protected GenericContainer createTargetContainer(int jmxPort) { + // JMX port is ignored here as we are using HTTP management interface + + String appWar = System.getProperty("app.war.path"); + Path appWarPath = Paths.get(appWar); + assertThat(appWarPath).isNotEmptyFile().isReadable(); + + return new GenericContainer<>( + new ImageFromDockerfile() + .withDockerfileFromBuilder( + builder -> + builder + .from("quay.io/wildfly/wildfly:32.0.1.Final-jdk11") + // user/pwd needed for remote JMX access + .run("/opt/jboss/wildfly/bin/add-user.sh user password --silent") + // standalone with management (HTTP) interface enabled + .cmd( + "/opt/jboss/wildfly/bin/standalone.sh -b 0.0.0.0 -bmanagement 0.0.0.0") + .expose(WILDFLY_SERVICE_PORT, WILDFLY_MANAGEMENT_PORT) + .build())) + .withCopyFileToContainer( + MountableFile.forHostPath(appWarPath), + "/opt/jboss/wildfly/standalone/deployments/testapp.war") + .withStartupTimeout(Duration.ofMinutes(2)) + .withExposedPorts(WILDFLY_SERVICE_PORT, WILDFLY_MANAGEMENT_PORT) + .waitingFor(Wait.forListeningPorts(WILDFLY_SERVICE_PORT, WILDFLY_MANAGEMENT_PORT)); + } + + @Override + protected String scraperBaseImage() { + // we need to run the scraper with Java 11+ because jboss client jar is using Java 11 + return "eclipse-temurin:11.0.25_9-jdk-noble"; + } + + @Override + protected JmxScraperContainer customizeScraperContainer( + JmxScraperContainer scraper, GenericContainer target, Path tempDir) { + + Path tempJbossClient; + // copy jboss-client.jar from jboss/wildfly container + try { + tempJbossClient = Files.createTempFile(tempDir, "jboss_", "_test").toAbsolutePath(); + target.copyFileFromContainer( + "/opt/jboss/wildfly/bin/client/jboss-client.jar", tempJbossClient.toString()); + } catch (IOException e) { + throw new IllegalStateException(e); + } + + return scraper + .withTargetSystem("wildfly") + // Copy jboss-client.jar and add it to scraper classpath + .withCopyFileToContainer(MountableFile.forHostPath(tempJbossClient), "/jboss-client.jar") + .withExtraJar("/jboss-client.jar") + // Using jboss remote HTTP protocol provided in jboss-client.jar + .withServiceUrl("service:jmx:remote+http://targetsystem:" + WILDFLY_MANAGEMENT_PORT) + // Admin user created when creating container + // When scraper is running on same host as jboss/wildfly a local file challenge can be used + // for authentication, but here we have to use valid credentials for remote access + .withUser("user") + .withPassword("password"); + } + + @Override + protected void verifyMetrics() { + waitAndAssertMetrics( + metric -> + assertSumWithAttributes( + metric, + "wildfly.session.count", + "The number of sessions created.", + "{session}", + attrs -> attrs.containsOnly(entry("deployment", "testapp.war"))), + metric -> + assertSumWithAttributes( + metric, + "wildfly.session.active", + "The number of currently active sessions.", + "{session}", + /* isMonotonic= */ false, + attrs -> attrs.containsOnly(entry("deployment", "testapp.war"))), + metric -> + assertSumWithAttributes( + metric, + "wildfly.session.expired", + "The number of sessions that have expired.", + "{session}", + attrs -> attrs.containsOnly(entry("deployment", "testapp.war"))), + metric -> + assertSumWithAttributes( + metric, + "wildfly.session.rejected", + "The number of sessions that have been rejected.", + "{session}", + attrs -> attrs.containsOnly(entry("deployment", "testapp.war"))), + metric -> + assertSumWithAttributes( + metric, + "wildfly.request.count", + "The number of requests received.", + "{request}", + attrs -> + attrs.containsOnly( + entry("server", "default-server"), entry("listener", "default"))), + metric -> + assertSumWithAttributes( + metric, + "wildfly.request.time", + "The total amount of time spent on requests.", + "ns", + attrs -> + attrs.containsOnly( + entry("server", "default-server"), entry("listener", "default"))), + metric -> + assertSumWithAttributes( + metric, + "wildfly.request.server_error", + "The number of requests that have resulted in a 5xx response.", + "{request}", + attrs -> + attrs.containsOnly( + entry("server", "default-server"), entry("listener", "default"))), + metric -> + assertSumWithAttributes( + metric, + "wildfly.network.io", + "The number of bytes transmitted.", + "By", + attrs -> + attrs.containsOnly( + entry("server", "default-server"), + entry("listener", "default"), + entry("state", "in")), + attrs -> + attrs.containsOnly( + entry("server", "default-server"), + entry("listener", "default"), + entry("state", "out"))), + metric -> + assertSumWithAttributes( + metric, + "wildfly.jdbc.connection.open", + "The number of open jdbc connections.", + "{connection}", + attrs -> + attrs.containsOnly(entry("data_source", "ExampleDS"), entry("state", "active")), + attrs -> + attrs.containsOnly(entry("data_source", "ExampleDS"), entry("state", "idle"))), + metric -> + assertSumWithAttributes( + metric, + "wildfly.jdbc.request.wait", + "The number of jdbc connections that had to wait before opening.", + "{request}", + attrs -> attrs.containsOnly(entry("data_source", "ExampleDS"))), + metric -> + assertSum( + metric, + "wildfly.jdbc.transaction.count", + "The number of transactions created.", + "{transaction}"), + metric -> + assertSumWithAttributes( + metric, + "wildfly.jdbc.rollback.count", + "The number of transactions rolled back.", + "{transaction}", + attrs -> attrs.containsOnly(entry("cause", "system")), + attrs -> attrs.containsOnly(entry("cause", "resource")), + attrs -> attrs.containsOnly(entry("cause", "application")))); + } +} diff --git a/jmx-scraper/src/main/java/io/opentelemetry/contrib/jmxscraper/JmxConnectorBuilder.java b/jmx-scraper/src/main/java/io/opentelemetry/contrib/jmxscraper/JmxConnectorBuilder.java index eebca5fd4..5f34129ed 100644 --- a/jmx-scraper/src/main/java/io/opentelemetry/contrib/jmxscraper/JmxConnectorBuilder.java +++ b/jmx-scraper/src/main/java/io/opentelemetry/contrib/jmxscraper/JmxConnectorBuilder.java @@ -31,7 +31,7 @@ public class JmxConnectorBuilder { private static final Logger logger = Logger.getLogger(JmxConnectorBuilder.class.getName()); private final JMXServiceURL url; - @Nullable private String userName; + @Nullable private String user; @Nullable private String password; @Nullable private String profile; @Nullable private String realm; @@ -50,8 +50,13 @@ public static JmxConnectorBuilder createNew(String url) { } @CanIgnoreReturnValue - public JmxConnectorBuilder userCredentials(String userName, String password) { - this.userName = userName; + public JmxConnectorBuilder withUser(String user) { + this.user = user; + return this; + } + + @CanIgnoreReturnValue + public JmxConnectorBuilder withPassword(String password) { this.password = password; return this; } @@ -97,8 +102,8 @@ public JMXConnector build() throws IOException { private Map buildEnv() { Map env = new HashMap<>(); - if (userName != null && password != null) { - env.put(JMXConnector.CREDENTIALS, new String[] {userName, password}); + if (user != null && password != null) { + env.put(JMXConnector.CREDENTIALS, new String[] {user, password}); } if (profile != null) { @@ -118,7 +123,7 @@ private Map buildEnv() { callbacks -> { for (Callback callback : callbacks) { if (callback instanceof NameCallback) { - ((NameCallback) callback).setName(userName); + ((NameCallback) callback).setName(user); } else if (callback instanceof PasswordCallback) { char[] pwd = password == null ? null : password.toCharArray(); ((PasswordCallback) callback).setPassword(pwd); 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 fdee1e0e4..fe82c698d 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 @@ -19,6 +19,7 @@ import java.util.Arrays; import java.util.Collections; import java.util.List; +import java.util.Optional; import java.util.Properties; import java.util.concurrent.atomic.AtomicBoolean; import java.util.logging.Logger; @@ -61,8 +62,12 @@ public static void main(String[] args) { JmxMetricInsight service = JmxMetricInsight.createService( GlobalOpenTelemetry.get(), config.getIntervalMilliseconds()); - JmxScraper jmxScraper = - new JmxScraper(JmxConnectorBuilder.createNew(config.getServiceUrl()), service, config); + JmxConnectorBuilder connectorBuilder = JmxConnectorBuilder.createNew(config.getServiceUrl()); + + Optional.ofNullable(config.getUsername()).ifPresent(connectorBuilder::withUser); + Optional.ofNullable(config.getPassword()).ifPresent(connectorBuilder::withPassword); + + JmxScraper jmxScraper = new JmxScraper(connectorBuilder, service, config); jmxScraper.start(); } catch (ArgumentsParsingException e) { diff --git a/jmx-scraper/src/main/resources/wildfly.yaml b/jmx-scraper/src/main/resources/wildfly.yaml new file mode 100644 index 000000000..687699407 --- /dev/null +++ b/jmx-scraper/src/main/resources/wildfly.yaml @@ -0,0 +1,104 @@ +--- + +rules: + - bean: jboss.as:deployment=*,subsystem=undertow + metricAttribute: + deployment: param(deployment) + prefix: wildfly.session. + unit: "{session}" + mapping: + sessionsCreated: + metric: count + type: counter + desc: The number of sessions created. + activeSessions: + metric: active + type: updowncounter + desc: The number of currently active sessions. + expiredSessions: + metric: expired + type: counter + desc: The number of sessions that have expired. + rejectedSessions: + metric: rejected + type: counter + desc: The number of sessions that have been rejected. + + - bean: jboss.as:subsystem=undertow,server=*,http-listener=* + metricAttribute: + server: param(server) + listener: param(http-listener) + prefix: wildfly. + type: counter + mapping: + requestCount: + metric: request.count + unit: "{request}" + desc: The number of requests received. + processingTime: + metric: request.time + unit: ns + desc: The total amount of time spent on requests. + errorCount: + metric: request.server_error + unit: "{request}" + desc: The number of requests that have resulted in a 5xx response. + bytesSent: + metric: &metric network.io + unit: &unit By + desc: &desc "The number of bytes transmitted." + metricAttribute: + state: const(out) + bytesReceived: + metric: *metric + unit: *unit + desc: *desc + metricAttribute: + state: const(in) + + - bean: jboss.as:subsystem=datasources,data-source=*,statistics=pool + metricAttribute: + data_source: param(data-source) + type: counter + prefix: wildfly.jdbc. + mapping: + ActiveCount: + metric: &metric connection.open + unit: &unit "{connection}" + desc: &desc The number of open jdbc connections. + metricAttribute: + state: const(active) + IdleCount: + metric: *metric + unit: *unit + desc: *desc + metricAttribute: + state: const(idle) + WaitCount: + metric: request.wait + unit: "{request}" + desc: The number of jdbc connections that had to wait before opening. + + - bean: jboss.as:subsystem=transactions + prefix: wildfly.jdbc. + unit: "{transaction}" + type: counter + mapping: + numberOfTransactions: + metric: transaction.count + desc: The number of transactions created. + numberOfSystemRollbacks: + metric: rollback.count + desc: The number of transactions rolled back. + metricAttribute: + cause: const(system) + numberOfResourceRollbacks: + metric: rollback.count + desc: The number of transactions rolled back. + metricAttribute: + cause: const(resource) + numberOfApplicationRollbacks: + metric: rollback.count + desc: The number of transactions rolled back. + metricAttribute: + cause: const(application) diff --git a/jmx-scraper/test-app/README.md b/jmx-scraper/test-app/README.md new file mode 100644 index 000000000..2aa7b5c94 --- /dev/null +++ b/jmx-scraper/test-app/README.md @@ -0,0 +1,4 @@ +# test-app + +This is a test CLI application that is only used for integration tests. +The goal of this application is to publish custom MBeans in JMX and then allow end-to-end testing. diff --git a/jmx-scraper/test-app/build.gradle.kts b/jmx-scraper/test-app/build.gradle.kts new file mode 100644 index 000000000..721f6fed7 --- /dev/null +++ b/jmx-scraper/test-app/build.gradle.kts @@ -0,0 +1,19 @@ +plugins { + application +} + +description = "JMX metrics scraper - test application" + +java { + toolchain { + languageVersion = JavaLanguageVersion.of(8) + } +} + +tasks { + jar { + manifest { + attributes["Main-Class"] = "io.opentelemetry.contrib.jmxscraper.testapp.TestApp" + } + } +} diff --git a/jmx-scraper/src/integrationTest/java/io/opentelemetry/contrib/jmxscraper/TestApp.java b/jmx-scraper/test-app/src/main/java/io/opentelemetry/contrib/jmxscraper/testapp/TestApp.java similarity index 73% rename from jmx-scraper/src/integrationTest/java/io/opentelemetry/contrib/jmxscraper/TestApp.java rename to jmx-scraper/test-app/src/main/java/io/opentelemetry/contrib/jmxscraper/testapp/TestApp.java index 1316ca036..9883abceb 100644 --- a/jmx-scraper/src/integrationTest/java/io/opentelemetry/contrib/jmxscraper/TestApp.java +++ b/jmx-scraper/test-app/src/main/java/io/opentelemetry/contrib/jmxscraper/testapp/TestApp.java @@ -3,17 +3,14 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.contrib.jmxscraper; +package io.opentelemetry.contrib.jmxscraper.testapp; import java.lang.management.ManagementFactory; import javax.management.MBeanServer; import javax.management.ObjectName; -@SuppressWarnings("all") -public class TestApp implements TestAppMXBean { - - public static final String APP_STARTED_MSG = "app started"; - public static final String OBJECT_NAME = "io.opentelemetry.test:name=TestApp"; +@SuppressWarnings("all") // for busy wait + stdout +public class TestApp implements TestAppMxBean { private volatile boolean running; @@ -34,13 +31,13 @@ static TestApp start() { TestApp app = new TestApp(); MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); try { - ObjectName objectName = new ObjectName(OBJECT_NAME); + ObjectName objectName = new ObjectName("io.opentelemetry.test:name=TestApp"); mbs.registerMBean(app, objectName); } catch (Exception e) { throw new RuntimeException(e); } app.running = true; - System.out.println(APP_STARTED_MSG); + System.out.println("app started"); return app; } diff --git a/jmx-scraper/src/integrationTest/java/io/opentelemetry/contrib/jmxscraper/TestAppMXBean.java b/jmx-scraper/test-app/src/main/java/io/opentelemetry/contrib/jmxscraper/testapp/TestAppMxBean.java similarity index 55% rename from jmx-scraper/src/integrationTest/java/io/opentelemetry/contrib/jmxscraper/TestAppMXBean.java rename to jmx-scraper/test-app/src/main/java/io/opentelemetry/contrib/jmxscraper/testapp/TestAppMxBean.java index 11ea69905..3038b46b5 100644 --- a/jmx-scraper/src/integrationTest/java/io/opentelemetry/contrib/jmxscraper/TestAppMXBean.java +++ b/jmx-scraper/test-app/src/main/java/io/opentelemetry/contrib/jmxscraper/testapp/TestAppMxBean.java @@ -3,10 +3,13 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.contrib.jmxscraper; +package io.opentelemetry.contrib.jmxscraper.testapp; +import javax.management.MXBean; + +@MXBean @SuppressWarnings("unused") -public interface TestAppMXBean { +public interface TestAppMxBean { int getIntValue(); diff --git a/jmx-scraper/test-webapp/README.md b/jmx-scraper/test-webapp/README.md new file mode 100644 index 000000000..f7c3af0e0 --- /dev/null +++ b/jmx-scraper/test-webapp/README.md @@ -0,0 +1,4 @@ +# test-webapp + +This is a test web application that is only used for integration tests when metrics are published +only when an application is deployed to the container, for example with Wildfly session metrics. diff --git a/jmx-scraper/test-webapp/build.gradle.kts b/jmx-scraper/test-webapp/build.gradle.kts new file mode 100644 index 000000000..ac863095b --- /dev/null +++ b/jmx-scraper/test-webapp/build.gradle.kts @@ -0,0 +1,15 @@ +plugins { + war +} + +description = "JMX metrics scraper - test web application" + +dependencies { + providedCompile("jakarta.servlet:jakarta.servlet-api:5.0.0") +} + +java { + toolchain { + languageVersion = JavaLanguageVersion.of(8) + } +} diff --git a/jmx-scraper/test-webapp/src/main/java/io/opentelemetry/contrib/jmxscraper/testwebapp/SimpleServlet.java b/jmx-scraper/test-webapp/src/main/java/io/opentelemetry/contrib/jmxscraper/testwebapp/SimpleServlet.java new file mode 100644 index 000000000..01feb7332 --- /dev/null +++ b/jmx-scraper/test-webapp/src/main/java/io/opentelemetry/contrib/jmxscraper/testwebapp/SimpleServlet.java @@ -0,0 +1,24 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.contrib.jmxscraper.testwebapp; + +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.io.PrintWriter; + +public class SimpleServlet extends HttpServlet { + + private static final long serialVersionUID = 3726145372238690099L; + + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException { + try (PrintWriter out = resp.getWriter()) { + out.write("hello!"); + } + } +} diff --git a/jmx-scraper/test-webapp/src/main/webapp/WEB-INF/web.xml b/jmx-scraper/test-webapp/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 000000000..5ede05330 --- /dev/null +++ b/jmx-scraper/test-webapp/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,14 @@ + + + servlet_name + io.opentelemetry.contrib.jmxscraper.testwebapp.SimpleServlet + + + servlet_name + /* + + diff --git a/settings.gradle.kts b/settings.gradle.kts index c2a3b27e5..d6489ed97 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -72,6 +72,8 @@ include(":jfr-events") include(":jfr-connection") include(":jmx-metrics") include(":jmx-scraper") +include(":jmx-scraper:test-app") +include(":jmx-scraper:test-webapp") include(":maven-extension") include(":micrometer-meter-provider") include(":noop-api")