diff --git a/instrumentation/jmx-metrics/javaagent/src/main/java/io/opentelemetry/instrumentation/javaagent/jmx/JmxMetricInsightInstaller.java b/instrumentation/jmx-metrics/javaagent/src/main/java/io/opentelemetry/instrumentation/javaagent/jmx/JmxMetricInsightInstaller.java index 2c2d989ba9f7..c639cac53f05 100644 --- a/instrumentation/jmx-metrics/javaagent/src/main/java/io/opentelemetry/instrumentation/javaagent/jmx/JmxMetricInsightInstaller.java +++ b/instrumentation/jmx-metrics/javaagent/src/main/java/io/opentelemetry/instrumentation/javaagent/jmx/JmxMetricInsightInstaller.java @@ -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; @@ -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 = diff --git a/instrumentation/oshi/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/oshi/OshiMetricsInstaller.java b/instrumentation/oshi/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/oshi/OshiMetricsInstaller.java index 923f95dea061..edca2dfdb8dc 100644 --- a/instrumentation/oshi/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/oshi/OshiMetricsInstaller.java +++ b/instrumentation/oshi/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/oshi/OshiMetricsInstaller.java @@ -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; @@ -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)) { diff --git a/instrumentation/runtime-telemetry/runtime-telemetry-java8/javaagent/src/main/java/io/opentelemetry/instrumentation/javaagent/runtimemetrics/java8/JarAnalyzerInstaller.java b/instrumentation/runtime-telemetry/runtime-telemetry-java8/javaagent/src/main/java/io/opentelemetry/instrumentation/javaagent/runtimemetrics/java8/JarAnalyzerInstaller.java index f16b00cb2903..51ac4a7f1bb3 100644 --- a/instrumentation/runtime-telemetry/runtime-telemetry-java8/javaagent/src/main/java/io/opentelemetry/instrumentation/javaagent/runtimemetrics/java8/JarAnalyzerInstaller.java +++ b/instrumentation/runtime-telemetry/runtime-telemetry-java8/javaagent/src/main/java/io/opentelemetry/instrumentation/javaagent/runtimemetrics/java8/JarAnalyzerInstaller.java @@ -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; @@ -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); diff --git a/javaagent-extension-api/build.gradle.kts b/javaagent-extension-api/build.gradle.kts index 96852314dcb8..144a3a459bf3 100644 --- a/javaagent-extension-api/build.gradle.kts +++ b/javaagent-extension-api/build.gradle.kts @@ -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") +} diff --git a/javaagent-extension-api/src/main/java/io/opentelemetry/javaagent/extension/AgentListener.java b/javaagent-extension-api/src/main/java/io/opentelemetry/javaagent/extension/AgentListener.java index 8b5578104dba..4cbcbfb8970b 100644 --- a/javaagent-extension-api/src/main/java/io/opentelemetry/javaagent/extension/AgentListener.java +++ b/javaagent-extension-api/src/main/java/io/opentelemetry/javaagent/extension/AgentListener.java @@ -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; @@ -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( - 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"); - } } diff --git a/javaagent-extension-api/src/main/java/io/opentelemetry/javaagent/extension/ConfigPropertiesUtil.java b/javaagent-extension-api/src/main/java/io/opentelemetry/javaagent/extension/ConfigPropertiesUtil.java new file mode 100644 index 000000000000..2e164e71ffb5 --- /dev/null +++ b/javaagent-extension-api/src/main/java/io/opentelemetry/javaagent/extension/ConfigPropertiesUtil.java @@ -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"); + } +} diff --git a/javaagent-extension-api/src/main/java/io/opentelemetry/javaagent/extension/DeclarativeConfigPropertiesBridge.java b/javaagent-extension-api/src/main/java/io/opentelemetry/javaagent/extension/DeclarativeConfigPropertiesBridge.java index 03bc9737c030..01ad41ef222f 100644 --- a/javaagent-extension-api/src/main/java/io/opentelemetry/javaagent/extension/DeclarativeConfigPropertiesBridge.java +++ b/javaagent-extension-api/src/main/java/io/opentelemetry/javaagent/extension/DeclarativeConfigPropertiesBridge.java @@ -130,15 +130,20 @@ public Map getMap(String propertyName) { @Nullable private T getPropertyValue( String property, BiFunction 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++) { @@ -146,6 +151,7 @@ private T getPropertyValue( } } String lastPart = segments[segments.length - 1]; + return extractor.apply(target, lastPart); } } diff --git a/javaagent-extension-api/src/test/java/io/opentelemetry/javaagent/extension/ConfigPropertiesUtilTest.java b/javaagent-extension-api/src/test/java/io/opentelemetry/javaagent/extension/ConfigPropertiesUtilTest.java new file mode 100644 index 000000000000..4894b44f6b86 --- /dev/null +++ b/javaagent-extension-api/src/test/java/io/opentelemetry/javaagent/extension/ConfigPropertiesUtilTest.java @@ -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 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 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 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); + } + } +} diff --git a/javaagent-extension-api/src/test/java/io/opentelemetry/javaagent/extension/DeclarativeConfigPropertiesBridgeTest.java b/javaagent-extension-api/src/test/java/io/opentelemetry/javaagent/extension/DeclarativeConfigPropertiesBridgeTest.java index 24df92885e11..cd4f032ed187 100644 --- a/javaagent-extension-api/src/test/java/io/opentelemetry/javaagent/extension/DeclarativeConfigPropertiesBridgeTest.java +++ b/javaagent-extension-api/src/test/java/io/opentelemetry/javaagent/extension/DeclarativeConfigPropertiesBridgeTest.java @@ -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; @@ -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(); } } diff --git a/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/AgentInstaller.java b/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/AgentInstaller.java index 830c2de21c0a..f431334b72c9 100644 --- a/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/AgentInstaller.java +++ b/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/AgentInstaller.java @@ -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; @@ -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)));