diff --git a/declarative-config-bridge/src/main/java/io/opentelemetry/instrumentation/config/bridge/ConfigPropertiesBackedConfigProvider.java b/declarative-config-bridge/src/main/java/io/opentelemetry/instrumentation/config/bridge/ConfigPropertiesBackedConfigProvider.java index f228934b55bc..9497f8875077 100644 --- a/declarative-config-bridge/src/main/java/io/opentelemetry/instrumentation/config/bridge/ConfigPropertiesBackedConfigProvider.java +++ b/declarative-config-bridge/src/main/java/io/opentelemetry/instrumentation/config/bridge/ConfigPropertiesBackedConfigProvider.java @@ -5,9 +5,12 @@ package io.opentelemetry.instrumentation.config.bridge; +import com.google.errorprone.annotations.CanIgnoreReturnValue; import io.opentelemetry.api.incubator.config.ConfigProvider; import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties; import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; +import java.util.HashMap; +import java.util.Map; /** * A {@link ConfigProvider} implementation backed by {@link ConfigProperties}. @@ -20,13 +23,33 @@ public final class ConfigPropertiesBackedConfigProvider implements ConfigProvide private final DeclarativeConfigProperties instrumentationConfig; public static ConfigProvider create(ConfigProperties configProperties) { - return new ConfigPropertiesBackedConfigProvider(configProperties); + return new ConfigPropertiesBackedConfigProvider( + ConfigPropertiesBackedDeclarativeConfigProperties.createInstrumentationConfig( + configProperties)); } - private ConfigPropertiesBackedConfigProvider(ConfigProperties configProperties) { - this.instrumentationConfig = - ConfigPropertiesBackedDeclarativeConfigProperties.createInstrumentationConfig( - configProperties); + public static Builder builder() { + return new Builder(); + } + + private ConfigPropertiesBackedConfigProvider(DeclarativeConfigProperties instrumentationConfig) { + this.instrumentationConfig = instrumentationConfig; + } + + public static final class Builder { + private final Map mappings = new HashMap<>(); + + @CanIgnoreReturnValue + public Builder addMapping(String declarativeProperty, String configProperty) { + mappings.put(declarativeProperty, configProperty); + return this; + } + + public ConfigProvider build(ConfigProperties configProperties) { + return new ConfigPropertiesBackedConfigProvider( + ConfigPropertiesBackedDeclarativeConfigProperties.createInstrumentationConfig( + configProperties, mappings)); + } } @Override diff --git a/declarative-config-bridge/src/main/java/io/opentelemetry/instrumentation/config/bridge/ConfigPropertiesBackedDeclarativeConfigProperties.java b/declarative-config-bridge/src/main/java/io/opentelemetry/instrumentation/config/bridge/ConfigPropertiesBackedDeclarativeConfigProperties.java index 3a0f042ffe70..91367aa33931 100644 --- a/declarative-config-bridge/src/main/java/io/opentelemetry/instrumentation/config/bridge/ConfigPropertiesBackedDeclarativeConfigProperties.java +++ b/declarative-config-bridge/src/main/java/io/opentelemetry/instrumentation/config/bridge/ConfigPropertiesBackedDeclarativeConfigProperties.java @@ -6,6 +6,7 @@ package io.opentelemetry.instrumentation.config.bridge; import static java.util.Collections.emptyList; +import static java.util.Collections.emptyMap; import static java.util.Collections.emptySet; import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties; @@ -97,16 +98,26 @@ public final class ConfigPropertiesBackedDeclarativeConfigProperties private final ConfigProperties configProperties; private final List path; + private final Map mappings; public static DeclarativeConfigProperties createInstrumentationConfig( ConfigProperties configProperties) { - return new ConfigPropertiesBackedDeclarativeConfigProperties(configProperties, emptyList()); + return createInstrumentationConfig(configProperties, emptyMap()); + } + + public static DeclarativeConfigProperties createInstrumentationConfig( + ConfigProperties configProperties, Map mappings) { + Map mergedMappings = new HashMap<>(SPECIAL_MAPPINGS); + mergedMappings.putAll(mappings); + return new ConfigPropertiesBackedDeclarativeConfigProperties( + configProperties, emptyList(), mergedMappings); } private ConfigPropertiesBackedDeclarativeConfigProperties( - ConfigProperties configProperties, List path) { + ConfigProperties configProperties, List path, Map mappings) { this.configProperties = configProperties; this.path = path; + this.mappings = mappings; } @Nullable @@ -165,7 +176,8 @@ public Double getDouble(String name) { public DeclarativeConfigProperties getStructured(String name) { List newPath = new ArrayList<>(path); newPath.add(name); - return new ConfigPropertiesBackedDeclarativeConfigProperties(configProperties, newPath); + return new ConfigPropertiesBackedDeclarativeConfigProperties( + configProperties, newPath, mappings); } @Nullable @@ -211,7 +223,7 @@ private String resolvePropertyKey(String name) { String fullPath = pathWithName(name); // Check explicit property mappings first - String mappedKey = SPECIAL_MAPPINGS.get(fullPath); + String mappedKey = mappings.get(fullPath); if (mappedKey != null) { return mappedKey; } diff --git a/declarative-config-bridge/src/test/java/io/opentelemetry/instrumentation/config/bridge/ConfigPropertiesBackedConfigProviderTest.java b/declarative-config-bridge/src/test/java/io/opentelemetry/instrumentation/config/bridge/ConfigPropertiesBackedConfigProviderTest.java new file mode 100644 index 000000000000..4e3c37a14109 --- /dev/null +++ b/declarative-config-bridge/src/test/java/io/opentelemetry/instrumentation/config/bridge/ConfigPropertiesBackedConfigProviderTest.java @@ -0,0 +1,67 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.config.bridge; + +import static org.assertj.core.api.Assertions.assertThat; + +import io.opentelemetry.api.incubator.config.ConfigProvider; +import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties; +import io.opentelemetry.sdk.autoconfigure.spi.internal.DefaultConfigProperties; +import java.util.HashMap; +import java.util.Map; +import org.junit.jupiter.api.Test; + +class ConfigPropertiesBackedConfigProviderTest { + @Test + void testBuilderWithMappings() { + Map properties = new HashMap<>(); + properties.put("my.custom.property", "custom-value"); + + // ConfigProperties has "my.custom.property" + // We want to map declarative "my.declarative.prop" to "my.custom.property" + + ConfigProvider provider = + ConfigPropertiesBackedConfigProvider.builder() + .addMapping("my.declarative.prop", "my.custom.property") + .build(DefaultConfigProperties.createFromMap(properties)); + + DeclarativeConfigProperties config = provider.getInstrumentationConfig(); + + // DeclarativeConfigProperties structure lookup + // pathWithName("my.declarative.prop") should map to "my.custom.property" + // However, structure is hierarchical. + // getStructured("my").getStructured("declarative").getString("prop") -> + // "my.declarative.prop" + + assertThat(config.getStructured("my").getStructured("declarative").getString("prop")) + .isEqualTo("custom-value"); + } + + @Test + void testCreateUsesDefaults() { + // Verify that create() still supports the default mappings (e.g. + // otel.instrumentation...) + // e.g. "java.common.http.known_methods" -> + // "otel.instrumentation.http.known-methods" + + Map properties = new HashMap<>(); + properties.put("otel.instrumentation.http.known-methods", "GET,POST"); + + ConfigProvider provider = + ConfigPropertiesBackedConfigProvider.create( + DefaultConfigProperties.createFromMap(properties)); + + DeclarativeConfigProperties config = provider.getInstrumentationConfig(); + + assertThat( + config + .getStructured("java") + .getStructured("common") + .getStructured("http") + .getString("known_methods")) + .isEqualTo("GET,POST"); + } +} diff --git a/declarative-config-bridge/src/test/java/io/opentelemetry/instrumentation/config/bridge/ConfigPropertiesBackedDeclarativeConfigPropertiesTest.java b/declarative-config-bridge/src/test/java/io/opentelemetry/instrumentation/config/bridge/ConfigPropertiesBackedDeclarativeConfigPropertiesTest.java index 92176a14d2f2..1410069d2204 100644 --- a/declarative-config-bridge/src/test/java/io/opentelemetry/instrumentation/config/bridge/ConfigPropertiesBackedDeclarativeConfigPropertiesTest.java +++ b/declarative-config-bridge/src/test/java/io/opentelemetry/instrumentation/config/bridge/ConfigPropertiesBackedDeclarativeConfigPropertiesTest.java @@ -99,6 +99,28 @@ void testGeneralHttpListMapping() { .containsExactly("header1", "header2"); } + @Test + void testCustomMappingsIncludeSpecialMappings() { + Map properties = new HashMap<>(); + properties.put("otel.custom.enabled", "true"); + properties.put("otel.instrumentation.http.client.capture-request-headers", "header1,header2"); + Map customMappings = new HashMap<>(); + customMappings.put("java.custom.enabled", "otel.custom.enabled"); + + DeclarativeConfigProperties config = + ConfigPropertiesBackedDeclarativeConfigProperties.createInstrumentationConfig( + DefaultConfigProperties.createFromMap(properties), customMappings); + + assertThat(config.getStructured("java").getStructured("custom").getBoolean("enabled")).isTrue(); + assertThat( + config + .getStructured("general") + .getStructured("http") + .getStructured("client") + .getScalarList("request_captured_headers", String.class)) + .containsExactly("header1", "header2"); + } + @Test void testJavaCommonServicePeerMapping() { DeclarativeConfigProperties config =