Skip to content

Commit 71e6258

Browse files
committed
start wiring things together
1 parent e4f4769 commit 71e6258

File tree

6 files changed

+126
-57
lines changed

6 files changed

+126
-57
lines changed

dependencyManagement/build.gradle.kts

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ data class DependencySet(val group: String, val version: String, val modules: Li
77
val dependencyVersions = hashMapOf<String, String>()
88
rootProject.extra["versions"] = dependencyVersions
99

10-
val otelInstrumentationVersion = "2.8.0-alpha"
10+
val otelInstrumentationVersion = "2.9.0-alpha-SNAPSHOT"
1111

1212
val DEPENDENCY_BOMS = listOf(
1313
"com.fasterxml.jackson:jackson-bom:2.17.2",
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
package io.opentelemetry.contrib.jmxscraper.target_systems;
2+
3+
public class JvmIntegrationTest {
4+
}

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

+66-14
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,11 @@
1616
import io.opentelemetry.proto.collector.metrics.v1.ExportMetricsServiceResponse;
1717
import io.opentelemetry.proto.collector.metrics.v1.MetricsServiceGrpc;
1818
import java.io.IOException;
19+
import java.time.Duration;
1920
import java.util.ArrayList;
21+
import java.util.Arrays;
2022
import java.util.List;
23+
import java.util.Locale;
2124
import java.util.concurrent.BlockingQueue;
2225
import java.util.concurrent.ExecutionException;
2326
import java.util.concurrent.LinkedBlockingDeque;
@@ -32,10 +35,13 @@
3235
import org.testcontainers.containers.GenericContainer;
3336
import org.testcontainers.containers.Network;
3437
import org.testcontainers.containers.output.Slf4jLogConsumer;
38+
import org.testcontainers.containers.wait.strategy.Wait;
39+
import org.testcontainers.utility.MountableFile;
3540

3641
public abstract class TargetSystemIntegrationTest {
3742

3843
private static final Logger logger = LoggerFactory.getLogger(TargetSystemIntegrationTest.class);
44+
private static String otlpEndpoint;
3945

4046
/**
4147
* Create target system container
@@ -45,14 +51,14 @@ public abstract class TargetSystemIntegrationTest {
4551
*/
4652
protected abstract GenericContainer<?> createTargetContainer(int jmxPort);
4753

48-
// assert on received metrics
54+
protected abstract String getTargetSystem();
4955

5056
private static Network network;
5157
private static OtlpGrpcServer otlpServer;
5258
private GenericContainer<?> target;
5359
private GenericContainer<?> scraper;
5460

55-
// private static final String OTLP_HOST = "host.testcontainers.internal";
61+
private static final String OTLP_HOST = "host.testcontainers.internal";
5662
private static final int JMX_PORT = 9999;
5763

5864
@BeforeAll
@@ -61,7 +67,7 @@ static void beforeAll() {
6167
otlpServer = new OtlpGrpcServer();
6268
otlpServer.start();
6369
Testcontainers.exposeHostPorts(otlpServer.httpPort());
64-
// String otlpEndpoint = "http://" + OTLP_HOST + ":" + otlpServer.httpPort();
70+
otlpEndpoint = "http://" + OTLP_HOST + ":" + otlpServer.httpPort();
6571
}
6672

6773
@AfterAll
@@ -98,21 +104,21 @@ void endToEndTest() {
98104
.withNetworkAliases("target_system");
99105
target.start();
100106

107+
String targetHost = target.getHost();
108+
Integer targetPort = target.getMappedPort(JMX_PORT);
101109
logger.info(
102-
"Target system started, JMX port: {} mapped to {}:{}",
103-
JMX_PORT,
104-
target.getHost(),
105-
target.getMappedPort(JMX_PORT));
110+
"Target system started, JMX port: {} mapped to {}:{}", JMX_PORT, targetHost, targetPort);
106111

107-
scraper = createScraperContainer();
112+
scraper =
113+
createScraperContainer(otlpEndpoint, getTargetSystem(), null, "target_system", JMX_PORT);
114+
logger.info(
115+
"starting scraper with JVM arguments : {}", String.join(" ", scraper.getCommandParts()));
108116

109-
// TODO: start scraper container
110-
// scraper.start();
117+
scraper.start();
111118

112119
// TODO : wait for metrics to be sent and add assertions on what is being captured
113120
// for now we just test that we can connect to remote JMX using our client.
114-
try (JMXConnector connector =
115-
JmxRemoteClient.createNew(target.getHost(), target.getMappedPort(JMX_PORT)).connect()) {
121+
try (JMXConnector connector = JmxRemoteClient.createNew(targetHost, targetPort).connect()) {
116122
assertThat(connector.getMBeanServerConnection()).isNotNull();
117123
} catch (IOException e) {
118124
throw new RuntimeException(e);
@@ -121,8 +127,54 @@ void endToEndTest() {
121127
assertThat(otlpServer.getMetrics()).isEmpty();
122128
}
123129

124-
private static GenericContainer<?> createScraperContainer() {
125-
return null;
130+
protected GenericContainer<?> createScraperContainer(
131+
String otlpEndpoint,
132+
String targetSystem,
133+
String customYaml,
134+
String targetHost,
135+
int targetPort) {
136+
137+
String scraperJarPath = System.getProperty("shadow.jar.path");
138+
assertThat(scraperJarPath).isNotNull();
139+
140+
// TODO: adding a way to provide 'host:port' syntax would make this easier for common use
141+
String url =
142+
String.format(
143+
Locale.getDefault(),
144+
"service:jmx:rmi:///jndi/rmi://%s:%d/jmxrmi",
145+
targetHost,
146+
targetPort);
147+
148+
// for now only configure through JVM args
149+
List<String> arguments =
150+
new ArrayList<>(
151+
Arrays.asList(
152+
"java",
153+
"-Dotel.exporter.otlp.endpoint=" + otlpEndpoint,
154+
"-Dotel.jmx.target.system=" + targetSystem,
155+
"-Dotel.jmx.interval.milliseconds=1000",
156+
"-Dotel.jmx.service.url=" + url,
157+
"-jar",
158+
"/scraper.jar"));
159+
160+
GenericContainer<?> scraper =
161+
new GenericContainer<>("openjdk:8u272-jre-slim")
162+
.withNetwork(network)
163+
.withCopyFileToContainer(MountableFile.forHostPath(scraperJarPath), "/scraper.jar")
164+
.withLogConsumer(new Slf4jLogConsumer(logger))
165+
.waitingFor(
166+
Wait.forLogMessage(".*JMX scraping started.*", 1)
167+
.withStartupTimeout(Duration.ofSeconds(10)));
168+
169+
if (customYaml != null) {
170+
arguments.add("-Dotel.jmx.config=/custom.yaml");
171+
scraper.withCopyFileToContainer(
172+
MountableFile.forClasspathResource(customYaml), "/custom.yaml");
173+
}
174+
175+
scraper.withCommand(arguments.toArray(new String[0]));
176+
177+
return scraper;
126178
}
127179

128180
private static class OtlpGrpcServer extends ServerExtension {

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

+5
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,11 @@
1212

1313
public class TomcatIntegrationTest extends TargetSystemIntegrationTest {
1414

15+
@Override
16+
protected String getTargetSystem() {
17+
return "tomcat";
18+
}
19+
1520
@Override
1621
protected GenericContainer<?> createTargetContainer(int jmxPort) {
1722
return new GenericContainer<>(

jmx-scraper/src/main/java/io/opentelemetry/contrib/jmxscraper/JmxScraper.java

+35-28
Original file line numberDiff line numberDiff line change
@@ -5,29 +5,38 @@
55

66
package io.opentelemetry.contrib.jmxscraper;
77

8+
import io.opentelemetry.api.GlobalOpenTelemetry;
9+
import io.opentelemetry.contrib.jmxscraper.client.JmxRemoteClient;
810
import io.opentelemetry.contrib.jmxscraper.config.ConfigurationException;
911
import io.opentelemetry.contrib.jmxscraper.config.JmxScraperConfig;
1012
import io.opentelemetry.contrib.jmxscraper.config.JmxScraperConfigFactory;
11-
import io.opentelemetry.contrib.jmxscraper.jmx.JmxClient;
13+
import io.opentelemetry.instrumentation.jmx.engine.JmxMetricInsight;
14+
import io.opentelemetry.instrumentation.jmx.engine.MetricConfiguration;
1215
import java.io.DataInputStream;
1316
import java.io.IOException;
1417
import java.io.InputStream;
15-
import java.net.MalformedURLException;
1618
import java.nio.file.Files;
1719
import java.nio.file.Paths;
1820
import java.util.Arrays;
21+
import java.util.Collections;
1922
import java.util.List;
2023
import java.util.Properties;
2124
import java.util.concurrent.Executors;
2225
import java.util.concurrent.ScheduledExecutorService;
2326
import java.util.concurrent.TimeUnit;
2427
import java.util.logging.Logger;
28+
import javax.annotation.Nullable;
29+
import javax.management.MBeanServerConnection;
30+
import javax.management.remote.JMXConnector;
2531

2632
public class JmxScraper {
2733
private static final Logger logger = Logger.getLogger(JmxScraper.class.getName());
2834
private static final int EXECUTOR_TERMINATION_TIMEOUT_MS = 5000;
2935
private final ScheduledExecutorService exec = Executors.newSingleThreadScheduledExecutor();
3036
private final JmxScraperConfig config;
37+
private final JmxRemoteClient client;
38+
private final JmxMetricInsight service;
39+
@Nullable private MBeanServerConnection connection;
3140

3241
/**
3342
* Main method to create and run a {@link JmxScraper} instance.
@@ -43,14 +52,7 @@ public static void main(String[] args) {
4352
JmxScraper jmxScraper = new JmxScraper(config);
4453
jmxScraper.start();
4554

46-
Runtime.getRuntime()
47-
.addShutdownHook(
48-
new Thread() {
49-
@Override
50-
public void run() {
51-
jmxScraper.shutdown();
52-
}
53-
});
55+
Runtime.getRuntime().addShutdownHook(new Thread(jmxScraper::shutdown));
5456
} catch (ArgumentsParsingException e) {
5557
System.err.println(
5658
"Usage: java -jar <path_to_jmxscraper.jar> "
@@ -106,31 +108,36 @@ private static void loadPropertiesFromPath(Properties props, String path)
106108
JmxScraper(JmxScraperConfig config) throws ConfigurationException {
107109
this.config = config;
108110

109-
try {
110-
@SuppressWarnings("unused") // TODO: Temporary
111-
JmxClient jmxClient = new JmxClient(config);
112-
} catch (MalformedURLException e) {
113-
throw new ConfigurationException("Malformed serviceUrl: ", e);
111+
String serviceUrl = config.getServiceUrl();
112+
if (serviceUrl == null) {
113+
throw new ConfigurationException("missing service URL");
114+
}
115+
int interval = config.getIntervalMilliseconds();
116+
if (interval < 0) {
117+
throw new ConfigurationException("interval must be positive");
114118
}
119+
this.client = JmxRemoteClient.createNew(serviceUrl);
120+
this.service = JmxMetricInsight.createService(GlobalOpenTelemetry.get(), interval);
115121
}
116122

117-
@SuppressWarnings("FutureReturnValueIgnored") // TODO: Temporary
118123
private void start() {
119-
exec.scheduleWithFixedDelay(
120-
() -> {
121-
logger.fine("JMX scraping triggered");
122-
// try {
123-
// runner.run();
124-
// } catch (Throwable e) {
125-
// logger.log(Level.SEVERE, "Error gathering JMX metrics", e);
126-
// }
127-
},
128-
0,
129-
config.getIntervalMilliseconds(),
130-
TimeUnit.MILLISECONDS);
124+
try {
125+
JMXConnector connector = client.connect();
126+
connection = connector.getMBeanServerConnection();
127+
} catch (IOException e) {
128+
throw new IllegalStateException(e);
129+
}
130+
service.startRemote(getMetricConfig(config), () -> Collections.singletonList(connection));
131131
logger.info("JMX scraping started");
132132
}
133133

134+
@SuppressWarnings("unused")
135+
private static MetricConfiguration getMetricConfig(JmxScraperConfig config) {
136+
MetricConfiguration metricConfig = new MetricConfiguration();
137+
138+
return metricConfig;
139+
}
140+
134141
private void shutdown() {
135142
logger.info("Shutting down JmxScraper and exporting final metrics.");
136143
// Prevent new tasks to be submitted

jmx-scraper/src/main/java/io/opentelemetry/contrib/jmxscraper/client/JmxRemoteClient.java

+15-14
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,10 @@
1111
import java.security.Provider;
1212
import java.security.Security;
1313
import java.util.HashMap;
14+
import java.util.Locale;
1415
import java.util.Map;
1516
import java.util.logging.Level;
1617
import java.util.logging.Logger;
17-
import javax.annotation.Nonnull;
1818
import javax.annotation.Nullable;
1919
import javax.management.remote.JMXConnector;
2020
import javax.management.remote.JMXConnectorFactory;
@@ -30,21 +30,23 @@ public class JmxRemoteClient {
3030

3131
private static final Logger logger = Logger.getLogger(JmxRemoteClient.class.getName());
3232

33-
private final String host;
34-
private final int port;
33+
private final JMXServiceURL url;
3534
@Nullable private String userName;
3635
@Nullable private String password;
3736
@Nullable private String profile;
3837
@Nullable private String realm;
3938
private boolean sslRegistry;
4039

41-
private JmxRemoteClient(@Nonnull String host, int port) {
42-
this.host = host;
43-
this.port = port;
40+
private JmxRemoteClient(JMXServiceURL url) {
41+
this.url = url;
4442
}
4543

4644
public static JmxRemoteClient createNew(String host, int port) {
47-
return new JmxRemoteClient(host, port);
45+
return new JmxRemoteClient(buildUrl(host, port));
46+
}
47+
48+
public static JmxRemoteClient createNew(String url) {
49+
return new JmxRemoteClient(buildUrl(url));
4850
}
4951

5052
@CanIgnoreReturnValue
@@ -109,7 +111,6 @@ public JMXConnector connect() throws IOException {
109111
logger.log(Level.WARNING, "SASL unsupported in current environment: " + e.getMessage(), e);
110112
}
111113

112-
JMXServiceURL url = buildUrl(host, port);
113114
try {
114115
if (sslRegistry) {
115116
return doConnectSslRegistry(url, env);
@@ -132,14 +133,14 @@ public JMXConnector doConnectSslRegistry(JMXServiceURL url, Map<String, Object>
132133
}
133134

134135
private static JMXServiceURL buildUrl(String host, int port) {
135-
StringBuilder sb = new StringBuilder("service:jmx:rmi:///jndi/rmi://");
136-
if (host != null) {
137-
sb.append(host);
138-
}
139-
sb.append(":").append(port).append("/jmxrmi");
136+
return buildUrl(
137+
String.format(
138+
Locale.getDefault(), "service:jmx:rmi:///jndi/rmi://%s:%d/jmxrmi", host, port));
139+
}
140140

141+
private static JMXServiceURL buildUrl(String url) {
141142
try {
142-
return new JMXServiceURL(sb.toString());
143+
return new JMXServiceURL(url);
143144
} catch (MalformedURLException e) {
144145
throw new IllegalArgumentException("invalid url", e);
145146
}

0 commit comments

Comments
 (0)