diff --git a/instrumentation/jmx-metrics/README.md b/instrumentation/jmx-metrics/README.md index 44db4b4b5902..6aa194a8ad0e 100644 --- a/instrumentation/jmx-metrics/README.md +++ b/instrumentation/jmx-metrics/README.md @@ -30,7 +30,7 @@ No targets are enabled by default. The supported target environments are listed - [jetty](library/jetty.md) - [kafka-broker](javaagent/kafka-broker.md) - [tomcat](library/tomcat.md) -- [wildfly](javaagent/wildfly.md) +- [wildfly](library/wildfly.md) - [hadoop](javaagent/hadoop.md) The [jvm](library/jvm.md) metrics definitions are also included in the [jmx-metrics library](./library) diff --git a/instrumentation/jmx-metrics/javaagent/src/main/resources/jmx/rules/wildfly.yaml b/instrumentation/jmx-metrics/javaagent/src/main/resources/jmx/rules/wildfly.yaml deleted file mode 100644 index b6d4c543257b..000000000000 --- a/instrumentation/jmx-metrics/javaagent/src/main/resources/jmx/rules/wildfly.yaml +++ /dev/null @@ -1,83 +0,0 @@ ---- -rules: - - bean: jboss.as:deployment=*,subsystem=undertow - metricAttribute: - deployment: param(deployment) - prefix: wildfly.session. - type: counter - unit: "1" - mapping: - sessionsCreated: - activeSessions: - type: updowncounter - expiredSessions: - rejectedSessions: - - bean: jboss.as:subsystem=undertow,server=*,http-listener=* - metricAttribute: - server: param(server) - listener: param(http-listener) - prefix: wildfly.request. - type: counter - unit: "1" - mapping: - requestCount: - processingTime: - unit: ns - errorCount: - - bean: jboss.as:subsystem=undertow,server=*,http-listener=* - metricAttribute: - server: param(server) - listener: param(http-listener) - type: counter - unit: By - mapping: - bytesSent: - metric: wildfly.network.io - desc: Total number of bytes transferred - metricAttribute: - direction: const(out) - bytesReceived: - metric: wildfly.network.io - desc: Total number of bytes transferred - metricAttribute: - direction: const(in) - - bean: jboss.as:subsystem=datasources,data-source=*,statistics=pool - unit: "1" - metricAttribute: - data_source: param(data-source) - mapping: - ActiveCount: - metric: wildfly.db.client.connections.usage - metricAttribute: - state: const(used) - desc: The number of open jdbc connections - IdleCount: - metric: wildfly.db.client.connections.usage - metricAttribute: - state: const(idle) - desc: The number of open jdbc connections - WaitCount: - metric: wildfly.db.client.connections.WaitCount - type: counter - - bean: jboss.as:subsystem=transactions - type: counter - prefix: wildfly.db.client. - unit: "{transactions}" - mapping: - numberOfTransactions: - metric: transaction.NumberOfTransactions - numberOfApplicationRollbacks: - metric: rollback.count - metricAttribute: - cause: const(application) - desc: The total number of transactions rolled back - numberOfResourceRollbacks: - metric: rollback.count - metricAttribute: - cause: const(resource) - desc: The total number of transactions rolled back - numberOfSystemRollbacks: - metric: rollback.count - metricAttribute: - cause: const(system) - desc: The total number of transactions rolled back diff --git a/instrumentation/jmx-metrics/javaagent/src/test/java/io/opentelemetry/instrumentation/javaagent/jmx/JmxMetricInsightInstallerTest.java b/instrumentation/jmx-metrics/javaagent/src/test/java/io/opentelemetry/instrumentation/javaagent/jmx/JmxMetricInsightInstallerTest.java index c12eb6faec2f..50c029cf8b79 100644 --- a/instrumentation/jmx-metrics/javaagent/src/test/java/io/opentelemetry/instrumentation/javaagent/jmx/JmxMetricInsightInstallerTest.java +++ b/instrumentation/jmx-metrics/javaagent/src/test/java/io/opentelemetry/instrumentation/javaagent/jmx/JmxMetricInsightInstallerTest.java @@ -32,8 +32,7 @@ class JmxMetricInsightInstallerTest { private static final String PATH_TO_ALL_EXISTING_RULES = "src/main/resources/jmx/rules"; private static final Set FILES_TO_BE_TESTED = new HashSet<>( - Arrays.asList( - "activemq.yaml", "camel.yaml", "hadoop.yaml", "kafka-broker.yaml", "wildfly.yaml")); + Arrays.asList("activemq.yaml", "camel.yaml", "hadoop.yaml", "kafka-broker.yaml")); @Test void testToVerifyExistingRulesAreValid() throws Exception { diff --git a/instrumentation/jmx-metrics/javaagent/wildfly.md b/instrumentation/jmx-metrics/javaagent/wildfly.md deleted file mode 100644 index 637453a4ee33..000000000000 --- a/instrumentation/jmx-metrics/javaagent/wildfly.md +++ /dev/null @@ -1,18 +0,0 @@ -# Wildfly Metrics - -Here is the list of metrics based on MBeans exposed by Wildfly. - -| Metric Name | Type | Attributes | Description | -| -------------------------------------------------- | ------------- | ------------------ | ----------------------------------------------------------------------- | -| wildfly.network.io | Counter | direction, server | Total number of bytes transferred | -| wildfly.request.errorCount | Counter | server, listener | The number of 500 responses that have been sent by this listener | -| wildfly.request.requestCount | Counter | server, listener | The number of requests this listener has served | -| wildfly.request.processingTime | Counter | server, listener | The total processing time of all requests handed by this listener | -| wildfly.session.expiredSession | Counter | deployment | Number of sessions that have expired | -| wildfly.session.rejectedSessions | Counter | deployment | Number of rejected sessions | -| wildfly.session.sessionsCreated | Counter | deployment | Total sessions created | -| wildfly.session.activeSessions | UpDownCounter | deployment | Number of active sessions | -| wildfly.db.client.connections.usage | Gauge | data_source, state | The number of open jdbc connections | -| wildfly.db.client.connections.WaitCount | Counter | data_source | The number of requests that had to wait to obtain a physical connection | -| wildfly.db.client.rollback.count | Counter | cause | The total number of transactions rolled back | -| wildfly.db.client.transaction.NumberOfTransactions | Counter | | The total number of transactions (top-level and nested) created | diff --git a/instrumentation/jmx-metrics/library/build.gradle.kts b/instrumentation/jmx-metrics/library/build.gradle.kts index 86f0cb0515a8..059b4615dcaa 100644 --- a/instrumentation/jmx-metrics/library/build.gradle.kts +++ b/instrumentation/jmx-metrics/library/build.gradle.kts @@ -21,15 +21,20 @@ tasks { test { // get packaged agent jar for testing val shadowTask = project(":javaagent").tasks.named("shadowJar").get() - dependsOn(shadowTask) + val testAppTask = project(":instrumentation:jmx-metrics:testing-webapp").tasks.named("war") + dependsOn(testAppTask) + inputs.files(layout.files(shadowTask)) .withPropertyName("javaagent") .withNormalizer(ClasspathNormalizer::class) doFirst { - jvmArgs("-Dio.opentelemetry.javaagent.path=${shadowTask.archiveFile.get()}") + jvmArgs( + "-Dio.opentelemetry.javaagent.path=${shadowTask.archiveFile.get()}", + "-Dio.opentelemetry.testapp.path=${testAppTask.get().archiveFile.get().asFile.absolutePath}" + ) } } } diff --git a/instrumentation/jmx-metrics/library/src/main/resources/jmx/rules/wildfly.yaml b/instrumentation/jmx-metrics/library/src/main/resources/jmx/rules/wildfly.yaml new file mode 100644 index 000000000000..aa08c1a5469e --- /dev/null +++ b/instrumentation/jmx-metrics/library/src/main/resources/jmx/rules/wildfly.yaml @@ -0,0 +1,147 @@ +--- +rules: + + - bean: jboss.as:deployment=*,subsystem=undertow + prefix: wildfly.session. + metricAttribute: + wildfly.deployment: param(deployment) + unit: "{session}" + mapping: + # wildfly.session.created + sessionsCreated: + metric: created + type: counter + desc: The number of sessions created + # wildfly.session.active.count + activeSessions: + metric: active.count + type: updowncounter + desc: The number of active sessions + # wildfly.session.active.limit + maxActiveSessions: + metric: active.limit + type: updowncounter + desc: The maximum number of active sessions + # discard negative values used to indicate absence of limit + dropNegativeValues: true + # wildfly.session.expired + expiredSessions: + metric: expired + type: counter + desc: The number of expired sessions + # wildfly.session.rejected + rejectedSessions: + metric: rejected + type: counter + desc: The number of rejected sessions + + - bean: jboss.as:subsystem=undertow,server=*,http-listener=* + prefix: wildfly. + metricAttribute: + wildfly.server: param(server) + wildfly.listener: param(http-listener) + type: counter + mapping: + # wildfly.request.count + requestCount: + metric: request.count + unit: "{request}" + desc: The number of requests served + # wildfly.request.duration.sum + processingTime: + metric: request.duration.sum + sourceUnit: ns + unit: s + desc: The total amount of time spent processing requests + # wildfly.error.count + errorCount: + metric: error.count + unit: "{request}" + desc: The number of requests that have resulted in a 5xx response + + # wildly.network.io + - bean: jboss.as:subsystem=undertow,server=*,http-listener=* + metricAttribute: + wildfly.server: param(server) + wildfly.listener: param(http-listener) + type: counter + unit: By + mapping: + bytesSent: + metric: &metric wildfly.network.io + desc: &desc Total number of bytes transferred + metricAttribute: + network.io.direction: const(transmit) + bytesReceived: + metric: *metric + desc: *desc + metricAttribute: + network.io.direction: const(receive) + + - bean: jboss.as:subsystem=datasources,data-source=*,statistics=pool + prefix: wildfly.db.client.connection. + metricAttribute: + db.client.connection.pool.name: param(data-source) + mapping: + # wildfly.db.client.connection.count + ActiveCount: + metric: &metric count + type: &type updowncounter + unit: &unit "{connection}" + desc: &desc The number of open physical database connections + metricAttribute: + db.client.connection.state: const(used) + IdleCount: + metric: *metric + type: *type + unit: *unit + desc: *desc + metricAttribute: + db.client.connection.state: const(idle) + # wildfly.db.client.connection.wait.count + WaitCount: + metric: wait.count + type: counter + # In this context, 'request' means 'connection request' + unit: "{request}" + desc: The number of connection requests that had to wait to obtain it + + - bean: jboss.as:subsystem=transactions + prefix: wildfly.transaction. + unit: "{transaction}" + mapping: + # wildfly.transaction.count + numberOfInflightTransactions: + metric: count + type: updowncounter + desc: The number of in-flight transactions + # wildfly.transaction.created + numberOfTransactions: + metric: created + type: counter + desc: The total number of transactions created + # wildfly.transaction.committed + numberOfCommittedTransactions: + metric: committed + type: counter + desc: The total number of transactions committed + # wildfly.transaction.rollback + numberOfApplicationRollbacks: + metric: &metric rollback + type: &type counter + metricAttribute: + wildfly.rollback.cause: const(application) + desc: &desc The total number of transactions rolled back + numberOfResourceRollbacks: + metric: *metric + type: *type + metricAttribute: + wildfly.rollback.cause: const(resource) + desc: *desc + numberOfSystemRollbacks: + metric: *metric + type: *type + metricAttribute: + wildfly.rollback.cause: const(system) + desc: *desc + diff --git a/instrumentation/jmx-metrics/library/src/test/java/io/opentelemetry/instrumentation/jmx/rules/JettyIntegrationTest.java b/instrumentation/jmx-metrics/library/src/test/java/io/opentelemetry/instrumentation/jmx/rules/JettyTest.java similarity index 86% rename from instrumentation/jmx-metrics/library/src/test/java/io/opentelemetry/instrumentation/jmx/rules/JettyIntegrationTest.java rename to instrumentation/jmx-metrics/library/src/test/java/io/opentelemetry/instrumentation/jmx/rules/JettyTest.java index 22fd3ad378e3..01fd575c3af3 100644 --- a/instrumentation/jmx-metrics/library/src/test/java/io/opentelemetry/instrumentation/jmx/rules/JettyIntegrationTest.java +++ b/instrumentation/jmx-metrics/library/src/test/java/io/opentelemetry/instrumentation/jmx/rules/JettyTest.java @@ -19,9 +19,8 @@ import org.junit.jupiter.params.provider.ValueSource; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.wait.strategy.Wait; -import org.testcontainers.images.builder.ImageFromDockerfile; -public class JettyIntegrationTest extends TargetSystemTest { +public class JettyTest extends TargetSystemTest { private static final int JETTY_PORT = 8080; @@ -48,25 +47,20 @@ void testCollectedMetrics(int jettyMajorVersion) { // with older versions deployment and session management are available by default jettyModules.add("stats"); } - String addModulesArg = "--add-to-startd=" + String.join(",", jettyModules); + String moduleArg = "--module=" + String.join(",", jettyModules); GenericContainer container = - new GenericContainer<>( - new ImageFromDockerfile() - .withDockerfileFromBuilder( - builder -> - builder - .from("jetty:" + jettyMajorVersion) - .run("java", "-jar", "/usr/local/jetty/start.jar", addModulesArg) - .run("mkdir -p /var/lib/jetty/webapps/ROOT/") - .run("touch /var/lib/jetty/webapps/ROOT/index.html") - .build())) + new GenericContainer<>("jetty:" + jettyMajorVersion) + .withCommand(moduleArg) .withEnv("JAVA_OPTIONS", String.join(" ", jvmArgs)) .withStartupTimeout(Duration.ofMinutes(2)) .withExposedPorts(JETTY_PORT) .waitingFor(Wait.forListeningPorts(JETTY_PORT)); - copyFilesToTarget(container, yamlFiles); + copyAgentToTarget(container); + copyYamlFilesToTarget(container, yamlFiles); + // Deploy example web application for session-related metrics + copyTestWebAppToTarget(container, "/var/lib/jetty/webapps/ROOT.war"); startTarget(container); diff --git a/instrumentation/jmx-metrics/library/src/test/java/io/opentelemetry/instrumentation/jmx/rules/JvmTargetSystemTest.java b/instrumentation/jmx-metrics/library/src/test/java/io/opentelemetry/instrumentation/jmx/rules/JvmTest.java similarity index 98% rename from instrumentation/jmx-metrics/library/src/test/java/io/opentelemetry/instrumentation/jmx/rules/JvmTargetSystemTest.java rename to instrumentation/jmx-metrics/library/src/test/java/io/opentelemetry/instrumentation/jmx/rules/JvmTest.java index 99b7a1d56585..1bba1c62b1b6 100644 --- a/instrumentation/jmx-metrics/library/src/test/java/io/opentelemetry/instrumentation/jmx/rules/JvmTargetSystemTest.java +++ b/instrumentation/jmx-metrics/library/src/test/java/io/opentelemetry/instrumentation/jmx/rules/JvmTest.java @@ -20,7 +20,7 @@ import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.wait.strategy.Wait; -class JvmTargetSystemTest extends TargetSystemTest { +class JvmTest extends TargetSystemTest { @ParameterizedTest @ValueSource( @@ -47,7 +47,8 @@ void testJvmMetrics(String image) { .withExposedPorts(8080) .waitingFor(Wait.forListeningPorts(8080)); - copyFilesToTarget(target, yamlFiles); + copyAgentToTarget(target); + copyYamlFilesToTarget(target, yamlFiles); startTarget(target); diff --git a/instrumentation/jmx-metrics/library/src/test/java/io/opentelemetry/instrumentation/jmx/rules/TargetSystemTest.java b/instrumentation/jmx-metrics/library/src/test/java/io/opentelemetry/instrumentation/jmx/rules/TargetSystemTest.java index baf7b6540d01..4facd775f965 100644 --- a/instrumentation/jmx-metrics/library/src/test/java/io/opentelemetry/instrumentation/jmx/rules/TargetSystemTest.java +++ b/instrumentation/jmx-metrics/library/src/test/java/io/opentelemetry/instrumentation/jmx/rules/TargetSystemTest.java @@ -56,11 +56,13 @@ public class TargetSystemTest { private static final Logger targetSystemLogger = LoggerFactory.getLogger("targetSystem"); private static final String AGENT_PATH = "/opentelemetry-instrumentation-javaagent.jar"; + protected static final String APP_PATH = "/testapp.war"; private static final Network network = Network.newNetwork(); private static OtlpGrpcServer otlpServer; private static Path agentPath; + private static Path testAppPath; private static String otlpEndpoint; private GenericContainer targetSystem; @@ -73,10 +75,16 @@ static void beforeAll() { Testcontainers.exposeHostPorts(otlpServer.httpPort()); otlpEndpoint = "http://host.testcontainers.internal:" + otlpServer.httpPort(); - String path = System.getProperty("io.opentelemetry.javaagent.path"); - assertThat(path).isNotNull(); - agentPath = Paths.get(path); - assertThat(agentPath).isReadable().isNotEmptyFile(); + TargetSystemTest.agentPath = getArtifactPath("io.opentelemetry.javaagent.path"); + TargetSystemTest.testAppPath = getArtifactPath("io.opentelemetry.testapp.path"); + } + + private static Path getArtifactPath(String systemProperty) { + String pathValue = System.getProperty(systemProperty); + assertThat(pathValue).isNotNull(); + Path path = Paths.get(pathValue); + assertThat(path).isReadable().isNotEmptyFile(); + return path; } @BeforeEach @@ -182,11 +190,12 @@ protected void startTarget( targetSystem.start(); } - protected static void copyFilesToTarget(GenericContainer target, List yamlFiles) { - // copy agent to target system + protected static void copyAgentToTarget(GenericContainer target) { + logger.info("copying java agent {} to container {}", agentPath, AGENT_PATH); target.withCopyFileToContainer(MountableFile.forHostPath(agentPath), AGENT_PATH); + } - // copy yaml files to target system + protected static void copyYamlFilesToTarget(GenericContainer target, List yamlFiles) { for (String file : yamlFiles) { String resourcePath = yamlResourcePath(file); String destPath = containerYamlPath(file); @@ -195,6 +204,11 @@ protected static void copyFilesToTarget(GenericContainer target, List } } + protected static void copyTestWebAppToTarget(GenericContainer target, String targetPath) { + logger.info("copying test application {} to container {}", testAppPath, targetPath); + target.withCopyFileToContainer(MountableFile.forHostPath(testAppPath), targetPath); + } + private static String yamlResourcePath(String yaml) { return "jmx/rules/" + yaml; } diff --git a/instrumentation/jmx-metrics/library/src/test/java/io/opentelemetry/instrumentation/jmx/rules/TomcatIntegrationTest.java b/instrumentation/jmx-metrics/library/src/test/java/io/opentelemetry/instrumentation/jmx/rules/TomcatTest.java similarity index 89% rename from instrumentation/jmx-metrics/library/src/test/java/io/opentelemetry/instrumentation/jmx/rules/TomcatIntegrationTest.java rename to instrumentation/jmx-metrics/library/src/test/java/io/opentelemetry/instrumentation/jmx/rules/TomcatTest.java index f55946785191..aa908c5de3ef 100644 --- a/instrumentation/jmx-metrics/library/src/test/java/io/opentelemetry/instrumentation/jmx/rules/TomcatIntegrationTest.java +++ b/instrumentation/jmx-metrics/library/src/test/java/io/opentelemetry/instrumentation/jmx/rules/TomcatTest.java @@ -15,19 +15,15 @@ import java.util.Collections; import java.util.List; import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.CsvSource; +import org.junit.jupiter.params.provider.ValueSource; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.wait.strategy.Wait; -public class TomcatIntegrationTest extends TargetSystemTest { +public class TomcatTest extends TargetSystemTest { @ParameterizedTest - @CsvSource({ - "tomcat:10.0, https://tomcat.apache.org/tomcat-10.0-doc/appdev/sample/sample.war", - "tomcat:9.0, https://tomcat.apache.org/tomcat-9.0-doc/appdev/sample/sample.war" - }) - void testCollectedMetrics(String dockerImageName, String sampleWebApplicationUrl) - throws Exception { + @ValueSource(strings = {"tomcat:10.0", "tomcat:9.0"}) + void testCollectedMetrics(String dockerImageName) { List yamlFiles = Collections.singletonList("tomcat.yaml"); yamlFiles.forEach(this::validateYamlSyntax); @@ -43,15 +39,14 @@ void testCollectedMetrics(String dockerImageName, String sampleWebApplicationUrl .withExposedPorts(8080) .waitingFor(Wait.forListeningPorts(8080)); - copyFilesToTarget(target, yamlFiles); - - startTarget(target); + copyAgentToTarget(target); + copyYamlFilesToTarget(target, yamlFiles); // Deploy example web application to the tomcat to enable reporting tomcat.session.active.count // metric - target.execInContainer("rm", "-fr", "/usr/local/tomcat/webapps/ROOT"); - target.execInContainer( - "curl", sampleWebApplicationUrl, "-o", "/usr/local/tomcat/webapps/ROOT.war"); + copyTestWebAppToTarget(target, "/usr/local/tomcat/webapps/ROOT.war"); + + startTarget(target); verifyMetrics(createMetricsVerifier()); } diff --git a/instrumentation/jmx-metrics/library/src/test/java/io/opentelemetry/instrumentation/jmx/rules/WildflyTest.java b/instrumentation/jmx-metrics/library/src/test/java/io/opentelemetry/instrumentation/jmx/rules/WildflyTest.java new file mode 100644 index 000000000000..1bf779937d82 --- /dev/null +++ b/instrumentation/jmx-metrics/library/src/test/java/io/opentelemetry/instrumentation/jmx/rules/WildflyTest.java @@ -0,0 +1,210 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.jmx.rules; + +import static io.opentelemetry.instrumentation.jmx.rules.assertions.DataPointAttributes.attribute; +import static io.opentelemetry.instrumentation.jmx.rules.assertions.DataPointAttributes.attributeGroup; +import static io.opentelemetry.instrumentation.jmx.rules.assertions.DataPointAttributes.attributeWithAnyValue; + +import io.opentelemetry.instrumentation.jmx.rules.assertions.AttributeMatcher; +import io.opentelemetry.instrumentation.jmx.rules.assertions.AttributeMatcherGroup; +import java.time.Duration; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.containers.wait.strategy.Wait; + +public class WildflyTest extends TargetSystemTest { + + private static final int WILDFLY_SERVICE_PORT = 8080; + + @ParameterizedTest + @ValueSource( + strings = { + // keep testing on old and deprecated version for compatibility + "jboss/wildfly:10.1.0.Final", + // recent/latest to be maintained as newer versions are released + "quay.io/wildfly/wildfly:36.0.1.Final-jdk21" + }) + public void testWildflyMetrics(String dockerImage) { + List yamlFiles = Collections.singletonList("wildfly.yaml"); + + yamlFiles.forEach(this::validateYamlSyntax); + + List jvmArgs = new ArrayList<>(); + jvmArgs.add(javaAgentJvmArgument()); + jvmArgs.addAll(javaPropertiesToJvmArgs(otelConfigProperties(yamlFiles))); + + GenericContainer target = + new GenericContainer<>(dockerImage) + .withStartupTimeout(Duration.ofMinutes(2)) + .withExposedPorts(WILDFLY_SERVICE_PORT) + .withEnv("JAVA_TOOL_OPTIONS", String.join(" ", jvmArgs)) + .waitingFor(Wait.forListeningPorts(WILDFLY_SERVICE_PORT)); + + copyAgentToTarget(target); + copyYamlFilesToTarget(target, yamlFiles); + copyTestWebAppToTarget(target, "/opt/jboss/wildfly/standalone/deployments/testapp.war"); + + startTarget(target); + + verifyMetrics(createMetricsVerifier()); + } + + private static MetricsVerifier createMetricsVerifier() { + AttributeMatcher deploymentAttribute = attribute("wildfly.deployment", "testapp.war"); + AttributeMatcher serverAttribute = attribute("wildfly.server", "default-server"); + AttributeMatcher listenerAttribute = attribute("wildfly.listener", "default"); + AttributeMatcherGroup serverListenerAttributes = + attributeGroup(serverAttribute, listenerAttribute); + + AttributeMatcher dataSourceAttribute = attribute("db.client.connection.pool.name", "ExampleDS"); + + return MetricsVerifier.create() + // session metrics + .add( + "wildfly.session.created", + metric -> + metric + .isCounter() + .hasDescription("The number of sessions created") + .hasUnit("{session}") + .hasDataPointsWithOneAttribute(deploymentAttribute)) + .add( + "wildfly.session.active.count", + metric -> + metric + .isUpDownCounter() + .hasDescription("The number of active sessions") + .hasUnit("{session}") + .hasDataPointsWithOneAttribute(deploymentAttribute)) + .add( + "wildfly.session.active.limit", + metric -> + metric + .isUpDownCounter() + .hasDescription("The maximum number of active sessions") + .hasUnit("{session}") + .hasDataPointsWithOneAttribute(deploymentAttribute)) + .add( + "wildfly.session.expired", + metric -> + metric + .isCounter() + .hasDescription("The number of expired sessions") + .hasUnit("{session}") + .hasDataPointsWithOneAttribute(deploymentAttribute)) + .add( + "wildfly.session.rejected", + metric -> + metric + .isCounter() + .hasDescription("The number of rejected sessions") + .hasUnit("{session}") + .hasDataPointsWithOneAttribute(deploymentAttribute)) + // request metrics + .add( + "wildfly.request.count", + metric -> + metric + .isCounter() + .hasDescription("The number of requests served") + .hasUnit("{request}") + .hasDataPointsWithAttributes(serverListenerAttributes)) + .add( + "wildfly.request.duration.sum", + metric -> + metric + .isCounter() + .hasDescription("The total amount of time spent processing requests") + .hasUnit("s") + .hasDataPointsWithAttributes(serverListenerAttributes)) + .add( + "wildfly.error.count", + metric -> + metric + .isCounter() + .hasDescription("The number of requests that have resulted in a 5xx response") + .hasUnit("{request}") + .hasDataPointsWithAttributes(serverListenerAttributes)) + // network io metrics + .add( + "wildfly.network.io", + metric -> + metric + .hasDescription("Total number of bytes transferred") + .hasUnit("By") + .isCounter() + .hasDataPointsWithAttributes( + attributeGroup( + attribute("network.io.direction", "receive"), + serverAttribute, + listenerAttribute), + attributeGroup( + attribute("network.io.direction", "transmit"), + serverAttribute, + listenerAttribute))) + // database connection pool metrics + .add( + "wildfly.db.client.connection.count", + metric -> + metric + .isUpDownCounter() + .hasDescription("The number of open physical database connections") + .hasUnit("{connection}") + .hasDataPointsWithAttributes( + attributeGroup( + dataSourceAttribute, attribute("db.client.connection.state", "used")), + attributeGroup( + dataSourceAttribute, attribute("db.client.connection.state", "idle")))) + .add( + "wildfly.db.client.connection.wait.count", + metric -> + metric + .isCounter() + .hasDescription( + "The number of connection requests that had to wait to obtain it") + .hasUnit("{request}") + .hasDataPointsWithOneAttribute(dataSourceAttribute)) + // transactions + .add( + "wildfly.transaction.count", + metric -> + metric + .isUpDownCounter() + .hasDescription("The number of in-flight transactions") + .hasUnit("{transaction}") + .hasDataPointsWithoutAttributes()) + .add( + "wildfly.transaction.created", + metric -> + metric + .isCounter() + .hasDescription("The total number of transactions created") + .hasUnit("{transaction}") + .hasDataPointsWithoutAttributes()) + .add( + "wildfly.transaction.rollback", + metric -> + metric + .isCounter() + .hasDescription("The total number of transactions rolled back") + .hasUnit("{transaction}") + // older versions do not report 'system' cause, hence non-strict assertion + .hasDataPointsWithOneAttribute(attributeWithAnyValue("wildfly.rollback.cause"))) + .add( + "wildfly.transaction.committed", + metric -> + metric + .isCounter() + .hasDescription("The total number of transactions committed") + .hasUnit("{transaction}") + .hasDataPointsWithoutAttributes()); + } +} diff --git a/instrumentation/jmx-metrics/library/wildfly.md b/instrumentation/jmx-metrics/library/wildfly.md new file mode 100644 index 000000000000..1bc42f0f26a2 --- /dev/null +++ b/instrumentation/jmx-metrics/library/wildfly.md @@ -0,0 +1,21 @@ +# Wildfly Metrics + +Here is the list of metrics based on MBeans exposed by Wildfly. + +| Metric Name | Type | Attributes | Description | +|-----------------------------------------|---------------|------------------------------------------------------------|-----------------------------------------------------------------| +| wildfly.network.io | Counter | wildfly.server, wildfly.listener, network.io.direction | Total number of bytes transferred | +| wildfly.error.count | Counter | wildfly.server, wildfly.listener | The number of requests that have resulted in a 5xx response | +| wildfly.request.count | Counter | wildfly.server, wildfly.listener | The number of requests served | +| wildfly.request.duration.sum | Counter | wildfly.server, wildfly.listener | The total amount of time spent processing requests | +| wildfly.session.expired | Counter | wildfly.deployment | The number of expired sessions | +| wildfly.session.rejected | Counter | wildfly.deployment | The number of rejected sessions | +| wildfly.session.created | Counter | wildfly.deployment | The number of sessions created | +| wildfly.session.active.count | UpDownCounter | wildfly.deployment | The number of active sessions | +| wildfly.session.active.limit | UpDownCounter | wildfly.deployment | The maximum supported number of active sessions | +| wildfly.db.client.connection.count | UpDownCounter | db.client.connection.pool.name, db.client.connection.state | The number of open physical database connections | +| wildfly.db.client.connection.wait.count | Counter | db.client.connection.pool.name | The number of connection requests that had to wait to obtain it | +| wildfly.transaction.count | UpDownCounter | | The number of in-flight transactions | +| wildfly.transaction.created | Counter | | The total number of transactions created | +| wildfly.transaction.committed | Counter | | The total number of transactions committed | +| wildfly.transaction.rollback | Counter | wildfly.rollback.cause | The total number of transactions rolled back | diff --git a/instrumentation/jmx-metrics/testing-webapp/README.md b/instrumentation/jmx-metrics/testing-webapp/README.md new file mode 100644 index 000000000000..97c507da7a64 --- /dev/null +++ b/instrumentation/jmx-metrics/testing-webapp/README.md @@ -0,0 +1,4 @@ +# test-webapp + +This is a test web application used for integration tests when metrics are published only when +an application is deployed to the container. For example, Wildfly or Tomcat session metrics. diff --git a/instrumentation/jmx-metrics/testing-webapp/build.gradle.kts b/instrumentation/jmx-metrics/testing-webapp/build.gradle.kts new file mode 100644 index 000000000000..a37c556ec707 --- /dev/null +++ b/instrumentation/jmx-metrics/testing-webapp/build.gradle.kts @@ -0,0 +1,13 @@ +plugins { + id("otel.java-conventions") + + war +} + +description = "JMX metrics - test web application" + +dependencies { + // using both servlet APIs to provide compatibility with most containers pre/post jarkarta renaming + compileOnly("jakarta.servlet:jakarta.servlet-api:5.0.0") + compileOnly("javax.servlet:javax.servlet-api:4.0.1") +} diff --git a/instrumentation/jmx-metrics/testing-webapp/src/main/java/io/opentelemetry/instrumentation/jmx/testapp/JakartaSimpleServlet.java b/instrumentation/jmx-metrics/testing-webapp/src/main/java/io/opentelemetry/instrumentation/jmx/testapp/JakartaSimpleServlet.java new file mode 100644 index 000000000000..21e318feb1f5 --- /dev/null +++ b/instrumentation/jmx-metrics/testing-webapp/src/main/java/io/opentelemetry/instrumentation/jmx/testapp/JakartaSimpleServlet.java @@ -0,0 +1,27 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.jmx.testapp; + +import jakarta.servlet.annotation.WebServlet; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.io.PrintWriter; + +// servlet that will be used for jakarta servlet containers +@WebServlet(urlPatterns = "/jakarta/*", name = "jakartaServlet") +public class JakartaSimpleServlet extends HttpServlet { + + private static final long serialVersionUID = 1L; + + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException { + try (PrintWriter out = resp.getWriter()) { + out.write("hello!"); + } + } +} diff --git a/instrumentation/jmx-metrics/testing-webapp/src/main/java/io/opentelemetry/instrumentation/jmx/testapp/JavaxSimpleServlet.java b/instrumentation/jmx-metrics/testing-webapp/src/main/java/io/opentelemetry/instrumentation/jmx/testapp/JavaxSimpleServlet.java new file mode 100644 index 000000000000..c985bbc8f219 --- /dev/null +++ b/instrumentation/jmx-metrics/testing-webapp/src/main/java/io/opentelemetry/instrumentation/jmx/testapp/JavaxSimpleServlet.java @@ -0,0 +1,27 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.jmx.testapp; + +import java.io.IOException; +import java.io.PrintWriter; +import javax.servlet.annotation.WebServlet; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +// servlet that will be used for javax servlet containers +@WebServlet(urlPatterns = "/javax/*", name = "javaxServlet") +public class JavaxSimpleServlet extends HttpServlet { + + private static final long serialVersionUID = 1L; + + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException { + try (PrintWriter out = resp.getWriter()) { + out.write("hello!"); + } + } +} diff --git a/instrumentation/jmx-metrics/testing-webapp/src/main/webapp/META-INF/context.xml b/instrumentation/jmx-metrics/testing-webapp/src/main/webapp/META-INF/context.xml new file mode 100644 index 000000000000..6e97a1bc3298 --- /dev/null +++ b/instrumentation/jmx-metrics/testing-webapp/src/main/webapp/META-INF/context.xml @@ -0,0 +1,4 @@ + + + + diff --git a/instrumentation/jmx-metrics/testing-webapp/src/main/webapp/WEB-INF/jboss-web.xml b/instrumentation/jmx-metrics/testing-webapp/src/main/webapp/WEB-INF/jboss-web.xml new file mode 100644 index 000000000000..c918e45ab373 --- /dev/null +++ b/instrumentation/jmx-metrics/testing-webapp/src/main/webapp/WEB-INF/jboss-web.xml @@ -0,0 +1,4 @@ + + + 42 + diff --git a/settings.gradle.kts b/settings.gradle.kts index fcb329f0c12e..006f87c48442 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -343,6 +343,7 @@ include(":instrumentation:jms:jms-common:javaagent") include(":instrumentation:jms:jms-common:javaagent-unit-tests") include(":instrumentation:jmx-metrics:javaagent") include(":instrumentation:jmx-metrics:library") +include(":instrumentation:jmx-metrics:testing-webapp") include(":instrumentation:jodd-http-4.2:javaagent") include(":instrumentation:jodd-http-4.2:javaagent-unit-tests") include(":instrumentation:jsf:jsf-jakarta-common:javaagent")