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

Open
wants to merge 22 commits into
base: main
Choose a base branch
from

Conversation

SylvainJuge
Copy link
Contributor

@SylvainJuge SylvainJuge commented Jan 17, 2025

Refactor jmx scraper configuration to use autoconfiguration.

This provides the following benefits:

  • allow using environment variables in addition to java system properties
  • override the default configuration using SDK API : config provider and config customizer

Now only the the otel.metric.export.interval SDK configuration should be used to define the metric sampling and export interval, compatibility is provided with existing otel.jmx.interval.milliseconds when needed.

When otel.metric.export.interval is not set, the default of 1 minute is applied which remains consistent with the SDK implementation and specification.

The ability to provide configuration through standard input, external properties and the JVM system properties have been preserved.

To be discussed

  • Should we align the default exporter to be otlp to align with the SDK default value, or should we keep the logging as we have currently when it's not set. Setting logging here by default mostly benefits onboarding as users can see what is being captured, but requires to explicitly include otlp which is set by default for the SDK autoconfiguration.
    • Answer: let's align

Follow-up tasks

In instrumentation (JMX insights), the otel.jmx.config option provides the path to a list of custom YAML metrics definitions, we currently use otel.jmx.custom.scraping.config which does not allow to use a list. Dealing with this was left outside of this PR and will be handled by this #1662.

@@ -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.

Comment on lines +102 to +112
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);
}
}
}
}
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.


package io.opentelemetry.contrib.jmxscraper.config;

public class ConfigurationException extends Exception {
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] there is already a similarly named runtime exception in the SDK autoconfiguration, so reusing it makes more sense and avoid ambiguity.

Comment on lines +123 to +127
if (exportInterval == null || exportInterval.isNegative() || exportInterval.isZero()) {
// if not explicitly set, use default value of 1 minute as defined in specification
scraperConfig.samplingInterval = Duration.ofMinutes(1);
} else {
scraperConfig.samplingInterval = exportInterval;
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] we have to do this because we don't have access to the default value that will be used by the SDK when an explicit value is not present.


@Override
public Map<String, String> apply(ConfigProperties config) {
Map<String, String> result = new HashMap<>();
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] the returned map contains the configuration items that will be explicitly set/overriden so that they can be later read by the SDK initialization. In addition to that we use this to build an instance of JmxScraperConfig from the provided ConfigProperties that will later be used to configure the jmx scraper.

@breedx-splk
Copy link
Contributor

Regarding the question of default logger -- I think we should continue to be consistent with the sdk and default to exporting otlp to localhost. 👍🏻 If a user needs to see details/logging they should be able to opt-into that I think.

@SylvainJuge
Copy link
Contributor Author

Regarding the question of default logger -- I think we should continue to be consistent with the sdk and default to exporting otlp to localhost. 👍🏻 If a user needs to see details/logging they should be able to opt-into that I think.

I agree here, staying consistent with the SDK defaults would provide a more consistent experience.

Also:

  • using "logging" mostly benefits the onboarding/setup phase, which is relatively short compared to the time it's being used with a real exporter, so we should make the most common use-case the easiest to use. Logging here is mostly relevant to investigate/debug.
  • this makes the jmx scraper "do nothing by default", which makes it appear "broken by default unless you configure it", in particular when the logs/standard outpout is not directly visible, it would be easy to not notice that nothing is being sent to OTLP.
  • if it was "otlp,logging" by default it would have at least do something useful by default, but then having to explicitly disable logging for production use is an extra burden.

Thus I'll align with the SDK unless we have a clear use-case that justifies it.

Comment on lines 44 to 45
| `otel.jmx.username` | user name for JMX connection |
| `otel.jmx.password` | password for JMX connection |
Copy link
Contributor

Choose a reason for hiding this comment

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

Doesn't have to be in this PR of course, but it might be nice to have an indication of which configs are required/optional and/or what the default values are.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I tried to do that with dedicated column but this wasn't looking great because

  • there is only a single mandatory option for the remote URL
  • other arguments are mandatory depending on the context (authentication enabled on remote JVM, if there is already a target system set or a custom yaml file used).

This is why I decided to document this mostly in the config option descriptions, I've clarified that for jmx authentication in d237a56

* Exception indicating something is wrong with the provided arguments or reading the configuration
* from them
*/
public class InvalidArgumentException extends Exception {
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this really providing any benefit over the built-in jdk IllegalArgumentException?

Copy link
Contributor Author

Choose a reason for hiding this comment

The 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 IllegalArgumentException which is a runtime exception if you think that makes things less confusing.

targetSystem.forEach(
s -> {
if (!AVAILABLE_TARGET_SYSTEMS.contains(s)) {
throw new ConfigurationException("unsupported target system: '" + s + "'");
Copy link
Contributor

Choose a reason for hiding this comment

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

Doesn't this get hidden because it's buried within a closure within forEach?

Could consider switching to if(!targetSystem.stream().allMatch(AVAILABLE_TARGET_SYSTEMS.contains(s)) ... or similar? Maybe a filter if the detailed logging of which system is necessary.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The exception does not get hidden here, this case is covered by the JmxScraperConfigTest#shouldFailValidation_invalidTargetSystem test case.

Copy link
Contributor

@breedx-splk breedx-splk left a comment

Choose a reason for hiding this comment

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

Had a few minor comments but LGTM! Stoked to have this autoconfig power.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants