Skip to content

Vendor specific instrumentation config options handling #14016

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

Merged
merged 11 commits into from
Jul 10, 2025
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import io.opentelemetry.instrumentation.jmx.engine.MetricConfiguration;
import io.opentelemetry.instrumentation.jmx.yaml.RuleParser;
import io.opentelemetry.javaagent.extension.AgentListener;
import io.opentelemetry.javaagent.extension.ConfigPropertiesUtil;
import io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdk;
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
import java.io.InputStream;
Expand All @@ -28,7 +29,7 @@ public class JmxMetricInsightInstaller implements AgentListener {

@Override
public void afterAgent(AutoConfiguredOpenTelemetrySdk autoConfiguredSdk) {
ConfigProperties config = AgentListener.resolveConfigProperties(autoConfiguredSdk);
ConfigProperties config = ConfigPropertiesUtil.resolveConfigProperties(autoConfiguredSdk);

if (config.getBoolean("otel.jmx.enabled", true)) {
JmxMetricInsight service =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

import com.google.auto.service.AutoService;
import io.opentelemetry.javaagent.extension.AgentListener;
import io.opentelemetry.javaagent.extension.ConfigPropertiesUtil;
import io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdk;
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
import java.lang.reflect.Method;
Expand All @@ -20,7 +21,7 @@ public class OshiMetricsInstaller implements AgentListener {

@Override
public void afterAgent(AutoConfiguredOpenTelemetrySdk autoConfiguredSdk) {
ConfigProperties config = AgentListener.resolveConfigProperties(autoConfiguredSdk);
ConfigProperties config = ConfigPropertiesUtil.resolveConfigProperties(autoConfiguredSdk);

boolean defaultEnabled = config.getBoolean("otel.instrumentation.common.default-enabled", true);
if (!config.getBoolean("otel.instrumentation.oshi.enabled", defaultEnabled)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

import com.google.auto.service.AutoService;
import io.opentelemetry.javaagent.bootstrap.InstrumentationHolder;
import io.opentelemetry.javaagent.extension.AgentListener;
import io.opentelemetry.javaagent.extension.ConfigPropertiesUtil;
import io.opentelemetry.javaagent.tooling.BeforeAgentListener;
import io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdk;
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
Expand All @@ -19,7 +19,8 @@ public class JarAnalyzerInstaller implements BeforeAgentListener {

@Override
public void beforeAgent(AutoConfiguredOpenTelemetrySdk autoConfiguredOpenTelemetrySdk) {
ConfigProperties config = AgentListener.resolveConfigProperties(autoConfiguredOpenTelemetrySdk);
ConfigProperties config =
ConfigPropertiesUtil.resolveConfigProperties(autoConfiguredOpenTelemetrySdk);

boolean enabled =
config.getBoolean("otel.instrumentation.runtime-telemetry.package-emitter.enabled", false);
Expand Down
5 changes: 5 additions & 0 deletions javaagent-extension-api/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,8 @@ dependencies {
// Used by byte-buddy but not brought in as a transitive dependency.
compileOnly("com.google.code.findbugs:annotations")
}

// Needed by mockito
configurations.testRuntimeClasspath {
exclude(group = "net.bytebuddy", module = "byte-buddy-dep")
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,7 @@

package io.opentelemetry.javaagent.extension;

import io.opentelemetry.api.incubator.config.ConfigProvider;
import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties;
import io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdk;
import io.opentelemetry.sdk.autoconfigure.internal.AutoConfigureUtil;
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
import io.opentelemetry.sdk.autoconfigure.spi.Ordered;
import java.lang.instrument.Instrumentation;
import net.bytebuddy.agent.builder.AgentBuilder;
Expand All @@ -29,26 +25,4 @@ public interface AgentListener extends Ordered {
* on an {@link Instrumentation}.
*/
void afterAgent(AutoConfiguredOpenTelemetrySdk autoConfiguredOpenTelemetrySdk);

/** Resolve {@link ConfigProperties} from the {@code autoConfiguredOpenTelemetrySdk}. */
static ConfigProperties resolveConfigProperties(
Copy link
Contributor Author

@robsunday robsunday Jun 10, 2025

Choose a reason for hiding this comment

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

[for reviewer] This was extracted to separate public class (see ConfigPropertiesUtil) so can be used also for BeforeAgentListener's

AutoConfiguredOpenTelemetrySdk autoConfiguredOpenTelemetrySdk) {
ConfigProperties sdkConfigProperties =
AutoConfigureUtil.getConfig(autoConfiguredOpenTelemetrySdk);
if (sdkConfigProperties != null) {
return sdkConfigProperties;
}
ConfigProvider configProvider =
AutoConfigureUtil.getConfigProvider(autoConfiguredOpenTelemetrySdk);
if (configProvider != null) {
DeclarativeConfigProperties instrumentationConfig = configProvider.getInstrumentationConfig();

if (instrumentationConfig != null) {
return new DeclarativeConfigPropertiesBridge(instrumentationConfig);
}
}
// Should never happen
throw new IllegalStateException(
"AutoConfiguredOpenTelemetrySdk does not have ConfigProperties or DeclarativeConfigProperties. This is likely a programming error in opentelemetry-java");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.javaagent.extension;

import io.opentelemetry.api.incubator.config.ConfigProvider;
import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties;
import io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdk;
import io.opentelemetry.sdk.autoconfigure.internal.AutoConfigureUtil;
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;

public class ConfigPropertiesUtil {
private ConfigPropertiesUtil() {}

/** Resolve {@link ConfigProperties} from the {@code autoConfiguredOpenTelemetrySdk}. */
public static ConfigProperties resolveConfigProperties(
AutoConfiguredOpenTelemetrySdk autoConfiguredOpenTelemetrySdk) {
ConfigProperties sdkConfigProperties =
AutoConfigureUtil.getConfig(autoConfiguredOpenTelemetrySdk);
if (sdkConfigProperties != null) {
return sdkConfigProperties;
}
ConfigProvider configProvider =
AutoConfigureUtil.getConfigProvider(autoConfiguredOpenTelemetrySdk);
if (configProvider != null) {
DeclarativeConfigProperties instrumentationConfig = configProvider.getInstrumentationConfig();

if (instrumentationConfig == null) {
instrumentationConfig = DeclarativeConfigProperties.empty();
}

return new DeclarativeConfigPropertiesBridge(instrumentationConfig);
}
// Should never happen
throw new IllegalStateException(
"AutoConfiguredOpenTelemetrySdk does not have ConfigProperties or DeclarativeConfigProperties. This is likely a programming error in opentelemetry-java");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -130,22 +130,28 @@ public Map<String, String> getMap(String propertyName) {
@Nullable
private <T> T getPropertyValue(
String property, BiFunction<DeclarativeConfigProperties, String, T> extractor) {
if (!property.startsWith(OTEL_INSTRUMENTATION_PREFIX)) {
if (instrumentationJavaNode == null) {
return null;
}
String suffix = property.substring(OTEL_INSTRUMENTATION_PREFIX.length());
// Split the remainder of the property on ".", and walk to the N-1 entry
String[] segments = suffix.split("\\.");

if (property.startsWith(OTEL_INSTRUMENTATION_PREFIX)) {
property = property.substring(OTEL_INSTRUMENTATION_PREFIX.length());
}
// Split the remainder of the property on "."
String[] segments = property.split("\\.");
if (segments.length == 0) {
return null;
}

// Extract the value by walking to the N-1 entry
DeclarativeConfigProperties target = instrumentationJavaNode;
if (segments.length > 1) {
for (int i = 0; i < segments.length - 1; i++) {
target = target.getStructured(segments[i], empty());
}
}
String lastPart = segments[segments.length - 1];

return extractor.apply(target, lastPart);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.javaagent.extension;

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

import io.opentelemetry.api.incubator.config.ConfigProvider;
import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties;
import io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdk;
import io.opentelemetry.sdk.autoconfigure.internal.AutoConfigureUtil;
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
import org.junit.jupiter.api.Test;
import org.mockito.MockedStatic;
import org.mockito.Mockito;

@SuppressWarnings("DoNotMockAutoValue")
class ConfigPropertiesUtilTest {
@Test
void shouldUseConfigPropertiesForAutoConfiguration() {
ConfigProperties configPropertiesMock = mock(ConfigProperties.class);
AutoConfiguredOpenTelemetrySdk sdkMock = mock(AutoConfiguredOpenTelemetrySdk.class);
try (MockedStatic<AutoConfigureUtil> autoConfigureUtilMock =
Mockito.mockStatic(AutoConfigureUtil.class)) {
autoConfigureUtilMock
.when(() -> AutoConfigureUtil.getConfig(sdkMock))
.thenReturn(configPropertiesMock);

ConfigProperties configProperties = ConfigPropertiesUtil.resolveConfigProperties(sdkMock);

assertThat(configProperties).isSameAs(configPropertiesMock);
}
}

@Test
void shouldUseConfigProviderForDeclarativeConfiguration() {
String propertyName = "testProperty";
String expectedValue = "the value";
DeclarativeConfigProperties javaNodeMock = mock(DeclarativeConfigProperties.class);
when(javaNodeMock.getString(propertyName)).thenReturn(expectedValue);

DeclarativeConfigProperties instrumentationConfigMock = mock(DeclarativeConfigProperties.class);
when(instrumentationConfigMock.getStructured(eq("java"), any())).thenReturn(javaNodeMock);

ConfigProvider configProviderMock = mock(ConfigProvider.class);
when(configProviderMock.getInstrumentationConfig()).thenReturn(instrumentationConfigMock);

AutoConfiguredOpenTelemetrySdk sdkMock = mock(AutoConfiguredOpenTelemetrySdk.class);

try (MockedStatic<AutoConfigureUtil> autoConfigureUtilMock =
Mockito.mockStatic(AutoConfigureUtil.class)) {
autoConfigureUtilMock.when(() -> AutoConfigureUtil.getConfig(sdkMock)).thenReturn(null);
autoConfigureUtilMock
.when(() -> AutoConfigureUtil.getConfigProvider(sdkMock))
.thenReturn(configProviderMock);

ConfigProperties configProperties = ConfigPropertiesUtil.resolveConfigProperties(sdkMock);

assertThat(configProperties.getString(propertyName)).isEqualTo(expectedValue);
}
}

@Test
void shouldUseConfigProviderForDeclarativeConfiguration_noInstrumentationConfig() {
AutoConfiguredOpenTelemetrySdk sdkMock = mock(AutoConfiguredOpenTelemetrySdk.class);
ConfigProvider configProviderMock = mock(ConfigProvider.class);
when(configProviderMock.getInstrumentationConfig()).thenReturn(null);

try (MockedStatic<AutoConfigureUtil> autoConfigureUtilMock =
Mockito.mockStatic(AutoConfigureUtil.class)) {
autoConfigureUtilMock.when(() -> AutoConfigureUtil.getConfig(sdkMock)).thenReturn(null);
autoConfigureUtilMock
.when(() -> AutoConfigureUtil.getConfigProvider(sdkMock))
.thenReturn(configProviderMock);

ConfigProperties configProperties = ConfigPropertiesUtil.resolveConfigProperties(sdkMock);

assertThat(configProperties.getString("testProperty")).isEqualTo(null);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,10 @@ class DeclarativeConfigPropertiesBridgeTest {
+ " map_key:\n"
+ " string_key1: value1\n"
+ " string_key2: value2\n"
+ " bool_key: true\n";
+ " bool_key: true\n"
+ " acme:\n"
+ " full_name:\n"
+ " preserved: true";

private ConfigProperties bridge;
private ConfigProperties emptyBridge;
Expand Down Expand Up @@ -132,5 +135,9 @@ void getProperties() {
.isEqualTo(Arrays.asList("value1", "value2"));
assertThat(bridge.getMap("otel.instrumentation.other-instrumentation.map_key", expectedMap))
.isEqualTo(expectedMap);

// verify vendor specific property names are preserved in unchanged form (prefix is not stripped
// as for otel.instrumentation.*)
assertThat(bridge.getBoolean("acme.full_name.preserved")).isTrue();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
import io.opentelemetry.javaagent.bootstrap.internal.AgentInstrumentationConfig;
import io.opentelemetry.javaagent.bootstrap.internal.ConfiguredResourceAttributesHolder;
import io.opentelemetry.javaagent.extension.AgentListener;
import io.opentelemetry.javaagent.extension.ConfigPropertiesUtil;
import io.opentelemetry.javaagent.extension.ignore.IgnoredTypesConfigurer;
import io.opentelemetry.javaagent.extension.instrumentation.internal.EarlyInstrumentationModule;
import io.opentelemetry.javaagent.tooling.asyncannotationsupport.WeakRefAsyncOperationEndStrategies;
Expand Down Expand Up @@ -166,7 +167,7 @@ private static void installBytebuddyAgent(
AutoConfiguredOpenTelemetrySdk autoConfiguredSdk =
installOpenTelemetrySdk(extensionClassLoader);

ConfigProperties sdkConfig = AgentListener.resolveConfigProperties(autoConfiguredSdk);
ConfigProperties sdkConfig = ConfigPropertiesUtil.resolveConfigProperties(autoConfiguredSdk);
AgentInstrumentationConfig.internalInitializeConfig(
new ConfigPropertiesBridge(
sdkConfig, AutoConfigureUtil.getConfigProvider(autoConfiguredSdk)));
Expand Down
Loading