Skip to content
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