-
Notifications
You must be signed in to change notification settings - Fork 169
jmx scraper with sdk autoconfig #1651
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
Changes from 19 commits
e7747ee
d3afdcd
3f17316
252e554
3fbf71d
45ce9be
96c6250
1a4ab3e
6793bbe
6924d9c
ce43846
1271bba
62d41a5
44e8f6e
0f25c60
20c5cc9
7288ef3
55a1e0e
dc064db
857ac4d
646e156
d237a56
851ad44
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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; | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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; | ||
|
|
@@ -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<>(); | ||
| } | ||
|
|
||
|
|
@@ -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 | ||
|
|
@@ -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); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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; | ||
|
|
@@ -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; | ||
|
|
@@ -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; | ||
|
|
@@ -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); | ||
SylvainJuge marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| 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); | ||
SylvainJuge marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| 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
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 | ||
|
|
@@ -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); | ||
SylvainJuge marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| } | ||
| } | ||
|
|
||
| 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); | ||
| } | ||
| } | ||
|
|
||
|
|
||
This file was deleted.
Uh oh!
There was an error while loading. Please reload this page.