jmx scraper align custom yaml config (#1678)

This commit is contained in:
SylvainJuge 2025-01-29 18:35:44 +01:00 committed by GitHub
parent 4cd6a0a777
commit 1615862cee
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 83 additions and 32 deletions

View File

@ -16,7 +16,7 @@ Minimal configuration required
- `otel.jmx.service.url` for example `service:jmx:rmi:///jndi/rmi://server:9999/jmxrmi` for `server`
host on port `9999` with RMI JMX connector.
- `otel.jmx.target.system` or `otel.jmx.custom.scraping.config`
- `otel.jmx.target.system` or `otel.jmx.config`
Configuration can be provided through:
@ -36,13 +36,13 @@ For example the `otel.jmx.service.url` option can be set with the `OTEL_JMX_SERV
## Configuration reference
| config option | description |
|-----------------------------------|----------------------------------------------------------------------------------------------|
| `otel.jmx.service.url` | mandatory JMX URL to connect to the remote JVM |
| `otel.jmx.target.system` | comma-separated list of systems to monitor, mandatory unless a custom configuration is used |
| `otel.jmx.custom.scraping.config` | path to a custom YAML metrics definition, mandatory when `otel.jmx.target.system` is not set |
| `otel.jmx.username` | user name for JMX connection, mandatory when JMX authentication is enabled on target JVM |
| `otel.jmx.password` | password for JMX connection, mandatory when JMX authentication is enabled on target JVM |
| config option | description |
|--------------------------|---------------------------------------------------------------------------------------------------------------------|
| `otel.jmx.service.url` | mandatory JMX URL to connect to the remote JVM |
| `otel.jmx.target.system` | comma-separated list of systems to monitor, mandatory unless a custom configuration is used |
| `otel.jmx.config` | comma-separated list of paths to custom YAML metrics definition, mandatory when `otel.jmx.target.system` is not set |
| `otel.jmx.username` | user name for JMX connection, mandatory when JMX authentication is enabled on target JVM |
| `otel.jmx.password` | password for JMX connection, mandatory when JMX authentication is enabled on target JVM |
Supported values for `otel.jmx.target.system`:

View File

@ -8,16 +8,20 @@ package io.opentelemetry.contrib.jmxscraper.config;
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
import io.opentelemetry.sdk.autoconfigure.spi.ConfigurationException;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.logging.Logger;
import javax.annotation.Nullable;
/** This class keeps application settings */
public class JmxScraperConfig {
private static final Logger logger = Logger.getLogger(JmxScraperConfig.class.getName());
// metric sdk configuration
static final String METRIC_EXPORT_INTERVAL = "otel.metric.export.interval";
@ -25,8 +29,13 @@ public class JmxScraperConfig {
static final String JMX_INTERVAL_LEGACY = "otel.jmx.interval.milliseconds";
static final String JMX_SERVICE_URL = "otel.jmx.service.url";
// TODO: align with instrumentation 'otel.jmx.config' + support list of values
static final String JMX_CUSTOM_CONFIG = "otel.jmx.custom.scraping.config";
// the same as config option defined in instrumentation
static final String JMX_CONFIG = "otel.jmx.config";
// not documented on purpose as it's deprecated
static final String JMX_CONFIG_LEGACY = "otel.jmx.custom.scraping.config";
static final String JMX_TARGET_SYSTEM = "otel.jmx.target.system";
static final String JMX_USERNAME = "otel.jmx.username";
@ -55,7 +64,7 @@ public class JmxScraperConfig {
private String serviceUrl = "";
@Nullable private String customJmxScrapingConfigPath;
private List<String> jmxConfig = Collections.emptyList();
private Set<String> targetSystems = Collections.emptySet();
@ -76,9 +85,8 @@ public class JmxScraperConfig {
return serviceUrl;
}
@Nullable
public String getCustomJmxScrapingConfigPath() {
return customJmxScrapingConfigPath;
public List<String> getJmxConfig() {
return jmxConfig;
}
public Set<String> getTargetSystems() {
@ -136,12 +144,25 @@ public class JmxScraperConfig {
}
scraperConfig.serviceUrl = serviceUrl;
// TODO: we could support multiple values
String customConfig = config.getString(JMX_CUSTOM_CONFIG, "");
List<String> jmxConfig = config.getList(JMX_CONFIG);
List<String> targetSystem = config.getList(JMX_TARGET_SYSTEM);
if (targetSystem.isEmpty() && customConfig.isEmpty()) {
// providing compatibility with the deprecated 'otel.jmx.custom.scraping.config' config option
String jmxConfigDeprecated = config.getString(JMX_CONFIG_LEGACY);
if (jmxConfigDeprecated != null) {
logger.warning(
JMX_CONFIG_LEGACY
+ " deprecated option is used, replacing with '"
+ JMX_CONFIG
+ "' is recommended");
List<String> list = new ArrayList<>(jmxConfig);
list.add(jmxConfigDeprecated);
jmxConfig = list;
}
if (targetSystem.isEmpty() && jmxConfig.isEmpty()) {
throw new ConfigurationException(
"at least one of '" + JMX_TARGET_SYSTEM + "' or '" + JMX_CUSTOM_CONFIG + "' must be set");
"at least one of '" + JMX_TARGET_SYSTEM + "' or '" + JMX_CONFIG + "' must be set");
}
targetSystem.forEach(
s -> {
@ -149,8 +170,9 @@ public class JmxScraperConfig {
throw new ConfigurationException("unsupported target system: '" + s + "'");
}
});
scraperConfig.customJmxScrapingConfigPath = customConfig;
scraperConfig.targetSystems = new HashSet<>(targetSystem);
scraperConfig.jmxConfig = Collections.unmodifiableList(jmxConfig);
scraperConfig.targetSystems = Collections.unmodifiableSet(new HashSet<>(targetSystem));
scraperConfig.username = config.getString("otel.jmx.username");
scraperConfig.password = config.getString("otel.jmx.password");

View File

@ -5,7 +5,8 @@
package io.opentelemetry.contrib.jmxscraper.config;
import static io.opentelemetry.contrib.jmxscraper.config.JmxScraperConfig.JMX_CUSTOM_CONFIG;
import static io.opentelemetry.contrib.jmxscraper.config.JmxScraperConfig.JMX_CONFIG;
import static io.opentelemetry.contrib.jmxscraper.config.JmxScraperConfig.JMX_CONFIG_LEGACY;
import static io.opentelemetry.contrib.jmxscraper.config.JmxScraperConfig.JMX_PASSWORD;
import static io.opentelemetry.contrib.jmxscraper.config.JmxScraperConfig.JMX_REALM;
import static io.opentelemetry.contrib.jmxscraper.config.JmxScraperConfig.JMX_REGISTRY_SSL;
@ -22,6 +23,8 @@ import java.time.Duration;
import java.util.Properties;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
class JmxScraperConfigTest {
private static Properties validProperties;
@ -31,7 +34,7 @@ class JmxScraperConfigTest {
validProperties = new Properties();
validProperties.setProperty(
JMX_SERVICE_URL, "jservice:jmx:rmi:///jndi/rmi://localhost:9010/jmxrmi");
validProperties.setProperty(JMX_CUSTOM_CONFIG, "/path/to/config.yaml");
validProperties.setProperty(JMX_CONFIG, "/path/to/config.yaml");
validProperties.setProperty(JMX_TARGET_SYSTEM, "tomcat, activemq");
validProperties.setProperty(JMX_REGISTRY_SSL, "true");
validProperties.setProperty(JMX_USERNAME, "some-user");
@ -50,7 +53,7 @@ class JmxScraperConfigTest {
// Then
assertThat(config.getServiceUrl())
.isEqualTo("jservice:jmx:rmi:///jndi/rmi://localhost:9010/jmxrmi");
assertThat(config.getCustomJmxScrapingConfigPath()).isEqualTo("/path/to/config.yaml");
assertThat(config.getJmxConfig()).containsExactly("/path/to/config.yaml");
assertThat(config.getTargetSystems()).containsExactlyInAnyOrder("tomcat", "activemq");
assertThat(config.getSamplingInterval()).isEqualTo(Duration.ofSeconds(10));
assertThat(config.getUsername()).isEqualTo("some-user");
@ -59,12 +62,17 @@ class JmxScraperConfigTest {
assertThat(config.getRealm()).isEqualTo("some-realm");
}
@Test
void shouldCreateMinimalValidConfiguration() {
@ParameterizedTest(name = "custom yaml = {arguments}")
@ValueSource(booleans = {true, false})
public void shouldCreateMinimalValidConfiguration(boolean customYaml) {
// Given
Properties properties = new Properties();
properties.setProperty(JMX_SERVICE_URL, "jservice:jmx:rmi:///jndi/rmi://localhost:9010/jmxrmi");
properties.setProperty(JMX_CUSTOM_CONFIG, "/file.properties");
if (customYaml) {
properties.setProperty(JMX_CONFIG, "/file.yaml");
} else {
properties.setProperty(JMX_TARGET_SYSTEM, "tomcat");
}
// When
JmxScraperConfig config = fromConfig(TestUtil.configProperties(properties));
@ -72,8 +80,15 @@ class JmxScraperConfigTest {
// Then
assertThat(config.getServiceUrl())
.isEqualTo("jservice:jmx:rmi:///jndi/rmi://localhost:9010/jmxrmi");
assertThat(config.getCustomJmxScrapingConfigPath()).isEqualTo("/file.properties");
assertThat(config.getTargetSystems()).isEmpty();
if (customYaml) {
assertThat(config.getJmxConfig()).containsExactly("/file.yaml");
assertThat(config.getTargetSystems()).isEmpty();
} else {
assertThat(config.getJmxConfig()).isEmpty();
assertThat(config.getTargetSystems()).containsExactly("tomcat");
}
assertThat(config.getSamplingInterval())
.describedAs("default sampling interval must align to default metric export interval")
.isEqualTo(Duration.ofMinutes(1));
@ -83,6 +98,21 @@ class JmxScraperConfigTest {
assertThat(config.getRealm()).isNull();
}
@Test
void legacyCustomScrapingConfig() {
// Given
Properties properties = new Properties();
properties.setProperty(JMX_SERVICE_URL, "jservice:jmx:rmi:///jndi/rmi://localhost:9010/jmxrmi");
properties.setProperty(JMX_CONFIG_LEGACY, "/file.yaml");
properties.setProperty(JMX_CONFIG, "/another.yaml");
// When
JmxScraperConfig config = fromConfig(TestUtil.configProperties(properties));
// Then
assertThat(config.getJmxConfig()).containsOnly("/file.yaml", "/another.yaml");
}
@Test
void shouldFailValidation_missingServiceUrl() {
// Given
@ -99,14 +129,13 @@ class JmxScraperConfigTest {
void shouldFailValidation_missingConfigPathAndTargetSystem() {
// Given
Properties properties = (Properties) validProperties.clone();
properties.remove(JMX_CUSTOM_CONFIG);
properties.remove(JMX_CONFIG);
properties.remove(JMX_TARGET_SYSTEM);
// When and Then
assertThatThrownBy(() -> fromConfig(TestUtil.configProperties(properties)))
.isInstanceOf(ConfigurationException.class)
.hasMessage(
"at least one of 'otel.jmx.target.system' or 'otel.jmx.custom.scraping.config' must be set");
.hasMessage("at least one of 'otel.jmx.target.system' or 'otel.jmx.config' must be set");
}
@Test

View File

@ -1,5 +1,5 @@
otel.jmx.service.url=service:jmx:rmi:///jndi/rmi://myhost:12345/jmxrmi
otel.jmx.custom.scraping.config=/my/scraping-config.yaml
otel.jmx.config=/my/scraping-config.yaml
otel.jmx.target.system=jvm,cassandra
otel.metrics.exporter=otlp
otel.metric.export.interval=1000