Skip to content

Commit

Permalink
Add jetty.yaml to JMX scraper (#1517)
Browse files Browse the repository at this point in the history
  • Loading branch information
SylvainJuge authored Nov 4, 2024
1 parent e0fbc71 commit df4960b
Show file tree
Hide file tree
Showing 9 changed files with 224 additions and 36 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ void endToEnd() throws InterruptedException {
metric,
"jetty.session.count",
"The number of sessions established in total.",
"{sessions}",
"{session}",
attrs -> attrs.containsKey("resource")),
metric ->
assertSumWithAttributes(
Expand All @@ -73,20 +73,20 @@ void endToEnd() throws InterruptedException {
"s",
attrs -> attrs.containsKey("resource")),
metric ->
assertSum(metric, "jetty.select.count", "The number of select calls.", "{operations}"),
assertSum(metric, "jetty.select.count", "The number of select calls.", "{operation}"),
metric ->
assertGaugeWithAttributes(
metric,
"jetty.thread.count",
"The current number of threads.",
"{threads}",
"{thread}",
attrs -> attrs.contains(entry("state", "busy")),
attrs -> attrs.contains(entry("state", "idle"))),
metric ->
assertGauge(
metric,
"jetty.thread.queue.count",
"The current number of threads in the queue.",
"{threads}"));
"{thread}"));
}
}
8 changes: 4 additions & 4 deletions jmx-metrics/src/main/resources/target-systems/jetty.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@
*/

def beanSelector = otel.mbean("org.eclipse.jetty.io:context=*,type=managedselector,id=*")
otel.instrument(beanSelector, "jetty.select.count", "The number of select calls.", "{operations}","selectCount", otel.&longCounterCallback)
otel.instrument(beanSelector, "jetty.select.count", "The number of select calls.", "{operation}","selectCount", otel.&longCounterCallback)

def beanSessions = otel.mbean("org.eclipse.jetty.server.session:context=*,type=sessionhandler,id=*")
otel.instrument(beanSessions, "jetty.session.count", "The number of sessions established in total.", "{sessions}",
otel.instrument(beanSessions, "jetty.session.count", "The number of sessions established in total.", "{session}",
["resource" : { mbean -> mbean.name().getKeyProperty("context") }],
"sessionsCreated", otel.&longCounterCallback)
otel.instrument(beanSessions, "jetty.session.time.total", "The total time sessions have been active.", "s",
Expand All @@ -29,9 +29,9 @@ otel.instrument(beanSessions, "jetty.session.time.max", "The maximum amount of t
"sessionTimeMax", otel.&longValueCallback)

def beanThreads = otel.mbean("org.eclipse.jetty.util.thread:type=queuedthreadpool,id=*")
otel.instrument(beanThreads, "jetty.thread.count", "The current number of threads.", "{threads}",
otel.instrument(beanThreads, "jetty.thread.count", "The current number of threads.", "{thread}",
[
"busyThreads":["state" : {"busy"}],
"idleThreads": ["state" : {"idle"}]
], otel.&longValueCallback)
otel.instrument(beanThreads, "jetty.thread.queue.count", "The current number of threads in the queue.", "{threads}","queueSize", otel.&longValueCallback)
otel.instrument(beanThreads, "jetty.thread.queue.count", "The current number of threads in the queue.", "{thread}","queueSize", otel.&longValueCallback)
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,7 @@ protected GenericContainer<?> createTargetContainer(int jmxPort) {
new ImageFromDockerfile()
.withDockerfileFromBuilder(
builder -> builder.from("apache/activemq-classic:5.18.6").build()))
.withEnv(
"JAVA_TOOL_OPTIONS",
"-Dcom.sun.management.jmxremote.port="
+ jmxPort
+ " -Dcom.sun.management.jmxremote.rmi.port="
+ jmxPort
+ " -Dcom.sun.management.jmxremote.ssl=false"
+ " -Dcom.sun.management.jmxremote.authenticate=false")
.withEnv("JAVA_TOOL_OPTIONS", genericJmxJvmArguments(jmxPort))
.withStartupTimeout(Duration.ofMinutes(2))
.waitingFor(Wait.forListeningPort());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,24 +18,17 @@
import org.assertj.core.api.MapAssert;
import org.testcontainers.containers.GenericContainer;
import org.testcontainers.containers.wait.strategy.Wait;
import org.testcontainers.images.builder.ImageFromDockerfile;

public class CassandraIntegrationTest extends TargetSystemIntegrationTest {

@Override
protected GenericContainer<?> createTargetContainer(int jmxPort) {
return new GenericContainer<>(
new ImageFromDockerfile()
.withDockerfileFromBuilder(builder -> builder.from("cassandra:5.0.2").build()))
return new GenericContainer<>("cassandra:5.0.2")
.withEnv(
"JVM_EXTRA_OPTS",
" -Dcassandra.jmx.remote.port="
+ jmxPort
+ " -Dcom.sun.management.jmxremote.rmi.port="
+ jmxPort
+ " -Dcom.sun.management.jmxremote.local.only=false"
+ " -Dcom.sun.management.jmxremote.ssl=false"
+ " -Dcom.sun.management.jmxremote.authenticate=false")
genericJmxJvmArguments(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));
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
/*
* 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.assertGauge;
import static io.opentelemetry.contrib.jmxscraper.target_systems.MetricAssertions.assertGaugeWithAttributes;
import static io.opentelemetry.contrib.jmxscraper.target_systems.MetricAssertions.assertSumWithAttributes;
import static io.opentelemetry.contrib.jmxscraper.target_systems.MetricAssertions.assertSumWithAttributesMultiplePoints;

import io.opentelemetry.contrib.jmxscraper.JmxScraperContainer;
import java.time.Duration;
import org.testcontainers.containers.GenericContainer;
import org.testcontainers.containers.wait.strategy.Wait;
import org.testcontainers.images.builder.ImageFromDockerfile;

public class JettyIntegrationTest extends TargetSystemIntegrationTest {

@Override
protected GenericContainer<?> createTargetContainer(int jmxPort) {
GenericContainer<?> container =
new GenericContainer<>(
new ImageFromDockerfile()
.withDockerfileFromBuilder(
builder ->
builder
.from("jetty:11")
.run(
"java",
"-jar",
"/usr/local/jetty/start.jar",
"--add-to-startd=jmx,stats,http")
.run("mkdir -p /var/lib/jetty/webapps/ROOT/")
.run("touch /var/lib/jetty/webapps/ROOT/index.html")
.build()));

container
.withEnv("JAVA_OPTIONS", genericJmxJvmArguments(jmxPort))
.withStartupTimeout(Duration.ofMinutes(2))
.waitingFor(Wait.forLogMessage(".*Started Server.*", 1));

return container;
}

@Override
protected JmxScraperContainer customizeScraperContainer(JmxScraperContainer scraper) {
return scraper.withTargetSystem("jetty");
}

@Override
protected void verifyMetrics() {
waitAndAssertMetrics(
metric ->
assertSumWithAttributes(
metric,
"jetty.session.count",
"The number of sessions established in total.",
"{session}",
attrs -> attrs.containsKey("resource")),
metric ->
assertSumWithAttributes(
metric,
"jetty.session.time.total",
"The total time sessions have been active.",
"s",
attrs -> attrs.containsKey("resource")),
metric ->
assertGaugeWithAttributes(
metric,
"jetty.session.time.max",
"The maximum amount of time a session has been active.",
"s",
attrs -> attrs.containsKey("resource")),
metric ->
assertSumWithAttributesMultiplePoints(
metric,
"jetty.select.count",
"The number of select calls.",
"{operation}",
/* isMonotonic= */ true,
// minor divergence from jetty.groovy with extra metrics attributes
attrs -> attrs.containsKey("context").containsKey("id")),
metric ->
assertGaugeWithAttributes(
metric,
"jetty.thread.count",
"The current number of threads.",
"{thread}",
attrs -> attrs.containsEntry("state", "busy"),
attrs -> attrs.containsEntry("state", "idle")),
metric ->
assertGauge(
metric,
"jetty.thread.queue.count",
"The current number of threads in the queue.",
"{thread}"));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,22 @@ static void assertSumWithAttributes(
assertAttributedPoints(metric.getSum().getDataPointsList(), attributeGroupAssertions);
}

@SafeVarargs
static void assertSumWithAttributesMultiplePoints(
Metric metric,
String name,
String description,
String unit,
boolean isMonotonic,
Consumer<MapAssert<String, String>>... attributeGroupAssertions) {
assertThat(metric.getName()).isEqualTo(name);
assertThat(metric.getDescription()).isEqualTo(description);
assertThat(metric.getUnit()).isEqualTo(unit);
assertThat(metric.hasSum()).isTrue();
assertThat(metric.getSum().getIsMonotonic()).isEqualTo(isMonotonic);
assertAttributedMultiplePoints(metric.getSum().getDataPointsList(), attributeGroupAssertions);
}

@SafeVarargs
static void assertGaugeWithAttributes(
Metric metric,
Expand Down Expand Up @@ -127,6 +143,7 @@ private static void assertAttributedPoints(
Arrays.stream(attributeGroupAssertions)
.map(assertion -> (Consumer<Map<String, String>>) m -> assertion.accept(assertThat(m)))
.toArray(Consumer[]::new);

assertThat(points)
.extracting(
numberDataPoint ->
Expand All @@ -136,4 +153,22 @@ private static void assertAttributedPoints(
KeyValue::getKey, keyValue -> keyValue.getValue().getStringValue())))
.satisfiesExactlyInAnyOrder(assertions);
}

@SuppressWarnings("unchecked")
private static void assertAttributedMultiplePoints(
List<NumberDataPoint> points,
Consumer<MapAssert<String, String>>... attributeGroupAssertions) {

points.stream()
.map(NumberDataPoint::getAttributesList)
.forEach(
kvList -> {
Map<String, String> kvMap =
kvList.stream()
.collect(
Collectors.toMap(KeyValue::getKey, kv -> kv.getValue().getStringValue()));
Arrays.stream(attributeGroupAssertions)
.forEach(assertion -> assertion.accept(assertThat(kvMap)));
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -196,4 +196,14 @@ public void export(
sb.http(0);
}
}

protected static String genericJmxJvmArguments(int port) {
return "-Dcom.sun.management.jmxremote.local.only=false"
+ " -Dcom.sun.management.jmxremote.authenticate=false"
+ " -Dcom.sun.management.jmxremote.ssl=false"
+ " -Dcom.sun.management.jmxremote.port="
+ port
+ " -Dcom.sun.management.jmxremote.rmi.port="
+ port;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,15 +30,7 @@ protected GenericContainer<?> createTargetContainer(int jmxPort) {
"https://tomcat.apache.org/tomcat-9.0-doc/appdev/sample/sample.war",
"/usr/local/tomcat/webapps/ROOT.war")
.build()))
.withEnv(
"CATALINA_OPTS",
"-Dcom.sun.management.jmxremote.local.only=false"
+ " -Dcom.sun.management.jmxremote.authenticate=false"
+ " -Dcom.sun.management.jmxremote.ssl=false"
+ " -Dcom.sun.management.jmxremote.port="
+ jmxPort
+ " -Dcom.sun.management.jmxremote.rmi.port="
+ jmxPort)
.withEnv("CATALINA_OPTS", genericJmxJvmArguments(jmxPort))
.withStartupTimeout(Duration.ofMinutes(2))
.waitingFor(Wait.forListeningPort());
}
Expand Down
65 changes: 65 additions & 0 deletions jmx-scraper/src/main/resources/jetty.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
---

rules:

- bean: org.eclipse.jetty.io:context=*,type=managedselector,id=*
mapping:
selectCount:
metric: jetty.select.count
type: counter
unit: "{operation}"
desc: The number of select calls.
metricAttribute:
# minor divergence from jetty.groovy with extra attribute(s)
# 'id' is a numerical value in [0,9] by default
# 'context' is a high cardinality value like 'HTTP_1_1@7674f035' but likely stable for the
# duration of the jetty process lifecycle
context: param(context)
id: param(id)

- bean: org.eclipse.jetty.server.session:context=*,type=sessionhandler,id=*
prefix: jetty.session.
metricAttribute:
resource: param(context)
mapping:
sessionsCreated:
metric: count
type: counter
unit: "{session}"
desc: The number of sessions established in total.
sessionTimeTotal:
metric: time.total
type: counter
unit: s
desc: The total time sessions have been active.
sessionTimeMax:
metric: time.max
# here a 'counter' seems more appropriate but gauge reflects jetty.groovy impl.
type: gauge
unit: s
desc: The maximum amount of time a session has been active.

- bean: org.eclipse.jetty.util.thread:type=queuedthreadpool,id=*
# here the 'id' can be ignored as it's usually a single value equal to '0'
prefix: jetty.thread.
unit: "{thread}"
mapping:
busyThreads:
metric: count
# here an 'updowncounter' seems more appropriate but gauge reflects jetty.groovy impl.
type: gauge
desc: The current number of threads.
metricAttribute:
state: const(busy)
idleThreads:
metric: count
# here an 'updowncounter' seems more appropriate but gauge reflects jetty.groovy impl.
type: gauge
desc: The current number of threads.
metricAttribute:
state: const(idle)
queueSize:
metric: queue.count
# here an 'updowncounter' seems more appropriate but gauge reflects jetty.groovy impl.
type: gauge
desc: The current number of threads in the queue.

0 comments on commit df4960b

Please sign in to comment.