diff --git a/instrumentation/jmx-metrics/README.md b/instrumentation/jmx-metrics/README.md index 82b520147e26..44db4b4b5902 100644 --- a/instrumentation/jmx-metrics/README.md +++ b/instrumentation/jmx-metrics/README.md @@ -27,7 +27,7 @@ No targets are enabled by default. The supported target environments are listed - [activemq](javaagent/activemq.md) - [camel](javaagent/camel.md) -- [jetty](javaagent/jetty.md) +- [jetty](library/jetty.md) - [kafka-broker](javaagent/kafka-broker.md) - [tomcat](library/tomcat.md) - [wildfly](javaagent/wildfly.md) diff --git a/instrumentation/jmx-metrics/javaagent/jetty.md b/instrumentation/jmx-metrics/javaagent/jetty.md deleted file mode 100644 index d771214cfc48..000000000000 --- a/instrumentation/jmx-metrics/javaagent/jetty.md +++ /dev/null @@ -1,16 +0,0 @@ -# Jetty Metrics - -Here is the list of metrics based on MBeans exposed by Jetty. - -| Metric Name | Type | Attributes | Description | -| ------------------------------ | ------------- | ------------ | ---------------------------------------------------- | -| jetty.session.sessionsCreated | Counter | resource | The number of sessions established in total | -| jetty.session.sessionTimeTotal | Counter | resource | The total time sessions have been active | -| jetty.session.sessionTimeMax | Gauge | resource | The maximum amount of time a session has been active | -| jetty.session.sessionTimeMean | Gauge | resource | The mean time sessions remain active | -| jetty.threads.busyThreads | UpDownCounter | | The current number of busy threads | -| jetty.threads.idleThreads | UpDownCounter | | The current number of idle threads | -| jetty.threads.maxThreads | UpDownCounter | | The maximum number of threads in the pool | -| jetty.threads.queueSize | UpDownCounter | | The current number of threads in the queue | -| jetty.io.selectCount | Counter | resource, id | The number of select calls | -| jetty.logging.LoggerCount | UpDownCounter | | The number of registered loggers by name | diff --git a/instrumentation/jmx-metrics/javaagent/src/main/resources/jmx/rules/jetty.yaml b/instrumentation/jmx-metrics/javaagent/src/main/resources/jmx/rules/jetty.yaml deleted file mode 100644 index ed5435d9cc20..000000000000 --- a/instrumentation/jmx-metrics/javaagent/src/main/resources/jmx/rules/jetty.yaml +++ /dev/null @@ -1,55 +0,0 @@ ---- -rules: - - bean: org.eclipse.jetty.server.session:context=*,type=sessionhandler,id=* - unit: s - prefix: jetty.session. - type: updowncounter - metricAttribute: - resource: param(context) - mapping: - sessionsCreated: - unit: "{sessions}" - type: counter - desc: The number of sessions established in total - sessionTimeTotal: - type: counter - desc: The total time sessions have been active - sessionTimeMax: - type: gauge - desc: The maximum amount of time a session has been active - sessionTimeMean: - type: gauge - desc: The mean time sessions remain active - - - bean: org.eclipse.jetty.util.thread:type=queuedthreadpool,id=* - prefix: jetty.threads. - unit: "{threads}" - type: updowncounter - mapping: - busyThreads: - desc: The current number of busy threads - idleThreads: - desc: The current number of idle threads - maxThreads: - desc: The maximum number of threads in the pool - queueSize: - desc: The current number of threads in the queue - - - bean: org.eclipse.jetty.io:context=*,type=managedselector,id=* - prefix: jetty.io. - metricAttribute: - resource: param(context) - id: param(id) - mapping: - selectCount: - type: counter - unit: "1" - desc: The number of select calls - - - bean: org.eclipse.jetty.logging:type=jettyloggerfactory,id=* - prefix: jetty.logging. - mapping: - LoggerCount: - type: updowncounter - unit: "1" - desc: The number of registered loggers by name 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 88d8d6787357..c12eb6faec2f 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 @@ -33,12 +33,7 @@ class JmxMetricInsightInstallerTest { private static final Set FILES_TO_BE_TESTED = new HashSet<>( Arrays.asList( - "activemq.yaml", - "camel.yaml", - "hadoop.yaml", - "jetty.yaml", - "kafka-broker.yaml", - "wildfly.yaml")); + "activemq.yaml", "camel.yaml", "hadoop.yaml", "kafka-broker.yaml", "wildfly.yaml")); @Test void testToVerifyExistingRulesAreValid() throws Exception { diff --git a/instrumentation/jmx-metrics/library/jetty.md b/instrumentation/jmx-metrics/library/jetty.md new file mode 100644 index 000000000000..0a5d2fb5d655 --- /dev/null +++ b/instrumentation/jmx-metrics/library/jetty.md @@ -0,0 +1,40 @@ +# Jetty Metrics + +Here is the list of metrics based on MBeans exposed by [Jetty](https://jetty.org/). + +The metrics captured and their respective attributes depend on the Jetty version: +- [Jetty 12 and later](#jetty-12-and-later) +- [Jetty 9 to 11](#jetty-9-to-11) + +## Jetty 12 and later + +Those metrics require the following Jetty modules to be enabled : `jmx`, `http`, `statistics`, `sessions` and at least one of `ee8-deploy`, `ee9-deploy` or `ee10-deploy`. + +| Metric Name | Type | Attributes | Description | +|-------------------------|---------------|---------------|-------------------------------------------| +| jetty.thread.count | UpDownCounter | | The current number of threads | +| jetty.thread.limit | UpDownCounter | | The maximum number of threads in the pool | +| jetty.thread.busy.count | UpDownCounter | | The current number of busy threads | +| jetty.thread.idle.count | UpDownCounter | | The current number of idle threads | +| jetty.thread.queue.size | UpDownCounter | | The current job queue size | +| jetty.io.select.count | Counter | | The number of select calls | +| jetty.session.count | UpDownCounter | jetty.context | Current number of active sessions | + +- `jetty.context` corresponds to the deployed application subfolder in `webapps` folder. + +## Jetty 9 to 11 + +Those metrics require the following Jetty modules to be enabled : `jmx`, `http` and `stats`. + +| Metric Name | Type | Attributes | Description | +|-----------------------------|---------------|---------------|-------------------------------------------| +| jetty.thread.count | UpDownCounter | | The current number of threads | +| jetty.thread.limit | UpDownCounter | | The maximum number of threads in the pool | +| jetty.thread.busy.count | UpDownCounter | | The current number of busy threads | +| jetty.thread.idle.count | UpDownCounter | | The current number of idle threads | +| jetty.thread.queue.size | UpDownCounter | | The current job queue size | +| jetty.io.select.count | Counter | | The number of select calls | +| jetty.session.created.count | Counter | jetty.context | The total number of created sessions | +| jetty.session.duration.sum | Counter | jetty.context | The cumulated session duration | + +- `jetty.context` corresponds to the deployed application subfolder in `webapps` folder. diff --git a/instrumentation/jmx-metrics/library/src/main/resources/jmx/rules/jetty.yaml b/instrumentation/jmx-metrics/library/src/main/resources/jmx/rules/jetty.yaml new file mode 100644 index 000000000000..c40cebbc6eaf --- /dev/null +++ b/instrumentation/jmx-metrics/library/src/main/resources/jmx/rules/jetty.yaml @@ -0,0 +1,83 @@ +--- +rules: + + # Thread metrics + - beans: + # Jetty 12 and later + - org.eclipse.jetty.util.thread:context=*,type=queuedthreadpool,id=* + # Jetty 9 to 11 + - org.eclipse.jetty.util.thread:type=queuedthreadpool,id=* + # usually a single mbean instance exists, thus the metric is aggregated (sum for updowncounter) + prefix: jetty.thread. + unit: "{thread}" + type: updowncounter + mapping: + # jetty.thread.count + threads: + metric: count + desc: The current number of threads + # jetty.thread.limit + maxThreads: + metric: limit + desc: The configured maximum number of threads in the pool + # jetty.thread.idle.count + idleThreads: + metric: idle.count + desc: The current number of idle threads + # jetty.thread.busy.count + busyThreads: + metric: busy.count + desc: The current number of busy threads + # jetty.thread.queue.size + queueSize: + metric: queue.size + desc: The current job queue size + + - bean: org.eclipse.jetty.io:context=*,type=managedselector,id=* + mapping: + # jetty.select.count + selectCount: + metric: jetty.select.count + type: counter + unit: "{operation}" + desc: The number of select calls + + # Session metrics for Jetty 12 + - bean: org.eclipse.jetty.session:context=*,type=defaultsessioncache,id=* + # Usually a single mbean instance exists per context, thus the metric is aggregated: sum for counter, + # gauge metrics will return invalid (last-value) with more than 1 mbean instance, thus none + # is included in this provided configuration. + prefix: jetty.session. + unit: "{session}" + metricAttribute: + # 'context' corresponds to the webapp context path + jetty.context: param(context) + mapping: + # jetty.session.count + sessionsCurrent: + metric: count + type: updowncounter + desc: Current number of active sessions + + # Session metrics for Jetty 9 to 11 + - bean: org.eclipse.jetty.server.session:context=*,type=sessionhandler,id=* + # Usually a single mbean instance exists per context, thus the metric is aggregated: sum for counter, + # gauge metrics will return invalid (last-value) with more than 1 mbean instance, thus none + # is included in this provided configuration. + prefix: jetty.session. + metricAttribute: + # 'context' corresponds to the webapp context path + jetty.context: param(context) + mapping: + # jetty.session.created.count + sessionsCreated: + metric: created.count + unit: "{session}" + type: counter + desc: The total number of created sessions + # jetty.session.duration.sum + sessionTimeTotal: + metric: duration.sum + unit: s + type: counter + desc: The cumulated session duration 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/JettyIntegrationTest.java new file mode 100644 index 000000000000..22fd3ad378e3 --- /dev/null +++ b/instrumentation/jmx-metrics/library/src/test/java/io/opentelemetry/instrumentation/jmx/rules/JettyIntegrationTest.java @@ -0,0 +1,160 @@ +/* + * 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.attributeWithAnyValue; + +import io.opentelemetry.instrumentation.jmx.rules.assertions.AttributeMatcher; +import java.time.Duration; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +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; +import org.testcontainers.images.builder.ImageFromDockerfile; + +public class JettyIntegrationTest extends TargetSystemTest { + + private static final int JETTY_PORT = 8080; + + @ParameterizedTest(name = "jetty:{arguments}") + @ValueSource(ints = {9, 10, 11, 12}) + void testCollectedMetrics(int jettyMajorVersion) { + + List yamlFiles = Collections.singletonList("jetty.yaml"); + + yamlFiles.forEach(this::validateYamlSyntax); + + List jvmArgs = new ArrayList<>(); + jvmArgs.add(javaAgentJvmArgument()); + jvmArgs.addAll(javaPropertiesToJvmArgs(otelConfigProperties(yamlFiles))); + + Set jettyModules = new HashSet<>(Arrays.asList("jmx", "http")); + if (jettyMajorVersion >= 12) { + jettyModules.add("statistics"); + // required for session management + jettyModules.add("sessions"); + // required for deployment support in 'webapps' folder + jettyModules.add("ee10-deploy"); + } else { + // with older versions deployment and session management are available by default + jettyModules.add("stats"); + } + String addModulesArg = "--add-to-startd=" + 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())) + .withEnv("JAVA_OPTIONS", String.join(" ", jvmArgs)) + .withStartupTimeout(Duration.ofMinutes(2)) + .withExposedPorts(JETTY_PORT) + .waitingFor(Wait.forListeningPorts(JETTY_PORT)); + + copyFilesToTarget(container, yamlFiles); + + startTarget(container); + + verifyMetrics(createMetricsVerifier(jettyMajorVersion)); + } + + private static MetricsVerifier createMetricsVerifier(int jettyMajorVersion) { + + MetricsVerifier verifier = + MetricsVerifier.create() + .add( + "jetty.thread.count", + metric -> + metric + .isUpDownCounter() + .hasDescription("The current number of threads") + .hasUnit("{thread}") + .hasDataPointsWithoutAttributes()) + .add( + "jetty.thread.limit", + metric -> + metric + .isUpDownCounter() + .hasDescription("The configured maximum number of threads in the pool") + .hasUnit("{thread}") + .hasDataPointsWithoutAttributes()) + .add( + "jetty.thread.idle.count", + metric -> + metric + .isUpDownCounter() + .hasDescription("The current number of idle threads") + .hasUnit("{thread}") + .hasDataPointsWithoutAttributes()) + .add( + "jetty.thread.busy.count", + metric -> + metric + .isUpDownCounter() + .hasDescription("The current number of busy threads") + .hasUnit("{thread}") + .hasDataPointsWithoutAttributes()) + .add( + "jetty.thread.queue.size", + metric -> + metric + .isUpDownCounter() + .hasDescription("The current job queue size") + .hasUnit("{thread}") + .hasDataPointsWithoutAttributes()) + .add( + "jetty.select.count", + metric -> + metric + .isCounter() + .hasDescription("The number of select calls") + .hasUnit("{operation}") + .hasDataPointsWithoutAttributes()); + + AttributeMatcher contextAttribute = attributeWithAnyValue("jetty.context"); + if (jettyMajorVersion >= 12) { + verifier.add( + "jetty.session.count", + metric -> + metric + .isUpDownCounter() + .hasDescription("Current number of active sessions") + .hasUnit("{session}") + .hasDataPointsWithOneAttribute(contextAttribute)); + } else { + verifier + .add( + "jetty.session.created.count", + metric -> + metric + .isCounter() + .hasDescription("The total number of created sessions") + .hasUnit("{session}") + .hasDataPointsWithOneAttribute(contextAttribute)) + .add( + "jetty.session.duration.sum", + metric -> + metric + .isCounter() + .hasDescription("The cumulated session duration") + .hasUnit("s") + .hasDataPointsWithOneAttribute(contextAttribute)); + } + return verifier; + } +} 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/TomcatIntegrationTest.java index 1ab0ab097915..f55946785191 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/TomcatIntegrationTest.java @@ -36,7 +36,6 @@ void testCollectedMetrics(String dockerImageName, String sampleWebApplicationUrl jvmArgs.add(javaAgentJvmArgument()); jvmArgs.addAll(javaPropertiesToJvmArgs(otelConfigProperties(yamlFiles))); - // testing with a basic tomcat image as test application to capture JVM metrics GenericContainer target = new GenericContainer<>(dockerImageName) .withEnv("CATALINA_OPTS", String.join(" ", jvmArgs))