Merge 0114599d92
into b79242d858
This commit is contained in:
commit
8c58123ccd
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.sdk.extension.incubator.fileconfig;
|
||||
|
||||
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");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,157 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.sdk.extension.incubator.fileconfig;
|
||||
|
||||
import static io.opentelemetry.api.incubator.config.DeclarativeConfigProperties.empty;
|
||||
|
||||
import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties;
|
||||
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
|
||||
import java.time.Duration;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.BiFunction;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* A {@link ConfigProperties} which resolves properties based on {@link
|
||||
* DeclarativeConfigProperties}.
|
||||
*
|
||||
* <p>Only properties starting with "otel.instrumentation." are resolved. Others return null (or
|
||||
* default value if provided).
|
||||
*
|
||||
* <p>To resolve:
|
||||
*
|
||||
* <ul>
|
||||
* <li>"otel.instrumentation" refers to the ".instrumentation.java" node
|
||||
* <li>The portion of the property after "otel.instrumentation." is split into segments based on
|
||||
* ".".
|
||||
* <li>For each N-1 segment, we walk down the tree to find the relevant leaf {@link
|
||||
* DeclarativeConfigProperties}.
|
||||
* <li>We extract the property from the resolved {@link DeclarativeConfigProperties} using the
|
||||
* last segment as the property key.
|
||||
* </ul>
|
||||
*
|
||||
* <p>For example, given the following YAML, asking for {@code
|
||||
* ConfigProperties#getString("otel.instrumentation.common.string_key")} yields "value":
|
||||
*
|
||||
* <pre>
|
||||
* instrumentation:
|
||||
* java:
|
||||
* common:
|
||||
* string_key: value
|
||||
* </pre>
|
||||
*/
|
||||
final class DeclarativeConfigPropertiesBridge implements ConfigProperties {
|
||||
|
||||
private static final String OTEL_INSTRUMENTATION_PREFIX = "otel.instrumentation.";
|
||||
|
||||
// The node at .instrumentation.java
|
||||
private final DeclarativeConfigProperties instrumentationJavaNode;
|
||||
|
||||
DeclarativeConfigPropertiesBridge(DeclarativeConfigProperties instrumentationNode) {
|
||||
instrumentationJavaNode = instrumentationNode.getStructured("java", empty());
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public String getString(String propertyName) {
|
||||
return getPropertyValue(propertyName, DeclarativeConfigProperties::getString);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Boolean getBoolean(String propertyName) {
|
||||
return getPropertyValue(propertyName, DeclarativeConfigProperties::getBoolean);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Integer getInt(String propertyName) {
|
||||
return getPropertyValue(propertyName, DeclarativeConfigProperties::getInt);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Long getLong(String propertyName) {
|
||||
return getPropertyValue(propertyName, DeclarativeConfigProperties::getLong);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Double getDouble(String propertyName) {
|
||||
return getPropertyValue(propertyName, DeclarativeConfigProperties::getDouble);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Duration getDuration(String propertyName) {
|
||||
Long millis = getPropertyValue(propertyName, DeclarativeConfigProperties::getLong);
|
||||
if (millis == null) {
|
||||
return null;
|
||||
}
|
||||
return Duration.ofMillis(millis);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getList(String propertyName) {
|
||||
List<String> propertyValue =
|
||||
getPropertyValue(
|
||||
propertyName,
|
||||
(properties, lastPart) -> properties.getScalarList(lastPart, String.class));
|
||||
return propertyValue == null ? Collections.emptyList() : propertyValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> getMap(String propertyName) {
|
||||
DeclarativeConfigProperties propertyValue =
|
||||
getPropertyValue(propertyName, DeclarativeConfigProperties::getStructured);
|
||||
if (propertyValue == null) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
Map<String, String> result = new HashMap<>();
|
||||
propertyValue
|
||||
.getPropertyKeys()
|
||||
.forEach(
|
||||
key -> {
|
||||
String value = propertyValue.getString(key);
|
||||
if (value == null) {
|
||||
return;
|
||||
}
|
||||
result.put(key, value);
|
||||
});
|
||||
return Collections.unmodifiableMap(result);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private <T> T getPropertyValue(
|
||||
String property, BiFunction<DeclarativeConfigProperties, String, T> extractor) {
|
||||
if (instrumentationJavaNode == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,87 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.sdk.extension.incubator.fileconfig;
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,141 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.sdk.extension.incubator.fileconfig;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
|
||||
import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.InstrumentationModel;
|
||||
import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.OpenTelemetryConfigurationModel;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.Duration;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
class DeclarativeConfigPropertiesBridgeTest {
|
||||
|
||||
private static final String YAML =
|
||||
"file_format: 0.4\n"
|
||||
+ "instrumentation/development:\n"
|
||||
+ " java:\n"
|
||||
+ " common:\n"
|
||||
+ " default-enabled: true\n"
|
||||
+ " runtime-telemetry:\n"
|
||||
+ " enabled: false\n"
|
||||
+ " example-instrumentation:\n"
|
||||
+ " string_key: value\n"
|
||||
+ " bool_key: true\n"
|
||||
+ " int_key: 1\n"
|
||||
+ " double_key: 1.1\n"
|
||||
+ " list_key:\n"
|
||||
+ " - value1\n"
|
||||
+ " - value2\n"
|
||||
+ " - true\n"
|
||||
+ " map_key:\n"
|
||||
+ " string_key1: value1\n"
|
||||
+ " string_key2: value2\n"
|
||||
+ " bool_key: true\n"
|
||||
+ " acme:\n"
|
||||
+ " full_name:\n"
|
||||
+ " preserved: true";
|
||||
|
||||
private ConfigProperties bridge;
|
||||
private ConfigProperties emptyBridge;
|
||||
|
||||
@BeforeEach
|
||||
void setup() {
|
||||
OpenTelemetryConfigurationModel model =
|
||||
DeclarativeConfiguration.parse(
|
||||
new ByteArrayInputStream(YAML.getBytes(StandardCharsets.UTF_8)));
|
||||
SdkConfigProvider configProvider = SdkConfigProvider.create(model);
|
||||
bridge =
|
||||
new DeclarativeConfigPropertiesBridge(
|
||||
Objects.requireNonNull(configProvider.getInstrumentationConfig()));
|
||||
|
||||
OpenTelemetryConfigurationModel emptyModel =
|
||||
new OpenTelemetryConfigurationModel()
|
||||
.withAdditionalProperty("instrumentation/development", new InstrumentationModel());
|
||||
SdkConfigProvider emptyConfigProvider = SdkConfigProvider.create(emptyModel);
|
||||
emptyBridge =
|
||||
new DeclarativeConfigPropertiesBridge(
|
||||
Objects.requireNonNull(emptyConfigProvider.getInstrumentationConfig()));
|
||||
}
|
||||
|
||||
@Test
|
||||
void getProperties() {
|
||||
// only properties starting with "otel.instrumentation." are resolved
|
||||
// asking for properties which don't exist or inaccessible shouldn't result in an error
|
||||
assertThat(bridge.getString("file_format")).isNull();
|
||||
assertThat(bridge.getString("file_format", "foo")).isEqualTo("foo");
|
||||
assertThat(emptyBridge.getBoolean("otel.instrumentation.common.default-enabled")).isNull();
|
||||
assertThat(emptyBridge.getBoolean("otel.instrumentation.common.default-enabled", true))
|
||||
.isTrue();
|
||||
|
||||
// common cases
|
||||
assertThat(bridge.getBoolean("otel.instrumentation.common.default-enabled")).isTrue();
|
||||
assertThat(bridge.getBoolean("otel.instrumentation.runtime-telemetry.enabled")).isFalse();
|
||||
|
||||
// check all the types
|
||||
Map<String, String> expectedMap = new HashMap<>();
|
||||
expectedMap.put("string_key1", "value1");
|
||||
expectedMap.put("string_key2", "value2");
|
||||
assertThat(bridge.getString("otel.instrumentation.example-instrumentation.string_key"))
|
||||
.isEqualTo("value");
|
||||
assertThat(bridge.getBoolean("otel.instrumentation.example-instrumentation.bool_key")).isTrue();
|
||||
assertThat(bridge.getInt("otel.instrumentation.example-instrumentation.int_key")).isEqualTo(1);
|
||||
assertThat(bridge.getLong("otel.instrumentation.example-instrumentation.int_key"))
|
||||
.isEqualTo(1L);
|
||||
assertThat(bridge.getDuration("otel.instrumentation.example-instrumentation.int_key"))
|
||||
.isEqualTo(Duration.ofMillis(1));
|
||||
assertThat(bridge.getDouble("otel.instrumentation.example-instrumentation.double_key"))
|
||||
.isEqualTo(1.1);
|
||||
assertThat(bridge.getList("otel.instrumentation.example-instrumentation.list_key"))
|
||||
.isEqualTo(Arrays.asList("value1", "value2"));
|
||||
assertThat(bridge.getMap("otel.instrumentation.example-instrumentation.map_key"))
|
||||
.isEqualTo(expectedMap);
|
||||
|
||||
// asking for properties with the wrong type returns null
|
||||
assertThat(bridge.getBoolean("otel.instrumentation.example-instrumentation.string_key"))
|
||||
.isNull();
|
||||
assertThat(bridge.getString("otel.instrumentation.example-instrumentation.bool_key")).isNull();
|
||||
assertThat(bridge.getString("otel.instrumentation.example-instrumentation.int_key")).isNull();
|
||||
assertThat(bridge.getString("otel.instrumentation.example-instrumentation.double_key"))
|
||||
.isNull();
|
||||
assertThat(bridge.getString("otel.instrumentation.example-instrumentation.list_key")).isNull();
|
||||
assertThat(bridge.getString("otel.instrumentation.example-instrumentation.map_key")).isNull();
|
||||
|
||||
// check all the types
|
||||
assertThat(bridge.getString("otel.instrumentation.other-instrumentation.string_key", "value"))
|
||||
.isEqualTo("value");
|
||||
assertThat(bridge.getBoolean("otel.instrumentation.other-instrumentation.bool_key", true))
|
||||
.isTrue();
|
||||
assertThat(bridge.getInt("otel.instrumentation.other-instrumentation.int_key", 1)).isEqualTo(1);
|
||||
assertThat(bridge.getLong("otel.instrumentation.other-instrumentation.int_key", 1L))
|
||||
.isEqualTo(1L);
|
||||
assertThat(
|
||||
bridge.getDuration(
|
||||
"otel.instrumentation.other-instrumentation.int_key", Duration.ofMillis(1)))
|
||||
.isEqualTo(Duration.ofMillis(1));
|
||||
assertThat(bridge.getDouble("otel.instrumentation.other-instrumentation.double_key", 1.1))
|
||||
.isEqualTo(1.1);
|
||||
assertThat(
|
||||
bridge.getList(
|
||||
"otel.instrumentation.other-instrumentation.list_key",
|
||||
Arrays.asList("value1", "value2")))
|
||||
.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();
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue