Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

jmx scraper with sdk autoconfig #1651

Merged
merged 23 commits into from
Jan 27, 2025
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ public class JmxScraperContainer extends GenericContainer<JmxScraperContainer> {
private final String endpoint;
private final Set<String> targetSystems;
private String serviceUrl;
private int intervalMillis;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[for reviewer] this is never changed for tests, so we can use the minimal hard-coded value of 1s.

private final Set<String> customYamlFiles;
private String user;
private String password;
Expand All @@ -44,7 +43,6 @@ public JmxScraperContainer(String otlpEndpoint, String baseImage) {
this.endpoint = otlpEndpoint;
this.targetSystems = new HashSet<>();
this.customYamlFiles = new HashSet<>();
this.intervalMillis = 1000;
this.extraJars = new ArrayList<>();
}

Expand All @@ -54,12 +52,6 @@ public JmxScraperContainer withTargetSystem(String targetSystem) {
return this;
}

@CanIgnoreReturnValue
public JmxScraperContainer withIntervalMillis(int intervalMillis) {
this.intervalMillis = intervalMillis;
return this;
}

@CanIgnoreReturnValue
public JmxScraperContainer withRmiServiceUrl(String host, int port) {
// TODO: adding a way to provide 'host:port' syntax would make this easier for end users
Expand Down Expand Up @@ -132,7 +124,8 @@ public void start() {
throw new IllegalStateException("Missing service URL");
}
arguments.add("-Dotel.jmx.service.url=" + serviceUrl);
arguments.add("-Dotel.jmx.interval.milliseconds=" + intervalMillis);
// always use a very short export interval for testing
arguments.add("-Dotel.metric.export.interval=1s");

if (user != null) {
arguments.add("-Dotel.jmx.username=" + user);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,8 @@ public class ArgumentsParsingException extends Exception {
public ArgumentsParsingException(String msg) {
super(msg);
}

public ArgumentsParsingException(String msg, Throwable cause) {
super(msg, cause);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,14 @@
package io.opentelemetry.contrib.jmxscraper;

import io.opentelemetry.api.GlobalOpenTelemetry;
import io.opentelemetry.contrib.jmxscraper.config.ConfigurationException;
import io.opentelemetry.contrib.jmxscraper.config.JmxScraperConfig;
import io.opentelemetry.contrib.jmxscraper.config.PropertiesCustomizer;
import io.opentelemetry.contrib.jmxscraper.config.PropertiesSupplier;
import io.opentelemetry.instrumentation.jmx.engine.JmxMetricInsight;
import io.opentelemetry.instrumentation.jmx.engine.MetricConfiguration;
import io.opentelemetry.instrumentation.jmx.yaml.RuleParser;
import io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdk;
import io.opentelemetry.sdk.autoconfigure.spi.ConfigurationException;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
Expand All @@ -19,9 +22,11 @@
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Properties;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.management.MBeanServerConnection;
import javax.management.remote.JMXConnector;
Expand All @@ -30,8 +35,6 @@ public class JmxScraper {
private static final Logger logger = Logger.getLogger(JmxScraper.class.getName());
private static final String CONFIG_ARG = "-config";

private static final String OTEL_AUTOCONFIGURE = "otel.java.global-autoconfigure.enabled";

private final JmxConnectorBuilder client;
private final JmxMetricInsight service;
private final JmxScraperConfig config;
Expand All @@ -43,58 +46,77 @@ public class JmxScraper {
*
* @param args - must be of the form "-config {jmx_config_path,'-'}"
*/
@SuppressWarnings({"SystemOut", "SystemExitOutsideMain"})
@SuppressWarnings("SystemExitOutsideMain")
public static void main(String[] args) {

// enable SDK auto-configure if not explicitly set by user
// TODO: refactor this to use AutoConfiguredOpenTelemetrySdk
if (System.getProperty(OTEL_AUTOCONFIGURE) == null) {
System.setProperty(OTEL_AUTOCONFIGURE, "true");
}
// set log format
System.setProperty("java.util.logging.SimpleFormatter.format", "%1$tF %1$tT %4$s %5$s%n");

try {
JmxScraperConfig config =
JmxScraperConfig.fromProperties(parseArgs(Arrays.asList(args)), System.getProperties());
// propagate effective user-provided configuration to JVM system properties
// this also enables SDK auto-configuration to use those properties
config.propagateSystemProperties();
Properties argsConfig = parseArgs(Arrays.asList(args));
propagateToSystemProperties(argsConfig);

// auto-configure and register SDK
PropertiesCustomizer configCustomizer = new PropertiesCustomizer();
AutoConfiguredOpenTelemetrySdk.builder()
.addPropertiesSupplier(new PropertiesSupplier(argsConfig))
.addPropertiesCustomizer(configCustomizer)
.setResultAsGlobal()
.build();

JmxScraperConfig scraperConfig = configCustomizer.getScraperConfig();

long exportSeconds = scraperConfig.getSamplingInterval().toMillis() / 1000;
logger.log(Level.INFO, "metrics export interval (seconds) = " + exportSeconds);

JmxMetricInsight service =
JmxMetricInsight.createService(
GlobalOpenTelemetry.get(), config.getIntervalMilliseconds());
JmxConnectorBuilder connectorBuilder = JmxConnectorBuilder.createNew(config.getServiceUrl());
GlobalOpenTelemetry.get(), scraperConfig.getSamplingInterval().toMillis());
JmxConnectorBuilder connectorBuilder =
JmxConnectorBuilder.createNew(scraperConfig.getServiceUrl());

Optional.ofNullable(config.getUsername()).ifPresent(connectorBuilder::withUser);
Optional.ofNullable(config.getPassword()).ifPresent(connectorBuilder::withPassword);
Optional.ofNullable(scraperConfig.getUsername()).ifPresent(connectorBuilder::withUser);
Optional.ofNullable(scraperConfig.getPassword()).ifPresent(connectorBuilder::withPassword);

JmxScraper jmxScraper = new JmxScraper(connectorBuilder, service, config);
JmxScraper jmxScraper = new JmxScraper(connectorBuilder, service, scraperConfig);
jmxScraper.start();

} catch (ConfigurationException e) {
logger.log(Level.SEVERE, "ERROR: invalid configuration ", e);
System.exit(1);
} catch (ArgumentsParsingException e) {
System.err.println("ERROR: " + e.getMessage());
System.err.println(
logger.log(Level.SEVERE, "ERROR: invalid configuration provided through arguments", e);
logger.info(
"Usage: java -jar <path_to_jmxscraper.jar> "
+ "-config <path_to_config.properties or - for stdin>");
System.exit(1);
} catch (ConfigurationException e) {
System.err.println(e.getMessage());
System.exit(1);
} catch (IOException e) {
System.err.println("Unable to connect " + e.getMessage());
logger.log(Level.SEVERE, "Unable to connect ", e);
System.exit(2);
} catch (RuntimeException e) {
e.printStackTrace(System.err);
logger.log(Level.SEVERE, e.getMessage(), e);
System.exit(3);
}
}

// package private for testing
static void propagateToSystemProperties(Properties properties) {
for (Map.Entry<Object, Object> entry : properties.entrySet()) {
String key = entry.getKey().toString();
String value = entry.getValue().toString();
if (key.startsWith("javax.net.ssl.keyStore") || key.startsWith("javax.net.ssl.trustStore")) {
if (System.getProperty(key) == null) {
System.setProperty(key, value);
}
}
}
}
Comment on lines +102 to +112
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[for reviewer] propagation of system properties is only used to set the java keystore/truststore options from the program arguments/standard input to the global JVM settings, so it was simpler to move it here. In practice this will likely be tested when we add tests with custom keystore/truststore by setting those configuration options from the standard input or the properties file.


/**
* Create {@link Properties} from command line options
*
* @param args application commandline arguments
*/
static Properties parseArgs(List<String> args)
throws ArgumentsParsingException, ConfigurationException {
static Properties parseArgs(List<String> args) throws ArgumentsParsingException {

if (args.isEmpty()) {
// empty properties from stdin or external file
Expand All @@ -116,23 +138,24 @@ static Properties parseArgs(List<String> args)
}
}

private static Properties loadPropertiesFromStdin() throws ConfigurationException {
private static Properties loadPropertiesFromStdin() throws ArgumentsParsingException {
Properties properties = new Properties();
try (InputStream is = new DataInputStream(System.in)) {
properties.load(is);
return properties;
} catch (IOException e) {
throw new ConfigurationException("Failed to read config properties from stdin", e);
throw new ArgumentsParsingException("Failed to read config properties from stdin", e);
}
}

private static Properties loadPropertiesFromPath(String path) throws ConfigurationException {
private static Properties loadPropertiesFromPath(String path) throws ArgumentsParsingException {
Properties properties = new Properties();
try (InputStream is = Files.newInputStream(Paths.get(path))) {
properties.load(is);
return properties;
} catch (IOException e) {
throw new ConfigurationException("Failed to read config properties file: '" + path + "'", e);
throw new ArgumentsParsingException(
"Failed to read config properties file: '" + path + "'", e);
}
}

Expand Down

This file was deleted.

Loading
Loading