Use autoconfigured ClassLoader to load declarative config (#6725)

This commit is contained in:
jack-berg 2024-09-20 14:00:04 -05:00 committed by GitHub
parent 39b24118a1
commit 325822ce85
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 84 additions and 28 deletions

View File

@ -107,8 +107,8 @@ public final class AutoConfiguredOpenTelemetrySdkBuilder implements AutoConfigur
private Function<ConfigProperties, ConfigProperties> configPropertiesCustomizer =
Function.identity();
private SpiHelper spiHelper =
SpiHelper.create(AutoConfiguredOpenTelemetrySdk.class.getClassLoader());
private ComponentLoader componentLoader =
SpiHelper.serviceComponentLoader(AutoConfiguredOpenTelemetrySdk.class.getClassLoader());
private boolean registerShutdownHook = true;
@ -401,14 +401,14 @@ public final class AutoConfiguredOpenTelemetrySdkBuilder implements AutoConfigur
public AutoConfiguredOpenTelemetrySdkBuilder setServiceClassLoader(
ClassLoader serviceClassLoader) {
requireNonNull(serviceClassLoader, "serviceClassLoader");
this.spiHelper = SpiHelper.create(serviceClassLoader);
this.componentLoader = SpiHelper.serviceComponentLoader(serviceClassLoader);
return this;
}
/** Sets the {@link ComponentLoader} to be used to load SPI implementations. */
AutoConfiguredOpenTelemetrySdkBuilder setComponentLoader(ComponentLoader componentLoader) {
requireNonNull(componentLoader, "componentLoader");
this.spiHelper = SpiHelper.create(componentLoader);
this.componentLoader = componentLoader;
return this;
}
@ -417,6 +417,7 @@ public final class AutoConfiguredOpenTelemetrySdkBuilder implements AutoConfigur
* the settings of this {@link AutoConfiguredOpenTelemetrySdkBuilder}.
*/
public AutoConfiguredOpenTelemetrySdk build() {
SpiHelper spiHelper = SpiHelper.create(componentLoader);
if (!customized) {
customized = true;
mergeSdkTracerProviderConfigurer();
@ -428,7 +429,8 @@ public final class AutoConfiguredOpenTelemetrySdkBuilder implements AutoConfigur
ConfigProperties config = getConfig();
AutoConfiguredOpenTelemetrySdk fromFileConfiguration = maybeConfigureFromFile(config);
AutoConfiguredOpenTelemetrySdk fromFileConfiguration =
maybeConfigureFromFile(config, componentLoader);
if (fromFileConfiguration != null) {
maybeRegisterShutdownHook(fromFileConfiguration.getOpenTelemetrySdk());
maybeSetAsGlobal(fromFileConfiguration.getOpenTelemetrySdk());
@ -527,7 +529,8 @@ public final class AutoConfiguredOpenTelemetrySdkBuilder implements AutoConfigur
}
@Nullable
private static AutoConfiguredOpenTelemetrySdk maybeConfigureFromFile(ConfigProperties config) {
private static AutoConfiguredOpenTelemetrySdk maybeConfigureFromFile(
ConfigProperties config, ComponentLoader componentLoader) {
String otelConfigFile = config.getString("otel.config.file");
if (otelConfigFile != null && !otelConfigFile.isEmpty()) {
logger.warning(
@ -552,8 +555,10 @@ public final class AutoConfiguredOpenTelemetrySdkBuilder implements AutoConfigur
Class<?> openTelemetryConfiguration =
Class.forName(
"io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.OpenTelemetryConfigurationModel");
Method create = configurationFactory.getMethod("create", openTelemetryConfiguration);
OpenTelemetrySdk sdk = (OpenTelemetrySdk) create.invoke(null, model);
Method create =
configurationFactory.getMethod(
"create", openTelemetryConfiguration, ComponentLoader.class);
OpenTelemetrySdk sdk = (OpenTelemetrySdk) create.invoke(null, model, componentLoader);
Method toConfigProperties =
configurationFactory.getMethod("toConfigProperties", openTelemetryConfiguration);
StructuredConfigProperties structuredConfigProperties =
@ -608,7 +613,7 @@ public final class AutoConfiguredOpenTelemetrySdkBuilder implements AutoConfigur
@SuppressWarnings("deprecation") // Support deprecated SdkTracerProviderConfigurer
private void mergeSdkTracerProviderConfigurer() {
for (io.opentelemetry.sdk.autoconfigure.spi.traces.SdkTracerProviderConfigurer configurer :
spiHelper.load(
componentLoader.load(
io.opentelemetry.sdk.autoconfigure.spi.traces.SdkTracerProviderConfigurer.class)) {
addTracerProviderCustomizer(
(builder, config) -> {

View File

@ -41,7 +41,7 @@ public final class SpiHelper {
/** Create a {@link SpiHelper} which loads SPIs using the {@code classLoader}. */
public static SpiHelper create(ClassLoader classLoader) {
return new SpiHelper(new ServiceLoaderComponentLoader(classLoader));
return new SpiHelper(serviceComponentLoader(classLoader));
}
/** Create a {@link SpiHelper} which loads SPIs using the {@code componentLoader}. */
@ -49,6 +49,16 @@ public final class SpiHelper {
return new SpiHelper(componentLoader);
}
/** Create a {@link ComponentLoader} which loads using the {@code classLoader}. */
public static ComponentLoader serviceComponentLoader(ClassLoader classLoader) {
return new ServiceLoaderComponentLoader(classLoader);
}
/** Return the backing underlying {@link ComponentLoader}. */
public ComponentLoader getComponentLoader() {
return componentLoader;
}
/**
* Load implementations of an SPI which are configurable (i.e. they accept {@link
* ConfigProperties}.

View File

@ -49,7 +49,8 @@ final class FileConfigUtil {
*/
static <T> T loadComponent(SpiHelper spiHelper, Class<T> type, String name, Object model) {
// Map model to generic structured config properties
StructuredConfigProperties config = FileConfiguration.toConfigProperties(model);
StructuredConfigProperties config =
FileConfiguration.toConfigProperties(model, spiHelper.getComponentLoader());
return spiHelper.loadComponent(type, name, config);
}
}

View File

@ -10,6 +10,7 @@ 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.ComponentLoader;
import io.opentelemetry.sdk.autoconfigure.internal.SpiHelper;
import io.opentelemetry.sdk.autoconfigure.spi.ConfigurationException;
import io.opentelemetry.sdk.autoconfigure.spi.internal.ComponentProvider;
@ -49,6 +50,8 @@ public final class FileConfiguration {
private static final Logger logger = Logger.getLogger(FileConfiguration.class.getName());
private static final Pattern ENV_VARIABLE_REFERENCE =
Pattern.compile("\\$\\{([a-zA-Z_][a-zA-Z0-9_]*)}");
private static final ComponentLoader DEFAULT_COMPONENT_LOADER =
SpiHelper.serviceComponentLoader(FileConfiguration.class.getClassLoader());
private static final ObjectMapper MAPPER;
@ -86,9 +89,24 @@ public final class FileConfiguration {
* @throws ConfigurationException if unable to interpret
*/
public static OpenTelemetrySdk create(OpenTelemetryConfigurationModel configurationModel) {
return create(configurationModel, DEFAULT_COMPONENT_LOADER);
}
/**
* Interpret the {@code configurationModel} to create {@link OpenTelemetrySdk} instance
* corresponding to the configuration.
*
* @param configurationModel the configuration model
* @param componentLoader the component loader used to load {@link ComponentProvider}
* implementations
* @return the {@link OpenTelemetrySdk}
* @throws ConfigurationException if unable to interpret
*/
public static OpenTelemetrySdk create(
OpenTelemetryConfigurationModel configurationModel, ComponentLoader componentLoader) {
return createAndMaybeCleanup(
OpenTelemetryConfigurationFactory.getInstance(),
SpiHelper.create(FileConfiguration.class.getClassLoader()),
SpiHelper.create(componentLoader),
configurationModel);
}
@ -130,7 +148,7 @@ public final class FileConfiguration {
*/
public static StructuredConfigProperties toConfigProperties(
OpenTelemetryConfigurationModel model) {
return toConfigProperties((Object) model);
return toConfigProperties(model, DEFAULT_COMPONENT_LOADER);
}
/**
@ -141,13 +159,14 @@ public final class FileConfiguration {
*/
public static StructuredConfigProperties toConfigProperties(InputStream configuration) {
Object yamlObj = loadYaml(configuration, System.getenv());
return toConfigProperties(yamlObj);
return toConfigProperties(yamlObj, DEFAULT_COMPONENT_LOADER);
}
static StructuredConfigProperties toConfigProperties(Object model) {
static StructuredConfigProperties toConfigProperties(
Object model, ComponentLoader componentLoader) {
Map<String, Object> configurationMap =
MAPPER.convertValue(model, new TypeReference<Map<String, Object>>() {});
return YamlStructuredConfigProperties.create(configurationMap);
return YamlStructuredConfigProperties.create(configurationMap, componentLoader);
}
/**
@ -162,21 +181,27 @@ public final class FileConfiguration {
// ComponentProvider
public static io.opentelemetry.sdk.trace.samplers.Sampler createSampler(
StructuredConfigProperties genericSamplerModel) {
SamplerModel samplerModel = convertToModel(genericSamplerModel, SamplerModel.class);
YamlStructuredConfigProperties yamlStructuredConfigProperties =
requireYamlStructuredConfigProperties(genericSamplerModel);
SamplerModel samplerModel = convertToModel(yamlStructuredConfigProperties, SamplerModel.class);
return createAndMaybeCleanup(
SamplerFactory.getInstance(),
SpiHelper.create(FileConfiguration.class.getClassLoader()),
SpiHelper.create(yamlStructuredConfigProperties.getComponentLoader()),
samplerModel);
}
static <T> T convertToModel(
StructuredConfigProperties structuredConfigProperties, Class<T> modelType) {
private static YamlStructuredConfigProperties requireYamlStructuredConfigProperties(
StructuredConfigProperties structuredConfigProperties) {
if (!(structuredConfigProperties instanceof YamlStructuredConfigProperties)) {
throw new ConfigurationException(
"Only YamlStructuredConfigProperties can be converted to model");
}
return MAPPER.convertValue(
((YamlStructuredConfigProperties) structuredConfigProperties).toMap(), modelType);
return (YamlStructuredConfigProperties) structuredConfigProperties;
}
static <T> T convertToModel(
YamlStructuredConfigProperties structuredConfigProperties, Class<T> modelType) {
return MAPPER.convertValue(structuredConfigProperties.toMap(), modelType);
}
static <M, R> R createAndMaybeCleanup(Factory<M, R> factory, SpiHelper spiHelper, M model) {

View File

@ -25,7 +25,9 @@ final class ResourceFactory
Resource> {
private static final StructuredConfigProperties EMPTY_CONFIG =
FileConfiguration.toConfigProperties(Collections.emptyMap());
FileConfiguration.toConfigProperties(
Collections.emptyMap(),
SpiHelper.serviceComponentLoader(ResourceFactory.class.getClassLoader()));
private static final ResourceFactory INSTANCE = new ResourceFactory();
private ResourceFactory() {}

View File

@ -8,6 +8,7 @@ package io.opentelemetry.sdk.extension.incubator.fileconfig;
import static java.util.stream.Collectors.joining;
import static java.util.stream.Collectors.toList;
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;
@ -37,14 +38,17 @@ final class YamlStructuredConfigProperties implements StructuredConfigProperties
private final Map<String, List<YamlStructuredConfigProperties>> listEntries;
private final Map<String, YamlStructuredConfigProperties> mapEntries;
private final ComponentLoader componentLoader;
private YamlStructuredConfigProperties(
Map<String, Object> simpleEntries,
Map<String, List<YamlStructuredConfigProperties>> listEntries,
Map<String, YamlStructuredConfigProperties> mapEntries) {
Map<String, YamlStructuredConfigProperties> mapEntries,
ComponentLoader componentLoader) {
this.simpleEntries = simpleEntries;
this.listEntries = listEntries;
this.mapEntries = mapEntries;
this.componentLoader = componentLoader;
}
/**
@ -57,7 +61,8 @@ final class YamlStructuredConfigProperties implements StructuredConfigProperties
* @see FileConfiguration#toConfigProperties(OpenTelemetryConfigurationModel)
*/
@SuppressWarnings("unchecked")
static YamlStructuredConfigProperties create(Map<String, Object> properties) {
static YamlStructuredConfigProperties create(
Map<String, Object> properties, ComponentLoader componentLoader) {
Map<String, Object> simpleEntries = new HashMap<>();
Map<String, List<YamlStructuredConfigProperties>> listEntries = new HashMap<>();
Map<String, YamlStructuredConfigProperties> mapEntries = new HashMap<>();
@ -75,13 +80,15 @@ final class YamlStructuredConfigProperties implements StructuredConfigProperties
if (isListOfMaps(value)) {
List<YamlStructuredConfigProperties> list =
((List<Map<String, Object>>) value)
.stream().map(YamlStructuredConfigProperties::create).collect(toList());
.stream()
.map(map -> YamlStructuredConfigProperties.create(map, componentLoader))
.collect(toList());
listEntries.put(key, list);
continue;
}
if (isMap(value)) {
YamlStructuredConfigProperties configProperties =
YamlStructuredConfigProperties.create((Map<String, Object>) value);
YamlStructuredConfigProperties.create((Map<String, Object>) value, componentLoader);
mapEntries.put(key, configProperties);
continue;
}
@ -91,7 +98,8 @@ final class YamlStructuredConfigProperties implements StructuredConfigProperties
+ "\" has unrecognized object type "
+ value.getClass().getName());
}
return new YamlStructuredConfigProperties(simpleEntries, listEntries, mapEntries);
return new YamlStructuredConfigProperties(
simpleEntries, listEntries, mapEntries, componentLoader);
}
private static boolean isPrimitiveList(Object object) {
@ -292,4 +300,9 @@ final class YamlStructuredConfigProperties implements StructuredConfigProperties
mapEntries.forEach((key, value) -> result.put(key, value.toMap()));
return Collections.unmodifiableMap(result);
}
/** Return the {@link ComponentLoader}. */
public ComponentLoader getComponentLoader() {
return componentLoader;
}
}