diff --git a/api/incubator/build.gradle.kts b/api/incubator/build.gradle.kts index bdf12950ac..205b5504a0 100644 --- a/api/incubator/build.gradle.kts +++ b/api/incubator/build.gradle.kts @@ -14,6 +14,9 @@ dependencies { annotationProcessor("com.google.auto.value:auto-value") + // To use parsed config file as input for InstrumentationConfigUtilTest + testImplementation(project(":sdk-extensions:incubator")) + testImplementation(project(":sdk:testing")) testImplementation(project(":api:testing-internal")) diff --git a/api/incubator/src/main/java/io/opentelemetry/api/incubator/config/ConfigProvider.java b/api/incubator/src/main/java/io/opentelemetry/api/incubator/config/ConfigProvider.java new file mode 100644 index 0000000000..62cc044e65 --- /dev/null +++ b/api/incubator/src/main/java/io/opentelemetry/api/incubator/config/ConfigProvider.java @@ -0,0 +1,37 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.api.incubator.config; + +import javax.annotation.Nullable; +import javax.annotation.concurrent.ThreadSafe; + +/** + * A registry for accessing declarative configuration. + * + *

The name Provider is for consistency with other languages and it is NOT loaded + * using reflection. + * + *

See {@link InstrumentationConfigUtil} for convenience methods for extracting config from + * {@link ConfigProvider}. + */ +@ThreadSafe +public interface ConfigProvider { + + /** + * Returns the {@link DeclarativeConfigProperties} corresponding to instrumentation + * config, or {@code null} if unavailable. + * + * @return the instrumentation {@link DeclarativeConfigProperties} + */ + @Nullable + DeclarativeConfigProperties getInstrumentationConfig(); + + /** Returns a no-op {@link ConfigProvider}. */ + static ConfigProvider noop() { + return () -> null; + } +} diff --git a/api/incubator/src/main/java/io/opentelemetry/api/incubator/config/DeclarativeConfigException.java b/api/incubator/src/main/java/io/opentelemetry/api/incubator/config/DeclarativeConfigException.java new file mode 100644 index 0000000000..3ce49c6454 --- /dev/null +++ b/api/incubator/src/main/java/io/opentelemetry/api/incubator/config/DeclarativeConfigException.java @@ -0,0 +1,22 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.api.incubator.config; + +/** An exception that is thrown when errors occur with declarative configuration. */ +public final class DeclarativeConfigException extends RuntimeException { + + private static final long serialVersionUID = 3036584181551130522L; + + /** Create a new configuration exception with specified {@code message} and without a cause. */ + public DeclarativeConfigException(String message) { + super(message); + } + + /** Create a new configuration exception with specified {@code message} and {@code cause}. */ + public DeclarativeConfigException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/sdk-extensions/autoconfigure-spi/src/main/java/io/opentelemetry/sdk/autoconfigure/spi/internal/StructuredConfigProperties.java b/api/incubator/src/main/java/io/opentelemetry/api/incubator/config/DeclarativeConfigProperties.java similarity index 68% rename from sdk-extensions/autoconfigure-spi/src/main/java/io/opentelemetry/sdk/autoconfigure/spi/internal/StructuredConfigProperties.java rename to api/incubator/src/main/java/io/opentelemetry/api/incubator/config/DeclarativeConfigProperties.java index ced8800561..ef8e2343f2 100644 --- a/sdk-extensions/autoconfigure-spi/src/main/java/io/opentelemetry/sdk/autoconfigure/spi/internal/StructuredConfigProperties.java +++ b/api/incubator/src/main/java/io/opentelemetry/api/incubator/config/DeclarativeConfigProperties.java @@ -3,20 +3,18 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.sdk.autoconfigure.spi.internal; +package io.opentelemetry.api.incubator.config; import static io.opentelemetry.api.internal.ConfigUtil.defaultIfNull; -import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; -import io.opentelemetry.sdk.autoconfigure.spi.ConfigurationException; import java.util.List; import java.util.Set; import javax.annotation.Nullable; /** - * An interface for accessing structured configuration data. + * An interface for accessing declarative configuration data. * - *

An instance of {@link StructuredConfigProperties} is equivalent to a An instance of {@link DeclarativeConfigProperties} is equivalent to a YAML mapping node. It has accessors for * reading scalar properties, {@link #getStructured(String)} for reading children which are * themselves mappings, and {@link #getStructuredList(String)} for reading children which are @@ -25,24 +23,24 @@ import javax.annotation.Nullable; *

This class is internal and is hence not for public use. Its APIs are unstable and can change * at any time. */ -public interface StructuredConfigProperties { +public interface DeclarativeConfigProperties { /** - * Return an empty {@link StructuredConfigProperties} instance. + * Return an empty {@link DeclarativeConfigProperties} instance. * *

Useful for walking the tree without checking for null. For example, to access a string key * nested at .foo.bar.baz, call: {@code config.getStructured("foo", empty()).getStructured("bar", * empty()).getString("baz")}. */ - static StructuredConfigProperties empty() { - return EmptyStructuredConfigProperties.getInstance(); + static DeclarativeConfigProperties empty() { + return EmptyDeclarativeConfigProperties.getInstance(); } /** * Returns a {@link String} configuration property. * * @return null if the property has not been configured - * @throws ConfigurationException if the property is not a valid scalar string + * @throws DeclarativeConfigException if the property is not a valid scalar string */ @Nullable String getString(String name); @@ -52,7 +50,7 @@ public interface StructuredConfigProperties { * * @return a {@link String} configuration property or {@code defaultValue} if a property with * {@code name} has not been configured - * @throws ConfigurationException if the property is not a valid scalar string + * @throws DeclarativeConfigException if the property is not a valid scalar string */ default String getString(String name, String defaultValue) { return defaultIfNull(getString(name), defaultValue); @@ -63,7 +61,7 @@ public interface StructuredConfigProperties { * {@link Boolean#parseBoolean(String)} for handling the values. * * @return null if the property has not been configured - * @throws ConfigurationException if the property is not a valid scalar boolean + * @throws DeclarativeConfigException if the property is not a valid scalar boolean */ @Nullable Boolean getBoolean(String name); @@ -73,7 +71,7 @@ public interface StructuredConfigProperties { * * @return a {@link Boolean} configuration property or {@code defaultValue} if a property with * {@code name} has not been configured - * @throws ConfigurationException if the property is not a valid scalar boolean + * @throws DeclarativeConfigException if the property is not a valid scalar boolean */ default boolean getBoolean(String name, boolean defaultValue) { return defaultIfNull(getBoolean(name), defaultValue); @@ -86,7 +84,7 @@ public interface StructuredConfigProperties { * {@link Long#intValue()} which may result in loss of precision. * * @return null if the property has not been configured - * @throws ConfigurationException if the property is not a valid scalar integer + * @throws DeclarativeConfigException if the property is not a valid scalar integer */ @Nullable Integer getInt(String name); @@ -99,7 +97,7 @@ public interface StructuredConfigProperties { * * @return a {@link Integer} configuration property or {@code defaultValue} if a property with * {@code name} has not been configured - * @throws ConfigurationException if the property is not a valid scalar integer + * @throws DeclarativeConfigException if the property is not a valid scalar integer */ default int getInt(String name, int defaultValue) { return defaultIfNull(getInt(name), defaultValue); @@ -109,7 +107,7 @@ public interface StructuredConfigProperties { * Returns a {@link Long} configuration property. * * @return null if the property has not been configured - * @throws ConfigurationException if the property is not a valid scalar long + * @throws DeclarativeConfigException if the property is not a valid scalar long */ @Nullable Long getLong(String name); @@ -119,7 +117,7 @@ public interface StructuredConfigProperties { * * @return a {@link Long} configuration property or {@code defaultValue} if a property with {@code * name} has not been configured - * @throws ConfigurationException if the property is not a valid scalar long + * @throws DeclarativeConfigException if the property is not a valid scalar long */ default long getLong(String name, long defaultValue) { return defaultIfNull(getLong(name), defaultValue); @@ -129,7 +127,7 @@ public interface StructuredConfigProperties { * Returns a {@link Double} configuration property. * * @return null if the property has not been configured - * @throws ConfigurationException if the property is not a valid scalar double + * @throws DeclarativeConfigException if the property is not a valid scalar double */ @Nullable Double getDouble(String name); @@ -139,7 +137,7 @@ public interface StructuredConfigProperties { * * @return a {@link Double} configuration property or {@code defaultValue} if a property with * {@code name} has not been configured - * @throws ConfigurationException if the property is not a valid scalar double + * @throws DeclarativeConfigException if the property is not a valid scalar double */ default double getDouble(String name, double defaultValue) { return defaultIfNull(getDouble(name), defaultValue); @@ -153,8 +151,8 @@ public interface StructuredConfigProperties { * @param scalarType the scalar type, one of {@link String}, {@link Boolean}, {@link Long} or * {@link Double} * @return a {@link List} configuration property, or null if the property has not been configured - * @throws ConfigurationException if the property is not a valid sequence of scalars, or if {@code - * scalarType} is not supported + * @throws DeclarativeConfigException if the property is not a valid sequence of scalars, or if + * {@code scalarType} is not supported */ @Nullable List getScalarList(String name, Class scalarType); @@ -163,56 +161,58 @@ public interface StructuredConfigProperties { * Returns a {@link List} configuration property. Entries which are not strings are converted to * their string representation. * - * @see ConfigProperties#getList(String name) + * @param name the property name + * @param scalarType the scalar type, one of {@link String}, {@link Boolean}, {@link Long} or + * {@link Double} * @return a {@link List} configuration property or {@code defaultValue} if a property with {@code * name} has not been configured - * @throws ConfigurationException if the property is not a valid sequence of scalars + * @throws DeclarativeConfigException if the property is not a valid sequence of scalars */ default List getScalarList(String name, Class scalarType, List defaultValue) { return defaultIfNull(getScalarList(name, scalarType), defaultValue); } /** - * Returns a {@link StructuredConfigProperties} configuration property. + * Returns a {@link DeclarativeConfigProperties} configuration property. * * @return a map-valued configuration property, or {@code null} if {@code name} has not been * configured - * @throws ConfigurationException if the property is not a mapping + * @throws DeclarativeConfigException if the property is not a mapping */ @Nullable - StructuredConfigProperties getStructured(String name); + DeclarativeConfigProperties getStructured(String name); /** - * Returns a {@link StructuredConfigProperties} configuration property. + * Returns a list of {@link DeclarativeConfigProperties} configuration property. * * @return a map-valued configuration property, or {@code defaultValue} if {@code name} has not * been configured - * @throws ConfigurationException if the property is not a mapping + * @throws DeclarativeConfigException if the property is not a mapping */ - default StructuredConfigProperties getStructured( - String name, StructuredConfigProperties defaultValue) { + default DeclarativeConfigProperties getStructured( + String name, DeclarativeConfigProperties defaultValue) { return defaultIfNull(getStructured(name), defaultValue); } /** - * Returns a list of {@link StructuredConfigProperties} configuration property. + * Returns a list of {@link DeclarativeConfigProperties} configuration property. * * @return a list of map-valued configuration property, or {@code null} if {@code name} has not * been configured - * @throws ConfigurationException if the property is not a sequence of mappings + * @throws DeclarativeConfigException if the property is not a sequence of mappings */ @Nullable - List getStructuredList(String name); + List getStructuredList(String name); /** - * Returns a list of {@link StructuredConfigProperties} configuration property. + * Returns a list of {@link DeclarativeConfigProperties} configuration property. * * @return a list of map-valued configuration property, or {@code defaultValue} if {@code name} * has not been configured - * @throws ConfigurationException if the property is not a sequence of mappings + * @throws DeclarativeConfigException if the property is not a sequence of mappings */ - default List getStructuredList( - String name, List defaultValue) { + default List getStructuredList( + String name, List defaultValue) { return defaultIfNull(getStructuredList(name), defaultValue); } diff --git a/sdk-extensions/autoconfigure-spi/src/main/java/io/opentelemetry/sdk/autoconfigure/spi/internal/EmptyStructuredConfigProperties.java b/api/incubator/src/main/java/io/opentelemetry/api/incubator/config/EmptyDeclarativeConfigProperties.java similarity index 63% rename from sdk-extensions/autoconfigure-spi/src/main/java/io/opentelemetry/sdk/autoconfigure/spi/internal/EmptyStructuredConfigProperties.java rename to api/incubator/src/main/java/io/opentelemetry/api/incubator/config/EmptyDeclarativeConfigProperties.java index 2315dfa013..77b8a26549 100644 --- a/sdk-extensions/autoconfigure-spi/src/main/java/io/opentelemetry/sdk/autoconfigure/spi/internal/EmptyStructuredConfigProperties.java +++ b/api/incubator/src/main/java/io/opentelemetry/api/incubator/config/EmptyDeclarativeConfigProperties.java @@ -3,22 +3,22 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.sdk.autoconfigure.spi.internal; +package io.opentelemetry.api.incubator.config; import java.util.Collections; import java.util.List; import java.util.Set; import javax.annotation.Nullable; -/** Empty instance of {@link StructuredConfigProperties}. */ -final class EmptyStructuredConfigProperties implements StructuredConfigProperties { +/** Empty instance of {@link DeclarativeConfigProperties}. */ +final class EmptyDeclarativeConfigProperties implements DeclarativeConfigProperties { - private static final EmptyStructuredConfigProperties INSTANCE = - new EmptyStructuredConfigProperties(); + private static final EmptyDeclarativeConfigProperties INSTANCE = + new EmptyDeclarativeConfigProperties(); - private EmptyStructuredConfigProperties() {} + private EmptyDeclarativeConfigProperties() {} - static EmptyStructuredConfigProperties getInstance() { + static EmptyDeclarativeConfigProperties getInstance() { return INSTANCE; } @@ -60,13 +60,13 @@ final class EmptyStructuredConfigProperties implements StructuredConfigPropertie @Nullable @Override - public StructuredConfigProperties getStructured(String name) { + public DeclarativeConfigProperties getStructured(String name) { return null; } @Nullable @Override - public List getStructuredList(String name) { + public List getStructuredList(String name) { return null; } diff --git a/api/incubator/src/main/java/io/opentelemetry/api/incubator/config/GlobalConfigProvider.java b/api/incubator/src/main/java/io/opentelemetry/api/incubator/config/GlobalConfigProvider.java new file mode 100644 index 0000000000..b0daef4968 --- /dev/null +++ b/api/incubator/src/main/java/io/opentelemetry/api/incubator/config/GlobalConfigProvider.java @@ -0,0 +1,63 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.api.incubator.config; + +import io.opentelemetry.api.GlobalOpenTelemetry; +import java.util.concurrent.atomic.AtomicReference; +import javax.annotation.Nullable; + +/** + * This class provides a temporary global accessor for {@link ConfigProvider} until the + * instrumentation config API is marked stable. It will eventually be merged into {@link + * GlobalOpenTelemetry}. + */ +// We intentionally assign to be used for error reporting. +@SuppressWarnings("StaticAssignmentOfThrowable") +public final class GlobalConfigProvider { + + private static final AtomicReference instance = + new AtomicReference<>(ConfigProvider.noop()); + + @SuppressWarnings("NonFinalStaticField") + @Nullable + private static volatile Throwable setInstanceCaller; + + private GlobalConfigProvider() {} + + /** Returns the globally registered {@link ConfigProvider}. */ + // instance cannot be set to null + @SuppressWarnings("NullAway") + public static ConfigProvider get() { + return instance.get(); + } + + /** + * Sets the global {@link ConfigProvider}. Future calls to {@link #get()} will return the provided + * {@link ConfigProvider} instance. This should be called once as early as possible in your + * application initialization logic. + * + * @throws IllegalStateException when called more than once + */ + public static void set(ConfigProvider configProvider) { + boolean changed = instance.compareAndSet(ConfigProvider.noop(), configProvider); + if (!changed && (configProvider != ConfigProvider.noop())) { + throw new IllegalStateException( + "GlobalConfigProvider.set has already been called. GlobalConfigProvider.set " + + "must be called only once before any calls to GlobalConfigProvider.get. " + + "Previous invocation set to cause of this exception.", + setInstanceCaller); + } + setInstanceCaller = new Throwable(); + } + + /** + * Unsets the global {@link ConfigProvider}. This is only meant to be used from tests which need + * to reconfigure {@link ConfigProvider}. + */ + public static void resetForTest() { + instance.set(ConfigProvider.noop()); + } +} diff --git a/api/incubator/src/main/java/io/opentelemetry/api/incubator/config/InstrumentationConfigUtil.java b/api/incubator/src/main/java/io/opentelemetry/api/incubator/config/InstrumentationConfigUtil.java new file mode 100644 index 0000000000..e1e95e9315 --- /dev/null +++ b/api/incubator/src/main/java/io/opentelemetry/api/incubator/config/InstrumentationConfigUtil.java @@ -0,0 +1,151 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.api.incubator.config; + +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.function.Function; +import javax.annotation.Nullable; + +/** + * A collection of convenience methods to extract instrumentation config from {@link + * ConfigProvider#getInstrumentationConfig()}. + */ +public class InstrumentationConfigUtil { + + /** + * Return a map representation of the peer service map entries in {@code + * .instrumentation.general.peer.service_mapping}, or null if none is configured. + * + * @throws DeclarativeConfigException if an unexpected type is encountered accessing the property + */ + @Nullable + public static Map peerServiceMapping(ConfigProvider configProvider) { + List serviceMappingList = + getOrNull( + configProvider, + config -> config.getStructuredList("service_mapping"), + "general", + "peer"); + if (serviceMappingList == null) { + return null; + } + Map serviceMapping = new LinkedHashMap<>(); + serviceMappingList.forEach( + entry -> { + String peer = entry.getString("peer"); + String service = entry.getString("service"); + if (peer != null && service != null) { + serviceMapping.put(peer, service); + } + }); + return serviceMapping.isEmpty() ? null : serviceMapping; + } + + /** + * Return {@code .instrumentation.general.http.client.request_captured_headers}, or null if none + * is configured. + * + * @throws DeclarativeConfigException if an unexpected type is encountered accessing the property + */ + @Nullable + public static List httpClientRequestCapturedHeaders(ConfigProvider configProvider) { + return getOrNull( + configProvider, + config -> config.getScalarList("request_captured_headers", String.class), + "general", + "http", + "client"); + } + + /** + * Return {@code .instrumentation.general.http.client.response_captured_headers}, or null if none + * is configured. + * + * @throws DeclarativeConfigException if an unexpected type is encountered accessing the property + */ + @Nullable + public static List httpClientResponseCapturedHeaders(ConfigProvider configProvider) { + return getOrNull( + configProvider, + config -> config.getScalarList("response_captured_headers", String.class), + "general", + "http", + "client"); + } + + /** + * Return {@code .instrumentation.general.http.server.request_captured_headers}, or null if none + * is configured. + * + * @throws DeclarativeConfigException if an unexpected type is encountered accessing the property + */ + @Nullable + public static List httpServerRequestCapturedHeaders(ConfigProvider configProvider) { + return getOrNull( + configProvider, + config -> config.getScalarList("request_captured_headers", String.class), + "general", + "http", + "server"); + } + + /** + * Return {@code .instrumentation.general.http.server.response_captured_headers}, or null if none + * is configured. + * + * @throws DeclarativeConfigException if an unexpected type is encountered accessing the property + */ + @Nullable + public static List httpSeverResponseCapturedHeaders(ConfigProvider configProvider) { + return getOrNull( + configProvider, + config -> config.getScalarList("response_captured_headers", String.class), + "general", + "http", + "server"); + } + + /** + * Return {@code .instrumentation.java.}, or null if none is configured. + * + * @throws DeclarativeConfigException if an unexpected type is encountered accessing the property + */ + @Nullable + public static DeclarativeConfigProperties javaInstrumentationConfig( + ConfigProvider configProvider, String instrumentationName) { + return getOrNull(configProvider, config -> config.getStructured(instrumentationName), "java"); + } + + /** + * Walk down the {@code segments} of {@link ConfigProvider#getInstrumentationConfig()} and call + * {@code accessor} on the terminal node. Returns null if {@link + * ConfigProvider#getInstrumentationConfig()} is null, or if null is encountered walking the + * {@code segments}, or if {@code accessor} returns null. + * + *

See other methods in {@link InstrumentationConfigUtil} for usage examples. + */ + @Nullable + public static T getOrNull( + ConfigProvider configProvider, + Function accessor, + String... segments) { + DeclarativeConfigProperties config = configProvider.getInstrumentationConfig(); + if (config == null) { + return null; + } + for (String segment : segments) { + config = config.getStructured(segment); + if (config == null) { + return null; + } + } + return accessor.apply(config); + } + + private InstrumentationConfigUtil() {} +} diff --git a/api/incubator/src/test/java/io/opentelemetry/api/incubator/ConfigProviderTest.java b/api/incubator/src/test/java/io/opentelemetry/api/incubator/ConfigProviderTest.java new file mode 100644 index 0000000000..9c9e4bf41e --- /dev/null +++ b/api/incubator/src/test/java/io/opentelemetry/api/incubator/ConfigProviderTest.java @@ -0,0 +1,20 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.api.incubator; + +import static org.assertj.core.api.Assertions.assertThat; + +import io.opentelemetry.api.incubator.config.ConfigProvider; +import org.junit.jupiter.api.Test; + +class ConfigProviderTest { + + @Test + void noopEquality() { + ConfigProvider noop = ConfigProvider.noop(); + assertThat(ConfigProvider.noop()).isSameAs(noop); + } +} diff --git a/api/incubator/src/test/java/io/opentelemetry/api/incubator/config/GlobalConfigProviderTest.java b/api/incubator/src/test/java/io/opentelemetry/api/incubator/config/GlobalConfigProviderTest.java new file mode 100644 index 0000000000..ecd837a529 --- /dev/null +++ b/api/incubator/src/test/java/io/opentelemetry/api/incubator/config/GlobalConfigProviderTest.java @@ -0,0 +1,44 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.api.incubator.config; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +class GlobalConfigProviderTest { + + @BeforeAll + static void beforeClass() { + GlobalConfigProvider.resetForTest(); + } + + @AfterEach + void after() { + GlobalConfigProvider.resetForTest(); + } + + @Test + void setAndGet() { + assertThat(GlobalConfigProvider.get()).isEqualTo(ConfigProvider.noop()); + ConfigProvider configProvider = DeclarativeConfigProperties::empty; + GlobalConfigProvider.set(configProvider); + assertThat(GlobalConfigProvider.get()).isSameAs(configProvider); + } + + @Test + void setThenSet() { + ConfigProvider configProvider = DeclarativeConfigProperties::empty; + GlobalConfigProvider.set(configProvider); + assertThatThrownBy(() -> GlobalConfigProvider.set(configProvider)) + .isInstanceOf(IllegalStateException.class) + .hasMessageContaining("GlobalConfigProvider.set has already been called") + .hasStackTraceContaining("setThenSet"); + } +} diff --git a/api/incubator/src/test/java/io/opentelemetry/api/incubator/config/InstrumentationConfigUtilTest.java b/api/incubator/src/test/java/io/opentelemetry/api/incubator/config/InstrumentationConfigUtilTest.java new file mode 100644 index 0000000000..94da0f2d10 --- /dev/null +++ b/api/incubator/src/test/java/io/opentelemetry/api/incubator/config/InstrumentationConfigUtilTest.java @@ -0,0 +1,169 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.api.incubator.config; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.google.common.collect.ImmutableMap; +import io.opentelemetry.sdk.extension.incubator.fileconfig.DeclarativeConfiguration; +import io.opentelemetry.sdk.extension.incubator.fileconfig.SdkConfigProvider; +import io.opentelemetry.sdk.extension.incubator.fileconfig.YamlDeclarativeConfigProperties; +import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.OpenTelemetryConfigurationModel; +import java.io.ByteArrayInputStream; +import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import org.junit.jupiter.api.Test; + +class InstrumentationConfigUtilTest { + + /** + * See kitchen-sink.yaml. + */ + private static final String kitchenSinkInstrumentationConfig = + "instrumentation:\n" + + " general:\n" + + " peer:\n" + + " service_mapping:\n" + + " - peer: 1.2.3.4\n" + + " service: FooService\n" + + " - peer: 2.3.4.5\n" + + " service: BarService\n" + + " http:\n" + + " client:\n" + + " request_captured_headers:\n" + + " - client-request-header1\n" + + " - client-request-header2\n" + + " response_captured_headers:\n" + + " - client-response-header1\n" + + " - client-response-header2\n" + + " server:\n" + + " request_captured_headers:\n" + + " - server-request-header1\n" + + " - server-request-header2\n" + + " response_captured_headers:\n" + + " - server-response-header1\n" + + " - server-response-header2\n" + + " java:\n" + + " example:\n" + + " property: \"value\""; + + private static final ConfigProvider kitchenSinkConfigProvider = + toConfigProvider(kitchenSinkInstrumentationConfig); + private static final ConfigProvider emptyInstrumentationConfigProvider = + toConfigProvider("instrumentation:\n"); + private static final ConfigProvider emptyGeneralConfigProvider = + toConfigProvider("instrumentation:\n general:\n"); + private static final ConfigProvider emptyHttpConfigProvider = + toConfigProvider("instrumentation:\n general:\n http:\n"); + + private static ConfigProvider toConfigProvider(String configYaml) { + OpenTelemetryConfigurationModel configuration = + DeclarativeConfiguration.parse( + new ByteArrayInputStream(configYaml.getBytes(StandardCharsets.UTF_8))); + return SdkConfigProvider.create(configuration); + } + + @Test + void peerServiceMapping() { + assertThat(InstrumentationConfigUtil.peerServiceMapping(kitchenSinkConfigProvider)) + .isEqualTo(ImmutableMap.of("1.2.3.4", "FooService", "2.3.4.5", "BarService")); + assertThat(InstrumentationConfigUtil.peerServiceMapping(emptyInstrumentationConfigProvider)) + .isNull(); + assertThat(InstrumentationConfigUtil.peerServiceMapping(emptyGeneralConfigProvider)).isNull(); + assertThat(InstrumentationConfigUtil.peerServiceMapping(emptyHttpConfigProvider)).isNull(); + } + + @Test + void httpClientRequestCapturedHeaders() { + assertThat( + InstrumentationConfigUtil.httpClientRequestCapturedHeaders(kitchenSinkConfigProvider)) + .isEqualTo(Arrays.asList("client-request-header1", "client-request-header2")); + assertThat( + InstrumentationConfigUtil.httpClientRequestCapturedHeaders( + emptyInstrumentationConfigProvider)) + .isNull(); + assertThat( + InstrumentationConfigUtil.httpClientRequestCapturedHeaders(emptyGeneralConfigProvider)) + .isNull(); + assertThat(InstrumentationConfigUtil.httpClientRequestCapturedHeaders(emptyHttpConfigProvider)) + .isNull(); + } + + @Test + void httpClientResponseCapturedHeaders() { + assertThat( + InstrumentationConfigUtil.httpClientResponseCapturedHeaders(kitchenSinkConfigProvider)) + .isEqualTo(Arrays.asList("client-response-header1", "client-response-header2")); + assertThat( + InstrumentationConfigUtil.httpClientResponseCapturedHeaders( + emptyInstrumentationConfigProvider)) + .isNull(); + assertThat( + InstrumentationConfigUtil.httpClientResponseCapturedHeaders(emptyGeneralConfigProvider)) + .isNull(); + assertThat(InstrumentationConfigUtil.httpClientResponseCapturedHeaders(emptyHttpConfigProvider)) + .isNull(); + } + + @Test + void httpServerRequestCapturedHeaders() { + assertThat( + InstrumentationConfigUtil.httpServerRequestCapturedHeaders(kitchenSinkConfigProvider)) + .isEqualTo(Arrays.asList("server-request-header1", "server-request-header2")); + assertThat( + InstrumentationConfigUtil.httpServerRequestCapturedHeaders( + emptyInstrumentationConfigProvider)) + .isNull(); + assertThat( + InstrumentationConfigUtil.httpServerRequestCapturedHeaders(emptyGeneralConfigProvider)) + .isNull(); + assertThat(InstrumentationConfigUtil.httpServerRequestCapturedHeaders(emptyHttpConfigProvider)) + .isNull(); + } + + @Test + void httpServerResponseCapturedHeaders() { + assertThat( + InstrumentationConfigUtil.httpSeverResponseCapturedHeaders(kitchenSinkConfigProvider)) + .isEqualTo(Arrays.asList("server-response-header1", "server-response-header2")); + assertThat( + InstrumentationConfigUtil.httpSeverResponseCapturedHeaders( + emptyInstrumentationConfigProvider)) + .isNull(); + assertThat( + InstrumentationConfigUtil.httpSeverResponseCapturedHeaders(emptyGeneralConfigProvider)) + .isNull(); + assertThat(InstrumentationConfigUtil.httpSeverResponseCapturedHeaders(emptyHttpConfigProvider)) + .isNull(); + } + + @Test + void javaInstrumentationConfig() { + assertThat( + InstrumentationConfigUtil.javaInstrumentationConfig( + kitchenSinkConfigProvider, "example")) + .isNotNull() + .isInstanceOfSatisfying( + YamlDeclarativeConfigProperties.class, + exampleConfig -> + assertThat(exampleConfig.toMap()).isEqualTo(ImmutableMap.of("property", "value"))); + assertThat( + InstrumentationConfigUtil.javaInstrumentationConfig(kitchenSinkConfigProvider, "foo")) + .isNull(); + assertThat( + InstrumentationConfigUtil.javaInstrumentationConfig( + emptyInstrumentationConfigProvider, "example")) + .isNull(); + assertThat( + InstrumentationConfigUtil.javaInstrumentationConfig( + emptyGeneralConfigProvider, "example")) + .isNull(); + assertThat( + InstrumentationConfigUtil.javaInstrumentationConfig(emptyHttpConfigProvider, "example")) + .isNull(); + } +} diff --git a/exporters/common/build.gradle.kts b/exporters/common/build.gradle.kts index bbca8bc416..8d5f38224e 100644 --- a/exporters/common/build.gradle.kts +++ b/exporters/common/build.gradle.kts @@ -13,6 +13,7 @@ dependencies { api(project(":api:all")) api(project(":sdk-extensions:autoconfigure-spi")) + compileOnly(project(":api:incubator")) compileOnly(project(":sdk:common")) compileOnly(project(":exporters:common:compile-stub")) diff --git a/exporters/common/src/main/java/io/opentelemetry/exporter/internal/ExporterBuilderUtil.java b/exporters/common/src/main/java/io/opentelemetry/exporter/internal/ExporterBuilderUtil.java index f67c7fe716..9e93cc38ab 100644 --- a/exporters/common/src/main/java/io/opentelemetry/exporter/internal/ExporterBuilderUtil.java +++ b/exporters/common/src/main/java/io/opentelemetry/exporter/internal/ExporterBuilderUtil.java @@ -9,7 +9,6 @@ import static io.opentelemetry.sdk.metrics.Aggregation.explicitBucketHistogram; import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; import io.opentelemetry.sdk.autoconfigure.spi.ConfigurationException; -import io.opentelemetry.sdk.autoconfigure.spi.internal.StructuredConfigProperties; import io.opentelemetry.sdk.common.export.MemoryMode; import io.opentelemetry.sdk.metrics.Aggregation; import io.opentelemetry.sdk.metrics.InstrumentType; @@ -63,22 +62,6 @@ public final class ExporterBuilderUtil { memoryModeConsumer.accept(memoryMode); } - /** Invoke the {@code memoryModeConsumer} with the configured {@link MemoryMode}. */ - public static void configureExporterMemoryMode( - StructuredConfigProperties config, Consumer memoryModeConsumer) { - String memoryModeStr = config.getString("memory_mode"); - if (memoryModeStr == null) { - return; - } - MemoryMode memoryMode; - try { - memoryMode = MemoryMode.valueOf(memoryModeStr.toUpperCase(Locale.ROOT)); - } catch (IllegalArgumentException e) { - throw new ConfigurationException("Unrecognized memory_mode: " + memoryModeStr, e); - } - memoryModeConsumer.accept(memoryMode); - } - /** * Invoke the {@code defaultAggregationSelectorConsumer} with the configured {@link * DefaultAggregationSelector}. @@ -126,30 +109,6 @@ public final class ExporterBuilderUtil { aggregationTemporalitySelectorConsumer.accept(temporalitySelector); } - public static void configureOtlpAggregationTemporality( - StructuredConfigProperties config, - Consumer aggregationTemporalitySelectorConsumer) { - String temporalityStr = config.getString("temporality_preference"); - if (temporalityStr == null) { - return; - } - AggregationTemporalitySelector temporalitySelector; - switch (temporalityStr.toLowerCase(Locale.ROOT)) { - case "cumulative": - temporalitySelector = AggregationTemporalitySelector.alwaysCumulative(); - break; - case "delta": - temporalitySelector = AggregationTemporalitySelector.deltaPreferred(); - break; - case "lowmemory": - temporalitySelector = AggregationTemporalitySelector.lowMemory(); - break; - default: - throw new ConfigurationException("Unrecognized temporality_preference: " + temporalityStr); - } - aggregationTemporalitySelectorConsumer.accept(temporalitySelector); - } - /** * Invoke the {@code defaultAggregationSelectorConsumer} with the configured {@link * DefaultAggregationSelector}. @@ -165,28 +124,5 @@ public final class ExporterBuilderUtil { } } - /** - * Invoke the {@code defaultAggregationSelectorConsumer} with the configured {@link - * DefaultAggregationSelector}. - */ - public static void configureOtlpHistogramDefaultAggregation( - StructuredConfigProperties config, - Consumer defaultAggregationSelectorConsumer) { - String defaultHistogramAggregation = config.getString("default_histogram_aggregation"); - if (defaultHistogramAggregation == null) { - return; - } - if (AggregationUtil.aggregationName(Aggregation.base2ExponentialBucketHistogram()) - .equalsIgnoreCase(defaultHistogramAggregation)) { - defaultAggregationSelectorConsumer.accept( - DefaultAggregationSelector.getDefault() - .with(InstrumentType.HISTOGRAM, Aggregation.base2ExponentialBucketHistogram())); - } else if (!AggregationUtil.aggregationName(explicitBucketHistogram()) - .equalsIgnoreCase(defaultHistogramAggregation)) { - throw new ConfigurationException( - "Unrecognized default_histogram_aggregation: " + defaultHistogramAggregation); - } - } - private ExporterBuilderUtil() {} } diff --git a/exporters/common/src/main/java/io/opentelemetry/exporter/internal/IncubatingExporterBuilderUtil.java b/exporters/common/src/main/java/io/opentelemetry/exporter/internal/IncubatingExporterBuilderUtil.java new file mode 100644 index 0000000000..f5992879cf --- /dev/null +++ b/exporters/common/src/main/java/io/opentelemetry/exporter/internal/IncubatingExporterBuilderUtil.java @@ -0,0 +1,93 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.exporter.internal; + +import static io.opentelemetry.sdk.metrics.Aggregation.explicitBucketHistogram; + +import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties; +import io.opentelemetry.sdk.autoconfigure.spi.ConfigurationException; +import io.opentelemetry.sdk.common.export.MemoryMode; +import io.opentelemetry.sdk.metrics.Aggregation; +import io.opentelemetry.sdk.metrics.InstrumentType; +import io.opentelemetry.sdk.metrics.export.AggregationTemporalitySelector; +import io.opentelemetry.sdk.metrics.export.DefaultAggregationSelector; +import io.opentelemetry.sdk.metrics.internal.aggregator.AggregationUtil; +import java.util.Locale; +import java.util.function.Consumer; + +/** + * Utilities for exporter builders. + * + *

This class is internal and is hence not for public use. Its APIs are unstable and can change + * at any time. + */ +public final class IncubatingExporterBuilderUtil { + + /** Invoke the {@code memoryModeConsumer} with the configured {@link MemoryMode}. */ + public static void configureExporterMemoryMode( + DeclarativeConfigProperties config, Consumer memoryModeConsumer) { + String memoryModeStr = config.getString("memory_mode"); + if (memoryModeStr == null) { + return; + } + MemoryMode memoryMode; + try { + memoryMode = MemoryMode.valueOf(memoryModeStr.toUpperCase(Locale.ROOT)); + } catch (IllegalArgumentException e) { + throw new ConfigurationException("Unrecognized memory_mode: " + memoryModeStr, e); + } + memoryModeConsumer.accept(memoryMode); + } + + public static void configureOtlpAggregationTemporality( + DeclarativeConfigProperties config, + Consumer aggregationTemporalitySelectorConsumer) { + String temporalityStr = config.getString("temporality_preference"); + if (temporalityStr == null) { + return; + } + AggregationTemporalitySelector temporalitySelector; + switch (temporalityStr.toLowerCase(Locale.ROOT)) { + case "cumulative": + temporalitySelector = AggregationTemporalitySelector.alwaysCumulative(); + break; + case "delta": + temporalitySelector = AggregationTemporalitySelector.deltaPreferred(); + break; + case "lowmemory": + temporalitySelector = AggregationTemporalitySelector.lowMemory(); + break; + default: + throw new ConfigurationException("Unrecognized temporality_preference: " + temporalityStr); + } + aggregationTemporalitySelectorConsumer.accept(temporalitySelector); + } + + /** + * Invoke the {@code defaultAggregationSelectorConsumer} with the configured {@link + * DefaultAggregationSelector}. + */ + public static void configureOtlpHistogramDefaultAggregation( + DeclarativeConfigProperties config, + Consumer defaultAggregationSelectorConsumer) { + String defaultHistogramAggregation = config.getString("default_histogram_aggregation"); + if (defaultHistogramAggregation == null) { + return; + } + if (AggregationUtil.aggregationName(Aggregation.base2ExponentialBucketHistogram()) + .equalsIgnoreCase(defaultHistogramAggregation)) { + defaultAggregationSelectorConsumer.accept( + DefaultAggregationSelector.getDefault() + .with(InstrumentType.HISTOGRAM, Aggregation.base2ExponentialBucketHistogram())); + } else if (!AggregationUtil.aggregationName(explicitBucketHistogram()) + .equalsIgnoreCase(defaultHistogramAggregation)) { + throw new ConfigurationException( + "Unrecognized default_histogram_aggregation: " + defaultHistogramAggregation); + } + } + + private IncubatingExporterBuilderUtil() {} +} diff --git a/exporters/logging-otlp/build.gradle.kts b/exporters/logging-otlp/build.gradle.kts index a65f52f852..0870f2c993 100644 --- a/exporters/logging-otlp/build.gradle.kts +++ b/exporters/logging-otlp/build.gradle.kts @@ -14,10 +14,12 @@ dependencies { implementation(project(":sdk:logs")) implementation(project(":exporters:otlp:common")) + compileOnly(project(":api:incubator")) implementation(project(":sdk-extensions:autoconfigure-spi")) implementation("com.fasterxml.jackson.core:jackson-core") + testImplementation(project(":api:incubator")) testImplementation(project(":sdk:testing")) testImplementation("com.google.guava:guava") diff --git a/exporters/logging-otlp/src/main/java/io/opentelemetry/exporter/logging/otlp/internal/logs/OtlpStdoutLogRecordExporterComponentProvider.java b/exporters/logging-otlp/src/main/java/io/opentelemetry/exporter/logging/otlp/internal/logs/OtlpStdoutLogRecordExporterComponentProvider.java index e80747a86e..57817346d7 100644 --- a/exporters/logging-otlp/src/main/java/io/opentelemetry/exporter/logging/otlp/internal/logs/OtlpStdoutLogRecordExporterComponentProvider.java +++ b/exporters/logging-otlp/src/main/java/io/opentelemetry/exporter/logging/otlp/internal/logs/OtlpStdoutLogRecordExporterComponentProvider.java @@ -5,9 +5,9 @@ package io.opentelemetry.exporter.logging.otlp.internal.logs; -import io.opentelemetry.exporter.internal.ExporterBuilderUtil; +import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties; +import io.opentelemetry.exporter.internal.IncubatingExporterBuilderUtil; import io.opentelemetry.sdk.autoconfigure.spi.internal.ComponentProvider; -import io.opentelemetry.sdk.autoconfigure.spi.internal.StructuredConfigProperties; import io.opentelemetry.sdk.logs.export.LogRecordExporter; /** @@ -30,9 +30,9 @@ public final class OtlpStdoutLogRecordExporterComponentProvider } @Override - public LogRecordExporter create(StructuredConfigProperties config) { + public LogRecordExporter create(DeclarativeConfigProperties config) { OtlpStdoutLogRecordExporterBuilder builder = OtlpStdoutLogRecordExporter.builder(); - ExporterBuilderUtil.configureExporterMemoryMode(config, builder::setMemoryMode); + IncubatingExporterBuilderUtil.configureExporterMemoryMode(config, builder::setMemoryMode); return builder.build(); } } diff --git a/exporters/logging-otlp/src/main/java/io/opentelemetry/exporter/logging/otlp/internal/metrics/OtlpStdoutMetricExporterComponentProvider.java b/exporters/logging-otlp/src/main/java/io/opentelemetry/exporter/logging/otlp/internal/metrics/OtlpStdoutMetricExporterComponentProvider.java index 2a69bd0c7d..c600edbd59 100644 --- a/exporters/logging-otlp/src/main/java/io/opentelemetry/exporter/logging/otlp/internal/metrics/OtlpStdoutMetricExporterComponentProvider.java +++ b/exporters/logging-otlp/src/main/java/io/opentelemetry/exporter/logging/otlp/internal/metrics/OtlpStdoutMetricExporterComponentProvider.java @@ -5,9 +5,9 @@ package io.opentelemetry.exporter.logging.otlp.internal.metrics; -import io.opentelemetry.exporter.internal.ExporterBuilderUtil; +import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties; +import io.opentelemetry.exporter.internal.IncubatingExporterBuilderUtil; import io.opentelemetry.sdk.autoconfigure.spi.internal.ComponentProvider; -import io.opentelemetry.sdk.autoconfigure.spi.internal.StructuredConfigProperties; import io.opentelemetry.sdk.metrics.export.MetricExporter; /** @@ -30,12 +30,12 @@ public final class OtlpStdoutMetricExporterComponentProvider } @Override - public MetricExporter create(StructuredConfigProperties config) { + public MetricExporter create(DeclarativeConfigProperties config) { OtlpStdoutMetricExporterBuilder builder = OtlpStdoutMetricExporter.builder(); - ExporterBuilderUtil.configureExporterMemoryMode(config, builder::setMemoryMode); - ExporterBuilderUtil.configureOtlpAggregationTemporality( + IncubatingExporterBuilderUtil.configureExporterMemoryMode(config, builder::setMemoryMode); + IncubatingExporterBuilderUtil.configureOtlpAggregationTemporality( config, builder::setAggregationTemporalitySelector); - ExporterBuilderUtil.configureOtlpHistogramDefaultAggregation( + IncubatingExporterBuilderUtil.configureOtlpHistogramDefaultAggregation( config, builder::setDefaultAggregationSelector); return builder.build(); } diff --git a/exporters/logging-otlp/src/main/java/io/opentelemetry/exporter/logging/otlp/internal/traces/OtlpStdoutSpanExporterComponentProvider.java b/exporters/logging-otlp/src/main/java/io/opentelemetry/exporter/logging/otlp/internal/traces/OtlpStdoutSpanExporterComponentProvider.java index c21029ed13..8a821b3977 100644 --- a/exporters/logging-otlp/src/main/java/io/opentelemetry/exporter/logging/otlp/internal/traces/OtlpStdoutSpanExporterComponentProvider.java +++ b/exporters/logging-otlp/src/main/java/io/opentelemetry/exporter/logging/otlp/internal/traces/OtlpStdoutSpanExporterComponentProvider.java @@ -5,9 +5,9 @@ package io.opentelemetry.exporter.logging.otlp.internal.traces; -import io.opentelemetry.exporter.internal.ExporterBuilderUtil; +import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties; +import io.opentelemetry.exporter.internal.IncubatingExporterBuilderUtil; import io.opentelemetry.sdk.autoconfigure.spi.internal.ComponentProvider; -import io.opentelemetry.sdk.autoconfigure.spi.internal.StructuredConfigProperties; import io.opentelemetry.sdk.trace.export.SpanExporter; /** @@ -30,9 +30,9 @@ public final class OtlpStdoutSpanExporterComponentProvider } @Override - public SpanExporter create(StructuredConfigProperties config) { + public SpanExporter create(DeclarativeConfigProperties config) { OtlpStdoutSpanExporterBuilder builder = OtlpStdoutSpanExporter.builder(); - ExporterBuilderUtil.configureExporterMemoryMode(config, builder::setMemoryMode); + IncubatingExporterBuilderUtil.configureExporterMemoryMode(config, builder::setMemoryMode); return builder.build(); } } diff --git a/exporters/logging-otlp/src/test/java/io/opentelemetry/exporter/logging/otlp/AbstractOtlpStdoutExporterTest.java b/exporters/logging-otlp/src/test/java/io/opentelemetry/exporter/logging/otlp/AbstractOtlpStdoutExporterTest.java index fbfd771a03..7605cc851d 100644 --- a/exporters/logging-otlp/src/test/java/io/opentelemetry/exporter/logging/otlp/AbstractOtlpStdoutExporterTest.java +++ b/exporters/logging-otlp/src/test/java/io/opentelemetry/exporter/logging/otlp/AbstractOtlpStdoutExporterTest.java @@ -15,10 +15,10 @@ import static org.mockito.Mockito.when; import com.google.common.collect.ImmutableList; import com.google.common.collect.Streams; import io.github.netmikey.logunit.api.LogCapturer; +import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties; import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; import io.opentelemetry.sdk.autoconfigure.spi.internal.ComponentProvider; import io.opentelemetry.sdk.autoconfigure.spi.internal.DefaultConfigProperties; -import io.opentelemetry.sdk.autoconfigure.spi.internal.StructuredConfigProperties; import io.opentelemetry.sdk.common.export.MemoryMode; import java.io.BufferedOutputStream; import java.io.ByteArrayOutputStream; @@ -306,7 +306,7 @@ abstract class AbstractOtlpStdoutExporterTest { @Test void componentProviderConfig() { - StructuredConfigProperties properties = mock(StructuredConfigProperties.class); + DeclarativeConfigProperties properties = mock(DeclarativeConfigProperties.class); T exporter = exporterFromComponentProvider(properties); assertThat(exporter).extracting("wrapperJsonObject").isEqualTo(true); @@ -328,7 +328,7 @@ abstract class AbstractOtlpStdoutExporterTest { } @SuppressWarnings("unchecked") - protected T exporterFromComponentProvider(StructuredConfigProperties properties) { + protected T exporterFromComponentProvider(DeclarativeConfigProperties properties) { return (T) ((ComponentProvider) loadSpi(ComponentProvider.class) diff --git a/exporters/logging-otlp/src/test/java/io/opentelemetry/exporter/logging/otlp/OtlpStdoutMetricExporterTest.java b/exporters/logging-otlp/src/test/java/io/opentelemetry/exporter/logging/otlp/OtlpStdoutMetricExporterTest.java index fc081e229f..df91f1e2cb 100644 --- a/exporters/logging-otlp/src/test/java/io/opentelemetry/exporter/logging/otlp/OtlpStdoutMetricExporterTest.java +++ b/exporters/logging-otlp/src/test/java/io/opentelemetry/exporter/logging/otlp/OtlpStdoutMetricExporterTest.java @@ -11,10 +11,10 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import com.google.common.collect.ImmutableMap; +import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties; import io.opentelemetry.exporter.logging.otlp.internal.metrics.OtlpStdoutMetricExporter; import io.opentelemetry.exporter.logging.otlp.internal.metrics.OtlpStdoutMetricExporterBuilder; import io.opentelemetry.sdk.autoconfigure.spi.internal.DefaultConfigProperties; -import io.opentelemetry.sdk.autoconfigure.spi.internal.StructuredConfigProperties; import io.opentelemetry.sdk.autoconfigure.spi.metrics.ConfigurableMetricExporterProvider; import io.opentelemetry.sdk.common.export.MemoryMode; import io.opentelemetry.sdk.metrics.Aggregation; @@ -81,7 +81,7 @@ class OtlpStdoutMetricExporterTest @Test void componentProviderMetricConfig() { - StructuredConfigProperties properties = mock(StructuredConfigProperties.class); + DeclarativeConfigProperties properties = mock(DeclarativeConfigProperties.class); when(properties.getString("temporality_preference")).thenReturn("DELTA"); when(properties.getString("default_histogram_aggregation")) .thenReturn("BASE2_EXPONENTIAL_BUCKET_HISTOGRAM"); diff --git a/exporters/logging/build.gradle.kts b/exporters/logging/build.gradle.kts index 6feefb00fe..82e9677183 100644 --- a/exporters/logging/build.gradle.kts +++ b/exporters/logging/build.gradle.kts @@ -12,6 +12,7 @@ dependencies { api(project(":sdk:all")) implementation(project(":sdk-extensions:autoconfigure-spi")) + compileOnly(project(":api:incubator")) testImplementation(project(":sdk:testing")) } diff --git a/exporters/logging/src/main/java/io/opentelemetry/exporter/logging/internal/ConsoleLogRecordExporterComponentProvider.java b/exporters/logging/src/main/java/io/opentelemetry/exporter/logging/internal/ConsoleLogRecordExporterComponentProvider.java index 8c9d048c3b..2d8141cc4f 100644 --- a/exporters/logging/src/main/java/io/opentelemetry/exporter/logging/internal/ConsoleLogRecordExporterComponentProvider.java +++ b/exporters/logging/src/main/java/io/opentelemetry/exporter/logging/internal/ConsoleLogRecordExporterComponentProvider.java @@ -5,9 +5,9 @@ package io.opentelemetry.exporter.logging.internal; +import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties; import io.opentelemetry.exporter.logging.SystemOutLogRecordExporter; import io.opentelemetry.sdk.autoconfigure.spi.internal.ComponentProvider; -import io.opentelemetry.sdk.autoconfigure.spi.internal.StructuredConfigProperties; import io.opentelemetry.sdk.logs.export.LogRecordExporter; /** @@ -30,7 +30,7 @@ public final class ConsoleLogRecordExporterComponentProvider } @Override - public LogRecordExporter create(StructuredConfigProperties config) { + public LogRecordExporter create(DeclarativeConfigProperties config) { return SystemOutLogRecordExporter.create(); } } diff --git a/exporters/logging/src/main/java/io/opentelemetry/exporter/logging/internal/ConsoleMetricExporterComponentProvider.java b/exporters/logging/src/main/java/io/opentelemetry/exporter/logging/internal/ConsoleMetricExporterComponentProvider.java index df5270ca11..6fab453403 100644 --- a/exporters/logging/src/main/java/io/opentelemetry/exporter/logging/internal/ConsoleMetricExporterComponentProvider.java +++ b/exporters/logging/src/main/java/io/opentelemetry/exporter/logging/internal/ConsoleMetricExporterComponentProvider.java @@ -5,9 +5,9 @@ package io.opentelemetry.exporter.logging.internal; +import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties; import io.opentelemetry.exporter.logging.LoggingMetricExporter; import io.opentelemetry.sdk.autoconfigure.spi.internal.ComponentProvider; -import io.opentelemetry.sdk.autoconfigure.spi.internal.StructuredConfigProperties; import io.opentelemetry.sdk.metrics.export.MetricExporter; /** @@ -30,7 +30,7 @@ public final class ConsoleMetricExporterComponentProvider } @Override - public MetricExporter create(StructuredConfigProperties config) { + public MetricExporter create(DeclarativeConfigProperties config) { return LoggingMetricExporter.create(); } } diff --git a/exporters/logging/src/main/java/io/opentelemetry/exporter/logging/internal/ConsoleSpanExporterComponentProvider.java b/exporters/logging/src/main/java/io/opentelemetry/exporter/logging/internal/ConsoleSpanExporterComponentProvider.java index a6fa1950bd..fd65a5acad 100644 --- a/exporters/logging/src/main/java/io/opentelemetry/exporter/logging/internal/ConsoleSpanExporterComponentProvider.java +++ b/exporters/logging/src/main/java/io/opentelemetry/exporter/logging/internal/ConsoleSpanExporterComponentProvider.java @@ -5,9 +5,9 @@ package io.opentelemetry.exporter.logging.internal; +import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties; import io.opentelemetry.exporter.logging.LoggingSpanExporter; import io.opentelemetry.sdk.autoconfigure.spi.internal.ComponentProvider; -import io.opentelemetry.sdk.autoconfigure.spi.internal.StructuredConfigProperties; import io.opentelemetry.sdk.trace.export.SpanExporter; /** @@ -29,7 +29,7 @@ public final class ConsoleSpanExporterComponentProvider implements ComponentProv } @Override - public SpanExporter create(StructuredConfigProperties config) { + public SpanExporter create(DeclarativeConfigProperties config) { return LoggingSpanExporter.create(); } } diff --git a/exporters/otlp/all/build.gradle.kts b/exporters/otlp/all/build.gradle.kts index 0c0ef6b75b..eb9ad982c6 100644 --- a/exporters/otlp/all/build.gradle.kts +++ b/exporters/otlp/all/build.gradle.kts @@ -20,6 +20,8 @@ dependencies { implementation(project(":exporters:sender:okhttp")) implementation(project(":sdk-extensions:autoconfigure-spi")) + compileOnly(project(":api:incubator")) + compileOnly("io.grpc:grpc-stub") testImplementation(project(":exporters:otlp:testing-internal")) diff --git a/exporters/otlp/all/src/main/java/io/opentelemetry/exporter/otlp/internal/OtlpConfigUtil.java b/exporters/otlp/all/src/main/java/io/opentelemetry/exporter/otlp/internal/OtlpConfigUtil.java index 999fc412a5..bf58047406 100644 --- a/exporters/otlp/all/src/main/java/io/opentelemetry/exporter/otlp/internal/OtlpConfigUtil.java +++ b/exporters/otlp/all/src/main/java/io/opentelemetry/exporter/otlp/internal/OtlpConfigUtil.java @@ -8,8 +8,6 @@ package io.opentelemetry.exporter.otlp.internal; import io.opentelemetry.exporter.internal.ExporterBuilderUtil; import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; import io.opentelemetry.sdk.autoconfigure.spi.ConfigurationException; -import io.opentelemetry.sdk.autoconfigure.spi.internal.DefaultConfigProperties; -import io.opentelemetry.sdk.autoconfigure.spi.internal.StructuredConfigProperties; import io.opentelemetry.sdk.common.export.MemoryMode; import io.opentelemetry.sdk.common.export.RetryPolicy; import java.io.File; @@ -20,8 +18,6 @@ import java.net.URL; import java.net.URLDecoder; import java.nio.charset.StandardCharsets; import java.time.Duration; -import java.util.Collections; -import java.util.List; import java.util.Map; import java.util.function.BiConsumer; import java.util.function.Consumer; @@ -52,18 +48,6 @@ public final class OtlpConfigUtil { return config.getString("otel.exporter.otlp.protocol", PROTOCOL_GRPC); } - /** Determine the configured OTLP protocol for the {@code dataType}. */ - public static String getStructuredConfigOtlpProtocol(StructuredConfigProperties config) { - // NOTE: The default OTLP protocol is different for declarative config than for env var / system - // property based config. This is intentional. OpenTelemetry changed the default protocol - // recommendation from grpc to http/protobuf, but the autoconfigure's env var / system property - // based config did not update to reflect this before stabilizing, and changing is a breaking - // change requiring a major version bump. Declarative config is not yet stable and therefore can - // switch to the current default recommendation, which aligns also aligns with the behavior of - // the OpenTelemetry Java Agent 2.x+. - return config.getString("protocol", PROTOCOL_HTTP_PROTOBUF); - } - /** Invoke the setters with the OTLP configuration for the {@code dataType}. */ @SuppressWarnings("TooManyParameters") public static void configureOtlpExporterBuilder( @@ -164,81 +148,7 @@ public final class OtlpConfigUtil { ExporterBuilderUtil.configureExporterMemoryMode(config, setMemoryMode); } - /** Invoke the setters with the OTLP configuration for the {@code dataType}. */ - @SuppressWarnings("TooManyParameters") - public static void configureOtlpExporterBuilder( - String dataType, - StructuredConfigProperties config, - Consumer setEndpoint, - BiConsumer addHeader, - Consumer setCompression, - Consumer setTimeout, - Consumer setTrustedCertificates, - BiConsumer setClientTls, - Consumer setRetryPolicy, - Consumer setMemoryMode) { - String protocol = getStructuredConfigOtlpProtocol(config); - boolean isHttpProtobuf = protocol.equals(PROTOCOL_HTTP_PROTOBUF); - URL endpoint = validateEndpoint(config.getString("endpoint"), isHttpProtobuf); - if (endpoint != null) { - setEndpoint.accept(endpoint.toString()); - } - - String headerList = config.getString("headers_list"); - if (headerList != null) { - ConfigProperties headersListConfig = - DefaultConfigProperties.createFromMap( - Collections.singletonMap("otel.exporter.otlp.headers", headerList)); - configureOtlpHeaders(headersListConfig, dataType, addHeader); - } - - List headers = config.getStructuredList("headers"); - if (headers != null) { - headers.forEach( - header -> { - String name = header.getString("name"); - String value = header.getString("value"); - if (name != null && value != null) { - addHeader.accept(name, value); - } - }); - } - - String compression = config.getString("compression"); - if (compression != null) { - setCompression.accept(compression); - } - - Integer timeoutMs = config.getInt("timeout"); - if (timeoutMs != null) { - setTimeout.accept(Duration.ofMillis(timeoutMs)); - } - - String certificatePath = config.getString("certificate"); - String clientKeyPath = config.getString("client_key"); - String clientKeyChainPath = config.getString("client_certificate"); - - if (clientKeyPath != null && clientKeyChainPath == null) { - throw new ConfigurationException( - "client_key provided without client_certificate - both client_key and client_certificate must be set"); - } else if (clientKeyPath == null && clientKeyChainPath != null) { - throw new ConfigurationException( - "client_certificate provided without client_key - both client_key and client_certificate must be set"); - } - byte[] certificateBytes = readFileBytes(certificatePath); - if (certificateBytes != null) { - setTrustedCertificates.accept(certificateBytes); - } - byte[] clientKeyBytes = readFileBytes(clientKeyPath); - byte[] clientKeyChainBytes = readFileBytes(clientKeyChainPath); - if (clientKeyBytes != null && clientKeyChainBytes != null) { - setClientTls.accept(clientKeyBytes, clientKeyChainBytes); - } - - ExporterBuilderUtil.configureExporterMemoryMode(config, setMemoryMode); - } - - private static void configureOtlpHeaders( + static void configureOtlpHeaders( ConfigProperties config, String dataType, BiConsumer addHeader) { Map headers = config.getMap("otel.exporter.otlp." + dataType + ".headers"); if (headers.isEmpty()) { @@ -266,7 +176,7 @@ public final class OtlpConfigUtil { } @Nullable - private static URL validateEndpoint(@Nullable String endpoint, boolean isHttpProtobuf) { + static URL validateEndpoint(@Nullable String endpoint, boolean isHttpProtobuf) { if (endpoint == null) { return null; } @@ -314,7 +224,7 @@ public final class OtlpConfigUtil { } @Nullable - private static byte[] readFileBytes(@Nullable String filePath) { + static byte[] readFileBytes(@Nullable String filePath) { if (filePath == null) { return null; } diff --git a/exporters/otlp/all/src/main/java/io/opentelemetry/exporter/otlp/internal/OtlpDeclarativeConfigUtil.java b/exporters/otlp/all/src/main/java/io/opentelemetry/exporter/otlp/internal/OtlpDeclarativeConfigUtil.java new file mode 100644 index 0000000000..55ed905364 --- /dev/null +++ b/exporters/otlp/all/src/main/java/io/opentelemetry/exporter/otlp/internal/OtlpDeclarativeConfigUtil.java @@ -0,0 +1,120 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.exporter.otlp.internal; + +import static io.opentelemetry.exporter.otlp.internal.OtlpConfigUtil.PROTOCOL_HTTP_PROTOBUF; +import static io.opentelemetry.exporter.otlp.internal.OtlpConfigUtil.configureOtlpHeaders; +import static io.opentelemetry.exporter.otlp.internal.OtlpConfigUtil.readFileBytes; +import static io.opentelemetry.exporter.otlp.internal.OtlpConfigUtil.validateEndpoint; + +import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties; +import io.opentelemetry.exporter.internal.IncubatingExporterBuilderUtil; +import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; +import io.opentelemetry.sdk.autoconfigure.spi.ConfigurationException; +import io.opentelemetry.sdk.autoconfigure.spi.internal.DefaultConfigProperties; +import io.opentelemetry.sdk.common.export.MemoryMode; +import io.opentelemetry.sdk.common.export.RetryPolicy; +import java.net.URL; +import java.time.Duration; +import java.util.Collections; +import java.util.List; +import java.util.function.BiConsumer; +import java.util.function.Consumer; + +/** + * This class is internal and is hence not for public use. Its APIs are unstable and can change at + * any time. + */ +public final class OtlpDeclarativeConfigUtil { + + /** Determine the configured OTLP protocol for the {@code dataType}. */ + public static String getStructuredConfigOtlpProtocol(DeclarativeConfigProperties config) { + // NOTE: The default OTLP protocol is different for declarative config than for env var / system + // property based config. This is intentional. OpenTelemetry changed the default protocol + // recommendation from grpc to http/protobuf, but the autoconfigure's env var / system property + // based config did not update to reflect this before stabilizing, and changing is a breaking + // change requiring a major version bump. Declarative config is not yet stable and therefore can + // switch to the current default recommendation, which aligns also aligns with the behavior of + // the OpenTelemetry Java Agent 2.x+. + return config.getString("protocol", PROTOCOL_HTTP_PROTOBUF); + } + + /** Invoke the setters with the OTLP configuration for the {@code dataType}. */ + @SuppressWarnings("TooManyParameters") + public static void configureOtlpExporterBuilder( + String dataType, + DeclarativeConfigProperties config, + Consumer setEndpoint, + BiConsumer addHeader, + Consumer setCompression, + Consumer setTimeout, + Consumer setTrustedCertificates, + BiConsumer setClientTls, + Consumer setRetryPolicy, + Consumer setMemoryMode) { + String protocol = getStructuredConfigOtlpProtocol(config); + boolean isHttpProtobuf = protocol.equals(PROTOCOL_HTTP_PROTOBUF); + URL endpoint = validateEndpoint(config.getString("endpoint"), isHttpProtobuf); + if (endpoint != null) { + setEndpoint.accept(endpoint.toString()); + } + + String headerList = config.getString("headers_list"); + if (headerList != null) { + ConfigProperties headersListConfig = + DefaultConfigProperties.createFromMap( + Collections.singletonMap("otel.exporter.otlp.headers", headerList)); + configureOtlpHeaders(headersListConfig, dataType, addHeader); + } + + List headers = config.getStructuredList("headers"); + if (headers != null) { + headers.forEach( + header -> { + String name = header.getString("name"); + String value = header.getString("value"); + if (name != null && value != null) { + addHeader.accept(name, value); + } + }); + } + + String compression = config.getString("compression"); + if (compression != null) { + setCompression.accept(compression); + } + + Integer timeoutMs = config.getInt("timeout"); + if (timeoutMs != null) { + setTimeout.accept(Duration.ofMillis(timeoutMs)); + } + + String certificatePath = config.getString("certificate"); + String clientKeyPath = config.getString("client_key"); + String clientKeyChainPath = config.getString("client_certificate"); + + if (clientKeyPath != null && clientKeyChainPath == null) { + throw new ConfigurationException( + "client_key provided without client_certificate - both client_key and client_certificate must be set"); + } else if (clientKeyPath == null && clientKeyChainPath != null) { + throw new ConfigurationException( + "client_certificate provided without client_key - both client_key and client_certificate must be set"); + } + byte[] certificateBytes = readFileBytes(certificatePath); + if (certificateBytes != null) { + setTrustedCertificates.accept(certificateBytes); + } + byte[] clientKeyBytes = readFileBytes(clientKeyPath); + byte[] clientKeyChainBytes = readFileBytes(clientKeyChainPath); + if (clientKeyBytes != null && clientKeyChainBytes != null) { + setClientTls.accept(clientKeyBytes, clientKeyChainBytes); + } + + IncubatingExporterBuilderUtil.configureExporterMemoryMode(config, setMemoryMode); + } + + private OtlpDeclarativeConfigUtil() {} +} diff --git a/exporters/otlp/all/src/main/java/io/opentelemetry/exporter/otlp/internal/OtlpLogRecordExporterComponentProvider.java b/exporters/otlp/all/src/main/java/io/opentelemetry/exporter/otlp/internal/OtlpLogRecordExporterComponentProvider.java index 9134976e49..e10fd8bdd3 100644 --- a/exporters/otlp/all/src/main/java/io/opentelemetry/exporter/otlp/internal/OtlpLogRecordExporterComponentProvider.java +++ b/exporters/otlp/all/src/main/java/io/opentelemetry/exporter/otlp/internal/OtlpLogRecordExporterComponentProvider.java @@ -9,13 +9,13 @@ import static io.opentelemetry.exporter.otlp.internal.OtlpConfigUtil.DATA_TYPE_L import static io.opentelemetry.exporter.otlp.internal.OtlpConfigUtil.PROTOCOL_GRPC; import static io.opentelemetry.exporter.otlp.internal.OtlpConfigUtil.PROTOCOL_HTTP_PROTOBUF; +import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties; import io.opentelemetry.exporter.otlp.http.logs.OtlpHttpLogRecordExporter; import io.opentelemetry.exporter.otlp.http.logs.OtlpHttpLogRecordExporterBuilder; import io.opentelemetry.exporter.otlp.logs.OtlpGrpcLogRecordExporter; import io.opentelemetry.exporter.otlp.logs.OtlpGrpcLogRecordExporterBuilder; import io.opentelemetry.sdk.autoconfigure.spi.ConfigurationException; import io.opentelemetry.sdk.autoconfigure.spi.internal.ComponentProvider; -import io.opentelemetry.sdk.autoconfigure.spi.internal.StructuredConfigProperties; import io.opentelemetry.sdk.logs.export.LogRecordExporter; /** @@ -39,13 +39,13 @@ public class OtlpLogRecordExporterComponentProvider } @Override - public LogRecordExporter create(StructuredConfigProperties config) { - String protocol = OtlpConfigUtil.getStructuredConfigOtlpProtocol(config); + public LogRecordExporter create(DeclarativeConfigProperties config) { + String protocol = OtlpDeclarativeConfigUtil.getStructuredConfigOtlpProtocol(config); if (protocol.equals(PROTOCOL_HTTP_PROTOBUF)) { OtlpHttpLogRecordExporterBuilder builder = httpBuilder(); - OtlpConfigUtil.configureOtlpExporterBuilder( + OtlpDeclarativeConfigUtil.configureOtlpExporterBuilder( DATA_TYPE_LOGS, config, builder::setEndpoint, @@ -61,7 +61,7 @@ public class OtlpLogRecordExporterComponentProvider } else if (protocol.equals(PROTOCOL_GRPC)) { OtlpGrpcLogRecordExporterBuilder builder = grpcBuilder(); - OtlpConfigUtil.configureOtlpExporterBuilder( + OtlpDeclarativeConfigUtil.configureOtlpExporterBuilder( DATA_TYPE_LOGS, config, builder::setEndpoint, diff --git a/exporters/otlp/all/src/main/java/io/opentelemetry/exporter/otlp/internal/OtlpMetricExporterComponentProvider.java b/exporters/otlp/all/src/main/java/io/opentelemetry/exporter/otlp/internal/OtlpMetricExporterComponentProvider.java index a08cab883b..c3b1771070 100644 --- a/exporters/otlp/all/src/main/java/io/opentelemetry/exporter/otlp/internal/OtlpMetricExporterComponentProvider.java +++ b/exporters/otlp/all/src/main/java/io/opentelemetry/exporter/otlp/internal/OtlpMetricExporterComponentProvider.java @@ -9,14 +9,14 @@ import static io.opentelemetry.exporter.otlp.internal.OtlpConfigUtil.DATA_TYPE_M import static io.opentelemetry.exporter.otlp.internal.OtlpConfigUtil.PROTOCOL_GRPC; import static io.opentelemetry.exporter.otlp.internal.OtlpConfigUtil.PROTOCOL_HTTP_PROTOBUF; -import io.opentelemetry.exporter.internal.ExporterBuilderUtil; +import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties; +import io.opentelemetry.exporter.internal.IncubatingExporterBuilderUtil; import io.opentelemetry.exporter.otlp.http.metrics.OtlpHttpMetricExporter; import io.opentelemetry.exporter.otlp.http.metrics.OtlpHttpMetricExporterBuilder; import io.opentelemetry.exporter.otlp.metrics.OtlpGrpcMetricExporter; import io.opentelemetry.exporter.otlp.metrics.OtlpGrpcMetricExporterBuilder; import io.opentelemetry.sdk.autoconfigure.spi.ConfigurationException; import io.opentelemetry.sdk.autoconfigure.spi.internal.ComponentProvider; -import io.opentelemetry.sdk.autoconfigure.spi.internal.StructuredConfigProperties; import io.opentelemetry.sdk.metrics.export.MetricExporter; /** @@ -39,13 +39,13 @@ public class OtlpMetricExporterComponentProvider implements ComponentProviderNOTE: when {@link #getType()} is {@link Resource}, the {@link #getName()} is not (currently) - * used, and {@link #create(StructuredConfigProperties)} is (currently) called with an empty {@link - * StructuredConfigProperties}. + * used, and {@link #create(DeclarativeConfigProperties)} is (currently) called with an empty {@link + * DeclarativeConfigProperties}. * *

This class is internal and is hence not for public use. Its APIs are unstable and can change * at any time. @@ -58,5 +59,5 @@ public interface ComponentProvider { */ // TODO (jack-berg): consider dynamic configuration use case before stabilizing in case that // affects any API decisions - T create(StructuredConfigProperties config); + T create(DeclarativeConfigProperties config); } diff --git a/sdk-extensions/autoconfigure/build.gradle.kts b/sdk-extensions/autoconfigure/build.gradle.kts index eac32f51cd..a00685f6e6 100644 --- a/sdk-extensions/autoconfigure/build.gradle.kts +++ b/sdk-extensions/autoconfigure/build.gradle.kts @@ -10,6 +10,8 @@ dependencies { api(project(":sdk:all")) api(project(":sdk-extensions:autoconfigure-spi")) + compileOnly(project(":api:incubator")) + annotationProcessor("com.google.auto.value:auto-value") testImplementation(project(":sdk:trace-shaded-deps")) @@ -45,7 +47,6 @@ testing { } register("testFullConfig") { dependencies { - implementation(project(":api:incubator")) implementation(project(":extensions:trace-propagators")) implementation(project(":exporters:logging")) implementation(project(":exporters:logging-otlp")) @@ -78,6 +79,13 @@ testing { } } } + register("testIncubating") { + dependencies { + implementation(project(":sdk-extensions:incubator")) + implementation(project(":exporters:logging")) + implementation(project(":sdk:testing")) + } + } } } diff --git a/sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/AutoConfiguredOpenTelemetrySdk.java b/sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/AutoConfiguredOpenTelemetrySdk.java index 666da0f89c..c5d9c77f4e 100644 --- a/sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/AutoConfiguredOpenTelemetrySdk.java +++ b/sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/AutoConfiguredOpenTelemetrySdk.java @@ -8,9 +8,10 @@ package io.opentelemetry.sdk.autoconfigure; import com.google.auto.value.AutoValue; import io.opentelemetry.api.GlobalOpenTelemetry; import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.api.incubator.config.ConfigProvider; import io.opentelemetry.sdk.OpenTelemetrySdk; +import io.opentelemetry.sdk.autoconfigure.internal.AutoConfigureUtil; import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; -import io.opentelemetry.sdk.autoconfigure.spi.internal.StructuredConfigProperties; import io.opentelemetry.sdk.resources.Resource; import javax.annotation.Nullable; import javax.annotation.concurrent.Immutable; @@ -48,9 +49,8 @@ public abstract class AutoConfiguredOpenTelemetrySdk { OpenTelemetrySdk sdk, Resource resource, @Nullable ConfigProperties config, - @Nullable StructuredConfigProperties structuredConfigProperties) { - return new AutoValue_AutoConfiguredOpenTelemetrySdk( - sdk, resource, config, structuredConfigProperties); + @Nullable Object configProvider) { + return new AutoValue_AutoConfiguredOpenTelemetrySdk(sdk, resource, config, configProvider); } /** @@ -70,19 +70,24 @@ public abstract class AutoConfiguredOpenTelemetrySdk { * Returns the {@link ConfigProperties} used for auto-configuration, or {@code null} if * declarative configuration was used. * - * @see #getStructuredConfig() + *

This method is experimental so not public. You may reflectively call it using {@link + * AutoConfigureUtil#getConfig(AutoConfiguredOpenTelemetrySdk)}. + * + * @see #getConfigProvider() */ @Nullable abstract ConfigProperties getConfig(); /** - * Returns the {@link StructuredConfigProperties} used for auto-configuration, or {@code null} if - * declarative configuration was not used. + * Returns the {@link ConfigProvider}, or {@code null} if declarative configuration was not used. + * + *

This method is experimental so not public. You may reflectively call it using {@link + * AutoConfigureUtil#getConfigProvider(AutoConfiguredOpenTelemetrySdk)}. * * @see #getConfig() */ @Nullable - abstract StructuredConfigProperties getStructuredConfig(); + abstract Object getConfigProvider(); AutoConfiguredOpenTelemetrySdk() {} } diff --git a/sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/AutoConfiguredOpenTelemetrySdkBuilder.java b/sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/AutoConfiguredOpenTelemetrySdkBuilder.java index 09cec8b892..0e1586497a 100644 --- a/sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/AutoConfiguredOpenTelemetrySdkBuilder.java +++ b/sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/AutoConfiguredOpenTelemetrySdkBuilder.java @@ -20,7 +20,6 @@ import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; import io.opentelemetry.sdk.autoconfigure.spi.ConfigurationException; import io.opentelemetry.sdk.autoconfigure.spi.internal.AutoConfigureListener; import io.opentelemetry.sdk.autoconfigure.spi.internal.DefaultConfigProperties; -import io.opentelemetry.sdk.autoconfigure.spi.internal.StructuredConfigProperties; import io.opentelemetry.sdk.logs.LogRecordProcessor; import io.opentelemetry.sdk.logs.SdkLoggerProvider; import io.opentelemetry.sdk.logs.SdkLoggerProviderBuilder; @@ -36,12 +35,7 @@ import io.opentelemetry.sdk.trace.SpanProcessor; import io.opentelemetry.sdk.trace.export.SpanExporter; import io.opentelemetry.sdk.trace.samplers.Sampler; import java.io.Closeable; -import java.io.FileInputStream; -import java.io.FileNotFoundException; import java.io.IOException; -import java.io.InputStream; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; @@ -65,6 +59,18 @@ public final class AutoConfiguredOpenTelemetrySdkBuilder implements AutoConfigur private static final Logger logger = Logger.getLogger(AutoConfiguredOpenTelemetrySdkBuilder.class.getName()); + private static final boolean INCUBATOR_AVAILABLE; + + static { + boolean incubatorAvailable = false; + try { + Class.forName("io.opentelemetry.sdk.extension.incubator.fileconfig.DeclarativeConfiguration"); + incubatorAvailable = true; + } catch (ClassNotFoundException e) { + // Not available + } + INCUBATOR_AVAILABLE = incubatorAvailable; + } @Nullable private ConfigProperties config; @@ -431,7 +437,8 @@ public final class AutoConfiguredOpenTelemetrySdkBuilder implements AutoConfigur maybeConfigureFromFile(config, componentLoader); if (fromFileConfiguration != null) { maybeRegisterShutdownHook(fromFileConfiguration.getOpenTelemetrySdk()); - maybeSetAsGlobal(fromFileConfiguration.getOpenTelemetrySdk()); + maybeSetAsGlobal( + fromFileConfiguration.getOpenTelemetrySdk(), fromFileConfiguration.getConfigProvider()); return fromFileConfiguration; } @@ -457,7 +464,7 @@ public final class AutoConfiguredOpenTelemetrySdkBuilder implements AutoConfigur OpenTelemetrySdk openTelemetrySdk = sdkBuilder.build(); maybeRegisterShutdownHook(openTelemetrySdk); - maybeSetAsGlobal(openTelemetrySdk); + maybeSetAsGlobal(openTelemetrySdk, null); callAutoConfigureListeners(spiHelper, openTelemetrySdk); return AutoConfiguredOpenTelemetrySdk.create(openTelemetrySdk, resource, config, null); @@ -548,44 +555,11 @@ public final class AutoConfiguredOpenTelemetrySdkBuilder implements AutoConfigur if (configurationFile == null || configurationFile.isEmpty()) { return null; } - logger.fine("Autoconfiguring from configuration file: " + configurationFile); - try (FileInputStream fis = new FileInputStream(configurationFile)) { - Class configurationFactory = - Class.forName("io.opentelemetry.sdk.extension.incubator.fileconfig.FileConfiguration"); - Method parse = configurationFactory.getMethod("parse", InputStream.class); - Object model = parse.invoke(null, fis); - Class openTelemetryConfiguration = - Class.forName( - "io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.OpenTelemetryConfigurationModel"); - Method create = - configurationFactory.getMethod( - "create", openTelemetryConfiguration, ComponentLoader.class); - OpenTelemetrySdk sdk = (OpenTelemetrySdk) create.invoke(null, model, componentLoader); - Method toConfigProperties = - configurationFactory.getMethod("toConfigProperties", openTelemetryConfiguration); - StructuredConfigProperties structuredConfigProperties = - (StructuredConfigProperties) toConfigProperties.invoke(null, model); - // Note: can't access declarative configuration resource without reflection so setting a dummy - // resource - return AutoConfiguredOpenTelemetrySdk.create( - sdk, Resource.getDefault(), null, structuredConfigProperties); - } catch (FileNotFoundException e) { - throw new ConfigurationException("Configuration file not found", e); - } catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException e) { + if (!INCUBATOR_AVAILABLE) { throw new ConfigurationException( - "Error configuring from file. Is opentelemetry-sdk-extension-incubator on the classpath?", - e); - } catch (InvocationTargetException e) { - Throwable cause = e.getCause(); - if (cause instanceof ConfigurationException) { - throw (ConfigurationException) cause; - } - throw new ConfigurationException("Unexpected error configuring from file", e); - } catch (IOException e) { - // IOException (other than FileNotFoundException which is caught above) is only thrown - // above by FileInputStream.close() - throw new ConfigurationException("Error closing file", e); + "Cannot autoconfigure from config file without opentelemetry-sdk-extension-incubator on the classpath"); } + return IncubatingUtil.configureFromFile(logger, configurationFile, componentLoader); } private void maybeRegisterShutdownHook(OpenTelemetrySdk openTelemetrySdk) { @@ -595,11 +569,15 @@ public final class AutoConfiguredOpenTelemetrySdkBuilder implements AutoConfigur Runtime.getRuntime().addShutdownHook(shutdownHook(openTelemetrySdk)); } - private void maybeSetAsGlobal(OpenTelemetrySdk openTelemetrySdk) { + private void maybeSetAsGlobal( + OpenTelemetrySdk openTelemetrySdk, @Nullable Object configProvider) { if (!setResultAsGlobal) { return; } GlobalOpenTelemetry.set(openTelemetrySdk); + if (INCUBATOR_AVAILABLE && configProvider != null) { + IncubatingUtil.setGlobalConfigProvider(configProvider); + } logger.log( Level.FINE, "Global OpenTelemetry set to {0} by autoconfiguration", openTelemetrySdk); } diff --git a/sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/IncubatingUtil.java b/sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/IncubatingUtil.java new file mode 100644 index 0000000000..a1280e241d --- /dev/null +++ b/sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/IncubatingUtil.java @@ -0,0 +1,88 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.sdk.autoconfigure; + +import io.opentelemetry.api.incubator.config.ConfigProvider; +import io.opentelemetry.api.incubator.config.DeclarativeConfigException; +import io.opentelemetry.api.incubator.config.GlobalConfigProvider; +import io.opentelemetry.sdk.OpenTelemetrySdk; +import io.opentelemetry.sdk.autoconfigure.internal.ComponentLoader; +import io.opentelemetry.sdk.autoconfigure.spi.ConfigurationException; +import io.opentelemetry.sdk.resources.Resource; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.Objects; +import java.util.logging.Logger; + +/** + * Utilities for interacting with incubating components ({@code + * io.opentelemetry:opentelemetry-api-incubator} and {@code + * io.opentelemetry:opentelemetry-sdk-extension-incubator}), which are not guaranteed to be present + * on the classpath. For all methods, callers MUST first separately reflectively confirm that the + * incubator is available on the classpath. + */ +final class IncubatingUtil { + + private IncubatingUtil() {} + + static AutoConfiguredOpenTelemetrySdk configureFromFile( + Logger logger, String configurationFile, ComponentLoader componentLoader) { + logger.fine("Autoconfiguring from configuration file: " + configurationFile); + try (FileInputStream fis = new FileInputStream(configurationFile)) { + Class declarativeConfiguration = + Class.forName( + "io.opentelemetry.sdk.extension.incubator.fileconfig.DeclarativeConfiguration"); + Method parse = declarativeConfiguration.getMethod("parse", InputStream.class); + Object model = parse.invoke(null, fis); + Class openTelemetryConfiguration = + Class.forName( + "io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.OpenTelemetryConfigurationModel"); + Method create = + declarativeConfiguration.getMethod( + "create", openTelemetryConfiguration, ComponentLoader.class); + OpenTelemetrySdk sdk = (OpenTelemetrySdk) create.invoke(null, model, componentLoader); + Class sdkConfigProvider = + Class.forName("io.opentelemetry.sdk.extension.incubator.fileconfig.SdkConfigProvider"); + Method createFileConfigProvider = + sdkConfigProvider.getMethod("create", openTelemetryConfiguration); + ConfigProvider configProvider = (ConfigProvider) createFileConfigProvider.invoke(null, model); + // Note: can't access file configuration resource without reflection so setting a dummy + // resource + return AutoConfiguredOpenTelemetrySdk.create( + sdk, Resource.getDefault(), null, configProvider); + } catch (FileNotFoundException e) { + throw new ConfigurationException("Configuration file not found", e); + } catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException e) { + throw new ConfigurationException( + "Error configuring from file. Is opentelemetry-sdk-extension-incubator on the classpath?", + e); + } catch (InvocationTargetException e) { + Throwable cause = e.getCause(); + if (cause instanceof DeclarativeConfigException) { + throw toConfigurationException((DeclarativeConfigException) cause); + } + throw new ConfigurationException("Unexpected error configuring from file", e); + } catch (IOException e) { + // IOException (other than FileNotFoundException which is caught above) is only thrown + // above by FileInputStream.close() + throw new ConfigurationException("Error closing file", e); + } + } + + private static ConfigurationException toConfigurationException( + DeclarativeConfigException exception) { + String message = Objects.requireNonNull(exception.getMessage()); + return new ConfigurationException(message, exception); + } + + static void setGlobalConfigProvider(Object configProvider) { + GlobalConfigProvider.set((ConfigProvider) configProvider); + } +} diff --git a/sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/internal/AutoConfigureUtil.java b/sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/internal/AutoConfigureUtil.java index fe18ac8d41..52f0236e8d 100644 --- a/sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/internal/AutoConfigureUtil.java +++ b/sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/internal/AutoConfigureUtil.java @@ -5,10 +5,10 @@ package io.opentelemetry.sdk.autoconfigure.internal; +import io.opentelemetry.api.incubator.config.ConfigProvider; import io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdk; import io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdkBuilder; import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; -import io.opentelemetry.sdk.autoconfigure.spi.internal.StructuredConfigProperties; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.function.Function; @@ -25,7 +25,7 @@ public final class AutoConfigureUtil { /** * Returns the {@link ConfigProperties} used for auto-configuration. * - * @return the config properties, or {@code null} if file based configuration is used + * @return the config properties, or {@code null} if declarative configuration is used */ @Nullable public static ConfigProperties getConfig( @@ -41,21 +41,21 @@ public final class AutoConfigureUtil { } /** - * Returns the {@link StructuredConfigProperties} used for auto-configuration when file based + * Returns the {@link ConfigProvider} resulting from auto-configuration when declarative * configuration is used. * - * @return the config properties, or {@code null} if file based configuration is NOT used + * @return the {@link ConfigProvider}, or {@code null} if declarative configuration is NOT used */ @Nullable - public static StructuredConfigProperties getStructuredConfig( + public static ConfigProvider getConfigProvider( AutoConfiguredOpenTelemetrySdk autoConfiguredOpenTelemetrySdk) { try { - Method method = AutoConfiguredOpenTelemetrySdk.class.getDeclaredMethod("getStructuredConfig"); + Method method = AutoConfiguredOpenTelemetrySdk.class.getDeclaredMethod("getConfigProvider"); method.setAccessible(true); - return (StructuredConfigProperties) method.invoke(autoConfiguredOpenTelemetrySdk); + return (ConfigProvider) method.invoke(autoConfiguredOpenTelemetrySdk); } catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) { throw new IllegalStateException( - "Error calling getStructuredConfig on AutoConfiguredOpenTelemetrySdk", e); + "Error calling getConfigProvider on AutoConfiguredOpenTelemetrySdk", e); } } diff --git a/sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/internal/SpiHelper.java b/sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/internal/SpiHelper.java index 6f3196a6f6..db4ca4ff64 100644 --- a/sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/internal/SpiHelper.java +++ b/sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/internal/SpiHelper.java @@ -6,11 +6,8 @@ package io.opentelemetry.sdk.autoconfigure.internal; import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; -import io.opentelemetry.sdk.autoconfigure.spi.ConfigurationException; import io.opentelemetry.sdk.autoconfigure.spi.Ordered; import io.opentelemetry.sdk.autoconfigure.spi.internal.AutoConfigureListener; -import io.opentelemetry.sdk.autoconfigure.spi.internal.ComponentProvider; -import io.opentelemetry.sdk.autoconfigure.spi.internal.StructuredConfigProperties; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; @@ -23,7 +20,6 @@ import java.util.Set; import java.util.function.BiFunction; import java.util.function.Function; import java.util.function.Supplier; -import java.util.stream.Collectors; /** * This class is internal and is hence not for public use. Its APIs are unstable and can change at @@ -90,53 +86,6 @@ public final class SpiHelper { return NamedSpiManager.create(nameToProvider); } - /** - * Find a registered {@link ComponentProvider} with {@link ComponentProvider#getType()} matching - * {@code type}, {@link ComponentProvider#getName()} matching {@code name}, and call {@link - * ComponentProvider#create(StructuredConfigProperties)} with the given {@code config}. - * - * @throws ConfigurationException if no matching providers are found, or if multiple are found - * (i.e. conflict), or if {@link ComponentProvider#create(StructuredConfigProperties)} throws - */ - @SuppressWarnings({"unchecked", "rawtypes"}) - public T loadComponent(Class type, String name, StructuredConfigProperties config) { - // TODO(jack-berg): cache loaded component providers - List componentProviders = load(ComponentProvider.class); - List> matchedProviders = - componentProviders.stream() - .map( - (Function>) - componentProvider -> componentProvider) - .filter( - componentProvider -> - componentProvider.getType() == type && name.equals(componentProvider.getName())) - .collect(Collectors.toList()); - if (matchedProviders.isEmpty()) { - throw new ConfigurationException( - "No component provider detected for " + type.getName() + " with name \"" + name + "\"."); - } - if (matchedProviders.size() > 1) { - throw new ConfigurationException( - "Component provider conflict. Multiple providers detected for " - + type.getName() - + " with name \"" - + name - + "\": " - + componentProviders.stream() - .map(provider -> provider.getClass().getName()) - .collect(Collectors.joining(",", "[", "]"))); - } - // Exactly one matching component provider - ComponentProvider provider = (ComponentProvider) matchedProviders.get(0); - - try { - return provider.create(config); - } catch (Throwable throwable) { - throw new ConfigurationException( - "Error configuring " + type.getName() + " with name \"" + name + "\"", throwable); - } - } - /** * Load implementations of an ordered SPI (i.e. implements {@link Ordered}). * diff --git a/sdk-extensions/autoconfigure/src/test/java/io/opentelemetry/sdk/autoconfigure/AutoConfiguredOpenTelemetrySdkTest.java b/sdk-extensions/autoconfigure/src/test/java/io/opentelemetry/sdk/autoconfigure/AutoConfiguredOpenTelemetrySdkTest.java index 9b72fbefb8..c7ae65de9a 100644 --- a/sdk-extensions/autoconfigure/src/test/java/io/opentelemetry/sdk/autoconfigure/AutoConfiguredOpenTelemetrySdkTest.java +++ b/sdk-extensions/autoconfigure/src/test/java/io/opentelemetry/sdk/autoconfigure/AutoConfiguredOpenTelemetrySdkTest.java @@ -69,7 +69,6 @@ import java.util.concurrent.TimeUnit; import java.util.function.BiFunction; import java.util.function.Consumer; import java.util.function.Supplier; -import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -675,26 +674,4 @@ class AutoConfiguredOpenTelemetrySdkTest { logs.assertContains("Error closing io.opentelemetry.sdk.trace.SdkTracerProvider: Error!"); } - - @Test - @SuppressLogger(AutoConfiguredOpenTelemetrySdkBuilder.class) - void configurationError_fileNotFound() { - assertThatThrownBy( - () -> - AutoConfiguredOpenTelemetrySdk.builder() - .addPropertiesSupplier(() -> singletonMap("otel.config.file", "foo")) - .addPropertiesSupplier( - () -> singletonMap("otel.experimental.config.file", "foo")) - .addPropertiesSupplier(() -> singletonMap("otel.sdk.disabled", "true")) - .build()) - .isInstanceOf(ConfigurationException.class) - .hasMessageContaining("Configuration file not found"); - - Assertions.assertDoesNotThrow( - () -> - AutoConfiguredOpenTelemetrySdk.builder() - .addPropertiesSupplier(() -> singletonMap("otel.experimental.config.file", "")) - .addPropertiesSupplier(() -> singletonMap("otel.sdk.disabled", "true")) - .build()); - } } diff --git a/sdk-extensions/autoconfigure/src/test/java/io/opentelemetry/sdk/autoconfigure/FileConfigurationTest.java b/sdk-extensions/autoconfigure/src/test/java/io/opentelemetry/sdk/autoconfigure/DeclarativeConfigurationTest.java similarity index 73% rename from sdk-extensions/autoconfigure/src/test/java/io/opentelemetry/sdk/autoconfigure/FileConfigurationTest.java rename to sdk-extensions/autoconfigure/src/test/java/io/opentelemetry/sdk/autoconfigure/DeclarativeConfigurationTest.java index 955e581769..8dec1132b5 100644 --- a/sdk-extensions/autoconfigure/src/test/java/io/opentelemetry/sdk/autoconfigure/FileConfigurationTest.java +++ b/sdk-extensions/autoconfigure/src/test/java/io/opentelemetry/sdk/autoconfigure/DeclarativeConfigurationTest.java @@ -7,7 +7,6 @@ package io.opentelemetry.sdk.autoconfigure; import static org.assertj.core.api.Assertions.assertThatThrownBy; -import io.github.netmikey.logunit.api.LogCapturer; import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; import io.opentelemetry.sdk.autoconfigure.spi.ConfigurationException; import io.opentelemetry.sdk.autoconfigure.spi.internal.DefaultConfigProperties; @@ -17,16 +16,9 @@ import java.nio.file.Files; import java.nio.file.Path; import java.util.Collections; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.RegisterExtension; import org.junit.jupiter.api.io.TempDir; -import org.slf4j.event.Level; -class FileConfigurationTest { - - @RegisterExtension - static final LogCapturer logCapturer = - LogCapturer.create() - .captureForLogger(AutoConfiguredOpenTelemetrySdkBuilder.class.getName(), Level.TRACE); +class DeclarativeConfigurationTest { @Test void configFile(@TempDir Path tempDir) throws IOException { @@ -50,7 +42,6 @@ class FileConfigurationTest { assertThatThrownBy(() -> AutoConfiguredOpenTelemetrySdk.builder().setConfig(config).build()) .isInstanceOf(ConfigurationException.class) .hasMessage( - "Error configuring from file. Is opentelemetry-sdk-extension-incubator on the classpath?"); - logCapturer.assertContains("Autoconfiguring from configuration file: " + path); + "Cannot autoconfigure from config file without opentelemetry-sdk-extension-incubator on the classpath"); } } diff --git a/sdk-extensions/autoconfigure/src/testFullConfig/java/io/opentelemetry/sdk/autoconfigure/FullConfigTest.java b/sdk-extensions/autoconfigure/src/testFullConfig/java/io/opentelemetry/sdk/autoconfigure/FullConfigTest.java index edad134ae5..0a0a905ae8 100644 --- a/sdk-extensions/autoconfigure/src/testFullConfig/java/io/opentelemetry/sdk/autoconfigure/FullConfigTest.java +++ b/sdk-extensions/autoconfigure/src/testFullConfig/java/io/opentelemetry/sdk/autoconfigure/FullConfigTest.java @@ -18,8 +18,6 @@ import io.grpc.stub.StreamObserver; import io.opentelemetry.api.GlobalOpenTelemetry; import io.opentelemetry.api.baggage.propagation.W3CBaggagePropagator; import io.opentelemetry.api.common.Attributes; -import io.opentelemetry.api.common.Value; -import io.opentelemetry.api.incubator.logs.ExtendedLogger; import io.opentelemetry.api.logs.Logger; import io.opentelemetry.api.logs.Severity; import io.opentelemetry.api.metrics.Meter; @@ -38,7 +36,6 @@ import io.opentelemetry.proto.collector.trace.v1.ExportTraceServiceResponse; import io.opentelemetry.proto.collector.trace.v1.TraceServiceGrpc; import io.opentelemetry.proto.common.v1.AnyValue; import io.opentelemetry.proto.common.v1.KeyValue; -import io.opentelemetry.proto.logs.v1.SeverityNumber; import io.opentelemetry.proto.metrics.v1.Metric; import io.opentelemetry.sdk.OpenTelemetrySdk; import java.util.ArrayList; @@ -204,13 +201,6 @@ public class FullConfigTest { logger.logRecordBuilder().setBody("debug log message").setSeverity(Severity.DEBUG).emit(); logger.logRecordBuilder().setBody("info log message").setSeverity(Severity.INFO).emit(); - ((ExtendedLogger) logger) - .logRecordBuilder() - .setEventName("namespace.test-name") - .setSeverity(Severity.INFO) - .setBody(Value.of(io.opentelemetry.api.common.KeyValue.of("cow", Value.of("moo")))) - .emit(); - openTelemetrySdk.getSdkTracerProvider().forceFlush().join(10, TimeUnit.SECONDS); openTelemetrySdk.getSdkLoggerProvider().forceFlush().join(10, TimeUnit.SECONDS); openTelemetrySdk.getSdkMeterProvider().forceFlush().join(10, TimeUnit.SECONDS); @@ -297,17 +287,6 @@ public class FullConfigTest { assertThat(logRecord.getBody().getStringValue()).isEqualTo("info log message"); assertThat(logRecord.getSeverityNumberValue()) .isEqualTo(Severity.INFO.getSeverityNumber()); - }, - logRecord -> { - assertThat(logRecord.getBody().getKvlistValue().getValuesList()) - .containsExactlyInAnyOrder( - KeyValue.newBuilder() - .setKey("cow") - .setValue(AnyValue.newBuilder().setStringValue("moo").build()) - .build()); - assertThat(logRecord.getSeverityNumber()) - .isEqualTo(SeverityNumber.SEVERITY_NUMBER_INFO); - assertThat(logRecord.getEventName()).isEqualTo("namespace.test-name"); }); } diff --git a/sdk-extensions/autoconfigure/src/testFullConfig/java/io/opentelemetry/sdk/autoconfigure/FileConfigurationTest.java b/sdk-extensions/autoconfigure/src/testIncubating/java/io/opentelemetry/sdk/autoconfigure/DeclarativeConfigurationTest.java similarity index 66% rename from sdk-extensions/autoconfigure/src/testFullConfig/java/io/opentelemetry/sdk/autoconfigure/FileConfigurationTest.java rename to sdk-extensions/autoconfigure/src/testIncubating/java/io/opentelemetry/sdk/autoconfigure/DeclarativeConfigurationTest.java index ef0b05b2da..22fe2c6ef9 100644 --- a/sdk-extensions/autoconfigure/src/testFullConfig/java/io/opentelemetry/sdk/autoconfigure/FileConfigurationTest.java +++ b/sdk-extensions/autoconfigure/src/testIncubating/java/io/opentelemetry/sdk/autoconfigure/DeclarativeConfigurationTest.java @@ -6,6 +6,8 @@ package io.opentelemetry.sdk.autoconfigure; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat; +import static java.util.Collections.singletonMap; +import static org.assertj.core.api.Assertions.assertThatCode; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.doReturn; @@ -17,13 +19,18 @@ import static org.mockito.Mockito.verify; import io.github.netmikey.logunit.api.LogCapturer; import io.opentelemetry.api.GlobalOpenTelemetry; import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.api.incubator.config.ConfigProvider; +import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties; +import io.opentelemetry.api.incubator.config.GlobalConfigProvider; +import io.opentelemetry.api.incubator.config.InstrumentationConfigUtil; import io.opentelemetry.exporter.logging.LoggingSpanExporter; import io.opentelemetry.internal.testing.CleanupExtension; +import io.opentelemetry.internal.testing.slf4j.SuppressLogger; import io.opentelemetry.sdk.OpenTelemetrySdk; +import io.opentelemetry.sdk.autoconfigure.internal.AutoConfigureUtil; import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; import io.opentelemetry.sdk.autoconfigure.spi.ConfigurationException; import io.opentelemetry.sdk.autoconfigure.spi.internal.DefaultConfigProperties; -import io.opentelemetry.sdk.autoconfigure.spi.internal.StructuredConfigProperties; import io.opentelemetry.sdk.resources.Resource; import io.opentelemetry.sdk.trace.SdkTracerProvider; import io.opentelemetry.sdk.trace.export.SimpleSpanProcessor; @@ -31,14 +38,16 @@ import java.io.IOException; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; +import java.util.Arrays; import java.util.Collections; +import org.assertj.core.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; import org.junit.jupiter.api.io.TempDir; import org.slf4j.event.Level; -class FileConfigurationTest { +class DeclarativeConfigurationTest { @RegisterExtension private static final CleanupExtension cleanup = new CleanupExtension(); @@ -63,13 +72,43 @@ class FileConfigurationTest { + " - simple:\n" + " exporter:\n" + " console: {}\n" - + "other:\n" - + " str_key: str_value\n" - + " map_key:\n" - + " str_key1: str_value1\n"; + + "instrumentation:\n" + + " general:\n" + + " http:\n" + + " client:\n" + + " request_captured_headers:\n" + + " - Content-Type\n" + + " - Accept\n" + + " java:\n" + + " example:\n" + + " key: value\n"; configFilePath = tempDir.resolve("otel-config.yaml"); Files.write(configFilePath, yaml.getBytes(StandardCharsets.UTF_8)); GlobalOpenTelemetry.resetForTest(); + GlobalConfigProvider.resetForTest(); + } + + @Test + @SuppressLogger(AutoConfiguredOpenTelemetrySdkBuilder.class) + void configFile_fileNotFound() { + assertThatThrownBy( + () -> + AutoConfiguredOpenTelemetrySdk.builder() + .addPropertiesSupplier(() -> singletonMap("otel.config.file", "foo")) + .addPropertiesSupplier( + () -> singletonMap("otel.experimental.config.file", "foo")) + .addPropertiesSupplier(() -> singletonMap("otel.sdk.disabled", "true")) + .build()) + .isInstanceOf(ConfigurationException.class) + .hasMessageContaining("Configuration file not found"); + + assertThatCode( + () -> + AutoConfiguredOpenTelemetrySdk.builder() + .addPropertiesSupplier(() -> singletonMap("otel.experimental.config.file", "")) + .addPropertiesSupplier(() -> singletonMap("otel.sdk.disabled", "true")) + .build()) + .doesNotThrowAnyException(); } @Test @@ -95,13 +134,14 @@ class FileConfigurationTest { builder.setConfig(config).build(); cleanup.addCloseable(autoConfiguredOpenTelemetrySdk.getOpenTelemetrySdk()); - assertThat(autoConfiguredOpenTelemetrySdk.getOpenTelemetrySdk().toString()) + Assertions.assertThat(autoConfiguredOpenTelemetrySdk.getOpenTelemetrySdk().toString()) .isEqualTo(expectedSdk.toString()); // AutoConfiguredOpenTelemetrySdk#getResource() is set to a dummy value when configuring from // file - assertThat(autoConfiguredOpenTelemetrySdk.getResource()).isEqualTo(Resource.getDefault()); + Assertions.assertThat(autoConfiguredOpenTelemetrySdk.getResource()) + .isEqualTo(Resource.getDefault()); verify(builder, times(1)).shutdownHook(autoConfiguredOpenTelemetrySdk.getOpenTelemetrySdk()); - assertThat(Runtime.getRuntime().removeShutdownHook(thread)).isTrue(); + Assertions.assertThat(Runtime.getRuntime().removeShutdownHook(thread)).isTrue(); logCapturer.assertContains("Autoconfiguring from configuration file: " + configFilePath); } @@ -131,7 +171,11 @@ class FileConfigurationTest { OpenTelemetrySdk openTelemetrySdk = autoConfiguredOpenTelemetrySdk.getOpenTelemetrySdk(); cleanup.addCloseable(openTelemetrySdk); - assertThat(GlobalOpenTelemetry.get()).extracting("delegate").isNotSameAs(openTelemetrySdk); + Assertions.assertThat(GlobalOpenTelemetry.get()) + .extracting("delegate") + .isNotSameAs(openTelemetrySdk); + assertThat(GlobalConfigProvider.get()) + .isNotSameAs(autoConfiguredOpenTelemetrySdk.getConfigProvider()); } @Test @@ -145,7 +189,11 @@ class FileConfigurationTest { OpenTelemetrySdk openTelemetrySdk = autoConfiguredOpenTelemetrySdk.getOpenTelemetrySdk(); cleanup.addCloseable(openTelemetrySdk); - assertThat(GlobalOpenTelemetry.get()).extracting("delegate").isSameAs(openTelemetrySdk); + Assertions.assertThat(GlobalOpenTelemetry.get()) + .extracting("delegate") + .isSameAs(openTelemetrySdk); + assertThat(GlobalConfigProvider.get()) + .isSameAs(autoConfiguredOpenTelemetrySdk.getConfigProvider()); } @Test @@ -174,7 +222,7 @@ class FileConfigurationTest { } @Test - void configFile_StructuredConfigProperties() { + void configFile_ConfigProvider() { ConfigProperties config = DefaultConfigProperties.createFromMap( Collections.singletonMap("otel.experimental.config.file", configFilePath.toString())); @@ -185,14 +233,19 @@ class FileConfigurationTest { cleanup.addCloseable(openTelemetrySdk); // getConfig() should return ExtendedConfigProperties generic representation of the config file - StructuredConfigProperties structuredConfigProps = - autoConfiguredOpenTelemetrySdk.getStructuredConfig(); - assertThat(structuredConfigProps).isNotNull(); - StructuredConfigProperties otherProps = structuredConfigProps.getStructured("other"); - assertThat(otherProps).isNotNull(); - assertThat(otherProps.getString("str_key")).isEqualTo("str_value"); - StructuredConfigProperties otherMapKeyProps = otherProps.getStructured("map_key"); - assertThat(otherMapKeyProps).isNotNull(); - assertThat(otherMapKeyProps.getString("str_key1")).isEqualTo("str_value1"); + ConfigProvider globalConfigProvider = GlobalConfigProvider.get(); + assertThat(globalConfigProvider) + .isNotNull() + .isSameAs(AutoConfigureUtil.getConfigProvider(autoConfiguredOpenTelemetrySdk)); + DeclarativeConfigProperties instrumentationConfig = + globalConfigProvider.getInstrumentationConfig(); + assertThat(instrumentationConfig).isNotNull(); + + // Extract instrumentation config from ConfigProvider + assertThat(InstrumentationConfigUtil.httpClientRequestCapturedHeaders(globalConfigProvider)) + .isEqualTo(Arrays.asList("Content-Type", "Accept")); + assertThat(InstrumentationConfigUtil.javaInstrumentationConfig(globalConfigProvider, "example")) + .isNotNull() + .satisfies(exampleConfig -> assertThat(exampleConfig.getString("key")).isEqualTo("value")); } } diff --git a/sdk-extensions/incubator/build.gradle.kts b/sdk-extensions/incubator/build.gradle.kts index 92be979672..20e91bd2e5 100644 --- a/sdk-extensions/incubator/build.gradle.kts +++ b/sdk-extensions/incubator/build.gradle.kts @@ -25,6 +25,7 @@ dependencies { implementation("org.snakeyaml:snakeyaml-engine") // io.opentelemetry.sdk.extension.incubator.fileconfig + api(project(":api:incubator")) implementation("com.fasterxml.jackson.core:jackson-databind") api("com.fasterxml.jackson.core:jackson-annotations") implementation("com.fasterxml.jackson.dataformat:jackson-dataformat-yaml") @@ -39,7 +40,8 @@ dependencies { testImplementation(project(":sdk-extensions:jaeger-remote-sampler")) testImplementation(project(":extensions:trace-propagators")) // As a part of the tests we check that we can parse examples without error. The https://github.com/open-telemetry/opentelemetry-configuration/blob/main/examples/kitchen-sink.yam contains a reference to the xray propagator - testImplementation("io.opentelemetry.contrib:opentelemetry-aws-xray-propagator") + // TODO: add when updated to reflect new API locations + // testImplementation("io.opentelemetry.contrib:opentelemetry-aws-xray-propagator") testImplementation("com.linecorp.armeria:armeria-junit5") testImplementation("com.google.guava:guava-testlib") diff --git a/sdk-extensions/incubator/src/main/java/io/opentelemetry/sdk/extension/incubator/fileconfig/AggregationFactory.java b/sdk-extensions/incubator/src/main/java/io/opentelemetry/sdk/extension/incubator/fileconfig/AggregationFactory.java index 5626d27c21..b856d0da4a 100644 --- a/sdk-extensions/incubator/src/main/java/io/opentelemetry/sdk/extension/incubator/fileconfig/AggregationFactory.java +++ b/sdk-extensions/incubator/src/main/java/io/opentelemetry/sdk/extension/incubator/fileconfig/AggregationFactory.java @@ -5,8 +5,8 @@ package io.opentelemetry.sdk.extension.incubator.fileconfig; +import io.opentelemetry.api.incubator.config.DeclarativeConfigException; import io.opentelemetry.sdk.autoconfigure.internal.SpiHelper; -import io.opentelemetry.sdk.autoconfigure.spi.ConfigurationException; import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.AggregationModel; import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.Base2ExponentialBucketHistogramModel; import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.ExplicitBucketHistogramModel; @@ -50,7 +50,7 @@ final class AggregationFactory implements Factory try { return Aggregation.base2ExponentialBucketHistogram(maxSize, maxScale); } catch (IllegalArgumentException e) { - throw new ConfigurationException("Invalid exponential bucket histogram", e); + throw new DeclarativeConfigException("Invalid exponential bucket histogram", e); } } ExplicitBucketHistogramModel explicitBucketHistogram = model.getExplicitBucketHistogram(); @@ -62,7 +62,7 @@ final class AggregationFactory implements Factory try { return Aggregation.explicitBucketHistogram(boundaries); } catch (IllegalArgumentException e) { - throw new ConfigurationException("Invalid explicit bucket histogram", e); + throw new DeclarativeConfigException("Invalid explicit bucket histogram", e); } } diff --git a/sdk-extensions/incubator/src/main/java/io/opentelemetry/sdk/extension/incubator/fileconfig/AttributeListFactory.java b/sdk-extensions/incubator/src/main/java/io/opentelemetry/sdk/extension/incubator/fileconfig/AttributeListFactory.java index 23d08e7fae..c79f1503e3 100644 --- a/sdk-extensions/incubator/src/main/java/io/opentelemetry/sdk/extension/incubator/fileconfig/AttributeListFactory.java +++ b/sdk-extensions/incubator/src/main/java/io/opentelemetry/sdk/extension/incubator/fileconfig/AttributeListFactory.java @@ -10,8 +10,8 @@ import static java.util.stream.Collectors.toList; import io.opentelemetry.api.common.AttributeKey; import io.opentelemetry.api.common.Attributes; import io.opentelemetry.api.common.AttributesBuilder; +import io.opentelemetry.api.incubator.config.DeclarativeConfigException; import io.opentelemetry.sdk.autoconfigure.internal.SpiHelper; -import io.opentelemetry.sdk.autoconfigure.spi.ConfigurationException; import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.AttributeNameValueModel; import java.io.Closeable; import java.util.List; @@ -115,7 +115,7 @@ final class AttributeListFactory implements Factoryopen-telemetry/opentelemetry-configuration. - * - * @see #parseAndCreate(InputStream) + * Configure {@link OpenTelemetrySdk} using declarative + * configuration. For most users, this means calling {@link #parseAndCreate(InputStream)} with a + * YAML + * configuration file. */ -public final class FileConfiguration { +public final class DeclarativeConfiguration { - private static final Logger logger = Logger.getLogger(FileConfiguration.class.getName()); + private static final Logger logger = Logger.getLogger(DeclarativeConfiguration.class.getName()); private static final Pattern ENV_VARIABLE_REFERENCE = Pattern.compile("\\$\\{([a-zA-Z_][a-zA-Z0-9_]*)(:-([^\n}]*))?}"); private static final ComponentLoader DEFAULT_COMPONENT_LOADER = - SpiHelper.serviceComponentLoader(FileConfiguration.class.getClassLoader()); + SpiHelper.serviceComponentLoader(DeclarativeConfiguration.class.getClassLoader()); private static final ObjectMapper MAPPER; @@ -70,12 +72,12 @@ public final class FileConfiguration { MAPPER.configOverride(Boolean.class).setSetterInfo(JsonSetter.Value.forValueNulls(Nulls.SET)); } - private FileConfiguration() {} + private DeclarativeConfiguration() {} /** * Combines {@link #parse(InputStream)} and {@link #create(OpenTelemetryConfigurationModel)}. * - * @throws ConfigurationException if unable to parse or interpret + * @throws DeclarativeConfigException if unable to parse or interpret */ public static OpenTelemetrySdk parseAndCreate(InputStream inputStream) { OpenTelemetryConfigurationModel configurationModel = parse(inputStream); @@ -88,7 +90,7 @@ public final class FileConfiguration { * * @param configurationModel the configuration model * @return the {@link OpenTelemetrySdk} - * @throws ConfigurationException if unable to interpret + * @throws DeclarativeConfigException if unable to interpret */ public static OpenTelemetrySdk create(OpenTelemetryConfigurationModel configurationModel) { return create(configurationModel, DEFAULT_COMPONENT_LOADER); @@ -102,7 +104,7 @@ public final class FileConfiguration { * @param componentLoader the component loader used to load {@link ComponentProvider} * implementations * @return the {@link OpenTelemetrySdk} - * @throws ConfigurationException if unable to interpret + * @throws DeclarativeConfigException if unable to interpret */ public static OpenTelemetrySdk create( OpenTelemetryConfigurationModel configurationModel, ComponentLoader componentLoader) { @@ -118,13 +120,13 @@ public final class FileConfiguration { *

Before parsing, environment variable substitution is performed as described in {@link * EnvSubstitutionConstructor}. * - * @throws ConfigurationException if unable to parse + * @throws DeclarativeConfigException if unable to parse */ public static OpenTelemetryConfigurationModel parse(InputStream configuration) { try { return parse(configuration, System.getenv()); } catch (RuntimeException e) { - throw new ConfigurationException("Unable to parse configuration input stream", e); + throw new DeclarativeConfigException("Unable to parse configuration input stream", e); } } @@ -143,35 +145,35 @@ public final class FileConfiguration { } /** - * Convert the {@code model} to a generic {@link StructuredConfigProperties}. + * Convert the {@code model} to a generic {@link DeclarativeConfigProperties}. * * @param model the configuration model - * @return a generic {@link StructuredConfigProperties} representation of the model + * @return a generic {@link DeclarativeConfigProperties} representation of the model */ - public static StructuredConfigProperties toConfigProperties( + public static DeclarativeConfigProperties toConfigProperties( OpenTelemetryConfigurationModel model) { return toConfigProperties(model, DEFAULT_COMPONENT_LOADER); } /** - * Convert the {@code configuration} YAML to a generic {@link StructuredConfigProperties}. + * Convert the {@code configuration} YAML to a generic {@link DeclarativeConfigProperties}. * * @param configuration configuration YAML - * @return a generic {@link StructuredConfigProperties} representation of the model + * @return a generic {@link DeclarativeConfigProperties} representation of the model */ - public static StructuredConfigProperties toConfigProperties(InputStream configuration) { + public static DeclarativeConfigProperties toConfigProperties(InputStream configuration) { Object yamlObj = loadYaml(configuration, System.getenv()); return toConfigProperties(yamlObj, DEFAULT_COMPONENT_LOADER); } - static StructuredConfigProperties toConfigProperties( + static DeclarativeConfigProperties toConfigProperties( Object model, ComponentLoader componentLoader) { Map configurationMap = MAPPER.convertValue(model, new TypeReference>() {}); if (configurationMap == null) { configurationMap = Collections.emptyMap(); } - return YamlStructuredConfigProperties.create(configurationMap, componentLoader); + return YamlDeclarativeConfigProperties.create(configurationMap, componentLoader); } /** @@ -179,33 +181,33 @@ public final class FileConfiguration { * *

This is used when samplers are composed, with one sampler accepting one or more additional * samplers as config properties. The {@link ComponentProvider} implementation can call this to - * configure a delegate {@link SamplerModel} from the {@link StructuredConfigProperties} + * configure a delegate {@link SamplerModel} from the {@link DeclarativeConfigProperties} * corresponding to a particular config property. */ // TODO(jack-berg): add create methods for all SDK extension components supported by // ComponentProvider - public static Sampler createSampler(StructuredConfigProperties genericSamplerModel) { - YamlStructuredConfigProperties yamlStructuredConfigProperties = - requireYamlStructuredConfigProperties(genericSamplerModel); - SamplerModel samplerModel = convertToModel(yamlStructuredConfigProperties, SamplerModel.class); + public static Sampler createSampler(DeclarativeConfigProperties genericSamplerModel) { + YamlDeclarativeConfigProperties yamlDeclarativeConfigProperties = + requireYamlDeclarativeConfigProperties(genericSamplerModel); + SamplerModel samplerModel = convertToModel(yamlDeclarativeConfigProperties, SamplerModel.class); return createAndMaybeCleanup( SamplerFactory.getInstance(), - SpiHelper.create(yamlStructuredConfigProperties.getComponentLoader()), + SpiHelper.create(yamlDeclarativeConfigProperties.getComponentLoader()), samplerModel); } - private static YamlStructuredConfigProperties requireYamlStructuredConfigProperties( - StructuredConfigProperties structuredConfigProperties) { - if (!(structuredConfigProperties instanceof YamlStructuredConfigProperties)) { - throw new ConfigurationException( - "Only YamlStructuredConfigProperties can be converted to model"); + private static YamlDeclarativeConfigProperties requireYamlDeclarativeConfigProperties( + DeclarativeConfigProperties declarativeConfigProperties) { + if (!(declarativeConfigProperties instanceof YamlDeclarativeConfigProperties)) { + throw new DeclarativeConfigException( + "Only YamlDeclarativeConfigProperties can be converted to model"); } - return (YamlStructuredConfigProperties) structuredConfigProperties; + return (YamlDeclarativeConfigProperties) declarativeConfigProperties; } static T convertToModel( - YamlStructuredConfigProperties structuredConfigProperties, Class modelType) { - return MAPPER.convertValue(structuredConfigProperties.toMap(), modelType); + YamlDeclarativeConfigProperties yamlDeclarativeConfigProperties, Class modelType) { + return MAPPER.convertValue(yamlDeclarativeConfigProperties.toMap(), modelType); } static R createAndMaybeCleanup(Factory factory, SpiHelper spiHelper, M model) { @@ -223,10 +225,10 @@ public final class FileConfiguration { "Error closing " + closeable.getClass().getName() + ": " + ex.getMessage()); } } - if (e instanceof ConfigurationException) { + if (e instanceof DeclarativeConfigException) { throw e; } - throw new ConfigurationException("Unexpected configuration error", e); + throw new DeclarativeConfigException("Unexpected configuration error", e); } } diff --git a/sdk-extensions/incubator/src/main/java/io/opentelemetry/sdk/extension/incubator/fileconfig/FileConfigUtil.java b/sdk-extensions/incubator/src/main/java/io/opentelemetry/sdk/extension/incubator/fileconfig/FileConfigUtil.java index f4c1346dc7..a84143ede6 100644 --- a/sdk-extensions/incubator/src/main/java/io/opentelemetry/sdk/extension/incubator/fileconfig/FileConfigUtil.java +++ b/sdk-extensions/incubator/src/main/java/io/opentelemetry/sdk/extension/incubator/fileconfig/FileConfigUtil.java @@ -5,12 +5,14 @@ package io.opentelemetry.sdk.extension.incubator.fileconfig; +import io.opentelemetry.api.incubator.config.DeclarativeConfigException; +import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties; import io.opentelemetry.sdk.autoconfigure.internal.SpiHelper; -import io.opentelemetry.sdk.autoconfigure.spi.ConfigurationException; import io.opentelemetry.sdk.autoconfigure.spi.internal.ComponentProvider; -import io.opentelemetry.sdk.autoconfigure.spi.internal.StructuredConfigProperties; import java.io.Closeable; import java.util.List; +import java.util.function.Function; +import java.util.stream.Collectors; import javax.annotation.Nullable; final class FileConfigUtil { @@ -34,7 +36,7 @@ final class FileConfigUtil { static T requireNonNull(@Nullable T object, String description) { if (object == null) { - throw new ConfigurationException(description + " is required but is null"); + throw new DeclarativeConfigException(description + " is required but is null"); } return object; } @@ -42,15 +44,63 @@ final class FileConfigUtil { /** * Find a registered {@link ComponentProvider} which {@link ComponentProvider#getType()} matching * {@code type}, {@link ComponentProvider#getName()} matching {@code name}, and call {@link - * ComponentProvider#create(StructuredConfigProperties)} with the given {@code model}. + * ComponentProvider#create(DeclarativeConfigProperties)} with the given {@code model}. * - * @throws ConfigurationException if no matching providers are found, or if multiple are found - * (i.e. conflict), or if {@link ComponentProvider#create(StructuredConfigProperties)} throws + * @throws DeclarativeConfigException if no matching providers are found, or if multiple are found + * (i.e. conflict), or if {@link ComponentProvider#create(DeclarativeConfigProperties)} throws */ static T loadComponent(SpiHelper spiHelper, Class type, String name, Object model) { // Map model to generic structured config properties - StructuredConfigProperties config = - FileConfiguration.toConfigProperties(model, spiHelper.getComponentLoader()); - return spiHelper.loadComponent(type, name, config); + DeclarativeConfigProperties config = + DeclarativeConfiguration.toConfigProperties(model, spiHelper.getComponentLoader()); + return loadComponentHelper(spiHelper, type, name, config); + } + + /** + * Find a registered {@link ComponentProvider} with {@link ComponentProvider#getType()} matching + * {@code type}, {@link ComponentProvider#getName()} matching {@code name}, and call {@link + * ComponentProvider#create(DeclarativeConfigProperties)} with the given {@code config}. + * + * @throws DeclarativeConfigException if no matching providers are found, or if multiple are found + * (i.e. conflict), or if {@link ComponentProvider#create(DeclarativeConfigProperties)} throws + */ + @SuppressWarnings({"unchecked", "rawtypes"}) + private static T loadComponentHelper( + SpiHelper spiHelper, Class type, String name, DeclarativeConfigProperties config) { + // TODO(jack-berg): cache loaded component providers + List componentProviders = spiHelper.load(ComponentProvider.class); + List> matchedProviders = + componentProviders.stream() + .map( + (Function>) + componentProvider -> componentProvider) + .filter( + componentProvider -> + componentProvider.getType() == type && name.equals(componentProvider.getName())) + .collect(Collectors.toList()); + if (matchedProviders.isEmpty()) { + throw new DeclarativeConfigException( + "No component provider detected for " + type.getName() + " with name \"" + name + "\"."); + } + if (matchedProviders.size() > 1) { + throw new DeclarativeConfigException( + "Component provider conflict. Multiple providers detected for " + + type.getName() + + " with name \"" + + name + + "\": " + + componentProviders.stream() + .map(provider -> provider.getClass().getName()) + .collect(Collectors.joining(",", "[", "]"))); + } + // Exactly one matching component provider + ComponentProvider provider = (ComponentProvider) matchedProviders.get(0); + + try { + return provider.create(config); + } catch (Throwable throwable) { + throw new DeclarativeConfigException( + "Error configuring " + type.getName() + " with name \"" + name + "\"", throwable); + } } } diff --git a/sdk-extensions/incubator/src/main/java/io/opentelemetry/sdk/extension/incubator/fileconfig/InstrumentSelectorFactory.java b/sdk-extensions/incubator/src/main/java/io/opentelemetry/sdk/extension/incubator/fileconfig/InstrumentSelectorFactory.java index 767908c659..d45d1ac321 100644 --- a/sdk-extensions/incubator/src/main/java/io/opentelemetry/sdk/extension/incubator/fileconfig/InstrumentSelectorFactory.java +++ b/sdk-extensions/incubator/src/main/java/io/opentelemetry/sdk/extension/incubator/fileconfig/InstrumentSelectorFactory.java @@ -5,8 +5,8 @@ package io.opentelemetry.sdk.extension.incubator.fileconfig; +import io.opentelemetry.api.incubator.config.DeclarativeConfigException; import io.opentelemetry.sdk.autoconfigure.internal.SpiHelper; -import io.opentelemetry.sdk.autoconfigure.spi.ConfigurationException; import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.SelectorModel; import io.opentelemetry.sdk.metrics.InstrumentSelector; import io.opentelemetry.sdk.metrics.InstrumentSelectorBuilder; @@ -36,7 +36,7 @@ final class InstrumentSelectorFactory implements Factory additionalProperties = model.getAdditionalProperties(); if (additionalProperties.size() > 1) { - throw new ConfigurationException( + throw new DeclarativeConfigException( "Invalid configuration - multiple log record exporters set: " + additionalProperties.keySet().stream().collect(joining(",", "[", "]"))); } @@ -61,7 +61,7 @@ final class LogRecordExporterFactory implements Factory additionalProperties = model.getAdditionalProperties(); if (additionalProperties.size() > 1) { - throw new ConfigurationException( + throw new DeclarativeConfigException( "Invalid configuration - multiple log record processors set: " + additionalProperties.keySet().stream().collect(joining(",", "[", "]"))); } @@ -93,7 +93,7 @@ final class LogRecordProcessorFactory processorKeyValue.getValue()); return FileConfigUtil.addAndReturn(closeables, logRecordProcessor); } else { - throw new ConfigurationException("log processor must be set"); + throw new DeclarativeConfigException("log processor must be set"); } } } diff --git a/sdk-extensions/incubator/src/main/java/io/opentelemetry/sdk/extension/incubator/fileconfig/MetricExporterFactory.java b/sdk-extensions/incubator/src/main/java/io/opentelemetry/sdk/extension/incubator/fileconfig/MetricExporterFactory.java index 6d9616f852..0b5e12aab8 100644 --- a/sdk-extensions/incubator/src/main/java/io/opentelemetry/sdk/extension/incubator/fileconfig/MetricExporterFactory.java +++ b/sdk-extensions/incubator/src/main/java/io/opentelemetry/sdk/extension/incubator/fileconfig/MetricExporterFactory.java @@ -7,8 +7,8 @@ package io.opentelemetry.sdk.extension.incubator.fileconfig; import static java.util.stream.Collectors.joining; +import io.opentelemetry.api.incubator.config.DeclarativeConfigException; import io.opentelemetry.sdk.autoconfigure.internal.SpiHelper; -import io.opentelemetry.sdk.autoconfigure.spi.ConfigurationException; import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.OtlpMetricModel; import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.PushMetricExporterModel; import io.opentelemetry.sdk.metrics.export.MetricExporter; @@ -41,7 +41,7 @@ final class MetricExporterFactory implements Factory additionalProperties = model.getAdditionalProperties(); if (additionalProperties.size() > 1) { - throw new ConfigurationException( + throw new DeclarativeConfigException( "Invalid configuration - multiple metric exporters set: " + additionalProperties.keySet().stream().collect(joining(",", "[", "]"))); } @@ -59,7 +59,7 @@ final class MetricExporterFactory implements Factory closeables) { OpenTelemetrySdkBuilder builder = OpenTelemetrySdk.builder(); if (!"0.3".equals(model.getFileFormat())) { - throw new ConfigurationException("Unsupported file format. Supported formats include: 0.3"); + throw new DeclarativeConfigException( + "Unsupported file format. Supported formats include: 0.3"); } if (Objects.equals(Boolean.TRUE, model.getDisabled())) { diff --git a/sdk-extensions/incubator/src/main/java/io/opentelemetry/sdk/extension/incubator/fileconfig/ResourceFactory.java b/sdk-extensions/incubator/src/main/java/io/opentelemetry/sdk/extension/incubator/fileconfig/ResourceFactory.java index 6e40822a6b..0b240007d3 100644 --- a/sdk-extensions/incubator/src/main/java/io/opentelemetry/sdk/extension/incubator/fileconfig/ResourceFactory.java +++ b/sdk-extensions/incubator/src/main/java/io/opentelemetry/sdk/extension/incubator/fileconfig/ResourceFactory.java @@ -7,13 +7,13 @@ package io.opentelemetry.sdk.extension.incubator.fileconfig; import static io.opentelemetry.sdk.internal.GlobUtil.toGlobPatternPredicate; +import io.opentelemetry.api.incubator.config.DeclarativeConfigException; +import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties; import io.opentelemetry.sdk.autoconfigure.ResourceConfiguration; import io.opentelemetry.sdk.autoconfigure.internal.SpiHelper; -import io.opentelemetry.sdk.autoconfigure.spi.ConfigurationException; import io.opentelemetry.sdk.autoconfigure.spi.Ordered; import io.opentelemetry.sdk.autoconfigure.spi.internal.ComponentProvider; import io.opentelemetry.sdk.autoconfigure.spi.internal.DefaultConfigProperties; -import io.opentelemetry.sdk.autoconfigure.spi.internal.StructuredConfigProperties; import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.AttributeNameValueModel; import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.DetectorAttributesModel; import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.DetectorsModel; @@ -31,10 +31,6 @@ import javax.annotation.Nullable; final class ResourceFactory implements Factory { - private static final StructuredConfigProperties EMPTY_CONFIG = - FileConfiguration.toConfigProperties( - Collections.emptyMap(), - SpiHelper.serviceComponentLoader(ResourceFactory.class.getClassLoader())); private static final ResourceFactory INSTANCE = new ResourceFactory(); private ResourceFactory() {} @@ -86,7 +82,7 @@ final class ResourceFactory implements Factory { *

In declarative configuration, a resource detector is a {@link ComponentProvider} with {@link * ComponentProvider#getType()} set to {@link Resource}. Unlike other {@link ComponentProvider}s, * the resource detector version does not use {@link ComponentProvider#getName()} (except for - * debug messages), and {@link ComponentProvider#create(StructuredConfigProperties)} is called + * debug messages), and {@link ComponentProvider#create(DeclarativeConfigProperties)} is called * with an empty instance. Additionally, the {@link Ordered#order()} value is respected for * resource detectors which implement {@link Ordered}. */ @@ -100,9 +96,9 @@ final class ResourceFactory implements Factory { } Resource resource; try { - resource = (Resource) componentProvider.create(EMPTY_CONFIG); + resource = (Resource) componentProvider.create(DeclarativeConfigProperties.empty()); } catch (Throwable throwable) { - throw new ConfigurationException( + throw new DeclarativeConfigException( "Error configuring " + Resource.class.getName() + " with name \"" diff --git a/sdk-extensions/incubator/src/main/java/io/opentelemetry/sdk/extension/incubator/fileconfig/SamplerFactory.java b/sdk-extensions/incubator/src/main/java/io/opentelemetry/sdk/extension/incubator/fileconfig/SamplerFactory.java index 0b38631e0d..491355418e 100644 --- a/sdk-extensions/incubator/src/main/java/io/opentelemetry/sdk/extension/incubator/fileconfig/SamplerFactory.java +++ b/sdk-extensions/incubator/src/main/java/io/opentelemetry/sdk/extension/incubator/fileconfig/SamplerFactory.java @@ -7,8 +7,8 @@ package io.opentelemetry.sdk.extension.incubator.fileconfig; import static java.util.stream.Collectors.joining; +import io.opentelemetry.api.incubator.config.DeclarativeConfigException; import io.opentelemetry.sdk.autoconfigure.internal.SpiHelper; -import io.opentelemetry.sdk.autoconfigure.spi.ConfigurationException; import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.JaegerRemoteModel; import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.ParentBasedModel; import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.SamplerModel; @@ -81,7 +81,7 @@ final class SamplerFactory implements Factory { if (!model.getAdditionalProperties().isEmpty()) { Map additionalProperties = model.getAdditionalProperties(); if (additionalProperties.size() > 1) { - throw new ConfigurationException( + throw new DeclarativeConfigException( "Invalid configuration - multiple samplers exporters set: " + additionalProperties.keySet().stream().collect(joining(",", "[", "]"))); } @@ -95,7 +95,7 @@ final class SamplerFactory implements Factory { spiHelper, Sampler.class, exporterKeyValue.getKey(), exporterKeyValue.getValue()); return FileConfigUtil.addAndReturn(closeables, sampler); } else { - throw new ConfigurationException("sampler must be set"); + throw new DeclarativeConfigException("sampler must be set"); } } } diff --git a/sdk-extensions/incubator/src/main/java/io/opentelemetry/sdk/extension/incubator/fileconfig/SdkConfigProvider.java b/sdk-extensions/incubator/src/main/java/io/opentelemetry/sdk/extension/incubator/fileconfig/SdkConfigProvider.java new file mode 100644 index 0000000000..8c8d549e00 --- /dev/null +++ b/sdk-extensions/incubator/src/main/java/io/opentelemetry/sdk/extension/incubator/fileconfig/SdkConfigProvider.java @@ -0,0 +1,39 @@ +/* + * 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.extension.incubator.fileconfig.internal.model.OpenTelemetryConfigurationModel; +import javax.annotation.Nullable; + +/** SDK implementation of {@link ConfigProvider}. */ +public final class SdkConfigProvider implements ConfigProvider { + + @Nullable private final DeclarativeConfigProperties instrumentationConfig; + + private SdkConfigProvider(OpenTelemetryConfigurationModel model) { + DeclarativeConfigProperties configProperties = + DeclarativeConfiguration.toConfigProperties(model); + this.instrumentationConfig = configProperties.getStructured("instrumentation"); + } + + /** + * Create a {@link SdkConfigProvider} from the {@code model}. + * + * @param model the configuration model + * @return the {@link SdkConfigProvider} + */ + public static SdkConfigProvider create(OpenTelemetryConfigurationModel model) { + return new SdkConfigProvider(model); + } + + @Nullable + @Override + public DeclarativeConfigProperties getInstrumentationConfig() { + return instrumentationConfig; + } +} diff --git a/sdk-extensions/incubator/src/main/java/io/opentelemetry/sdk/extension/incubator/fileconfig/SpanExporterFactory.java b/sdk-extensions/incubator/src/main/java/io/opentelemetry/sdk/extension/incubator/fileconfig/SpanExporterFactory.java index 652b3917ac..a984c10903 100644 --- a/sdk-extensions/incubator/src/main/java/io/opentelemetry/sdk/extension/incubator/fileconfig/SpanExporterFactory.java +++ b/sdk-extensions/incubator/src/main/java/io/opentelemetry/sdk/extension/incubator/fileconfig/SpanExporterFactory.java @@ -7,8 +7,8 @@ package io.opentelemetry.sdk.extension.incubator.fileconfig; import static java.util.stream.Collectors.joining; +import io.opentelemetry.api.incubator.config.DeclarativeConfigException; import io.opentelemetry.sdk.autoconfigure.internal.SpiHelper; -import io.opentelemetry.sdk.autoconfigure.spi.ConfigurationException; import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.OtlpModel; import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.SpanExporterModel; import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.ZipkinModel; @@ -47,7 +47,7 @@ final class SpanExporterFactory implements Factory additionalProperties = model.getAdditionalProperties(); if (additionalProperties.size() > 1) { - throw new ConfigurationException( + throw new DeclarativeConfigException( "Invalid configuration - multiple span exporters set: " + additionalProperties.keySet().stream().collect(joining(",", "[", "]"))); } @@ -65,7 +65,7 @@ final class SpanExporterFactory implements Factory additionalProperties = model.getAdditionalProperties(); if (additionalProperties.size() > 1) { - throw new ConfigurationException( + throw new DeclarativeConfigException( "Invalid configuration - multiple span processors set: " + additionalProperties.keySet().stream().collect(joining(",", "[", "]"))); } @@ -89,7 +89,7 @@ final class SpanProcessorFactory implements Factory, TextMapPro if (model.contains("none")) { if (model.size() > 1) { - throw new ConfigurationException( + throw new DeclarativeConfigException( "propagators contains \"none\" along with other propagators"); } return TextMapPropagator.noop(); diff --git a/sdk-extensions/incubator/src/main/java/io/opentelemetry/sdk/extension/incubator/fileconfig/YamlStructuredConfigProperties.java b/sdk-extensions/incubator/src/main/java/io/opentelemetry/sdk/extension/incubator/fileconfig/YamlDeclarativeConfigProperties.java similarity index 74% rename from sdk-extensions/incubator/src/main/java/io/opentelemetry/sdk/extension/incubator/fileconfig/YamlStructuredConfigProperties.java rename to sdk-extensions/incubator/src/main/java/io/opentelemetry/sdk/extension/incubator/fileconfig/YamlDeclarativeConfigProperties.java index f302ee5c80..8a4a70704f 100644 --- a/sdk-extensions/incubator/src/main/java/io/opentelemetry/sdk/extension/incubator/fileconfig/YamlStructuredConfigProperties.java +++ b/sdk-extensions/incubator/src/main/java/io/opentelemetry/sdk/extension/incubator/fileconfig/YamlDeclarativeConfigProperties.java @@ -8,14 +8,15 @@ package io.opentelemetry.sdk.extension.incubator.fileconfig; import static java.util.stream.Collectors.joining; import static java.util.stream.Collectors.toList; +import io.opentelemetry.api.incubator.config.DeclarativeConfigException; +import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties; import io.opentelemetry.sdk.autoconfigure.internal.ComponentLoader; -import io.opentelemetry.sdk.autoconfigure.spi.ConfigurationException; -import io.opentelemetry.sdk.autoconfigure.spi.internal.StructuredConfigProperties; import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.OpenTelemetryConfigurationModel; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; -import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Objects; @@ -24,27 +25,35 @@ import java.util.StringJoiner; import javax.annotation.Nullable; /** - * Implementation of {@link StructuredConfigProperties} which uses a declarative configuration model - * as a source. + * Implementation of {@link DeclarativeConfigProperties} which uses a file configuration model as a + * source. + * + *

This class is internal and is hence not for public use. Its APIs are unstable and can change + * at any time. * * @see #getStructured(String) Accessing nested maps * @see #getStructuredList(String) Accessing lists of maps - * @see FileConfiguration#toConfigProperties(Object, ComponentLoader) Converting configuration model - * to properties + * @see DeclarativeConfiguration#toConfigProperties(Object, ComponentLoader) Converting + * configuration model to properties */ -final class YamlStructuredConfigProperties implements StructuredConfigProperties { +public final class YamlDeclarativeConfigProperties implements DeclarativeConfigProperties { + + private static final Set> SUPPORTED_SCALAR_TYPES = + Collections.unmodifiableSet( + new LinkedHashSet<>( + Arrays.asList(String.class, Boolean.class, Long.class, Double.class))); /** Values are {@link #isPrimitive(Object)}, {@link List} of scalars. */ private final Map simpleEntries; - private final Map> listEntries; - private final Map mapEntries; + private final Map> listEntries; + private final Map mapEntries; private final ComponentLoader componentLoader; - private YamlStructuredConfigProperties( + private YamlDeclarativeConfigProperties( Map simpleEntries, - Map> listEntries, - Map mapEntries, + Map> listEntries, + Map mapEntries, ComponentLoader componentLoader) { this.simpleEntries = simpleEntries; this.listEntries = listEntries; @@ -53,20 +62,20 @@ final class YamlStructuredConfigProperties implements StructuredConfigProperties } /** - * Create a {@link YamlStructuredConfigProperties} from the {@code properties} map. + * Create a {@link YamlDeclarativeConfigProperties} from the {@code properties} map. * - *

{@code properties} is expected to be the output of YAML parsing (i.e. with Jackson {@link + *

{@code properties} is expected to be the output of YAML parsing (i.e. with Jackson {@code * com.fasterxml.jackson.databind.ObjectMapper}), and have values which are scalars, lists of * scalars, lists of maps, and maps. * - * @see FileConfiguration#toConfigProperties(OpenTelemetryConfigurationModel) + * @see DeclarativeConfiguration#toConfigProperties(OpenTelemetryConfigurationModel) */ @SuppressWarnings("unchecked") - static YamlStructuredConfigProperties create( + static YamlDeclarativeConfigProperties create( Map properties, ComponentLoader componentLoader) { - Map simpleEntries = new HashMap<>(); - Map> listEntries = new HashMap<>(); - Map mapEntries = new HashMap<>(); + Map simpleEntries = new LinkedHashMap<>(); + Map> listEntries = new LinkedHashMap<>(); + Map mapEntries = new LinkedHashMap<>(); for (Map.Entry entry : properties.entrySet()) { String key = entry.getKey(); Object value = entry.getValue(); @@ -79,34 +88,34 @@ final class YamlStructuredConfigProperties implements StructuredConfigProperties continue; } if (isListOfMaps(value)) { - List list = + List list = ((List>) value) .stream() - .map(map -> YamlStructuredConfigProperties.create(map, componentLoader)) + .map(map -> YamlDeclarativeConfigProperties.create(map, componentLoader)) .collect(toList()); listEntries.put(key, list); continue; } if (isMap(value)) { - YamlStructuredConfigProperties configProperties = - YamlStructuredConfigProperties.create((Map) value, componentLoader); + YamlDeclarativeConfigProperties configProperties = + YamlDeclarativeConfigProperties.create((Map) value, componentLoader); mapEntries.put(key, configProperties); continue; } - throw new ConfigurationException( + throw new DeclarativeConfigException( "Unable to initialize ExtendedConfigProperties. Key \"" + key + "\" has unrecognized object type " + value.getClass().getName()); } - return new YamlStructuredConfigProperties( + return new YamlDeclarativeConfigProperties( simpleEntries, listEntries, mapEntries, componentLoader); } private static boolean isPrimitiveList(Object object) { if (object instanceof List) { List list = (List) object; - return list.stream().allMatch(YamlStructuredConfigProperties::isPrimitive); + return list.stream().allMatch(YamlDeclarativeConfigProperties::isPrimitive); } return false; } @@ -178,16 +187,12 @@ final class YamlStructuredConfigProperties implements StructuredConfigProperties return doubleOrNull(simpleEntries.get(name)); } - private static final Set> SUPPORTED_SCALAR_TYPES = - Collections.unmodifiableSet( - new HashSet<>(Arrays.asList(String.class, Boolean.class, Long.class, Double.class))); - @Nullable @Override @SuppressWarnings("unchecked") public List getScalarList(String name, Class scalarType) { if (!SUPPORTED_SCALAR_TYPES.contains(scalarType)) { - throw new ConfigurationException( + throw new DeclarativeConfigException( "Unsupported scalar type " + scalarType.getName() + ". Supported types include " @@ -259,14 +264,14 @@ final class YamlStructuredConfigProperties implements StructuredConfigProperties @Nullable @Override - public StructuredConfigProperties getStructured(String name) { + public DeclarativeConfigProperties getStructured(String name) { return mapEntries.get(name); } @Nullable @Override - public List getStructuredList(String name) { - List value = listEntries.get(name); + public List getStructuredList(String name) { + List value = listEntries.get(name); if (value != null) { return Collections.unmodifiableList(value); } @@ -275,7 +280,7 @@ final class YamlStructuredConfigProperties implements StructuredConfigProperties @Override public Set getPropertyKeys() { - Set keys = new HashSet<>(); + Set keys = new LinkedHashSet<>(); keys.addAll(simpleEntries.keySet()); keys.addAll(listEntries.keySet()); keys.addAll(mapEntries.keySet()); @@ -284,7 +289,7 @@ final class YamlStructuredConfigProperties implements StructuredConfigProperties @Override public String toString() { - StringJoiner joiner = new StringJoiner(", ", "YamlStructuredConfigProperties{", "}"); + StringJoiner joiner = new StringJoiner(", ", "YamlDeclarativeConfigProperties{", "}"); simpleEntries.forEach((key, value) -> joiner.add(key + "=" + value)); listEntries.forEach((key, value) -> joiner.add(key + "=" + value)); mapEntries.forEach((key, value) -> joiner.add(key + "=" + value)); @@ -297,7 +302,7 @@ final class YamlStructuredConfigProperties implements StructuredConfigProperties listEntries.forEach( (key, value) -> result.put( - key, value.stream().map(YamlStructuredConfigProperties::toMap).collect(toList()))); + key, value.stream().map(YamlDeclarativeConfigProperties::toMap).collect(toList()))); mapEntries.forEach((key, value) -> result.put(key, value.toMap())); return Collections.unmodifiableMap(result); } diff --git a/sdk-extensions/incubator/src/test/java/io/opentelemetry/sdk/extension/incubator/fileconfig/AttributeListFactoryTest.java b/sdk-extensions/incubator/src/test/java/io/opentelemetry/sdk/extension/incubator/fileconfig/AttributeListFactoryTest.java index 59dd61c03c..2b3bca0521 100644 --- a/sdk-extensions/incubator/src/test/java/io/opentelemetry/sdk/extension/incubator/fileconfig/AttributeListFactoryTest.java +++ b/sdk-extensions/incubator/src/test/java/io/opentelemetry/sdk/extension/incubator/fileconfig/AttributeListFactoryTest.java @@ -10,8 +10,8 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.mockito.Mockito.mock; import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.incubator.config.DeclarativeConfigException; import io.opentelemetry.sdk.autoconfigure.internal.SpiHelper; -import io.opentelemetry.sdk.autoconfigure.spi.ConfigurationException; import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.AttributeNameValueModel; import java.util.Arrays; import java.util.Collections; @@ -31,7 +31,7 @@ class AttributeListFactoryTest { () -> AttributeListFactory.getInstance() .create(model, mock(SpiHelper.class), Collections.emptyList())) - .isInstanceOf(ConfigurationException.class) + .isInstanceOf(DeclarativeConfigException.class) .hasMessageContaining(expectedMessage); } diff --git a/sdk-extensions/incubator/src/test/java/io/opentelemetry/sdk/extension/incubator/fileconfig/FileConfigurationCreateTest.java b/sdk-extensions/incubator/src/test/java/io/opentelemetry/sdk/extension/incubator/fileconfig/DeclarativeConfigurationCreateTest.java similarity index 87% rename from sdk-extensions/incubator/src/test/java/io/opentelemetry/sdk/extension/incubator/fileconfig/FileConfigurationCreateTest.java rename to sdk-extensions/incubator/src/test/java/io/opentelemetry/sdk/extension/incubator/fileconfig/DeclarativeConfigurationCreateTest.java index 54f91614df..5b0e2fbca1 100644 --- a/sdk-extensions/incubator/src/test/java/io/opentelemetry/sdk/extension/incubator/fileconfig/FileConfigurationCreateTest.java +++ b/sdk-extensions/incubator/src/test/java/io/opentelemetry/sdk/extension/incubator/fileconfig/DeclarativeConfigurationCreateTest.java @@ -12,8 +12,8 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy; import com.linecorp.armeria.testing.junit5.server.SelfSignedCertificateExtension; import io.github.netmikey.logunit.api.LogCapturer; +import io.opentelemetry.api.incubator.config.DeclarativeConfigException; import io.opentelemetry.internal.testing.CleanupExtension; -import io.opentelemetry.sdk.autoconfigure.spi.ConfigurationException; import java.io.ByteArrayInputStream; import java.io.File; import java.io.IOException; @@ -28,7 +28,7 @@ import org.junit.jupiter.api.extension.RegisterExtension; import org.junit.jupiter.api.io.TempDir; import org.slf4j.event.Level; -class FileConfigurationCreateTest { +class DeclarativeConfigurationCreateTest { @RegisterExtension static final SelfSignedCertificateExtension serverTls = new SelfSignedCertificateExtension(); @@ -40,12 +40,12 @@ class FileConfigurationCreateTest { @RegisterExtension LogCapturer logCapturer = - LogCapturer.create().captureForLogger(FileConfiguration.class.getName(), Level.TRACE); + LogCapturer.create().captureForLogger(DeclarativeConfiguration.class.getName(), Level.TRACE); /** * Verify each example in open-telemetry/opentelemetry-configuration/examples - * can pass {@link FileConfiguration#parseAndCreate(InputStream)}. + * can pass {@link DeclarativeConfiguration#parseAndCreate(InputStream)}. */ @Test void parseAndCreate_Examples(@TempDir Path tempDir) @@ -90,12 +90,15 @@ class FileConfigurationCreateTest { "client_certificate: .*\n", "client_certificate: " + clientCertificatePath.replace("\\", "\\\\") - + System.lineSeparator()); + + System.lineSeparator()) + // TODO: remove once updated ComponentProvider SPI contract implemented in + // https://github.com/open-telemetry/opentelemetry-java-contrib/tree/main/aws-xray-propagator + .replaceAll("xray,", ""); InputStream is = new ByteArrayInputStream(rewrittenExampleContent.getBytes(StandardCharsets.UTF_8)); // Verify that file can be parsed and interpreted without error - assertThatCode(() -> cleanup.addCloseable(FileConfiguration.parseAndCreate(is))) + assertThatCode(() -> cleanup.addCloseable(DeclarativeConfiguration.parseAndCreate(is))) .as("Example file: " + example.getName()) .doesNotThrowAnyException(); } @@ -119,9 +122,9 @@ class FileConfigurationCreateTest { assertThatThrownBy( () -> - FileConfiguration.parseAndCreate( + DeclarativeConfiguration.parseAndCreate( new ByteArrayInputStream(yaml.getBytes(StandardCharsets.UTF_8)))) - .isInstanceOf(ConfigurationException.class) + .isInstanceOf(DeclarativeConfigException.class) .hasMessage( "No component provider detected for io.opentelemetry.sdk.logs.export.LogRecordExporter with name \"foo\"."); logCapturer.assertContains( @@ -144,9 +147,8 @@ class FileConfigurationCreateTest { assertThatCode( () -> - FileConfiguration.parseAndCreate( + DeclarativeConfiguration.parseAndCreate( new ByteArrayInputStream(yaml.getBytes(StandardCharsets.UTF_8)))) .doesNotThrowAnyException(); - ; } } diff --git a/sdk-extensions/incubator/src/test/java/io/opentelemetry/sdk/extension/incubator/fileconfig/FileConfigurationParseTest.java b/sdk-extensions/incubator/src/test/java/io/opentelemetry/sdk/extension/incubator/fileconfig/DeclarativeConfigurationParseTest.java similarity index 98% rename from sdk-extensions/incubator/src/test/java/io/opentelemetry/sdk/extension/incubator/fileconfig/FileConfigurationParseTest.java rename to sdk-extensions/incubator/src/test/java/io/opentelemetry/sdk/extension/incubator/fileconfig/DeclarativeConfigurationParseTest.java index 7de63dcb45..f8ad7330c0 100644 --- a/sdk-extensions/incubator/src/test/java/io/opentelemetry/sdk/extension/incubator/fileconfig/FileConfigurationParseTest.java +++ b/sdk-extensions/incubator/src/test/java/io/opentelemetry/sdk/extension/incubator/fileconfig/DeclarativeConfigurationParseTest.java @@ -8,7 +8,7 @@ package io.opentelemetry.sdk.extension.incubator.fileconfig; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; -import io.opentelemetry.sdk.autoconfigure.spi.ConfigurationException; +import io.opentelemetry.api.incubator.config.DeclarativeConfigException; import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.AggregationModel; import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.AlwaysOffModel; import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.AlwaysOnModel; @@ -76,15 +76,15 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; -class FileConfigurationParseTest { +class DeclarativeConfigurationParseTest { @Test void parse_BadInputStream() { assertThatThrownBy( () -> - FileConfiguration.parseAndCreate( + DeclarativeConfiguration.parseAndCreate( new ByteArrayInputStream("foo".getBytes(StandardCharsets.UTF_8)))) - .isInstanceOf(ConfigurationException.class) + .isInstanceOf(DeclarativeConfigException.class) .hasMessage("Unable to parse configuration input stream"); } @@ -446,7 +446,7 @@ class FileConfigurationParseTest { try (FileInputStream configExampleFile = new FileInputStream(System.getenv("CONFIG_EXAMPLE_DIR") + "/kitchen-sink.yaml")) { - OpenTelemetryConfigurationModel config = FileConfiguration.parse(configExampleFile); + OpenTelemetryConfigurationModel config = DeclarativeConfiguration.parse(configExampleFile); // General config assertThat(config.getFileFormat()).isEqualTo("0.3"); @@ -499,7 +499,7 @@ class FileConfigurationParseTest { + " aggregation:\n" + " drop: {}\n"; OpenTelemetryConfigurationModel objectPlaceholderModel = - FileConfiguration.parse( + DeclarativeConfiguration.parse( new ByteArrayInputStream(objectPlaceholderString.getBytes(StandardCharsets.UTF_8))); String noOjbectPlaceholderString = @@ -517,7 +517,7 @@ class FileConfigurationParseTest { + " aggregation:\n" + " drop:\n"; OpenTelemetryConfigurationModel noObjectPlaceholderModel = - FileConfiguration.parse( + DeclarativeConfiguration.parse( new ByteArrayInputStream(noOjbectPlaceholderString.getBytes(StandardCharsets.UTF_8))); SpanExporterModel exporter = @@ -551,7 +551,8 @@ class FileConfigurationParseTest { + " ratio:\n"; // Double OpenTelemetryConfigurationModel model = - FileConfiguration.parse(new ByteArrayInputStream(yaml.getBytes(StandardCharsets.UTF_8))); + DeclarativeConfiguration.parse( + new ByteArrayInputStream(yaml.getBytes(StandardCharsets.UTF_8))); assertThat(model.getFileFormat()).isNull(); assertThat(model.getDisabled()).isNull(); @@ -573,7 +574,7 @@ class FileConfigurationParseTest { @MethodSource("coreSchemaValuesArgs") void coreSchemaValues(String rawYaml, Object expectedYamlResult) { Object yaml = - FileConfiguration.loadYaml( + DeclarativeConfiguration.loadYaml( new ByteArrayInputStream(rawYaml.getBytes(StandardCharsets.UTF_8)), Collections.emptyMap()); assertThat(yaml).isEqualTo(expectedYamlResult); @@ -601,7 +602,7 @@ class FileConfigurationParseTest { environmentVariables.put("HEX", "0xdeadbeef"); Object yaml = - FileConfiguration.loadYaml( + DeclarativeConfiguration.loadYaml( new ByteArrayInputStream(rawYaml.getBytes(StandardCharsets.UTF_8)), environmentVariables); assertThat(yaml).isEqualTo(expectedYamlResult); @@ -689,7 +690,7 @@ class FileConfigurationParseTest { Map envVars = new HashMap<>(); envVars.put("OTEL_EXPORTER_OTLP_ENDPOINT", "http://collector:4317"); OpenTelemetryConfigurationModel model = - FileConfiguration.parse( + DeclarativeConfiguration.parse( new ByteArrayInputStream(yaml.getBytes(StandardCharsets.UTF_8)), envVars); assertThat(model) .isEqualTo( diff --git a/sdk-extensions/incubator/src/test/java/io/opentelemetry/sdk/extension/incubator/fileconfig/InstrumentSelectorFactoryTest.java b/sdk-extensions/incubator/src/test/java/io/opentelemetry/sdk/extension/incubator/fileconfig/InstrumentSelectorFactoryTest.java index fd9a264070..3f3192e585 100644 --- a/sdk-extensions/incubator/src/test/java/io/opentelemetry/sdk/extension/incubator/fileconfig/InstrumentSelectorFactoryTest.java +++ b/sdk-extensions/incubator/src/test/java/io/opentelemetry/sdk/extension/incubator/fileconfig/InstrumentSelectorFactoryTest.java @@ -9,8 +9,8 @@ import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.asser import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.mockito.Mockito.mock; +import io.opentelemetry.api.incubator.config.DeclarativeConfigException; import io.opentelemetry.sdk.autoconfigure.internal.SpiHelper; -import io.opentelemetry.sdk.autoconfigure.spi.ConfigurationException; import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.SelectorModel; import io.opentelemetry.sdk.metrics.InstrumentSelector; import io.opentelemetry.sdk.metrics.InstrumentType; @@ -25,7 +25,7 @@ class InstrumentSelectorFactoryTest { () -> InstrumentSelectorFactory.getInstance() .create(new SelectorModel(), mock(SpiHelper.class), Collections.emptyList())) - .isInstanceOf(ConfigurationException.class) + .isInstanceOf(DeclarativeConfigException.class) .hasMessage("Invalid selector"); } diff --git a/sdk-extensions/incubator/src/test/java/io/opentelemetry/sdk/extension/incubator/fileconfig/LogRecordExporterFactoryTest.java b/sdk-extensions/incubator/src/test/java/io/opentelemetry/sdk/extension/incubator/fileconfig/LogRecordExporterFactoryTest.java index 802b1b9d4c..39b95d7e75 100644 --- a/sdk-extensions/incubator/src/test/java/io/opentelemetry/sdk/extension/incubator/fileconfig/LogRecordExporterFactoryTest.java +++ b/sdk-extensions/incubator/src/test/java/io/opentelemetry/sdk/extension/incubator/fileconfig/LogRecordExporterFactoryTest.java @@ -8,17 +8,18 @@ package io.opentelemetry.sdk.extension.incubator.fileconfig; import static io.opentelemetry.sdk.extension.incubator.fileconfig.FileConfigTestUtil.createTempFileWithContent; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; -import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; import com.google.common.collect.ImmutableMap; import com.linecorp.armeria.testing.junit5.server.SelfSignedCertificateExtension; +import io.opentelemetry.api.incubator.config.DeclarativeConfigException; +import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties; import io.opentelemetry.exporter.otlp.http.logs.OtlpHttpLogRecordExporter; import io.opentelemetry.internal.testing.CleanupExtension; import io.opentelemetry.sdk.autoconfigure.internal.SpiHelper; -import io.opentelemetry.sdk.autoconfigure.spi.ConfigurationException; -import io.opentelemetry.sdk.autoconfigure.spi.internal.StructuredConfigProperties; +import io.opentelemetry.sdk.autoconfigure.spi.internal.ComponentProvider; import io.opentelemetry.sdk.extension.incubator.fileconfig.component.LogRecordExporterComponentProvider; import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.LogRecordExporterModel; import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.NameStringValuePairModel; @@ -31,12 +32,16 @@ import java.security.cert.CertificateEncodingException; import java.time.Duration; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.List; +import java.util.stream.Collectors; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.api.extension.RegisterExtension; import org.junit.jupiter.api.io.TempDir; import org.mockito.ArgumentCaptor; +import org.mockito.Mockito; import org.mockito.junit.jupiter.MockitoExtension; import org.mockito.junit.jupiter.MockitoSettings; import org.mockito.quality.Strictness; @@ -53,12 +58,36 @@ class LogRecordExporterFactoryTest { @RegisterExtension CleanupExtension cleanup = new CleanupExtension(); - private SpiHelper spiHelper = - SpiHelper.create(LogRecordExporterFactoryTest.class.getClassLoader()); + private final SpiHelper spiHelper = + spy(SpiHelper.create(SpanExporterFactoryTest.class.getClassLoader())); + private List> loadedComponentProviders = Collections.emptyList(); + + @BeforeEach + @SuppressWarnings("unchecked") + void setup() { + when(spiHelper.load(ComponentProvider.class)) + .thenAnswer( + invocation -> { + List> result = + (List>) invocation.callRealMethod(); + loadedComponentProviders = + result.stream().map(Mockito::spy).collect(Collectors.toList()); + return loadedComponentProviders; + }); + } + + private ComponentProvider getComponentProvider(String name, Class type) { + return loadedComponentProviders.stream() + .filter( + componentProvider -> + componentProvider.getName().equals(name) + && componentProvider.getType().equals(type)) + .findFirst() + .orElseThrow(IllegalStateException::new); + } @Test void create_OtlpDefaults() { - spiHelper = spy(spiHelper); List closeables = new ArrayList<>(); OtlpHttpLogRecordExporter expectedExporter = OtlpHttpLogRecordExporter.getDefault(); cleanup.addCloseable(expectedExporter); @@ -78,11 +107,11 @@ class LogRecordExporterFactoryTest { assertThat(exporter.toString()).isEqualTo(expectedExporter.toString()); - ArgumentCaptor configCaptor = - ArgumentCaptor.forClass(StructuredConfigProperties.class); - verify(spiHelper) - .loadComponent(eq(LogRecordExporter.class), eq("otlp"), configCaptor.capture()); - StructuredConfigProperties configProperties = configCaptor.getValue(); + ArgumentCaptor configCaptor = + ArgumentCaptor.forClass(DeclarativeConfigProperties.class); + ComponentProvider componentProvider = getComponentProvider("otlp", LogRecordExporter.class); + verify(componentProvider).create(configCaptor.capture()); + DeclarativeConfigProperties configProperties = configCaptor.getValue(); assertThat(configProperties.getString("protocol")).isNull(); assertThat(configProperties.getString("endpoint")).isNull(); assertThat(configProperties.getStructured("headers")).isNull(); @@ -96,7 +125,6 @@ class LogRecordExporterFactoryTest { @Test void create_OtlpConfigured(@TempDir Path tempDir) throws CertificateEncodingException, IOException { - spiHelper = spy(spiHelper); List closeables = new ArrayList<>(); OtlpHttpLogRecordExporter expectedExporter = OtlpHttpLogRecordExporter.builder() @@ -147,14 +175,14 @@ class LogRecordExporterFactoryTest { assertThat(exporter.toString()).isEqualTo(expectedExporter.toString()); - ArgumentCaptor configCaptor = - ArgumentCaptor.forClass(StructuredConfigProperties.class); - verify(spiHelper) - .loadComponent(eq(LogRecordExporter.class), eq("otlp"), configCaptor.capture()); - StructuredConfigProperties configProperties = configCaptor.getValue(); + ArgumentCaptor configCaptor = + ArgumentCaptor.forClass(DeclarativeConfigProperties.class); + ComponentProvider componentProvider = getComponentProvider("otlp", LogRecordExporter.class); + verify(componentProvider).create(configCaptor.capture()); + DeclarativeConfigProperties configProperties = configCaptor.getValue(); assertThat(configProperties.getString("protocol")).isEqualTo("http/protobuf"); assertThat(configProperties.getString("endpoint")).isEqualTo("http://example:4318/v1/logs"); - List headers = configProperties.getStructuredList("headers"); + List headers = configProperties.getStructuredList("headers"); assertThat(headers) .isNotNull() .satisfiesExactly( @@ -187,7 +215,7 @@ class LogRecordExporterFactoryTest { "unknown_key", ImmutableMap.of("key1", "value1")), spiHelper, new ArrayList<>())) - .isInstanceOf(ConfigurationException.class) + .isInstanceOf(DeclarativeConfigException.class) .hasMessage( "No component provider detected for io.opentelemetry.sdk.logs.export.LogRecordExporter with name \"unknown_key\"."); cleanup.addCloseables(closeables); diff --git a/sdk-extensions/incubator/src/test/java/io/opentelemetry/sdk/extension/incubator/fileconfig/LogRecordProcessorFactoryTest.java b/sdk-extensions/incubator/src/test/java/io/opentelemetry/sdk/extension/incubator/fileconfig/LogRecordProcessorFactoryTest.java index e5e29c2698..0031451078 100644 --- a/sdk-extensions/incubator/src/test/java/io/opentelemetry/sdk/extension/incubator/fileconfig/LogRecordProcessorFactoryTest.java +++ b/sdk-extensions/incubator/src/test/java/io/opentelemetry/sdk/extension/incubator/fileconfig/LogRecordProcessorFactoryTest.java @@ -9,10 +9,10 @@ import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.asser import static org.assertj.core.api.Assertions.assertThatThrownBy; import com.google.common.collect.ImmutableMap; +import io.opentelemetry.api.incubator.config.DeclarativeConfigException; import io.opentelemetry.exporter.otlp.http.logs.OtlpHttpLogRecordExporter; import io.opentelemetry.internal.testing.CleanupExtension; import io.opentelemetry.sdk.autoconfigure.internal.SpiHelper; -import io.opentelemetry.sdk.autoconfigure.spi.ConfigurationException; import io.opentelemetry.sdk.extension.incubator.fileconfig.component.LogRecordProcessorComponentProvider; import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.BatchLogRecordProcessorModel; import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.LogRecordExporterModel; @@ -44,7 +44,7 @@ class LogRecordProcessorFactoryTest { new LogRecordProcessorModel().withBatch(new BatchLogRecordProcessorModel()), spiHelper, Collections.emptyList())) - .isInstanceOf(ConfigurationException.class) + .isInstanceOf(DeclarativeConfigException.class) .hasMessage("batch log record processor exporter is required but is null"); } @@ -112,7 +112,7 @@ class LogRecordProcessorFactoryTest { .withSimple(new SimpleLogRecordProcessorModel()), spiHelper, Collections.emptyList())) - .isInstanceOf(ConfigurationException.class) + .isInstanceOf(DeclarativeConfigException.class) .hasMessage("simple log record processor exporter is required but is null"); } @@ -150,7 +150,7 @@ class LogRecordProcessorFactoryTest { "unknown_key", ImmutableMap.of("key1", "value1")), spiHelper, new ArrayList<>())) - .isInstanceOf(ConfigurationException.class) + .isInstanceOf(DeclarativeConfigException.class) .hasMessage( "No component provider detected for io.opentelemetry.sdk.logs.LogRecordProcessor with name \"unknown_key\"."); } diff --git a/sdk-extensions/incubator/src/test/java/io/opentelemetry/sdk/extension/incubator/fileconfig/MetricExporterFactoryTest.java b/sdk-extensions/incubator/src/test/java/io/opentelemetry/sdk/extension/incubator/fileconfig/MetricExporterFactoryTest.java index 0474bbcba6..51b066b2d9 100644 --- a/sdk-extensions/incubator/src/test/java/io/opentelemetry/sdk/extension/incubator/fileconfig/MetricExporterFactoryTest.java +++ b/sdk-extensions/incubator/src/test/java/io/opentelemetry/sdk/extension/incubator/fileconfig/MetricExporterFactoryTest.java @@ -8,18 +8,19 @@ package io.opentelemetry.sdk.extension.incubator.fileconfig; import static io.opentelemetry.sdk.extension.incubator.fileconfig.FileConfigTestUtil.createTempFileWithContent; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; -import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; import com.google.common.collect.ImmutableMap; import com.linecorp.armeria.testing.junit5.server.SelfSignedCertificateExtension; +import io.opentelemetry.api.incubator.config.DeclarativeConfigException; +import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties; import io.opentelemetry.exporter.logging.LoggingMetricExporter; import io.opentelemetry.exporter.otlp.http.metrics.OtlpHttpMetricExporter; import io.opentelemetry.internal.testing.CleanupExtension; import io.opentelemetry.sdk.autoconfigure.internal.SpiHelper; -import io.opentelemetry.sdk.autoconfigure.spi.ConfigurationException; -import io.opentelemetry.sdk.autoconfigure.spi.internal.StructuredConfigProperties; +import io.opentelemetry.sdk.autoconfigure.spi.internal.ComponentProvider; import io.opentelemetry.sdk.extension.incubator.fileconfig.component.MetricExporterComponentProvider; import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.ConsoleModel; import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.NameStringValuePairModel; @@ -36,12 +37,16 @@ import java.security.cert.CertificateEncodingException; import java.time.Duration; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.List; +import java.util.stream.Collectors; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.api.extension.RegisterExtension; import org.junit.jupiter.api.io.TempDir; import org.mockito.ArgumentCaptor; +import org.mockito.Mockito; import org.mockito.junit.jupiter.MockitoExtension; import org.mockito.junit.jupiter.MockitoSettings; import org.mockito.quality.Strictness; @@ -58,11 +63,36 @@ class MetricExporterFactoryTest { @RegisterExtension CleanupExtension cleanup = new CleanupExtension(); - private SpiHelper spiHelper = SpiHelper.create(MetricExporterFactoryTest.class.getClassLoader()); + private final SpiHelper spiHelper = + spy(SpiHelper.create(SpanExporterFactoryTest.class.getClassLoader())); + private List> loadedComponentProviders = Collections.emptyList(); + + @BeforeEach + @SuppressWarnings("unchecked") + void setup() { + when(spiHelper.load(ComponentProvider.class)) + .thenAnswer( + invocation -> { + List> result = + (List>) invocation.callRealMethod(); + loadedComponentProviders = + result.stream().map(Mockito::spy).collect(Collectors.toList()); + return loadedComponentProviders; + }); + } + + private ComponentProvider getComponentProvider(String name, Class type) { + return loadedComponentProviders.stream() + .filter( + componentProvider -> + componentProvider.getName().equals(name) + && componentProvider.getType().equals(type)) + .findFirst() + .orElseThrow(IllegalStateException::new); + } @Test void create_OtlpDefaults() { - spiHelper = spy(spiHelper); List closeables = new ArrayList<>(); OtlpHttpMetricExporter expectedExporter = OtlpHttpMetricExporter.getDefault(); cleanup.addCloseable(expectedExporter); @@ -80,10 +110,11 @@ class MetricExporterFactoryTest { assertThat(exporter.toString()).isEqualTo(expectedExporter.toString()); - ArgumentCaptor configCaptor = - ArgumentCaptor.forClass(StructuredConfigProperties.class); - verify(spiHelper).loadComponent(eq(MetricExporter.class), eq("otlp"), configCaptor.capture()); - StructuredConfigProperties configProperties = configCaptor.getValue(); + ArgumentCaptor configCaptor = + ArgumentCaptor.forClass(DeclarativeConfigProperties.class); + ComponentProvider componentProvider = getComponentProvider("otlp", MetricExporter.class); + verify(componentProvider).create(configCaptor.capture()); + DeclarativeConfigProperties configProperties = configCaptor.getValue(); assertThat(configProperties.getString("protocol")).isNull(); assertThat(configProperties.getString("endpoint")).isNull(); assertThat(configProperties.getStructured("headers")).isNull(); @@ -99,7 +130,6 @@ class MetricExporterFactoryTest { @Test void create_OtlpConfigured(@TempDir Path tempDir) throws CertificateEncodingException, IOException { - spiHelper = spy(spiHelper); List closeables = new ArrayList<>(); OtlpHttpMetricExporter expectedExporter = OtlpHttpMetricExporter.builder() @@ -158,13 +188,14 @@ class MetricExporterFactoryTest { assertThat(exporter.toString()).isEqualTo(expectedExporter.toString()); - ArgumentCaptor configCaptor = - ArgumentCaptor.forClass(StructuredConfigProperties.class); - verify(spiHelper).loadComponent(eq(MetricExporter.class), eq("otlp"), configCaptor.capture()); - StructuredConfigProperties configProperties = configCaptor.getValue(); + ArgumentCaptor configCaptor = + ArgumentCaptor.forClass(DeclarativeConfigProperties.class); + ComponentProvider componentProvider = getComponentProvider("otlp", MetricExporter.class); + verify(componentProvider).create(configCaptor.capture()); + DeclarativeConfigProperties configProperties = configCaptor.getValue(); assertThat(configProperties.getString("protocol")).isEqualTo("http/protobuf"); assertThat(configProperties.getString("endpoint")).isEqualTo("http://example:4318/v1/metrics"); - List headers = configProperties.getStructuredList("headers"); + List headers = configProperties.getStructuredList("headers"); assertThat(headers) .isNotNull() .satisfiesExactly( @@ -188,7 +219,6 @@ class MetricExporterFactoryTest { @Test void create_Console() { - spiHelper = spy(spiHelper); List closeables = new ArrayList<>(); LoggingMetricExporter expectedExporter = LoggingMetricExporter.create(); cleanup.addCloseable(expectedExporter); @@ -219,7 +249,7 @@ class MetricExporterFactoryTest { "unknown_key", ImmutableMap.of("key1", "value1")), spiHelper, new ArrayList<>())) - .isInstanceOf(ConfigurationException.class) + .isInstanceOf(DeclarativeConfigException.class) .hasMessage( "No component provider detected for io.opentelemetry.sdk.metrics.export.MetricExporter with name \"unknown_key\"."); } diff --git a/sdk-extensions/incubator/src/test/java/io/opentelemetry/sdk/extension/incubator/fileconfig/MetricReaderFactoryTest.java b/sdk-extensions/incubator/src/test/java/io/opentelemetry/sdk/extension/incubator/fileconfig/MetricReaderFactoryTest.java index db065d8c1c..287e897786 100644 --- a/sdk-extensions/incubator/src/test/java/io/opentelemetry/sdk/extension/incubator/fileconfig/MetricReaderFactoryTest.java +++ b/sdk-extensions/incubator/src/test/java/io/opentelemetry/sdk/extension/incubator/fileconfig/MetricReaderFactoryTest.java @@ -11,11 +11,11 @@ import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import io.github.netmikey.logunit.api.LogCapturer; +import io.opentelemetry.api.incubator.config.DeclarativeConfigException; import io.opentelemetry.exporter.otlp.http.metrics.OtlpHttpMetricExporter; import io.opentelemetry.exporter.prometheus.PrometheusHttpServer; import io.opentelemetry.internal.testing.CleanupExtension; import io.opentelemetry.sdk.autoconfigure.internal.SpiHelper; -import io.opentelemetry.sdk.autoconfigure.spi.ConfigurationException; import io.opentelemetry.sdk.autoconfigure.spi.internal.ComponentProvider; import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.MetricReaderModel; import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.OtlpMetricModel; @@ -40,7 +40,7 @@ class MetricReaderFactoryTest { @RegisterExtension LogCapturer logCapturer = - LogCapturer.create().captureForLogger(FileConfiguration.class.getName()); + LogCapturer.create().captureForLogger(DeclarativeConfiguration.class.getName()); private SpiHelper spiHelper = SpiHelper.create(MetricReaderFactoryTest.class.getClassLoader()); @@ -53,7 +53,7 @@ class MetricReaderFactoryTest { new MetricReaderModel().withPeriodic(new PeriodicMetricReaderModel()), spiHelper, Collections.emptyList())) - .isInstanceOf(ConfigurationException.class) + .isInstanceOf(DeclarativeConfigException.class) .hasMessage("periodic metric reader exporter is required but is null"); } @@ -179,7 +179,7 @@ class MetricReaderFactoryTest { new MetricReaderModel().withPull(new PullMetricReaderModel()), spiHelper, Collections.emptyList())) - .isInstanceOf(ConfigurationException.class) + .isInstanceOf(DeclarativeConfigException.class) .hasMessage("pull metric reader exporter is required but is null"); assertThatThrownBy( @@ -192,7 +192,7 @@ class MetricReaderFactoryTest { .withExporter(new PullMetricExporterModel())), spiHelper, Collections.emptyList())) - .isInstanceOf(ConfigurationException.class) + .isInstanceOf(DeclarativeConfigException.class) .hasMessage("prometheus is the only currently supported pull reader"); } diff --git a/sdk-extensions/incubator/src/test/java/io/opentelemetry/sdk/extension/incubator/fileconfig/OpenTelemetryConfigurationFactoryTest.java b/sdk-extensions/incubator/src/test/java/io/opentelemetry/sdk/extension/incubator/fileconfig/OpenTelemetryConfigurationFactoryTest.java index 449eb59ab0..3d102c13df 100644 --- a/sdk-extensions/incubator/src/test/java/io/opentelemetry/sdk/extension/incubator/fileconfig/OpenTelemetryConfigurationFactoryTest.java +++ b/sdk-extensions/incubator/src/test/java/io/opentelemetry/sdk/extension/incubator/fileconfig/OpenTelemetryConfigurationFactoryTest.java @@ -10,6 +10,7 @@ import static io.opentelemetry.sdk.trace.samplers.Sampler.alwaysOn; import static org.assertj.core.api.Assertions.assertThatThrownBy; import io.opentelemetry.api.baggage.propagation.W3CBaggagePropagator; +import io.opentelemetry.api.incubator.config.DeclarativeConfigException; import io.opentelemetry.api.trace.propagation.W3CTraceContextPropagator; import io.opentelemetry.context.propagation.ContextPropagators; import io.opentelemetry.context.propagation.TextMapPropagator; @@ -22,7 +23,6 @@ import io.opentelemetry.extension.trace.propagation.OtTracePropagator; import io.opentelemetry.internal.testing.CleanupExtension; import io.opentelemetry.sdk.OpenTelemetrySdk; import io.opentelemetry.sdk.autoconfigure.internal.SpiHelper; -import io.opentelemetry.sdk.autoconfigure.spi.ConfigurationException; import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.AlwaysOnModel; import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.AttributeNameValueModel; import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.BatchLogRecordProcessorModel; @@ -82,7 +82,7 @@ class OpenTelemetryConfigurationFactoryTest { () -> OpenTelemetryConfigurationFactory.getInstance() .create(testCase, spiHelper, closeables)) - .isInstanceOf(ConfigurationException.class) + .isInstanceOf(DeclarativeConfigException.class) .hasMessage("Unsupported file format. Supported formats include: 0.3"); cleanup.addCloseables(closeables); } diff --git a/sdk-extensions/incubator/src/test/java/io/opentelemetry/sdk/extension/incubator/fileconfig/ResourceFactoryTest.java b/sdk-extensions/incubator/src/test/java/io/opentelemetry/sdk/extension/incubator/fileconfig/ResourceFactoryTest.java index e863a4ecb5..7c0b954faf 100644 --- a/sdk-extensions/incubator/src/test/java/io/opentelemetry/sdk/extension/incubator/fileconfig/ResourceFactoryTest.java +++ b/sdk-extensions/incubator/src/test/java/io/opentelemetry/sdk/extension/incubator/fileconfig/ResourceFactoryTest.java @@ -26,7 +26,7 @@ import org.junit.jupiter.params.provider.MethodSource; class ResourceFactoryTest { - private SpiHelper spiHelper = SpiHelper.create(MetricExporterFactoryTest.class.getClassLoader()); + private SpiHelper spiHelper = SpiHelper.create(ResourceFactoryTest.class.getClassLoader()); @Test void create() { diff --git a/sdk-extensions/incubator/src/test/java/io/opentelemetry/sdk/extension/incubator/fileconfig/SamplerFactoryTest.java b/sdk-extensions/incubator/src/test/java/io/opentelemetry/sdk/extension/incubator/fileconfig/SamplerFactoryTest.java index 363bb30197..f57073d524 100644 --- a/sdk-extensions/incubator/src/test/java/io/opentelemetry/sdk/extension/incubator/fileconfig/SamplerFactoryTest.java +++ b/sdk-extensions/incubator/src/test/java/io/opentelemetry/sdk/extension/incubator/fileconfig/SamplerFactoryTest.java @@ -9,10 +9,10 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; import com.google.common.collect.ImmutableMap; +import io.opentelemetry.api.incubator.config.DeclarativeConfigException; import io.opentelemetry.internal.testing.CleanupExtension; import io.opentelemetry.internal.testing.slf4j.SuppressLogger; import io.opentelemetry.sdk.autoconfigure.internal.SpiHelper; -import io.opentelemetry.sdk.autoconfigure.spi.ConfigurationException; import io.opentelemetry.sdk.extension.incubator.fileconfig.component.SamplerComponentProvider; import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.AlwaysOffModel; import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.AlwaysOnModel; @@ -140,7 +140,7 @@ class SamplerFactoryTest { "unknown_key", ImmutableMap.of("key1", "value1")), spiHelper, new ArrayList<>())) - .isInstanceOf(ConfigurationException.class) + .isInstanceOf(DeclarativeConfigException.class) .hasMessage( "No component provider detected for io.opentelemetry.sdk.trace.samplers.Sampler with name \"unknown_key\"."); cleanup.addCloseables(closeables); diff --git a/sdk-extensions/incubator/src/test/java/io/opentelemetry/sdk/extension/incubator/fileconfig/SpanExporterFactoryTest.java b/sdk-extensions/incubator/src/test/java/io/opentelemetry/sdk/extension/incubator/fileconfig/SpanExporterFactoryTest.java index bccfcc560c..2208967692 100644 --- a/sdk-extensions/incubator/src/test/java/io/opentelemetry/sdk/extension/incubator/fileconfig/SpanExporterFactoryTest.java +++ b/sdk-extensions/incubator/src/test/java/io/opentelemetry/sdk/extension/incubator/fileconfig/SpanExporterFactoryTest.java @@ -8,19 +8,20 @@ package io.opentelemetry.sdk.extension.incubator.fileconfig; import static io.opentelemetry.sdk.extension.incubator.fileconfig.FileConfigTestUtil.createTempFileWithContent; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; -import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; import com.google.common.collect.ImmutableMap; import com.linecorp.armeria.testing.junit5.server.SelfSignedCertificateExtension; +import io.opentelemetry.api.incubator.config.DeclarativeConfigException; +import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties; import io.opentelemetry.exporter.logging.LoggingSpanExporter; import io.opentelemetry.exporter.otlp.http.trace.OtlpHttpSpanExporter; import io.opentelemetry.exporter.zipkin.ZipkinSpanExporter; import io.opentelemetry.internal.testing.CleanupExtension; import io.opentelemetry.sdk.autoconfigure.internal.SpiHelper; -import io.opentelemetry.sdk.autoconfigure.spi.ConfigurationException; -import io.opentelemetry.sdk.autoconfigure.spi.internal.StructuredConfigProperties; +import io.opentelemetry.sdk.autoconfigure.spi.internal.ComponentProvider; import io.opentelemetry.sdk.extension.incubator.fileconfig.component.SpanExporterComponentProvider; import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.ConsoleModel; import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.NameStringValuePairModel; @@ -34,12 +35,16 @@ import java.security.cert.CertificateEncodingException; import java.time.Duration; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.List; +import java.util.stream.Collectors; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.api.extension.RegisterExtension; import org.junit.jupiter.api.io.TempDir; import org.mockito.ArgumentCaptor; +import org.mockito.Mockito; import org.mockito.junit.jupiter.MockitoExtension; import org.mockito.junit.jupiter.MockitoSettings; import org.mockito.quality.Strictness; @@ -56,11 +61,36 @@ class SpanExporterFactoryTest { @RegisterExtension CleanupExtension cleanup = new CleanupExtension(); - private SpiHelper spiHelper = SpiHelper.create(SpanExporterFactoryTest.class.getClassLoader()); + private final SpiHelper spiHelper = + spy(SpiHelper.create(SpanExporterFactoryTest.class.getClassLoader())); + private List> loadedComponentProviders = Collections.emptyList(); + + @BeforeEach + @SuppressWarnings("unchecked") + void setup() { + when(spiHelper.load(ComponentProvider.class)) + .thenAnswer( + invocation -> { + List> result = + (List>) invocation.callRealMethod(); + loadedComponentProviders = + result.stream().map(Mockito::spy).collect(Collectors.toList()); + return loadedComponentProviders; + }); + } + + private ComponentProvider getComponentProvider(String name, Class type) { + return loadedComponentProviders.stream() + .filter( + componentProvider -> + componentProvider.getName().equals(name) + && componentProvider.getType().equals(type)) + .findFirst() + .orElseThrow(IllegalStateException::new); + } @Test void create_OtlpDefaults() { - spiHelper = spy(spiHelper); List closeables = new ArrayList<>(); OtlpHttpSpanExporter expectedExporter = OtlpHttpSpanExporter.getDefault(); cleanup.addCloseable(expectedExporter); @@ -78,10 +108,11 @@ class SpanExporterFactoryTest { assertThat(exporter.toString()).isEqualTo(expectedExporter.toString()); - ArgumentCaptor configCaptor = - ArgumentCaptor.forClass(StructuredConfigProperties.class); - verify(spiHelper).loadComponent(eq(SpanExporter.class), eq("otlp"), configCaptor.capture()); - StructuredConfigProperties configProperties = configCaptor.getValue(); + ArgumentCaptor configCaptor = + ArgumentCaptor.forClass(DeclarativeConfigProperties.class); + ComponentProvider componentProvider = getComponentProvider("otlp", SpanExporter.class); + verify(componentProvider).create(configCaptor.capture()); + DeclarativeConfigProperties configProperties = configCaptor.getValue(); assertThat(configProperties.getString("protocol")).isNull(); assertThat(configProperties.getString("endpoint")).isNull(); assertThat(configProperties.getStructured("headers")).isNull(); @@ -95,7 +126,6 @@ class SpanExporterFactoryTest { @Test void create_OtlpConfigured(@TempDir Path tempDir) throws CertificateEncodingException, IOException { - spiHelper = spy(spiHelper); List closeables = new ArrayList<>(); OtlpHttpSpanExporter expectedExporter = OtlpHttpSpanExporter.builder() @@ -146,13 +176,14 @@ class SpanExporterFactoryTest { assertThat(exporter.toString()).isEqualTo(expectedExporter.toString()); - ArgumentCaptor configCaptor = - ArgumentCaptor.forClass(StructuredConfigProperties.class); - verify(spiHelper).loadComponent(eq(SpanExporter.class), eq("otlp"), configCaptor.capture()); - StructuredConfigProperties configProperties = configCaptor.getValue(); + ArgumentCaptor configCaptor = + ArgumentCaptor.forClass(DeclarativeConfigProperties.class); + ComponentProvider componentProvider = getComponentProvider("otlp", SpanExporter.class); + verify(componentProvider).create(configCaptor.capture()); + DeclarativeConfigProperties configProperties = configCaptor.getValue(); assertThat(configProperties.getString("protocol")).isEqualTo("http/protobuf"); assertThat(configProperties.getString("endpoint")).isEqualTo("http://example:4318/v1/traces"); - List headers = configProperties.getStructuredList("headers"); + List headers = configProperties.getStructuredList("headers"); assertThat(headers) .isNotNull() .satisfiesExactly( @@ -173,7 +204,6 @@ class SpanExporterFactoryTest { @Test void create_Console() { - spiHelper = spy(spiHelper); List closeables = new ArrayList<>(); LoggingSpanExporter expectedExporter = LoggingSpanExporter.create(); cleanup.addCloseable(expectedExporter); @@ -194,7 +224,6 @@ class SpanExporterFactoryTest { @Test void create_ZipkinDefaults() { - spiHelper = spy(spiHelper); List closeables = new ArrayList<>(); ZipkinSpanExporter expectedExporter = ZipkinSpanExporter.builder().build(); @@ -213,17 +242,17 @@ class SpanExporterFactoryTest { assertThat(exporter.toString()).isEqualTo(expectedExporter.toString()); - ArgumentCaptor configCaptor = - ArgumentCaptor.forClass(StructuredConfigProperties.class); - verify(spiHelper).loadComponent(eq(SpanExporter.class), eq("zipkin"), configCaptor.capture()); - StructuredConfigProperties configProperties = configCaptor.getValue(); + ArgumentCaptor configCaptor = + ArgumentCaptor.forClass(DeclarativeConfigProperties.class); + ComponentProvider componentProvider = getComponentProvider("zipkin", SpanExporter.class); + verify(componentProvider).create(configCaptor.capture()); + DeclarativeConfigProperties configProperties = configCaptor.getValue(); assertThat(configProperties.getString("endpoint")).isNull(); assertThat(configProperties.getLong("timeout")).isNull(); } @Test void create_ZipkinConfigured() { - spiHelper = spy(spiHelper); List closeables = new ArrayList<>(); ZipkinSpanExporter expectedExporter = ZipkinSpanExporter.builder() @@ -248,10 +277,11 @@ class SpanExporterFactoryTest { assertThat(exporter.toString()).isEqualTo(expectedExporter.toString()); - ArgumentCaptor configCaptor = - ArgumentCaptor.forClass(StructuredConfigProperties.class); - verify(spiHelper).loadComponent(eq(SpanExporter.class), eq("zipkin"), configCaptor.capture()); - StructuredConfigProperties configProperties = configCaptor.getValue(); + ArgumentCaptor configCaptor = + ArgumentCaptor.forClass(DeclarativeConfigProperties.class); + ComponentProvider componentProvider = getComponentProvider("zipkin", SpanExporter.class); + verify(componentProvider).create(configCaptor.capture()); + DeclarativeConfigProperties configProperties = configCaptor.getValue(); assertThat(configProperties.getString("endpoint")).isEqualTo("http://zipkin:9411/v1/v2/spans"); assertThat(configProperties.getLong("timeout")).isEqualTo(15_000); } @@ -270,7 +300,7 @@ class SpanExporterFactoryTest { "unknown_key", ImmutableMap.of("key1", "value1")), spiHelper, new ArrayList<>())) - .isInstanceOf(ConfigurationException.class) + .isInstanceOf(DeclarativeConfigException.class) .hasMessage( "No component provider detected for io.opentelemetry.sdk.trace.export.SpanExporter with name \"unknown_key\"."); cleanup.addCloseables(closeables); diff --git a/sdk-extensions/incubator/src/test/java/io/opentelemetry/sdk/extension/incubator/fileconfig/SpanProcessorFactoryTest.java b/sdk-extensions/incubator/src/test/java/io/opentelemetry/sdk/extension/incubator/fileconfig/SpanProcessorFactoryTest.java index 6da913c6f6..a1b7bb32ba 100644 --- a/sdk-extensions/incubator/src/test/java/io/opentelemetry/sdk/extension/incubator/fileconfig/SpanProcessorFactoryTest.java +++ b/sdk-extensions/incubator/src/test/java/io/opentelemetry/sdk/extension/incubator/fileconfig/SpanProcessorFactoryTest.java @@ -9,10 +9,10 @@ import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.asser import static org.assertj.core.api.Assertions.assertThatThrownBy; import com.google.common.collect.ImmutableMap; +import io.opentelemetry.api.incubator.config.DeclarativeConfigException; import io.opentelemetry.exporter.otlp.http.trace.OtlpHttpSpanExporter; import io.opentelemetry.internal.testing.CleanupExtension; import io.opentelemetry.sdk.autoconfigure.internal.SpiHelper; -import io.opentelemetry.sdk.autoconfigure.spi.ConfigurationException; import io.opentelemetry.sdk.extension.incubator.fileconfig.component.SpanProcessorComponentProvider; import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.BatchSpanProcessorModel; import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.OtlpModel; @@ -44,7 +44,7 @@ class SpanProcessorFactoryTest { new SpanProcessorModel().withBatch(new BatchSpanProcessorModel()), spiHelper, Collections.emptyList())) - .isInstanceOf(ConfigurationException.class) + .isInstanceOf(DeclarativeConfigException.class) .hasMessage("batch span processor exporter is required but is null"); } @@ -111,7 +111,7 @@ class SpanProcessorFactoryTest { new SpanProcessorModel().withSimple(new SimpleSpanProcessorModel()), spiHelper, Collections.emptyList())) - .isInstanceOf(ConfigurationException.class) + .isInstanceOf(DeclarativeConfigException.class) .hasMessage("simple span processor exporter is required but is null"); } @@ -149,7 +149,7 @@ class SpanProcessorFactoryTest { "unknown_key", ImmutableMap.of("key1", "value1")), spiHelper, new ArrayList<>())) - .isInstanceOf(ConfigurationException.class) + .isInstanceOf(DeclarativeConfigException.class) .hasMessage( "No component provider detected for io.opentelemetry.sdk.trace.SpanProcessor with name \"unknown_key\"."); } diff --git a/sdk-extensions/incubator/src/test/java/io/opentelemetry/sdk/extension/incubator/fileconfig/TextMapPropagatorFactoryTest.java b/sdk-extensions/incubator/src/test/java/io/opentelemetry/sdk/extension/incubator/fileconfig/TextMapPropagatorFactoryTest.java index d2a1d5d242..17d21a7f36 100644 --- a/sdk-extensions/incubator/src/test/java/io/opentelemetry/sdk/extension/incubator/fileconfig/TextMapPropagatorFactoryTest.java +++ b/sdk-extensions/incubator/src/test/java/io/opentelemetry/sdk/extension/incubator/fileconfig/TextMapPropagatorFactoryTest.java @@ -9,13 +9,13 @@ import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.asser import static org.assertj.core.api.Assertions.assertThatThrownBy; import io.opentelemetry.api.baggage.propagation.W3CBaggagePropagator; +import io.opentelemetry.api.incubator.config.DeclarativeConfigException; import io.opentelemetry.api.trace.propagation.W3CTraceContextPropagator; import io.opentelemetry.context.propagation.TextMapPropagator; import io.opentelemetry.extension.trace.propagation.B3Propagator; import io.opentelemetry.extension.trace.propagation.JaegerPropagator; import io.opentelemetry.extension.trace.propagation.OtTracePropagator; import io.opentelemetry.sdk.autoconfigure.internal.SpiHelper; -import io.opentelemetry.sdk.autoconfigure.spi.ConfigurationException; import io.opentelemetry.sdk.extension.incubator.fileconfig.component.TextMapPropagatorComponentProvider; import java.util.ArrayList; import java.util.Arrays; @@ -65,7 +65,7 @@ class TextMapPropagatorFactoryTest { () -> TextMapPropagatorFactory.getInstance() .create(Arrays.asList("none", "foo"), spiHelper, Collections.emptyList())) - .isInstanceOf(ConfigurationException.class) + .isInstanceOf(DeclarativeConfigException.class) .hasMessage("propagators contains \"none\" along with other propagators"); } @@ -75,7 +75,7 @@ class TextMapPropagatorFactoryTest { () -> TextMapPropagatorFactory.getInstance() .create(Collections.singletonList("foo"), spiHelper, Collections.emptyList())) - .isInstanceOf(ConfigurationException.class) + .isInstanceOf(DeclarativeConfigException.class) .hasMessage( "No component provider detected for io.opentelemetry.context.propagation.TextMapPropagator with name \"foo\"."); } @@ -91,7 +91,7 @@ class TextMapPropagatorFactoryTest { testTextMapPropagator -> assertThat(testTextMapPropagator.config) .isInstanceOfSatisfying( - YamlStructuredConfigProperties.class, + YamlDeclarativeConfigProperties.class, config -> assertThat(config.getPropertyKeys()).isEmpty())); } } diff --git a/sdk-extensions/incubator/src/test/java/io/opentelemetry/sdk/extension/incubator/fileconfig/YamlStructuredConfigPropertiesTest.java b/sdk-extensions/incubator/src/test/java/io/opentelemetry/sdk/extension/incubator/fileconfig/YamlDeclarativeConfigPropertiesTest.java similarity index 88% rename from sdk-extensions/incubator/src/test/java/io/opentelemetry/sdk/extension/incubator/fileconfig/YamlStructuredConfigPropertiesTest.java rename to sdk-extensions/incubator/src/test/java/io/opentelemetry/sdk/extension/incubator/fileconfig/YamlDeclarativeConfigPropertiesTest.java index 3bbd11bcef..14f213245f 100644 --- a/sdk-extensions/incubator/src/test/java/io/opentelemetry/sdk/extension/incubator/fileconfig/YamlStructuredConfigPropertiesTest.java +++ b/sdk-extensions/incubator/src/test/java/io/opentelemetry/sdk/extension/incubator/fileconfig/YamlDeclarativeConfigPropertiesTest.java @@ -5,11 +5,11 @@ package io.opentelemetry.sdk.extension.incubator.fileconfig; -import static io.opentelemetry.sdk.autoconfigure.spi.internal.StructuredConfigProperties.empty; +import static io.opentelemetry.api.incubator.config.DeclarativeConfigProperties.empty; import static org.assertj.core.api.Assertions.assertThat; import com.google.common.collect.ImmutableSet; -import io.opentelemetry.sdk.autoconfigure.spi.internal.StructuredConfigProperties; +import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties; import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.OpenTelemetryConfigurationModel; import java.io.ByteArrayInputStream; import java.nio.charset.StandardCharsets; @@ -19,7 +19,7 @@ import java.util.List; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -class YamlStructuredConfigPropertiesTest { +class YamlDeclarativeConfigPropertiesTest { private static final String extendedSchema = "file_format: \"0.3\"\n" @@ -56,23 +56,23 @@ class YamlStructuredConfigPropertiesTest { + " - str_key1: str_value1\n" + " int_key1: 2"; - private StructuredConfigProperties structuredConfigProps; + private DeclarativeConfigProperties structuredConfigProps; @BeforeEach void setup() { OpenTelemetryConfigurationModel configuration = - FileConfiguration.parse( + DeclarativeConfiguration.parse( new ByteArrayInputStream(extendedSchema.getBytes(StandardCharsets.UTF_8))); - structuredConfigProps = FileConfiguration.toConfigProperties(configuration); + structuredConfigProps = DeclarativeConfiguration.toConfigProperties(configuration); } @Test void configurationSchema() { // Validate can read declarative configuration schema properties assertThat(structuredConfigProps.getString("file_format")).isEqualTo("0.3"); - StructuredConfigProperties resourceProps = structuredConfigProps.getStructured("resource"); + DeclarativeConfigProperties resourceProps = structuredConfigProps.getStructured("resource"); assertThat(resourceProps).isNotNull(); - List resourceAttributesList = + List resourceAttributesList = resourceProps.getStructuredList("attributes"); assertThat(resourceAttributesList) .isNotNull() @@ -90,7 +90,7 @@ class YamlStructuredConfigPropertiesTest { // Validate can read properties not part of configuration schema // .other - StructuredConfigProperties otherProps = structuredConfigProps.getStructured("other"); + DeclarativeConfigProperties otherProps = structuredConfigProps.getStructured("other"); assertThat(otherProps).isNotNull(); assertThat(otherProps.getPropertyKeys()) .isEqualTo( @@ -135,14 +135,15 @@ class YamlStructuredConfigPropertiesTest { .isEqualTo(Collections.singletonList(true)); // .other.map_key - StructuredConfigProperties otherMapKeyProps = otherProps.getStructured("map_key"); + DeclarativeConfigProperties otherMapKeyProps = otherProps.getStructured("map_key"); assertThat(otherMapKeyProps).isNotNull(); assertThat(otherMapKeyProps.getPropertyKeys()) .isEqualTo(ImmutableSet.of("str_key1", "int_key1", "map_key1")); assertThat(otherMapKeyProps.getString("str_key1")).isEqualTo("str_value1"); assertThat(otherMapKeyProps.getInt("int_key1")).isEqualTo(2); // other.map_key.map_key1 - StructuredConfigProperties otherMapKeyMapKey1Props = otherMapKeyProps.getStructured("map_key1"); + DeclarativeConfigProperties otherMapKeyMapKey1Props = + otherMapKeyProps.getStructured("map_key1"); assertThat(otherMapKeyMapKey1Props).isNotNull(); assertThat(otherMapKeyMapKey1Props.getPropertyKeys()) .isEqualTo(ImmutableSet.of("str_key2", "int_key2")); @@ -150,22 +151,22 @@ class YamlStructuredConfigPropertiesTest { assertThat(otherMapKeyMapKey1Props.getInt("int_key2")).isEqualTo(3); // .other.list_key - List listKey = otherProps.getStructuredList("list_key"); + List listKey = otherProps.getStructuredList("list_key"); assertThat(listKey).hasSize(2); - StructuredConfigProperties listKeyProps1 = listKey.get(0); + DeclarativeConfigProperties listKeyProps1 = listKey.get(0); assertThat(listKeyProps1.getPropertyKeys()) .isEqualTo(ImmutableSet.of("str_key1", "int_key1", "map_key1")); assertThat(listKeyProps1.getString("str_key1")).isEqualTo("str_value1"); assertThat(listKeyProps1.getInt("int_key1")).isEqualTo(2); // .other.list_key[0] - StructuredConfigProperties listKeyProps1MapKeyProps = listKeyProps1.getStructured("map_key1"); + DeclarativeConfigProperties listKeyProps1MapKeyProps = listKeyProps1.getStructured("map_key1"); assertThat(listKeyProps1MapKeyProps).isNotNull(); assertThat(listKeyProps1MapKeyProps.getPropertyKeys()) .isEqualTo(ImmutableSet.of("str_key2", "int_key2")); assertThat(listKeyProps1MapKeyProps.getString("str_key2")).isEqualTo("str_value2"); assertThat(listKeyProps1MapKeyProps.getInt("int_key2")).isEqualTo(3); // .other.list_key[1] - StructuredConfigProperties listKeyProps2 = listKey.get(1); + DeclarativeConfigProperties listKeyProps2 = listKey.get(1); assertThat(listKeyProps2.getPropertyKeys()).isEqualTo(ImmutableSet.of("str_key1", "int_key1")); assertThat(listKeyProps2.getString("str_key1")).isEqualTo("str_value1"); assertThat(listKeyProps2.getInt("int_key1")).isEqualTo(2); @@ -213,7 +214,7 @@ class YamlStructuredConfigPropertiesTest { @Test void wrongType() { - StructuredConfigProperties otherProps = structuredConfigProps.getStructured("other"); + DeclarativeConfigProperties otherProps = structuredConfigProps.getStructured("other"); assertThat(otherProps).isNotNull(); assertThat(otherProps.getString("int_key")).isNull(); diff --git a/sdk-extensions/incubator/src/test/java/io/opentelemetry/sdk/extension/incubator/fileconfig/component/LogRecordExporterComponentProvider.java b/sdk-extensions/incubator/src/test/java/io/opentelemetry/sdk/extension/incubator/fileconfig/component/LogRecordExporterComponentProvider.java index 2f2d6f56c7..f21c1d1bf0 100644 --- a/sdk-extensions/incubator/src/test/java/io/opentelemetry/sdk/extension/incubator/fileconfig/component/LogRecordExporterComponentProvider.java +++ b/sdk-extensions/incubator/src/test/java/io/opentelemetry/sdk/extension/incubator/fileconfig/component/LogRecordExporterComponentProvider.java @@ -5,8 +5,8 @@ package io.opentelemetry.sdk.extension.incubator.fileconfig.component; +import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties; import io.opentelemetry.sdk.autoconfigure.spi.internal.ComponentProvider; -import io.opentelemetry.sdk.autoconfigure.spi.internal.StructuredConfigProperties; import io.opentelemetry.sdk.common.CompletableResultCode; import io.opentelemetry.sdk.logs.data.LogRecordData; import io.opentelemetry.sdk.logs.export.LogRecordExporter; @@ -24,15 +24,15 @@ public class LogRecordExporterComponentProvider implements ComponentProvider { @@ -21,7 +21,7 @@ public class ResourceComponentProvider implements ComponentProvider { } @Override - public Resource create(StructuredConfigProperties config) { + public Resource create(DeclarativeConfigProperties config) { return Resource.builder().put("shape", "square").put("color", "red").build(); } } diff --git a/sdk-extensions/incubator/src/test/java/io/opentelemetry/sdk/extension/incubator/fileconfig/component/ResourceOrderedFirstComponentProvider.java b/sdk-extensions/incubator/src/test/java/io/opentelemetry/sdk/extension/incubator/fileconfig/component/ResourceOrderedFirstComponentProvider.java index f2f41e5b95..181c7b469c 100644 --- a/sdk-extensions/incubator/src/test/java/io/opentelemetry/sdk/extension/incubator/fileconfig/component/ResourceOrderedFirstComponentProvider.java +++ b/sdk-extensions/incubator/src/test/java/io/opentelemetry/sdk/extension/incubator/fileconfig/component/ResourceOrderedFirstComponentProvider.java @@ -5,9 +5,9 @@ package io.opentelemetry.sdk.extension.incubator.fileconfig.component; +import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties; import io.opentelemetry.sdk.autoconfigure.spi.Ordered; import io.opentelemetry.sdk.autoconfigure.spi.internal.ComponentProvider; -import io.opentelemetry.sdk.autoconfigure.spi.internal.StructuredConfigProperties; import io.opentelemetry.sdk.resources.Resource; public class ResourceOrderedFirstComponentProvider implements ComponentProvider, Ordered { @@ -22,7 +22,7 @@ public class ResourceOrderedFirstComponentProvider implements ComponentProvider< } @Override - public Resource create(StructuredConfigProperties config) { + public Resource create(DeclarativeConfigProperties config) { return Resource.builder().put("order", "first").build(); } diff --git a/sdk-extensions/incubator/src/test/java/io/opentelemetry/sdk/extension/incubator/fileconfig/component/ResourceOrderedSecondComponentProvider.java b/sdk-extensions/incubator/src/test/java/io/opentelemetry/sdk/extension/incubator/fileconfig/component/ResourceOrderedSecondComponentProvider.java index 00017b2b7d..5cbb5e299e 100644 --- a/sdk-extensions/incubator/src/test/java/io/opentelemetry/sdk/extension/incubator/fileconfig/component/ResourceOrderedSecondComponentProvider.java +++ b/sdk-extensions/incubator/src/test/java/io/opentelemetry/sdk/extension/incubator/fileconfig/component/ResourceOrderedSecondComponentProvider.java @@ -5,9 +5,9 @@ package io.opentelemetry.sdk.extension.incubator.fileconfig.component; +import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties; import io.opentelemetry.sdk.autoconfigure.spi.Ordered; import io.opentelemetry.sdk.autoconfigure.spi.internal.ComponentProvider; -import io.opentelemetry.sdk.autoconfigure.spi.internal.StructuredConfigProperties; import io.opentelemetry.sdk.resources.Resource; public class ResourceOrderedSecondComponentProvider @@ -23,7 +23,7 @@ public class ResourceOrderedSecondComponentProvider } @Override - public Resource create(StructuredConfigProperties config) { + public Resource create(DeclarativeConfigProperties config) { return Resource.builder().put("order", "second").build(); } diff --git a/sdk-extensions/incubator/src/test/java/io/opentelemetry/sdk/extension/incubator/fileconfig/component/SamplerComponentProvider.java b/sdk-extensions/incubator/src/test/java/io/opentelemetry/sdk/extension/incubator/fileconfig/component/SamplerComponentProvider.java index 2d4c983e4d..3264dd91fc 100644 --- a/sdk-extensions/incubator/src/test/java/io/opentelemetry/sdk/extension/incubator/fileconfig/component/SamplerComponentProvider.java +++ b/sdk-extensions/incubator/src/test/java/io/opentelemetry/sdk/extension/incubator/fileconfig/component/SamplerComponentProvider.java @@ -6,10 +6,10 @@ package io.opentelemetry.sdk.extension.incubator.fileconfig.component; import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties; import io.opentelemetry.api.trace.SpanKind; import io.opentelemetry.context.Context; import io.opentelemetry.sdk.autoconfigure.spi.internal.ComponentProvider; -import io.opentelemetry.sdk.autoconfigure.spi.internal.StructuredConfigProperties; import io.opentelemetry.sdk.trace.data.LinkData; import io.opentelemetry.sdk.trace.samplers.Sampler; import io.opentelemetry.sdk.trace.samplers.SamplingResult; @@ -27,15 +27,15 @@ public class SamplerComponentProvider implements ComponentProvider { } @Override - public Sampler create(StructuredConfigProperties config) { + public Sampler create(DeclarativeConfigProperties config) { return new TestSampler(config); } public static class TestSampler implements Sampler { - public final StructuredConfigProperties config; + public final DeclarativeConfigProperties config; - private TestSampler(StructuredConfigProperties config) { + private TestSampler(DeclarativeConfigProperties config) { this.config = config; } diff --git a/sdk-extensions/incubator/src/test/java/io/opentelemetry/sdk/extension/incubator/fileconfig/component/SpanExporterComponentProvider.java b/sdk-extensions/incubator/src/test/java/io/opentelemetry/sdk/extension/incubator/fileconfig/component/SpanExporterComponentProvider.java index f387454f0f..ddaca3ca4b 100644 --- a/sdk-extensions/incubator/src/test/java/io/opentelemetry/sdk/extension/incubator/fileconfig/component/SpanExporterComponentProvider.java +++ b/sdk-extensions/incubator/src/test/java/io/opentelemetry/sdk/extension/incubator/fileconfig/component/SpanExporterComponentProvider.java @@ -5,8 +5,8 @@ package io.opentelemetry.sdk.extension.incubator.fileconfig.component; +import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties; import io.opentelemetry.sdk.autoconfigure.spi.internal.ComponentProvider; -import io.opentelemetry.sdk.autoconfigure.spi.internal.StructuredConfigProperties; import io.opentelemetry.sdk.common.CompletableResultCode; import io.opentelemetry.sdk.trace.data.SpanData; import io.opentelemetry.sdk.trace.export.SpanExporter; @@ -24,15 +24,15 @@ public class SpanExporterComponentProvider implements ComponentProvider