Skip to content

Commit e117b8a

Browse files
committed
copy declarative config bridge from instrumentation
1 parent 223864b commit e117b8a

File tree

6 files changed

+455
-0
lines changed

6 files changed

+455
-0
lines changed
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
plugins {
2+
id("otel.java-conventions")
3+
id("otel.publish-conventions")
4+
}
5+
6+
description = "OpenTelemetry extension that provides a bridge for declarative configuration."
7+
otelJava.moduleName.set("io.opentelemetry.contrib.sdk.declarative.config.bridge")
8+
9+
dependencies {
10+
// We use `compileOnly` dependency because during runtime all necessary classes are provided by
11+
// javaagent itself.
12+
compileOnly("io.opentelemetry:opentelemetry-sdk-extension-autoconfigure")
13+
compileOnly("io.opentelemetry:opentelemetry-sdk-extension-incubator")
14+
15+
testImplementation("io.opentelemetry:opentelemetry-sdk-extension-autoconfigure")
16+
testImplementation("io.opentelemetry:opentelemetry-sdk-extension-incubator")
17+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.contrib.sdk.autoconfigure;
7+
8+
import io.opentelemetry.api.incubator.config.ConfigProvider;
9+
import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties;
10+
import io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdk;
11+
import io.opentelemetry.sdk.autoconfigure.internal.AutoConfigureUtil;
12+
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
13+
import io.opentelemetry.sdk.extension.incubator.fileconfig.SdkConfigProvider;
14+
import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.OpenTelemetryConfigurationModel;
15+
16+
public class ConfigPropertiesUtil {
17+
private ConfigPropertiesUtil() {
18+
}
19+
20+
/**
21+
* Resolve {@link ConfigProperties} from the {@code autoConfiguredOpenTelemetrySdk}.
22+
*/
23+
public static ConfigProperties resolveConfigProperties(
24+
AutoConfiguredOpenTelemetrySdk autoConfiguredOpenTelemetrySdk) {
25+
ConfigProperties sdkConfigProperties =
26+
AutoConfigureUtil.getConfig(autoConfiguredOpenTelemetrySdk);
27+
if (sdkConfigProperties != null) {
28+
return sdkConfigProperties;
29+
}
30+
ConfigProvider configProvider =
31+
AutoConfigureUtil.getConfigProvider(autoConfiguredOpenTelemetrySdk);
32+
if (configProvider != null) {
33+
DeclarativeConfigProperties instrumentationConfig = configProvider.getInstrumentationConfig();
34+
35+
if (instrumentationConfig == null) {
36+
instrumentationConfig = DeclarativeConfigProperties.empty();
37+
}
38+
39+
return new DeclarativeConfigPropertiesBridge(instrumentationConfig);
40+
}
41+
// Should never happen
42+
throw new IllegalStateException(
43+
"AutoConfiguredOpenTelemetrySdk does not have ConfigProperties or DeclarativeConfigProperties. This is likely a programming error in opentelemetry-java");
44+
}
45+
46+
public static ConfigProperties resolveModel(OpenTelemetryConfigurationModel model) {
47+
SdkConfigProvider configProvider = SdkConfigProvider.create(model);
48+
DeclarativeConfigProperties instrumentationConfig = configProvider.getInstrumentationConfig();
49+
if (instrumentationConfig == null) {
50+
instrumentationConfig = DeclarativeConfigProperties.empty();
51+
}
52+
53+
return new DeclarativeConfigPropertiesBridge(instrumentationConfig);
54+
}
55+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.contrib.sdk.autoconfigure;
7+
8+
import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties;
9+
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
10+
11+
import javax.annotation.Nullable;
12+
import java.time.Duration;
13+
import java.util.Collections;
14+
import java.util.HashMap;
15+
import java.util.List;
16+
import java.util.Map;
17+
import java.util.function.BiFunction;
18+
19+
import static io.opentelemetry.api.incubator.config.DeclarativeConfigProperties.empty;
20+
21+
/**
22+
* A {@link ConfigProperties} which resolves properties based on {@link
23+
* DeclarativeConfigProperties}.
24+
*
25+
* <p>Only properties starting with "otel.instrumentation." are resolved. Others return null (or
26+
* default value if provided).
27+
*
28+
* <p>To resolve:
29+
*
30+
* <ul>
31+
* <li>"otel.instrumentation" refers to the ".instrumentation.java" node
32+
* <li>The portion of the property after "otel.instrumentation." is split into segments based on
33+
* ".".
34+
* <li>For each N-1 segment, we walk down the tree to find the relevant leaf {@link
35+
* DeclarativeConfigProperties}.
36+
* <li>We extract the property from the resolved {@link DeclarativeConfigProperties} using the
37+
* last segment as the property key.
38+
* </ul>
39+
*
40+
* <p>For example, given the following YAML, asking for {@code
41+
* ConfigProperties#getString("otel.instrumentation.common.string_key")} yields "value":
42+
*
43+
* <pre>
44+
* instrumentation:
45+
* java:
46+
* common:
47+
* string_key: value
48+
* </pre>
49+
*/
50+
final class DeclarativeConfigPropertiesBridge implements ConfigProperties {
51+
52+
private static final String OTEL_INSTRUMENTATION_PREFIX = "otel.instrumentation.";
53+
54+
// The node at .instrumentation.java
55+
private final DeclarativeConfigProperties instrumentationJavaNode;
56+
57+
DeclarativeConfigPropertiesBridge(DeclarativeConfigProperties instrumentationNode) {
58+
instrumentationJavaNode = instrumentationNode.getStructured("java", empty());
59+
}
60+
61+
@Nullable
62+
@Override
63+
public String getString(String propertyName) {
64+
return getPropertyValue(propertyName, DeclarativeConfigProperties::getString);
65+
}
66+
67+
@Nullable
68+
@Override
69+
public Boolean getBoolean(String propertyName) {
70+
return getPropertyValue(propertyName, DeclarativeConfigProperties::getBoolean);
71+
}
72+
73+
@Nullable
74+
@Override
75+
public Integer getInt(String propertyName) {
76+
return getPropertyValue(propertyName, DeclarativeConfigProperties::getInt);
77+
}
78+
79+
@Nullable
80+
@Override
81+
public Long getLong(String propertyName) {
82+
return getPropertyValue(propertyName, DeclarativeConfigProperties::getLong);
83+
}
84+
85+
@Nullable
86+
@Override
87+
public Double getDouble(String propertyName) {
88+
return getPropertyValue(propertyName, DeclarativeConfigProperties::getDouble);
89+
}
90+
91+
@Nullable
92+
@Override
93+
public Duration getDuration(String propertyName) {
94+
Long millis = getPropertyValue(propertyName, DeclarativeConfigProperties::getLong);
95+
if (millis == null) {
96+
return null;
97+
}
98+
return Duration.ofMillis(millis);
99+
}
100+
101+
@Override
102+
public List<String> getList(String propertyName) {
103+
List<String> propertyValue =
104+
getPropertyValue(
105+
propertyName,
106+
(properties, lastPart) -> properties.getScalarList(lastPart, String.class));
107+
return propertyValue == null ? Collections.emptyList() : propertyValue;
108+
}
109+
110+
@Override
111+
public Map<String, String> getMap(String propertyName) {
112+
DeclarativeConfigProperties propertyValue =
113+
getPropertyValue(propertyName, DeclarativeConfigProperties::getStructured);
114+
if (propertyValue == null) {
115+
return Collections.emptyMap();
116+
}
117+
Map<String, String> result = new HashMap<>();
118+
propertyValue
119+
.getPropertyKeys()
120+
.forEach(
121+
key -> {
122+
String value = propertyValue.getString(key);
123+
if (value == null) {
124+
return;
125+
}
126+
result.put(key, value);
127+
});
128+
return Collections.unmodifiableMap(result);
129+
}
130+
131+
@Nullable
132+
private <T> T getPropertyValue(
133+
String property, BiFunction<DeclarativeConfigProperties, String, T> extractor) {
134+
if (instrumentationJavaNode == null) {
135+
return null;
136+
}
137+
138+
if (property.startsWith(OTEL_INSTRUMENTATION_PREFIX)) {
139+
property = property.substring(OTEL_INSTRUMENTATION_PREFIX.length());
140+
}
141+
// Split the remainder of the property on "."
142+
String[] segments = property.split("\\.");
143+
if (segments.length == 0) {
144+
return null;
145+
}
146+
147+
// Extract the value by walking to the N-1 entry
148+
DeclarativeConfigProperties target = instrumentationJavaNode;
149+
if (segments.length > 1) {
150+
for (int i = 0; i < segments.length - 1; i++) {
151+
target = target.getStructured(segments[i], empty());
152+
}
153+
}
154+
String lastPart = segments[segments.length - 1];
155+
156+
return extractor.apply(target, lastPart);
157+
}
158+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.contrib.sdk.autoconfigure;
7+
8+
9+
import static org.mockito.ArgumentMatchers.any;
10+
import static org.mockito.ArgumentMatchers.eq;
11+
import static org.mockito.Mockito.mock;
12+
import static org.mockito.Mockito.when;
13+
14+
import io.opentelemetry.api.incubator.config.ConfigProvider;
15+
import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties;
16+
import io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdk;
17+
import io.opentelemetry.sdk.autoconfigure.internal.AutoConfigureUtil;
18+
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
19+
import org.junit.jupiter.api.Test;
20+
import org.mockito.MockedStatic;
21+
import org.mockito.Mockito;
22+
23+
@SuppressWarnings("DoNotMockAutoValue")
24+
class ConfigPropertiesUtilTest {
25+
@Test
26+
void shouldUseConfigPropertiesForAutoConfiguration() {
27+
ConfigProperties configPropertiesMock = mock(ConfigProperties.class);
28+
AutoConfiguredOpenTelemetrySdk sdkMock = mock(AutoConfiguredOpenTelemetrySdk.class);
29+
try (MockedStatic<AutoConfigureUtil> autoConfigureUtilMock =
30+
Mockito.mockStatic(AutoConfigureUtil.class)) {
31+
autoConfigureUtilMock
32+
.when(() -> AutoConfigureUtil.getConfig(sdkMock))
33+
.thenReturn(configPropertiesMock);
34+
35+
ConfigProperties configProperties = ConfigPropertiesUtil.resolveConfigProperties(sdkMock);
36+
37+
assertThat(configProperties).isSameAs(configPropertiesMock);
38+
}
39+
}
40+
41+
@Test
42+
void shouldUseConfigProviderForDeclarativeConfiguration() {
43+
String propertyName = "testProperty";
44+
String expectedValue = "the value";
45+
DeclarativeConfigProperties javaNodeMock = mock(DeclarativeConfigProperties.class);
46+
when(javaNodeMock.getString(propertyName)).thenReturn(expectedValue);
47+
48+
DeclarativeConfigProperties instrumentationConfigMock = mock(DeclarativeConfigProperties.class);
49+
when(instrumentationConfigMock.getStructured(eq("java"), any())).thenReturn(javaNodeMock);
50+
51+
ConfigProvider configProviderMock = mock(ConfigProvider.class);
52+
when(configProviderMock.getInstrumentationConfig()).thenReturn(instrumentationConfigMock);
53+
54+
AutoConfiguredOpenTelemetrySdk sdkMock = mock(AutoConfiguredOpenTelemetrySdk.class);
55+
56+
try (MockedStatic<AutoConfigureUtil> autoConfigureUtilMock =
57+
Mockito.mockStatic(AutoConfigureUtil.class)) {
58+
autoConfigureUtilMock.when(() -> AutoConfigureUtil.getConfig(sdkMock)).thenReturn(null);
59+
autoConfigureUtilMock
60+
.when(() -> AutoConfigureUtil.getConfigProvider(sdkMock))
61+
.thenReturn(configProviderMock);
62+
63+
ConfigProperties configProperties = ConfigPropertiesUtil.resolveConfigProperties(sdkMock);
64+
65+
assertThat(configProperties.getString(propertyName)).isEqualTo(expectedValue);
66+
}
67+
}
68+
69+
@Test
70+
void shouldUseConfigProviderForDeclarativeConfiguration_noInstrumentationConfig() {
71+
AutoConfiguredOpenTelemetrySdk sdkMock = mock(AutoConfiguredOpenTelemetrySdk.class);
72+
ConfigProvider configProviderMock = mock(ConfigProvider.class);
73+
when(configProviderMock.getInstrumentationConfig()).thenReturn(null);
74+
75+
try (MockedStatic<AutoConfigureUtil> autoConfigureUtilMock =
76+
Mockito.mockStatic(AutoConfigureUtil.class)) {
77+
autoConfigureUtilMock.when(() -> AutoConfigureUtil.getConfig(sdkMock)).thenReturn(null);
78+
autoConfigureUtilMock
79+
.when(() -> AutoConfigureUtil.getConfigProvider(sdkMock))
80+
.thenReturn(configProviderMock);
81+
82+
ConfigProperties configProperties = ConfigPropertiesUtil.resolveConfigProperties(sdkMock);
83+
84+
assertThat(configProperties.getString("testProperty")).isEqualTo(null);
85+
}
86+
}
87+
}

0 commit comments

Comments
 (0)