Skip to content

Commit 74a3d78

Browse files
authored
add wildfly.yaml to JMX scraper (#1531)
1 parent 6ea0fa4 commit 74a3d78

File tree

27 files changed

+609
-74
lines changed

27 files changed

+609
-74
lines changed

jmx-metrics/src/integrationTest/java/io/opentelemetry/contrib/jmxmetrics/target_systems/WildflyIntegrationTest.java

+7-7
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ void endToEnd() {
5454
metric,
5555
"wildfly.request.count",
5656
"The number of requests received.",
57-
"{requests}",
57+
"{request}",
5858
attrs ->
5959
attrs.containsOnly(
6060
entry("server", "default-server"), entry("listener", "default"))),
@@ -72,7 +72,7 @@ void endToEnd() {
7272
metric,
7373
"wildfly.request.server_error",
7474
"The number of requests that have resulted in a 5xx response.",
75-
"{requests}",
75+
"{request}",
7676
attrs ->
7777
attrs.containsOnly(
7878
entry("server", "default-server"), entry("listener", "default"))),
@@ -81,7 +81,7 @@ void endToEnd() {
8181
metric,
8282
"wildfly.network.io",
8383
"The number of bytes transmitted.",
84-
"by",
84+
"By",
8585
attrs ->
8686
attrs.containsOnly(
8787
entry("server", "default-server"),
@@ -97,7 +97,7 @@ void endToEnd() {
9797
metric,
9898
"wildfly.jdbc.connection.open",
9999
"The number of open jdbc connections.",
100-
"{connections}",
100+
"{connection}",
101101
attrs ->
102102
attrs.containsOnly(entry("data_source", "ExampleDS"), entry("state", "active")),
103103
attrs ->
@@ -107,20 +107,20 @@ void endToEnd() {
107107
metric,
108108
"wildfly.jdbc.request.wait",
109109
"The number of jdbc connections that had to wait before opening.",
110-
"{requests}",
110+
"{request}",
111111
attrs -> attrs.containsOnly(entry("data_source", "ExampleDS"))),
112112
metric ->
113113
assertSum(
114114
metric,
115115
"wildfly.jdbc.transaction.count",
116116
"The number of transactions created.",
117-
"{transactions}"),
117+
"{transaction}"),
118118
metric ->
119119
assertSumWithAttributes(
120120
metric,
121121
"wildfly.jdbc.rollback.count",
122122
"The number of transactions rolled back.",
123-
"{transactions}",
123+
"{transaction}",
124124
attrs -> attrs.containsOnly(entry("cause", "system")),
125125
attrs -> attrs.containsOnly(entry("cause", "resource")),
126126
attrs -> attrs.containsOnly(entry("cause", "application"))));

jmx-metrics/src/main/resources/target-systems/wildfly.groovy

+15-11
Original file line numberDiff line numberDiff line change
@@ -15,46 +15,50 @@
1515
*/
1616

1717
def beanWildflyDeployment = otel.mbeans("jboss.as:deployment=*,subsystem=undertow")
18-
otel.instrument(beanWildflyDeployment, "wildfly.session.count", "The number of sessions created.", "{sessions}",
18+
// no test covers sessions
19+
otel.instrument(beanWildflyDeployment, "wildfly.session.count", "The number of sessions created.", "{session}",
1920
["deployment": { mbean -> mbean.name().getKeyProperty("deployment")}],
2021
"sessionsCreated", otel.&longCounterCallback)
21-
otel.instrument(beanWildflyDeployment, "wildfly.session.active", "The number of currently active sessions.", "{sessions}",
22+
23+
otel.instrument(beanWildflyDeployment, "wildfly.session.active", "The number of currently active sessions.", "{session}",
2224
["deployment": { mbean -> mbean.name().getKeyProperty("deployment")}],
2325
"activeSessions", otel.&longUpDownCounterCallback)
24-
otel.instrument(beanWildflyDeployment, "wildfly.session.expired", "The number of sessions that have expired.", "{sessions}",
26+
otel.instrument(beanWildflyDeployment, "wildfly.session.expired", "The number of sessions that have expired.", "{session}",
2527
["deployment": { mbean -> mbean.name().getKeyProperty("deployment")}],
2628
"expiredSessions", otel.&longCounterCallback)
27-
otel.instrument(beanWildflyDeployment, "wildfly.session.rejected", "The number of sessions that have been rejected.", "{sessions}",
29+
otel.instrument(beanWildflyDeployment, "wildfly.session.rejected", "The number of sessions that have been rejected.", "{session}",
2830
["deployment": { mbean -> mbean.name().getKeyProperty("deployment")}],
2931
"rejectedSessions", otel.&longCounterCallback)
3032

33+
34+
3135
def beanWildflyHttpListener = otel.mbeans("jboss.as:subsystem=undertow,server=*,http-listener=*")
32-
otel.instrument(beanWildflyHttpListener, "wildfly.request.count", "The number of requests received.", "{requests}",
36+
otel.instrument(beanWildflyHttpListener, "wildfly.request.count", "The number of requests received.", "{request}",
3337
["server": { mbean -> mbean.name().getKeyProperty("server")}, "listener": { mbean -> mbean.name().getKeyProperty("http-listener")}],
3438
"requestCount", otel.&longCounterCallback)
3539
otel.instrument(beanWildflyHttpListener, "wildfly.request.time", "The total amount of time spent on requests.", "ns",
3640
["server": { mbean -> mbean.name().getKeyProperty("server")}, "listener": { mbean -> mbean.name().getKeyProperty("http-listener")}],
3741
"processingTime", otel.&longCounterCallback)
38-
otel.instrument(beanWildflyHttpListener, "wildfly.request.server_error", "The number of requests that have resulted in a 5xx response.", "{requests}",
42+
otel.instrument(beanWildflyHttpListener, "wildfly.request.server_error", "The number of requests that have resulted in a 5xx response.", "{request}",
3943
["server": { mbean -> mbean.name().getKeyProperty("server")}, "listener": { mbean -> mbean.name().getKeyProperty("http-listener")}],
4044
"errorCount", otel.&longCounterCallback)
41-
otel.instrument(beanWildflyHttpListener, "wildfly.network.io", "The number of bytes transmitted.", "by",
45+
otel.instrument(beanWildflyHttpListener, "wildfly.network.io", "The number of bytes transmitted.", "By",
4246
["server": { mbean -> mbean.name().getKeyProperty("server")}, "listener": { mbean -> mbean.name().getKeyProperty("http-listener")}],
4347
["bytesSent":["state":{"out"}], "bytesReceived":["state":{"in"}]],
4448
otel.&longCounterCallback)
4549

4650
def beanWildflyDataSource = otel.mbeans("jboss.as:subsystem=datasources,data-source=*,statistics=pool")
47-
otel.instrument(beanWildflyDataSource, "wildfly.jdbc.connection.open", "The number of open jdbc connections.", "{connections}",
51+
otel.instrument(beanWildflyDataSource, "wildfly.jdbc.connection.open", "The number of open jdbc connections.", "{connection}",
4852
["data_source": { mbean -> mbean.name().getKeyProperty("data-source")}],
4953
["ActiveCount":["state":{"active"}], "IdleCount":["state":{"idle"}]],
5054
otel.&longUpDownCounterCallback)
51-
otel.instrument(beanWildflyDataSource, "wildfly.jdbc.request.wait", "The number of jdbc connections that had to wait before opening.", "{requests}",
55+
otel.instrument(beanWildflyDataSource, "wildfly.jdbc.request.wait", "The number of jdbc connections that had to wait before opening.", "{request}",
5256
["data_source": { mbean -> mbean.name().getKeyProperty("data-source")}],
5357
"WaitCount", otel.&longCounterCallback)
5458

5559
def beanWildflyTransaction = otel.mbean("jboss.as:subsystem=transactions")
56-
otel.instrument(beanWildflyTransaction, "wildfly.jdbc.transaction.count", "The number of transactions created.", "{transactions}",
60+
otel.instrument(beanWildflyTransaction, "wildfly.jdbc.transaction.count", "The number of transactions created.", "{transaction}",
5761
"numberOfTransactions", otel.&longCounterCallback)
58-
otel.instrument(beanWildflyTransaction, "wildfly.jdbc.rollback.count", "The number of transactions rolled back.", "{transactions}",
62+
otel.instrument(beanWildflyTransaction, "wildfly.jdbc.rollback.count", "The number of transactions rolled back.", "{transaction}",
5963
["numberOfSystemRollbacks":["cause":{"system"}], "numberOfResourceRollbacks":["cause":{"resource"}], "numberOfApplicationRollbacks":["cause":{"application"}]],
6064
otel.&longCounterCallback)

jmx-scraper/README.md

+42
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,48 @@ The JMX MBeans and their metric mappings are defined in YAML and reuse implement
77
This is currently a work-in-progress component not ready to be used in production.
88
The end goal is to provide an alternative to the [JMX Gatherer](../jmx-metrics/README.md) utility.
99

10+
## Usage
11+
12+
The general command to invoke JMX scraper is `java -jar scraper.jar <config>`, where `scraper.jar`
13+
is the `build/libs/opentelemetry-jmx-scraper-<version>.jar` packaged binary when building this module.
14+
15+
Minimal configuration required
16+
17+
- `otel.jmx.service.url` for example `service:jmx:rmi:///jndi/rmi://server:9999/jmxrmi` for `server`
18+
host on port `9999` with RMI JMX connector.
19+
- `otel.jmx.target.system` or `otel.jmx.custom.scraping.config`
20+
21+
Configuration can be provided through:
22+
23+
- command line arguments:
24+
`java -jar scraper.jar --config otel.jmx.service.url=service:jmx:rmi:///jndi/rmi://tomcat:9010/jmxrmi otel.jmx.target.system=tomcat`.
25+
- command line arguments JVM system properties:
26+
`java -Dotel.jmx.service.url=service:jmx:rmi:///jndi/rmi://tomcat:9010/jmxrmi -Dotel.jmx.target.system=tomcat -jar scraper.jar`.
27+
- java properties file: `java -jar scraper.jar -config config.properties`.
28+
- stdin: `java -jar scraper.jar -config -` where `otel.jmx.target.system=tomcat` and
29+
`otel.jmx.service.url=service:jmx:rmi:///jndi/rmi://tomcat:9010/jmxrmi` is written to stdin.
30+
31+
TODO: update this once autoconfiguration is supported
32+
33+
### Configuration reference
34+
35+
TODO
36+
37+
### Extra libraries in classpath
38+
39+
By default, only the RMI JMX connector is provided by the JVM, so it might be required to add extra
40+
libraries in the classpath when connecting to remote JVMs that are not directly accessible with RMI.
41+
42+
One known example of this is the Wildfly/Jboss HTTP management interface for which the `jboss-client.jar`
43+
needs to be used to support `otel.jmx.service.url` = `service:jmx:remote+http://server:9999`.
44+
45+
When doing so, the `java -jar` command can´t be used, we have to provide the classpath with
46+
`-cp`/`--class-path`/`-classpath` option and provide the main class file name:
47+
48+
```
49+
java -cp scraper.jar:jboss-client.jar io.opentelemetry.contrib.jmxscraper.JmxScraper <config>
50+
```
51+
1052
## Component owners
1153

1254
- [Jason Plumb](https://github.com/breedx-splk), Splunk

jmx-scraper/build.gradle.kts

+9-10
Original file line numberDiff line numberDiff line change
@@ -60,9 +60,16 @@ tasks {
6060

6161
withType<Test>().configureEach {
6262
dependsOn(shadowJar)
63-
dependsOn(named("appJar"))
6463
systemProperty("shadow.jar.path", shadowJar.get().archiveFile.get().asFile.absolutePath)
65-
systemProperty("app.jar.path", named<Jar>("appJar").get().archiveFile.get().asFile.absolutePath)
64+
65+
val testAppTask = project("test-app").tasks.named<Jar>("jar")
66+
dependsOn(testAppTask)
67+
systemProperty("app.jar.path", testAppTask.get().archiveFile.get().asFile.absolutePath)
68+
69+
val testWarTask = project("test-webapp").tasks.named<Jar>("war")
70+
dependsOn(testWarTask)
71+
systemProperty("app.war.path", testWarTask.get().archiveFile.get().asFile.absolutePath)
72+
6673
systemProperty("gradle.project.version", "${project.version}")
6774
}
6875

@@ -74,14 +81,6 @@ tasks {
7481
}
7582
}
7683

77-
tasks.register<Jar>("appJar") {
78-
from(sourceSets.get("integrationTest").output)
79-
archiveClassifier.set("app")
80-
manifest {
81-
attributes["Main-Class"] = "io.opentelemetry.contrib.jmxscraper.TestApp"
82-
}
83-
}
84-
8584
// Don't publish non-shadowed jar (shadowJar is in shadowRuntimeElements)
8685
with(components["java"] as AdhocComponentWithVariants) {
8786
configurations.forEach {

jmx-scraper/src/integrationTest/java/io/opentelemetry/contrib/jmxscraper/JmxConnectorBuilderTest.java

+3-2
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,8 @@ void loginPwdAuth() {
5050
testConnector(
5151
() ->
5252
JmxConnectorBuilder.createNew(app.getHost(), app.getMappedPort(port))
53-
.userCredentials(login, pwd)
53+
.withUser(login)
54+
.withPassword(pwd)
5455
.build());
5556
}
5657
}
@@ -75,7 +76,7 @@ private static void testConnector(ConnectorSupplier connectorSupplier) {
7576
.satisfies(
7677
connection -> {
7778
try {
78-
ObjectName name = new ObjectName(TestApp.OBJECT_NAME);
79+
ObjectName name = new ObjectName("io.opentelemetry.test:name=TestApp");
7980
Object value = connection.getAttribute(name, "IntValue");
8081
assertThat(value).isEqualTo(42);
8182
} catch (Exception e) {

jmx-scraper/src/integrationTest/java/io/opentelemetry/contrib/jmxscraper/JmxScraperContainer.java

+67-7
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,12 @@ public class JmxScraperContainer extends GenericContainer<JmxScraperContainer> {
2626
private String serviceUrl;
2727
private int intervalMillis;
2828
private final Set<String> customYamlFiles;
29+
private String user;
30+
private String password;
31+
private final List<String> extraJars;
2932

30-
public JmxScraperContainer(String otlpEndpoint) {
31-
super("openjdk:8u342-jre-slim");
33+
public JmxScraperContainer(String otlpEndpoint, String baseImage) {
34+
super(baseImage);
3235

3336
String scraperJarPath = System.getProperty("shadow.jar.path");
3437
assertThat(scraperJarPath).isNotNull();
@@ -42,6 +45,7 @@ public JmxScraperContainer(String otlpEndpoint) {
4245
this.targetSystems = new HashSet<>();
4346
this.customYamlFiles = new HashSet<>();
4447
this.intervalMillis = 1000;
48+
this.extraJars = new ArrayList<>();
4549
}
4650

4751
@CanIgnoreReturnValue
@@ -57,11 +61,52 @@ public JmxScraperContainer withIntervalMillis(int intervalMillis) {
5761
}
5862

5963
@CanIgnoreReturnValue
60-
public JmxScraperContainer withService(String host, int port) {
64+
public JmxScraperContainer withRmiServiceUrl(String host, int port) {
6165
// TODO: adding a way to provide 'host:port' syntax would make this easier for end users
62-
this.serviceUrl =
66+
return withServiceUrl(
6367
String.format(
64-
Locale.getDefault(), "service:jmx:rmi:///jndi/rmi://%s:%d/jmxrmi", host, port);
68+
Locale.getDefault(), "service:jmx:rmi:///jndi/rmi://%s:%d/jmxrmi", host, port));
69+
}
70+
71+
@CanIgnoreReturnValue
72+
public JmxScraperContainer withServiceUrl(String serviceUrl) {
73+
this.serviceUrl = serviceUrl;
74+
return this;
75+
}
76+
77+
/**
78+
* Sets JMX user login
79+
*
80+
* @param user user login
81+
* @return this
82+
*/
83+
@CanIgnoreReturnValue
84+
public JmxScraperContainer withUser(String user) {
85+
this.user = user;
86+
return this;
87+
}
88+
89+
/**
90+
* Sets JMX password
91+
*
92+
* @param password user password
93+
* @return this
94+
*/
95+
@CanIgnoreReturnValue
96+
public JmxScraperContainer withPassword(String password) {
97+
this.password = password;
98+
return this;
99+
}
100+
101+
/**
102+
* Adds path to an extra jar for classpath
103+
*
104+
* @param jarPath path to an extra jar that should be added to jmx scraper classpath
105+
* @return this
106+
*/
107+
@CanIgnoreReturnValue
108+
public JmxScraperContainer withExtraJar(String jarPath) {
109+
this.extraJars.add(jarPath);
65110
return this;
66111
}
67112

@@ -89,15 +134,30 @@ public void start() {
89134
arguments.add("-Dotel.jmx.service.url=" + serviceUrl);
90135
arguments.add("-Dotel.jmx.interval.milliseconds=" + intervalMillis);
91136

137+
if (user != null) {
138+
arguments.add("-Dotel.jmx.username=" + user);
139+
}
140+
if (password != null) {
141+
arguments.add("-Dotel.jmx.password=" + password);
142+
}
143+
92144
if (!customYamlFiles.isEmpty()) {
93145
for (String yaml : customYamlFiles) {
94146
this.withCopyFileToContainer(MountableFile.forClasspathResource(yaml), yaml);
95147
}
96148
arguments.add("-Dotel.jmx.config=" + String.join(",", customYamlFiles));
97149
}
98150

99-
arguments.add("-jar");
100-
arguments.add("/scraper.jar");
151+
if (extraJars.isEmpty()) {
152+
// using "java -jar" to start
153+
arguments.add("-jar");
154+
arguments.add("/scraper.jar");
155+
} else {
156+
// using "java -cp" to start
157+
arguments.add("-cp");
158+
arguments.add("/scraper.jar:" + String.join(":", extraJars));
159+
arguments.add("io.opentelemetry.contrib.jmxscraper.JmxScraper");
160+
}
101161

102162
this.withCommand(arguments.toArray(new String[0]));
103163

jmx-scraper/src/integrationTest/java/io/opentelemetry/contrib/jmxscraper/TestAppContainer.java

+2-3
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
import org.testcontainers.shaded.com.google.errorprone.annotations.CanIgnoreReturnValue;
2222
import org.testcontainers.utility.MountableFile;
2323

24-
/** Test container that allows to execute {@link TestApp} in an isolated container */
24+
/** Test container that allows to execute TestApp in an isolated container */
2525
public class TestAppContainer extends GenericContainer<TestAppContainer> {
2626

2727
private final Map<String, String> properties;
@@ -38,8 +38,7 @@ public TestAppContainer() {
3838

3939
this.withCopyFileToContainer(MountableFile.forHostPath(appJar), "/app.jar")
4040
.waitingFor(
41-
Wait.forLogMessage(TestApp.APP_STARTED_MSG + "\\n", 1)
42-
.withStartupTimeout(Duration.ofSeconds(5)))
41+
Wait.forLogMessage("app started\\n", 1).withStartupTimeout(Duration.ofSeconds(5)))
4342
.withCommand("java", "-jar", "/app.jar");
4443
}
4544

jmx-scraper/src/integrationTest/java/io/opentelemetry/contrib/jmxscraper/target_systems/ActiveMqIntegrationTest.java

+7-2
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,16 @@
1010
import static org.assertj.core.api.Assertions.entry;
1111

1212
import io.opentelemetry.contrib.jmxscraper.JmxScraperContainer;
13+
import java.nio.file.Path;
1314
import java.time.Duration;
1415
import org.testcontainers.containers.GenericContainer;
1516
import org.testcontainers.containers.wait.strategy.Wait;
1617
import org.testcontainers.images.builder.ImageFromDockerfile;
1718

1819
public class ActiveMqIntegrationTest extends TargetSystemIntegrationTest {
1920

21+
private static final int ACTIVEMQ_PORT = 61616;
22+
2023
@Override
2124
protected GenericContainer<?> createTargetContainer(int jmxPort) {
2225
return new GenericContainer<>(
@@ -25,11 +28,13 @@ protected GenericContainer<?> createTargetContainer(int jmxPort) {
2528
builder -> builder.from("apache/activemq-classic:5.18.6").build()))
2629
.withEnv("JAVA_TOOL_OPTIONS", genericJmxJvmArguments(jmxPort))
2730
.withStartupTimeout(Duration.ofMinutes(2))
28-
.waitingFor(Wait.forListeningPort());
31+
.withExposedPorts(ACTIVEMQ_PORT, jmxPort)
32+
.waitingFor(Wait.forListeningPorts(ACTIVEMQ_PORT, jmxPort));
2933
}
3034

3135
@Override
32-
protected JmxScraperContainer customizeScraperContainer(JmxScraperContainer scraper) {
36+
protected JmxScraperContainer customizeScraperContainer(
37+
JmxScraperContainer scraper, GenericContainer<?> target, Path tempDir) {
3338
return scraper.withTargetSystem("activemq");
3439
}
3540

0 commit comments

Comments
 (0)