Skip to content

Commit

Permalink
start wiring things together
Browse files Browse the repository at this point in the history
  • Loading branch information
SylvainJuge committed Sep 17, 2024
1 parent e4f4769 commit 71e6258
Show file tree
Hide file tree
Showing 6 changed files with 126 additions and 57 deletions.
2 changes: 1 addition & 1 deletion dependencyManagement/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ data class DependencySet(val group: String, val version: String, val modules: Li
val dependencyVersions = hashMapOf<String, String>()
rootProject.extra["versions"] = dependencyVersions

val otelInstrumentationVersion = "2.8.0-alpha"
val otelInstrumentationVersion = "2.9.0-alpha-SNAPSHOT"

val DEPENDENCY_BOMS = listOf(
"com.fasterxml.jackson:jackson-bom:2.17.2",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package io.opentelemetry.contrib.jmxscraper.target_systems;

public class JvmIntegrationTest {
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,11 @@
import io.opentelemetry.proto.collector.metrics.v1.ExportMetricsServiceResponse;
import io.opentelemetry.proto.collector.metrics.v1.MetricsServiceGrpc;
import java.io.IOException;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.LinkedBlockingDeque;
Expand All @@ -32,10 +35,13 @@
import org.testcontainers.containers.GenericContainer;
import org.testcontainers.containers.Network;
import org.testcontainers.containers.output.Slf4jLogConsumer;
import org.testcontainers.containers.wait.strategy.Wait;
import org.testcontainers.utility.MountableFile;

public abstract class TargetSystemIntegrationTest {

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

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

// assert on received metrics
protected abstract String getTargetSystem();

private static Network network;
private static OtlpGrpcServer otlpServer;
private GenericContainer<?> target;
private GenericContainer<?> scraper;

// private static final String OTLP_HOST = "host.testcontainers.internal";
private static final String OTLP_HOST = "host.testcontainers.internal";
private static final int JMX_PORT = 9999;

@BeforeAll
Expand All @@ -61,7 +67,7 @@ static void beforeAll() {
otlpServer = new OtlpGrpcServer();
otlpServer.start();
Testcontainers.exposeHostPorts(otlpServer.httpPort());
// String otlpEndpoint = "http://" + OTLP_HOST + ":" + otlpServer.httpPort();
otlpEndpoint = "http://" + OTLP_HOST + ":" + otlpServer.httpPort();
}

@AfterAll
Expand Down Expand Up @@ -98,21 +104,21 @@ void endToEndTest() {
.withNetworkAliases("target_system");
target.start();

String targetHost = target.getHost();
Integer targetPort = target.getMappedPort(JMX_PORT);
logger.info(
"Target system started, JMX port: {} mapped to {}:{}",
JMX_PORT,
target.getHost(),
target.getMappedPort(JMX_PORT));
"Target system started, JMX port: {} mapped to {}:{}", JMX_PORT, targetHost, targetPort);

scraper = createScraperContainer();
scraper =
createScraperContainer(otlpEndpoint, getTargetSystem(), null, "target_system", JMX_PORT);
logger.info(
"starting scraper with JVM arguments : {}", String.join(" ", scraper.getCommandParts()));

// TODO: start scraper container
// scraper.start();
scraper.start();

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

private static GenericContainer<?> createScraperContainer() {
return null;
protected GenericContainer<?> createScraperContainer(
String otlpEndpoint,
String targetSystem,
String customYaml,
String targetHost,
int targetPort) {

String scraperJarPath = System.getProperty("shadow.jar.path");
assertThat(scraperJarPath).isNotNull();

// TODO: adding a way to provide 'host:port' syntax would make this easier for common use
String url =
String.format(
Locale.getDefault(),
"service:jmx:rmi:///jndi/rmi://%s:%d/jmxrmi",
targetHost,
targetPort);

// for now only configure through JVM args
List<String> arguments =
new ArrayList<>(
Arrays.asList(
"java",
"-Dotel.exporter.otlp.endpoint=" + otlpEndpoint,
"-Dotel.jmx.target.system=" + targetSystem,
"-Dotel.jmx.interval.milliseconds=1000",
"-Dotel.jmx.service.url=" + url,
"-jar",
"/scraper.jar"));

GenericContainer<?> scraper =
new GenericContainer<>("openjdk:8u272-jre-slim")
.withNetwork(network)
.withCopyFileToContainer(MountableFile.forHostPath(scraperJarPath), "/scraper.jar")
.withLogConsumer(new Slf4jLogConsumer(logger))
.waitingFor(
Wait.forLogMessage(".*JMX scraping started.*", 1)
.withStartupTimeout(Duration.ofSeconds(10)));

if (customYaml != null) {
arguments.add("-Dotel.jmx.config=/custom.yaml");
scraper.withCopyFileToContainer(
MountableFile.forClasspathResource(customYaml), "/custom.yaml");
}

scraper.withCommand(arguments.toArray(new String[0]));

return scraper;
}

private static class OtlpGrpcServer extends ServerExtension {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@

public class TomcatIntegrationTest extends TargetSystemIntegrationTest {

@Override
protected String getTargetSystem() {
return "tomcat";
}

@Override
protected GenericContainer<?> createTargetContainer(int jmxPort) {
return new GenericContainer<>(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,29 +5,38 @@

package io.opentelemetry.contrib.jmxscraper;

import io.opentelemetry.api.GlobalOpenTelemetry;
import io.opentelemetry.contrib.jmxscraper.client.JmxRemoteClient;
import io.opentelemetry.contrib.jmxscraper.config.ConfigurationException;
import io.opentelemetry.contrib.jmxscraper.config.JmxScraperConfig;
import io.opentelemetry.contrib.jmxscraper.config.JmxScraperConfigFactory;
import io.opentelemetry.contrib.jmxscraper.jmx.JmxClient;
import io.opentelemetry.instrumentation.jmx.engine.JmxMetricInsight;
import io.opentelemetry.instrumentation.jmx.engine.MetricConfiguration;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Properties;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.logging.Logger;
import javax.annotation.Nullable;
import javax.management.MBeanServerConnection;
import javax.management.remote.JMXConnector;

public class JmxScraper {
private static final Logger logger = Logger.getLogger(JmxScraper.class.getName());
private static final int EXECUTOR_TERMINATION_TIMEOUT_MS = 5000;
private final ScheduledExecutorService exec = Executors.newSingleThreadScheduledExecutor();
private final JmxScraperConfig config;
private final JmxRemoteClient client;
private final JmxMetricInsight service;
@Nullable private MBeanServerConnection connection;

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

Runtime.getRuntime()
.addShutdownHook(
new Thread() {
@Override
public void run() {
jmxScraper.shutdown();
}
});
Runtime.getRuntime().addShutdownHook(new Thread(jmxScraper::shutdown));
} catch (ArgumentsParsingException e) {
System.err.println(
"Usage: java -jar <path_to_jmxscraper.jar> "
Expand Down Expand Up @@ -106,31 +108,36 @@ private static void loadPropertiesFromPath(Properties props, String path)
JmxScraper(JmxScraperConfig config) throws ConfigurationException {
this.config = config;

try {
@SuppressWarnings("unused") // TODO: Temporary
JmxClient jmxClient = new JmxClient(config);
} catch (MalformedURLException e) {
throw new ConfigurationException("Malformed serviceUrl: ", e);
String serviceUrl = config.getServiceUrl();
if (serviceUrl == null) {
throw new ConfigurationException("missing service URL");
}
int interval = config.getIntervalMilliseconds();
if (interval < 0) {
throw new ConfigurationException("interval must be positive");
}
this.client = JmxRemoteClient.createNew(serviceUrl);
this.service = JmxMetricInsight.createService(GlobalOpenTelemetry.get(), interval);
}

@SuppressWarnings("FutureReturnValueIgnored") // TODO: Temporary
private void start() {
exec.scheduleWithFixedDelay(
() -> {
logger.fine("JMX scraping triggered");
// try {
// runner.run();
// } catch (Throwable e) {
// logger.log(Level.SEVERE, "Error gathering JMX metrics", e);
// }
},
0,
config.getIntervalMilliseconds(),
TimeUnit.MILLISECONDS);
try {
JMXConnector connector = client.connect();
connection = connector.getMBeanServerConnection();
} catch (IOException e) {
throw new IllegalStateException(e);
}
service.startRemote(getMetricConfig(config), () -> Collections.singletonList(connection));
logger.info("JMX scraping started");
}

@SuppressWarnings("unused")
private static MetricConfiguration getMetricConfig(JmxScraperConfig config) {
MetricConfiguration metricConfig = new MetricConfiguration();

return metricConfig;
}

private void shutdown() {
logger.info("Shutting down JmxScraper and exporting final metrics.");
// Prevent new tasks to be submitted
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@
import java.security.Provider;
import java.security.Security;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.management.remote.JMXConnector;
import javax.management.remote.JMXConnectorFactory;
Expand All @@ -30,21 +30,23 @@ public class JmxRemoteClient {

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

private final String host;
private final int port;
private final JMXServiceURL url;
@Nullable private String userName;
@Nullable private String password;
@Nullable private String profile;
@Nullable private String realm;
private boolean sslRegistry;

private JmxRemoteClient(@Nonnull String host, int port) {
this.host = host;
this.port = port;
private JmxRemoteClient(JMXServiceURL url) {
this.url = url;
}

public static JmxRemoteClient createNew(String host, int port) {
return new JmxRemoteClient(host, port);
return new JmxRemoteClient(buildUrl(host, port));
}

public static JmxRemoteClient createNew(String url) {
return new JmxRemoteClient(buildUrl(url));
}

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

JMXServiceURL url = buildUrl(host, port);
try {
if (sslRegistry) {
return doConnectSslRegistry(url, env);
Expand All @@ -132,14 +133,14 @@ public JMXConnector doConnectSslRegistry(JMXServiceURL url, Map<String, Object>
}

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

private static JMXServiceURL buildUrl(String url) {
try {
return new JMXServiceURL(sb.toString());
return new JMXServiceURL(url);
} catch (MalformedURLException e) {
throw new IllegalArgumentException("invalid url", e);
}
Expand Down

0 comments on commit 71e6258

Please sign in to comment.