-
Notifications
You must be signed in to change notification settings - Fork 134
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
base: main
Are you sure you want to change the base?
Changes from all commits
e7747ee
d3afdcd
3f17316
252e554
3fbf71d
45ce9be
96c6250
1a4ab3e
6793bbe
6924d9c
ce43846
1271bba
62d41a5
44e8f6e
0f25c60
20c5cc9
7288ef3
55a1e0e
dc064db
857ac4d
646e156
d237a56
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
/* | ||
* Copyright The OpenTelemetry Authors | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
package io.opentelemetry.contrib.jmxscraper; | ||
|
||
/** | ||
* Exception indicating something is wrong with the provided arguments or reading the configuration | ||
* from them | ||
*/ | ||
public class InvalidArgumentException extends Exception { | ||
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. Is this really providing any benefit over the built-in jdk 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. The main benefit is that it's a checked exception and that it forces the caller to take care of it. However, given the very limited scope this has, we could definitely replace it with the 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. Yeah, I think IllegalArgumentException was designed to be thrown as result of method parameters validation, not the application cmd arguments. |
||
|
||
private static final long serialVersionUID = 0L; | ||
|
||
public InvalidArgumentException(String msg) { | ||
super(msg); | ||
} | ||
|
||
public InvalidArgumentException(String msg, Throwable cause) { | ||
super(msg, cause); | ||
} | ||
} |
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,69 +46,88 @@ 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 (ArgumentsParsingException e) { | ||
System.err.println("ERROR: " + e.getMessage()); | ||
System.err.println( | ||
} catch (ConfigurationException e) { | ||
logger.log(Level.SEVERE, "invalid configuration ", e); | ||
System.exit(1); | ||
} catch (InvalidArgumentException e) { | ||
logger.log(Level.SEVERE, "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
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 InvalidArgumentException { | ||
|
||
if (args.isEmpty()) { | ||
// empty properties from stdin or external file | ||
// config could still be provided through JVM system properties | ||
return new Properties(); | ||
} | ||
if (args.size() != 2) { | ||
throw new ArgumentsParsingException("Exactly two arguments expected, got " + args.size()); | ||
throw new InvalidArgumentException("Exactly two arguments expected, got " + args.size()); | ||
} | ||
if (!args.get(0).equalsIgnoreCase(CONFIG_ARG)) { | ||
throw new ArgumentsParsingException("Unexpected first argument must be '" + CONFIG_ARG + "'"); | ||
throw new InvalidArgumentException("Unexpected first argument must be '" + CONFIG_ARG + "'"); | ||
} | ||
|
||
String path = args.get(1); | ||
|
@@ -116,27 +138,30 @@ static Properties parseArgs(List<String> args) | |
} | ||
} | ||
|
||
private static Properties loadPropertiesFromStdin() throws ConfigurationException { | ||
private static Properties loadPropertiesFromStdin() throws InvalidArgumentException { | ||
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); | ||
// an IO error is very unlikely here | ||
throw new InvalidArgumentException("Failed to read config properties from stdin", e); | ||
} | ||
} | ||
|
||
private static Properties loadPropertiesFromPath(String path) throws ConfigurationException { | ||
private static Properties loadPropertiesFromPath(String path) throws InvalidArgumentException { | ||
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 InvalidArgumentException( | ||
"Failed to read config properties file: '" + path + "'", e); | ||
} | ||
} | ||
|
||
JmxScraper(JmxConnectorBuilder client, JmxMetricInsight service, JmxScraperConfig config) { | ||
private JmxScraper( | ||
JmxConnectorBuilder client, JmxMetricInsight service, JmxScraperConfig config) { | ||
this.client = client; | ||
this.service = service; | ||
this.config = config; | ||
|
This file was deleted.
There was a problem hiding this comment.
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.