-
Notifications
You must be signed in to change notification settings - Fork 169
jmx-scraper implement stable service.instance.id #2270
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 8 commits
7d6e440
f46cf5c
cd6143e
614ce54
12acf64
04c675e
3fdb909
48bbe80
487ca2d
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 |
|---|---|---|
|
|
@@ -5,13 +5,14 @@ | |
|
|
||
| package io.opentelemetry.contrib.jmxscraper; | ||
|
|
||
| import static io.opentelemetry.semconv.incubating.ServiceIncubatingAttributes.SERVICE_INSTANCE_ID; | ||
| import static java.util.Arrays.asList; | ||
| import static java.util.Collections.singletonList; | ||
| import static java.util.Optional.ofNullable; | ||
| import static java.util.logging.Level.INFO; | ||
| import static java.util.logging.Level.SEVERE; | ||
|
|
||
| import io.opentelemetry.api.GlobalOpenTelemetry; | ||
| import io.opentelemetry.api.OpenTelemetry; | ||
| import io.opentelemetry.api.common.Attributes; | ||
| import io.opentelemetry.contrib.jmxscraper.config.JmxScraperConfig; | ||
| import io.opentelemetry.contrib.jmxscraper.config.PropertiesCustomizer; | ||
| import io.opentelemetry.contrib.jmxscraper.config.PropertiesSupplier; | ||
|
|
@@ -20,22 +21,30 @@ | |
| import io.opentelemetry.instrumentation.jmx.yaml.RuleParser; | ||
| import io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdk; | ||
| import io.opentelemetry.sdk.autoconfigure.spi.ConfigurationException; | ||
| import io.opentelemetry.sdk.resources.Resource; | ||
| import java.io.DataInputStream; | ||
| import java.io.IOException; | ||
| import java.io.InputStream; | ||
| import java.nio.charset.StandardCharsets; | ||
| import java.nio.file.Files; | ||
| import java.nio.file.Path; | ||
| import java.nio.file.Paths; | ||
| import java.util.ArrayList; | ||
| import java.util.Arrays; | ||
| import java.util.List; | ||
| import java.util.Map; | ||
| import java.util.Properties; | ||
| import java.util.UUID; | ||
| import java.util.concurrent.atomic.AtomicBoolean; | ||
| import java.util.logging.Level; | ||
| import java.util.logging.Logger; | ||
| import javax.annotation.Nullable; | ||
| import javax.management.MBeanServerConnection; | ||
| import javax.management.ObjectName; | ||
| import javax.management.remote.JMXConnector; | ||
|
|
||
| public final class JmxScraper { | ||
|
|
||
| private static final Logger logger = Logger.getLogger(JmxScraper.class.getName()); | ||
| private static final String CONFIG_ARG = "-config"; | ||
| private static final String TEST_ARG = "-test"; | ||
|
|
@@ -64,36 +73,40 @@ public static void main(String[] args) { | |
| Properties argsConfig = argsToConfig(effectiveArgs); | ||
| propagateToSystemProperties(argsConfig); | ||
|
|
||
| // auto-configure and register SDK | ||
| PropertiesCustomizer configCustomizer = new PropertiesCustomizer(); | ||
| AutoConfiguredOpenTelemetrySdk.builder() | ||
| .addPropertiesSupplier(new PropertiesSupplier(argsConfig)) | ||
| .addPropertiesCustomizer(configCustomizer) | ||
| .setResultAsGlobal() | ||
| .build(); | ||
|
|
||
| // auto-configure SDK | ||
| OpenTelemetry openTelemetry = | ||
| AutoConfiguredOpenTelemetrySdk.builder() | ||
| .addPropertiesSupplier(new PropertiesSupplier(argsConfig)) | ||
| .addPropertiesCustomizer(configCustomizer) | ||
| // we rely on the config customizer to be executed first to get effective config | ||
| .addResourceCustomizer( | ||
| (resource, configProperties) -> { | ||
| UUID instanceId = | ||
| getRemoteServiceInstanceId(configCustomizer.getConnectorBuilder()); | ||
| if (resource.getAttribute(SERVICE_INSTANCE_ID) != null || instanceId == null) { | ||
| return resource; | ||
| } | ||
| logger.log(Level.INFO, "remote service instance ID: " + instanceId); | ||
| return resource.merge( | ||
| Resource.create(Attributes.of(SERVICE_INSTANCE_ID, instanceId.toString()))); | ||
| }) | ||
| .build() | ||
| .getOpenTelemetrySdk(); | ||
|
|
||
| // scraper configuration and connector builder are built using effective SDK configuration | ||
| // thus we have to get it after the SDK is built | ||
| JmxScraperConfig scraperConfig = configCustomizer.getScraperConfig(); | ||
|
|
||
| long exportSeconds = scraperConfig.getSamplingInterval().toMillis() / 1000; | ||
| logger.log(INFO, "metrics export interval (seconds) = " + exportSeconds); | ||
|
|
||
| JmxMetricInsight service = | ||
| JmxMetricInsight.createService( | ||
| GlobalOpenTelemetry.get(), scraperConfig.getSamplingInterval().toMillis()); | ||
| JmxConnectorBuilder connectorBuilder = | ||
| JmxConnectorBuilder.createNew(scraperConfig.getServiceUrl()); | ||
|
|
||
| ofNullable(scraperConfig.getUsername()).ifPresent(connectorBuilder::withUser); | ||
| ofNullable(scraperConfig.getPassword()).ifPresent(connectorBuilder::withPassword); | ||
|
|
||
| if (scraperConfig.isRegistrySsl()) { | ||
| connectorBuilder.withSslRegistry(); | ||
| } | ||
| JmxConnectorBuilder connectorBuilder = configCustomizer.getConnectorBuilder(); | ||
|
|
||
| if (testMode) { | ||
| System.exit(testConnection(connectorBuilder) ? 0 : 1); | ||
| } else { | ||
| JmxScraper jmxScraper = new JmxScraper(connectorBuilder, service, scraperConfig); | ||
| JmxMetricInsight jmxInsight = | ||
| JmxMetricInsight.createService( | ||
| openTelemetry, scraperConfig.getSamplingInterval().toMillis()); | ||
| JmxScraper jmxScraper = new JmxScraper(connectorBuilder, jmxInsight, scraperConfig); | ||
| jmxScraper.start(); | ||
| } | ||
| } catch (ConfigurationException e) { | ||
|
|
@@ -117,7 +130,6 @@ public static void main(String[] args) { | |
|
|
||
| private static boolean testConnection(JmxConnectorBuilder connectorBuilder) { | ||
| try (JMXConnector connector = connectorBuilder.build()) { | ||
|
|
||
| MBeanServerConnection connection = connector.getMBeanServerConnection(); | ||
| Integer mbeanCount = connection.getMBeanCount(); | ||
| if (mbeanCount > 0) { | ||
|
|
@@ -133,6 +145,30 @@ private static boolean testConnection(JmxConnectorBuilder connectorBuilder) { | |
| } | ||
| } | ||
|
|
||
| @Nullable | ||
| static UUID getRemoteServiceInstanceId(JmxConnectorBuilder connectorBuilder) { | ||
SylvainJuge marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| try (JMXConnector jmxConnector = connectorBuilder.build()) { | ||
| MBeanServerConnection connection = jmxConnector.getMBeanServerConnection(); | ||
|
|
||
| StringBuilder id = new StringBuilder(); | ||
| try { | ||
| ObjectName objectName = new ObjectName("java.lang:type=Runtime"); | ||
| for (String attribute : Arrays.asList("StartTime", "Name")) { | ||
| Object value = connection.getAttribute(objectName, attribute); | ||
| if (id.length() > 0) { | ||
| id.append(" "); | ||
| } | ||
| id.append(value); | ||
| } | ||
|
Comment on lines
+157
to
+164
Contributor
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 description in #2206 includes the
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. Including the |
||
| return UUID.nameUUIDFromBytes(id.toString().getBytes(StandardCharsets.UTF_8)); | ||
| } catch (Exception e) { | ||
| throw new IllegalStateException(e); | ||
| } | ||
| } catch (IOException e) { | ||
| return null; | ||
| } | ||
| } | ||
|
|
||
| // package private for testing | ||
| static void propagateToSystemProperties(Properties properties) { | ||
| for (Map.Entry<Object, Object> entry : properties.entrySet()) { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -17,16 +17,20 @@ | |
|
|
||
| class PropertiesCustomizerTest { | ||
|
|
||
| private static final String DUMMY_URL = "service:jmx:rmi:///jndi/rmi://host:999/jmxrmi"; | ||
|
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] we now have to test with an URL that can be parsed because the parsing is triggered, while it used to not be parsed previously. |
||
|
|
||
| @Test | ||
| void tryGetConfigBeforeApply() { | ||
| void tryGetBeforeApply() { | ||
| assertThatThrownBy(() -> new PropertiesCustomizer().getScraperConfig()) | ||
| .isInstanceOf(IllegalStateException.class); | ||
| assertThatThrownBy(() -> new PropertiesCustomizer().getConnectorBuilder()) | ||
| .isInstanceOf(IllegalStateException.class); | ||
| } | ||
|
|
||
| @Test | ||
| void defaultOtlpExporter() { | ||
| Map<String, String> map = new HashMap<>(); | ||
| map.put("otel.jmx.service.url", "dummy-url"); | ||
| map.put("otel.jmx.service.url", DUMMY_URL); | ||
| map.put("otel.jmx.target.system", "jvm"); | ||
| ConfigProperties config = DefaultConfigProperties.createFromMap(map); | ||
|
|
||
|
|
@@ -37,7 +41,7 @@ void defaultOtlpExporter() { | |
| @Test | ||
| void explicitExporterSet() { | ||
| Map<String, String> map = new HashMap<>(); | ||
| map.put("otel.jmx.service.url", "dummy-url"); | ||
| map.put("otel.jmx.service.url", DUMMY_URL); | ||
| map.put("otel.jmx.target.system", "jvm"); | ||
| map.put("otel.metrics.exporter", "otlp,logging"); | ||
| ConfigProperties config = DefaultConfigProperties.createFromMap(map); | ||
|
|
@@ -49,7 +53,7 @@ void explicitExporterSet() { | |
| @Test | ||
| void getSomeConfiguration() { | ||
| Map<String, String> map = new HashMap<>(); | ||
| map.put("otel.jmx.service.url", "dummy-url"); | ||
| map.put("otel.jmx.service.url", DUMMY_URL); | ||
| map.put("otel.jmx.target.system", "jvm"); | ||
| map.put("otel.metrics.exporter", "otlp"); | ||
| ConfigProperties config = DefaultConfigProperties.createFromMap(map); | ||
|
|
@@ -67,7 +71,7 @@ void getSomeConfiguration() { | |
| @Test | ||
| void setSdkMetricExportFromJmxInterval() { | ||
| Map<String, String> map = new HashMap<>(); | ||
| map.put("otel.jmx.service.url", "dummy-url"); | ||
| map.put("otel.jmx.service.url", DUMMY_URL); | ||
| map.put("otel.jmx.target.system", "jvm"); | ||
| map.put("otel.metrics.exporter", "otlp"); | ||
| map.put("otel.jmx.interval.milliseconds", "10000"); | ||
|
|
@@ -83,7 +87,7 @@ void setSdkMetricExportFromJmxInterval() { | |
| @Test | ||
| void sdkMetricExportIntervalPriority() { | ||
| Map<String, String> map = new HashMap<>(); | ||
| map.put("otel.jmx.service.url", "dummy-url"); | ||
| map.put("otel.jmx.service.url", DUMMY_URL); | ||
| map.put("otel.jmx.target.system", "jvm"); | ||
| map.put("otel.metrics.exporter", "otlp"); | ||
| map.put("otel.jmx.interval.milliseconds", "10000"); | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.