This class is internal and is hence not for public use. Its APIs are unstable and can change
+ * at any time.
+ */
+public class PrometheusComponentProvider implements ComponentProvider {
+
+ @Override
+ public Class getType() {
+ return MetricReader.class;
+ }
+
+ @Override
+ public String getName() {
+ return "prometheus";
+ }
+
+ @Override
+ public MetricReader create(StructuredConfigProperties config) {
+ PrometheusHttpServerBuilder prometheusBuilder = PrometheusHttpServer.builder();
+
+ Integer port = config.getInt("port");
+ if (port != null) {
+ prometheusBuilder.setPort(port);
+ }
+ String host = config.getString("host");
+ if (host != null) {
+ prometheusBuilder.setHost(host);
+ }
+
+ return prometheusBuilder.build();
+ }
+}
diff --git a/exporters/prometheus/src/main/resources/META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.internal.ComponentProvider b/exporters/prometheus/src/main/resources/META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.internal.ComponentProvider
new file mode 100644
index 00000000000..f3c72966e4b
--- /dev/null
+++ b/exporters/prometheus/src/main/resources/META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.internal.ComponentProvider
@@ -0,0 +1 @@
+io.opentelemetry.exporter.prometheus.internal.PrometheusComponentProvider
diff --git a/sdk-extensions/autoconfigure-spi/src/main/java/io/opentelemetry/sdk/autoconfigure/spi/internal/ComponentProvider.java b/sdk-extensions/autoconfigure-spi/src/main/java/io/opentelemetry/sdk/autoconfigure/spi/internal/ComponentProvider.java
new file mode 100644
index 00000000000..344dc18267c
--- /dev/null
+++ b/sdk-extensions/autoconfigure-spi/src/main/java/io/opentelemetry/sdk/autoconfigure/spi/internal/ComponentProvider.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.sdk.autoconfigure.spi.internal;
+
+import io.opentelemetry.sdk.trace.export.SpanExporter;
+
+/**
+ * Provides configured instances of SDK extension components. {@link ComponentProvider} allows SDK
+ * extension components which are not part of the core SDK to be referenced in file based
+ * configuration.
+ *
+ * @param the type of the SDK extension component. See {@link #getType()}.
+ */
+// TODO (jack-berg): list the specific types which are supported in file configuration
+public interface ComponentProvider {
+
+ /**
+ * The type of SDK extension component. For example, if providing instances of a custom span
+ * exporter, the type would be {@link SpanExporter}.
+ */
+ Class getType();
+
+ /**
+ * The name of the exporter, to be referenced in configuration files. For example, if providing
+ * instances of a custom span exporter for the "acme" protocol, the name might be "acme".
+ *
+ *
This name MUST not be the same as any other component provider name which returns components
+ * of the same {@link #getType() type}.
+ */
+ String getName();
+
+ /**
+ * Configure an instance of the SDK extension component according to the {@code config}.
+ *
+ * @param config the configuration provided where the component is referenced in a configuration
+ * file.
+ * @return an instance the SDK extension component
+ */
+ // TODO (jack-berg): consider dynamic configuration use case before stabilizing in case that
+ // affects any API decisions
+ T create(StructuredConfigProperties config);
+}
diff --git a/sdk-extensions/autoconfigure-spi/src/main/java/io/opentelemetry/sdk/autoconfigure/spi/internal/StructuredConfigProperties.java b/sdk-extensions/autoconfigure-spi/src/main/java/io/opentelemetry/sdk/autoconfigure/spi/internal/StructuredConfigProperties.java
new file mode 100644
index 00000000000..ad1e3b5de3c
--- /dev/null
+++ b/sdk-extensions/autoconfigure-spi/src/main/java/io/opentelemetry/sdk/autoconfigure/spi/internal/StructuredConfigProperties.java
@@ -0,0 +1,187 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.sdk.autoconfigure.spi.internal;
+
+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 instance of {@link StructuredConfigProperties} 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
+ * sequences of mappings.
+ */
+public interface StructuredConfigProperties {
+
+ /**
+ * 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
+ */
+ @Nullable
+ String getString(String name);
+
+ /**
+ * Returns a {@link String} configuration property.
+ *
+ * @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
+ */
+ default String getString(String name, String defaultValue) {
+ return defaultIfNull(getString(name), defaultValue);
+ }
+
+ /**
+ * Returns a {@link Boolean} configuration property. Implementations should use the same rules as
+ * {@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
+ */
+ @Nullable
+ Boolean getBoolean(String name);
+
+ /**
+ * Returns a {@link Boolean} configuration property.
+ *
+ * @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
+ */
+ default boolean getBoolean(String name, boolean defaultValue) {
+ return defaultIfNull(getBoolean(name), defaultValue);
+ }
+
+ /**
+ * Returns a {@link Integer} configuration property.
+ *
+ *
If the underlying config property is {@link Long}, it is converted to {@link Integer} with
+ * {@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
+ */
+ @Nullable
+ Integer getInt(String name);
+
+ /**
+ * Returns a {@link Integer} configuration property.
+ *
+ *
If the underlying config property is {@link Long}, it is converted to {@link Integer} with
+ * {@link Long#intValue()} which may result in loss of precision.
+ *
+ * @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
+ */
+ default int getInt(String name, int defaultValue) {
+ return defaultIfNull(getInt(name), defaultValue);
+ }
+
+ /**
+ * 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
+ */
+ @Nullable
+ Long getLong(String name);
+
+ /**
+ * Returns a {@link Long} configuration property.
+ *
+ * @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
+ */
+ default long getLong(String name, long defaultValue) {
+ return defaultIfNull(getLong(name), defaultValue);
+ }
+
+ /**
+ * 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
+ */
+ @Nullable
+ Double getDouble(String name);
+
+ /**
+ * Returns a {@link Double} configuration property.
+ *
+ * @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
+ */
+ default double getDouble(String name, double defaultValue) {
+ return defaultIfNull(getDouble(name), defaultValue);
+ }
+
+ /**
+ * Returns a {@link List} configuration property. Empty values and values which do not map to the
+ * {@code scalarType} will be removed.
+ *
+ * @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 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
+ */
+ @Nullable
+ List getScalarList(String name, Class scalarType);
+
+ /**
+ * Returns a {@link List} configuration property. Entries which are not strings are converted to
+ * their string representation.
+ *
+ * @see ConfigProperties#getList(String name)
+ * @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
+ */
+ default List getScalarList(String name, Class scalarType, List defaultValue) {
+ return defaultIfNull(getScalarList(name, scalarType), defaultValue);
+ }
+
+ /**
+ * Returns a {@link StructuredConfigProperties} 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
+ */
+ @Nullable
+ StructuredConfigProperties getStructured(String name);
+
+ /**
+ * Returns a list of {@link StructuredConfigProperties} 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
+ */
+ @Nullable
+ List getStructuredList(String name);
+
+ /**
+ * Returns a set of all configuration property keys.
+ *
+ * @return the configuration property keys
+ */
+ Set getPropertyKeys();
+}
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 997df49062c..6751da7ad7f 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
@@ -10,7 +10,9 @@
import io.opentelemetry.api.OpenTelemetry;
import io.opentelemetry.sdk.OpenTelemetrySdk;
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;
/**
@@ -43,8 +45,12 @@ public static AutoConfiguredOpenTelemetrySdkBuilder builder() {
}
static AutoConfiguredOpenTelemetrySdk create(
- OpenTelemetrySdk sdk, Resource resource, ConfigProperties config) {
- return new AutoValue_AutoConfiguredOpenTelemetrySdk(sdk, resource, config);
+ OpenTelemetrySdk sdk,
+ Resource resource,
+ @Nullable ConfigProperties config,
+ @Nullable StructuredConfigProperties structuredConfigProperties) {
+ return new AutoValue_AutoConfiguredOpenTelemetrySdk(
+ sdk, resource, config, structuredConfigProperties);
}
/**
@@ -60,8 +66,23 @@ static AutoConfiguredOpenTelemetrySdk create(
/** Returns the {@link Resource} that was auto-configured. */
abstract Resource getResource();
- /** Returns the {@link ConfigProperties} used for auto-configuration. */
+ /**
+ * Returns the {@link ConfigProperties} used for auto-configuration, or {@code null} if file
+ * configuration was used.
+ *
+ * @see #getStructuredConfig()
+ */
+ @Nullable
abstract ConfigProperties getConfig();
+ /**
+ * Returns the {@link StructuredConfigProperties} used for auto-configuration, or {@code null} if
+ * file configuration was not used.
+ *
+ * @see #getConfig()
+ */
+ @Nullable
+ abstract StructuredConfigProperties getStructuredConfig();
+
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 923f1535257..7fd5d515f4c 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
@@ -21,6 +21,7 @@
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;
@@ -505,7 +506,7 @@ public AutoConfiguredOpenTelemetrySdk build() {
maybeSetAsGlobal(openTelemetrySdk);
callAutoConfigureListeners(spiHelper, openTelemetrySdk);
- return AutoConfiguredOpenTelemetrySdk.create(openTelemetrySdk, resource, config);
+ return AutoConfiguredOpenTelemetrySdk.create(openTelemetrySdk, resource, config, null);
} catch (RuntimeException e) {
logger.info(
"Error encountered during autoconfiguration. Closing partially configured components.");
@@ -546,11 +547,21 @@ private static AutoConfiguredOpenTelemetrySdk maybeConfigureFromFile(ConfigPrope
try {
Class> configurationFactory =
Class.forName("io.opentelemetry.sdk.extension.incubator.fileconfig.FileConfiguration");
- Method parseAndCreate = configurationFactory.getMethod("parseAndCreate", InputStream.class);
- OpenTelemetrySdk sdk = (OpenTelemetrySdk) parseAndCreate.invoke(null, fis);
+ 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.OpenTelemetryConfiguration");
+ Method create = configurationFactory.getMethod("create", openTelemetryConfiguration);
+ OpenTelemetrySdk sdk = (OpenTelemetrySdk) create.invoke(null, model);
+ Method toConfigProperties =
+ configurationFactory.getMethod("toConfigProperties", openTelemetryConfiguration);
+ StructuredConfigProperties structuredConfigProperties =
+ (StructuredConfigProperties) toConfigProperties.invoke(null, model);
// Note: can't access file configuration resource without reflection so setting a dummy
// resource
- return AutoConfiguredOpenTelemetrySdk.create(sdk, Resource.getDefault(), config);
+ return AutoConfiguredOpenTelemetrySdk.create(
+ sdk, Resource.getDefault(), null, structuredConfigProperties);
} catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException e) {
throw new ConfigurationException(
"Error configuring from file. Is opentelemetry-sdk-extension-incubator on the classpath?",
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 8a50d782031..fe18ac8d41b 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
@@ -8,9 +8,11 @@
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;
+import javax.annotation.Nullable;
/**
* This class is internal and is hence not for public use. Its APIs are unstable and can change at
@@ -20,7 +22,12 @@ public final class AutoConfigureUtil {
private AutoConfigureUtil() {}
- /** Returns the {@link ConfigProperties} used for auto-configuration. */
+ /**
+ * Returns the {@link ConfigProperties} used for auto-configuration.
+ *
+ * @return the config properties, or {@code null} if file based configuration is used
+ */
+ @Nullable
public static ConfigProperties getConfig(
AutoConfiguredOpenTelemetrySdk autoConfiguredOpenTelemetrySdk) {
try {
@@ -33,6 +40,25 @@ public static ConfigProperties getConfig(
}
}
+ /**
+ * Returns the {@link StructuredConfigProperties} used for auto-configuration when file based
+ * configuration is used.
+ *
+ * @return the config properties, or {@code null} if file based configuration is NOT used
+ */
+ @Nullable
+ public static StructuredConfigProperties getStructuredConfig(
+ AutoConfiguredOpenTelemetrySdk autoConfiguredOpenTelemetrySdk) {
+ try {
+ Method method = AutoConfiguredOpenTelemetrySdk.class.getDeclaredMethod("getStructuredConfig");
+ method.setAccessible(true);
+ return (StructuredConfigProperties) method.invoke(autoConfiguredOpenTelemetrySdk);
+ } catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) {
+ throw new IllegalStateException(
+ "Error calling getStructuredConfig on AutoConfiguredOpenTelemetrySdk", e);
+ }
+ }
+
/** Sets the {@link ComponentLoader} to be used in the auto-configuration process. */
public static AutoConfiguredOpenTelemetrySdkBuilder setComponentLoader(
AutoConfiguredOpenTelemetrySdkBuilder builder, ComponentLoader componentLoader) {
diff --git a/sdk-extensions/autoconfigure/src/testFullConfig/java/io/opentelemetry/sdk/autoconfigure/FileConfigurationTest.java b/sdk-extensions/autoconfigure/src/testFullConfig/java/io/opentelemetry/sdk/autoconfigure/FileConfigurationTest.java
index a40884d032b..639f2955da3 100644
--- a/sdk-extensions/autoconfigure/src/testFullConfig/java/io/opentelemetry/sdk/autoconfigure/FileConfigurationTest.java
+++ b/sdk-extensions/autoconfigure/src/testFullConfig/java/io/opentelemetry/sdk/autoconfigure/FileConfigurationTest.java
@@ -6,7 +6,6 @@
package io.opentelemetry.sdk.autoconfigure;
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat;
-import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doReturn;
@@ -29,6 +28,7 @@
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.logs.internal.SdkEventLoggerProvider;
import io.opentelemetry.sdk.resources.Resource;
import io.opentelemetry.sdk.trace.SdkTracerProvider;
@@ -67,7 +67,11 @@ void setup() throws IOException {
+ " processors:\n"
+ " - simple:\n"
+ " exporter:\n"
- + " console: {}\n";
+ + " console: {}\n"
+ + "other:\n"
+ + " str_key: str_value\n"
+ + " map_key:\n"
+ + " str_key1: str_value1\n";
configFilePath = tempDir.resolve("otel-config.yaml");
Files.write(configFilePath, yaml.getBytes(StandardCharsets.UTF_8));
GlobalOpenTelemetry.resetForTest();
@@ -183,4 +187,27 @@ void configFile_Error(@TempDir Path tempDir) throws IOException {
.isInstanceOf(ConfigurationException.class)
.hasMessage("Unrecognized span exporter(s): [foo]");
}
+
+ @Test
+ void configFile_StructuredConfigProperties() {
+ ConfigProperties config =
+ DefaultConfigProperties.createFromMap(
+ Collections.singletonMap("otel.experimental.config.file", configFilePath.toString()));
+
+ AutoConfiguredOpenTelemetrySdk autoConfiguredOpenTelemetrySdk =
+ AutoConfiguredOpenTelemetrySdk.builder().setConfig(config).setResultAsGlobal().build();
+ OpenTelemetrySdk openTelemetrySdk = autoConfiguredOpenTelemetrySdk.getOpenTelemetrySdk();
+ 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");
+ }
}
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 f1c882a48b9..7b5bcd9ca2d 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,8 +5,14 @@
package io.opentelemetry.sdk.extension.incubator.fileconfig;
+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 {
@@ -27,4 +33,54 @@ static T assertNotNull(@Nullable T object, String description) {
}
return object;
}
+
+ /**
+ * 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}.
+ *
+ * @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"})
+ static T loadComponent(SpiHelper spiHelper, Class type, String name, Object model) {
+ // 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 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);
+
+ // Map model to generic structured config properties
+ StructuredConfigProperties config = FileConfiguration.toConfigProperties(model);
+
+ try {
+ return provider.create(config);
+ } catch (Throwable throwable) {
+ throw new ConfigurationException(
+ "Error configuring " + type.getName() + " with name \"" + name + "\"", throwable);
+ }
+ }
}
diff --git a/sdk-extensions/incubator/src/main/java/io/opentelemetry/sdk/extension/incubator/fileconfig/FileConfiguration.java b/sdk-extensions/incubator/src/main/java/io/opentelemetry/sdk/extension/incubator/fileconfig/FileConfiguration.java
index 94150a04b8a..82888ed5735 100644
--- a/sdk-extensions/incubator/src/main/java/io/opentelemetry/sdk/extension/incubator/fileconfig/FileConfiguration.java
+++ b/sdk-extensions/incubator/src/main/java/io/opentelemetry/sdk/extension/incubator/fileconfig/FileConfiguration.java
@@ -7,10 +7,12 @@
import com.fasterxml.jackson.annotation.JsonSetter;
import com.fasterxml.jackson.annotation.Nulls;
+import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.opentelemetry.sdk.OpenTelemetrySdk;
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.extension.incubator.fileconfig.internal.model.OpenTelemetryConfiguration;
import java.io.Closeable;
import java.io.IOException;
@@ -138,6 +140,23 @@ static Object loadYaml(InputStream inputStream, Map environmentV
return yaml.loadFromInputStream(inputStream);
}
+ /**
+ * Convert the {@code model} to a generic {@link StructuredConfigProperties}, which can be used to
+ * read configuration not part of the model.
+ *
+ * @param model the configuration model
+ * @return a generic {@link StructuredConfigProperties} representation of the model
+ */
+ public static StructuredConfigProperties toConfigProperties(OpenTelemetryConfiguration model) {
+ return toConfigProperties((Object) model);
+ }
+
+ static StructuredConfigProperties toConfigProperties(Object model) {
+ Map configurationMap =
+ MAPPER.convertValue(model, new TypeReference