Promote ComponentLoader to new opentelemetry-api-util, standardize SPI loading (#7446)

This commit is contained in:
jack-berg 2025-07-10 17:29:25 -05:00 committed by GitHub
parent 4821c37ec0
commit 78a917da2e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
63 changed files with 444 additions and 196 deletions

View File

@ -71,6 +71,7 @@ The OpenTelemetry API for recording telemetry.
| [API](./api/all) | OpenTelemetry API, including metrics, traces, baggage, context | `opentelemetry-api` | <!--VERSION_STABLE-->1.51.0<!--/VERSION_STABLE--> | [![Javadocs](https://www.javadoc.io/badge/io.opentelemetry/opentelemetry-api.svg)](https://www.javadoc.io/doc/io.opentelemetry/opentelemetry-api) |
| [API Incubator](./api/incubator) | API incubator, including pass through propagator, and extended tracer, and Event API | `opentelemetry-api-incubator` | <!--VERSION_UNSTABLE-->1.51.0-alpha<!--/VERSION_UNSTABLE--> | [![Javadocs](https://www.javadoc.io/badge/io.opentelemetry/opentelemetry-api-incubator.svg)](https://www.javadoc.io/doc/io.opentelemetry/opentelemetry-api-incubator) |
| [Context API](./context) | OpenTelemetry context API | `opentelemetry-context` | <!--VERSION_STABLE-->1.51.0<!--/VERSION_STABLE--> | [![Javadocs](https://www.javadoc.io/badge/io.opentelemetry/opentelemetry-context.svg)](https://www.javadoc.io/doc/io.opentelemetry/opentelemetry-context) |
| [Common](./common) | Common utility methods used across API components | `opentelemetry-common` | TODO: add after first publish | TODO: add link after first publish |
</details>
<details>

View File

@ -28,7 +28,7 @@ class SdkDesignTest {
* Ensures that all SDK methods that: - are defined in classes that extend or implement API model
* and are public (to exclude protected builders) - are public (avoids issues with protected
* methods returning classes unavailable to test's CL) - override or implement parent method
* return only API, Context or generic Java type.
* return only API, Context, Common, or generic Java type.
*/
@Test
void sdkImplementationOfApiClassesShouldReturnApiTypeOnly() {
@ -45,7 +45,11 @@ class SdkDesignTest {
.and(implementOrOverride())
.should()
.haveRawReturnType(
inPackage("io.opentelemetry.api..", "io.opentelemetry.context..", "java.."))
inPackage(
"io.opentelemetry.api..",
"io.opentelemetry.context..",
"io.opentelemetry.common..",
"java.."))
.orShould()
.haveRawReturnType("void");

View File

@ -7,6 +7,7 @@ package io.opentelemetry.api.incubator.config;
import static io.opentelemetry.api.internal.ConfigUtil.defaultIfNull;
import io.opentelemetry.common.ComponentLoader;
import java.util.List;
import java.util.Map;
import java.util.Set;
@ -228,4 +229,7 @@ public interface DeclarativeConfigProperties {
* @return the configuration property keys
*/
Set<String> getPropertyKeys();
/** Return a {@link ComponentLoader} that should be used to load SPIs. */
ComponentLoader getComponentLoader();
}

View File

@ -5,6 +5,7 @@
package io.opentelemetry.api.incubator.config;
import io.opentelemetry.common.ComponentLoader;
import java.util.Collections;
import java.util.List;
import java.util.Set;
@ -15,6 +16,8 @@ final class EmptyDeclarativeConfigProperties implements DeclarativeConfigPropert
private static final EmptyDeclarativeConfigProperties INSTANCE =
new EmptyDeclarativeConfigProperties();
private static final ComponentLoader COMPONENT_LOADER =
ComponentLoader.forClassLoader(EmptyDeclarativeConfigProperties.class.getClassLoader());
private EmptyDeclarativeConfigProperties() {}
@ -74,4 +77,9 @@ final class EmptyDeclarativeConfigProperties implements DeclarativeConfigPropert
public Set<String> getPropertyKeys() {
return Collections.emptySet();
}
@Override
public ComponentLoader getComponentLoader() {
return COMPONENT_LOADER;
}
}

12
common/build.gradle.kts Normal file
View File

@ -0,0 +1,12 @@
plugins {
id("otel.java-conventions")
id("otel.publish-conventions")
id("otel.animalsniffer-conventions")
}
description = "OpenTelemetry API Common"
otelJava.moduleName.set("io.opentelemetry.common")
dependencies {
}

View File

@ -0,0 +1,28 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.common;
import java.util.ServiceLoader;
/** A loader for components that are discovered via SPI. */
public interface ComponentLoader {
/**
* Load implementations of an SPI.
*
* @param spiClass the SPI class
* @param <T> the SPI type
* @return iterable of SPI implementations
*/
<T> Iterable<T> load(Class<T> spiClass);
/**
* Create an instance for the {@code classLoader} using {@link ServiceLoader#load(Class,
* ClassLoader)}.
*/
static ComponentLoader forClassLoader(ClassLoader classLoader) {
return new ServiceLoaderComponentLoader(classLoader);
}
}

View File

@ -0,0 +1,27 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.common;
import java.util.ServiceLoader;
class ServiceLoaderComponentLoader implements ComponentLoader {
private final ClassLoader classLoader;
ServiceLoaderComponentLoader(ClassLoader classLoader) {
this.classLoader = classLoader;
}
@Override
public <T> Iterable<T> load(Class<T> spiClass) {
return ServiceLoader.load(spiClass, classLoader);
}
@Override
public String toString() {
return "ServiceLoaderComponentLoader{classLoader=" + classLoader + "}";
}
}

View File

@ -10,6 +10,7 @@ description = "OpenTelemetry Context (Incubator)"
otelJava.moduleName.set("io.opentelemetry.context")
dependencies {
api(project(":common"))
// MustBeClosed
compileOnly("com.google.errorprone:error_prone_annotations")

View File

@ -37,9 +37,9 @@
package io.opentelemetry.context;
import io.opentelemetry.common.ComponentLoader;
import java.util.ArrayList;
import java.util.List;
import java.util.ServiceLoader;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Function;
import java.util.logging.Level;
@ -103,9 +103,10 @@ final class LazyStorage {
return ContextStorage.defaultStorage();
}
ComponentLoader componentLoader =
ComponentLoader.forClassLoader(LazyStorage.class.getClassLoader());
List<ContextStorageProvider> providers = new ArrayList<>();
for (ContextStorageProvider provider :
ServiceLoader.load(ContextStorageProvider.class, LazyStorage.class.getClassLoader())) {
for (ContextStorageProvider provider : componentLoader.load(ContextStorageProvider.class)) {
if (provider
.getClass()
.getName()

View File

@ -0,0 +1,7 @@
Comparing source compatibility of opentelemetry-common-1.52.0-SNAPSHOT.jar against
+++ NEW INTERFACE: PUBLIC(+) ABSTRACT(+) io.opentelemetry.common.ComponentLoader (not serializable)
+++ CLASS FILE FORMAT VERSION: 52.0 <- n.a.
+++ NEW SUPERCLASS: java.lang.Object
+++ NEW METHOD: PUBLIC(+) STATIC(+) io.opentelemetry.common.ComponentLoader forClassLoader(java.lang.ClassLoader)
+++ NEW METHOD: PUBLIC(+) ABSTRACT(+) java.lang.Iterable<T> load(java.lang.Class<T>)
GENERIC TEMPLATES: +++ T:java.lang.Object

View File

@ -1,2 +1,19 @@
Comparing source compatibility of opentelemetry-exporter-otlp-1.52.0-SNAPSHOT.jar against opentelemetry-exporter-otlp-1.51.0.jar
No changes.
*** MODIFIED CLASS: PUBLIC FINAL io.opentelemetry.exporter.otlp.http.logs.OtlpHttpLogRecordExporterBuilder (not serializable)
=== CLASS FILE FORMAT VERSION: 52.0 <- 52.0
+++ NEW METHOD: PUBLIC(+) io.opentelemetry.exporter.otlp.http.logs.OtlpHttpLogRecordExporterBuilder setComponentLoader(io.opentelemetry.common.ComponentLoader)
*** MODIFIED CLASS: PUBLIC FINAL io.opentelemetry.exporter.otlp.http.metrics.OtlpHttpMetricExporterBuilder (not serializable)
=== CLASS FILE FORMAT VERSION: 52.0 <- 52.0
+++ NEW METHOD: PUBLIC(+) io.opentelemetry.exporter.otlp.http.metrics.OtlpHttpMetricExporterBuilder setComponentLoader(io.opentelemetry.common.ComponentLoader)
*** MODIFIED CLASS: PUBLIC FINAL io.opentelemetry.exporter.otlp.http.trace.OtlpHttpSpanExporterBuilder (not serializable)
=== CLASS FILE FORMAT VERSION: 52.0 <- 52.0
+++ NEW METHOD: PUBLIC(+) io.opentelemetry.exporter.otlp.http.trace.OtlpHttpSpanExporterBuilder setComponentLoader(io.opentelemetry.common.ComponentLoader)
*** MODIFIED CLASS: PUBLIC FINAL io.opentelemetry.exporter.otlp.logs.OtlpGrpcLogRecordExporterBuilder (not serializable)
=== CLASS FILE FORMAT VERSION: 52.0 <- 52.0
+++ NEW METHOD: PUBLIC(+) io.opentelemetry.exporter.otlp.logs.OtlpGrpcLogRecordExporterBuilder setComponentLoader(io.opentelemetry.common.ComponentLoader)
*** MODIFIED CLASS: PUBLIC FINAL io.opentelemetry.exporter.otlp.metrics.OtlpGrpcMetricExporterBuilder (not serializable)
=== CLASS FILE FORMAT VERSION: 52.0 <- 52.0
+++ NEW METHOD: PUBLIC(+) io.opentelemetry.exporter.otlp.metrics.OtlpGrpcMetricExporterBuilder setComponentLoader(io.opentelemetry.common.ComponentLoader)
*** MODIFIED CLASS: PUBLIC FINAL io.opentelemetry.exporter.otlp.trace.OtlpGrpcSpanExporterBuilder (not serializable)
=== CLASS FILE FORMAT VERSION: 52.0 <- 52.0
+++ NEW METHOD: PUBLIC(+) io.opentelemetry.exporter.otlp.trace.OtlpGrpcSpanExporterBuilder setComponentLoader(io.opentelemetry.common.ComponentLoader)

View File

@ -1,2 +1,4 @@
Comparing source compatibility of opentelemetry-sdk-extension-autoconfigure-spi-1.52.0-SNAPSHOT.jar against opentelemetry-sdk-extension-autoconfigure-spi-1.51.0.jar
No changes.
*** MODIFIED INTERFACE: PUBLIC ABSTRACT io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties (not serializable)
=== CLASS FILE FORMAT VERSION: 52.0 <- 52.0
+++ NEW METHOD: PUBLIC(+) io.opentelemetry.common.ComponentLoader getComponentLoader()

View File

@ -1,2 +1,4 @@
Comparing source compatibility of opentelemetry-sdk-extension-autoconfigure-1.52.0-SNAPSHOT.jar against opentelemetry-sdk-extension-autoconfigure-1.51.0.jar
No changes.
*** MODIFIED CLASS: PUBLIC FINAL io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdkBuilder (not serializable)
=== CLASS FILE FORMAT VERSION: 52.0 <- 52.0
+++ NEW METHOD: PUBLIC(+) io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdkBuilder setComponentLoader(io.opentelemetry.common.ComponentLoader)

View File

@ -8,9 +8,9 @@ package io.opentelemetry.exporter.internal.compression;
import static io.opentelemetry.api.internal.Utils.checkArgument;
import static java.util.stream.Collectors.joining;
import io.opentelemetry.common.ComponentLoader;
import java.util.HashMap;
import java.util.Map;
import java.util.ServiceLoader;
import java.util.Set;
import javax.annotation.Nullable;
@ -25,7 +25,8 @@ import javax.annotation.Nullable;
public final class CompressorUtil {
private static final Map<String, Compressor> compressorRegistry =
buildCompressorRegistry(CompressorUtil.class.getClassLoader());
buildCompressorRegistry(
ComponentLoader.forClassLoader(CompressorUtil.class.getClassLoader()));
private CompressorUtil() {}
@ -44,16 +45,16 @@ public final class CompressorUtil {
* Validate that the {@code compressionMethod} is "none" or matches a registered compressor.
*
* @param compressionMethod the compression method to validate and resolve
* @param classLoader the class loader to use for loading SPI implementations, or null to use the
* default
* @param componentLoader the component loader to use for loading SPI implementations, or null to
* use the default
* @return {@code null} if {@code compressionMethod} is "none" or the registered compressor
* @throws IllegalArgumentException if no match is found
*/
@Nullable
public static Compressor validateAndResolveCompressor(
String compressionMethod, @Nullable ClassLoader classLoader) {
String compressionMethod, @Nullable ComponentLoader componentLoader) {
Map<String, Compressor> registry =
classLoader == null ? compressorRegistry : buildCompressorRegistry(classLoader);
componentLoader == null ? compressorRegistry : buildCompressorRegistry(componentLoader);
Set<String> supportedEncodings = registry.keySet();
Compressor compressor = registry.get(compressionMethod);
@ -64,9 +65,9 @@ public final class CompressorUtil {
return compressor;
}
private static Map<String, Compressor> buildCompressorRegistry(ClassLoader classLoader) {
private static Map<String, Compressor> buildCompressorRegistry(ComponentLoader componentLoader) {
Map<String, Compressor> compressors = new HashMap<>();
for (CompressorProvider spi : ServiceLoader.load(CompressorProvider.class, classLoader)) {
for (CompressorProvider spi : componentLoader.load(CompressorProvider.class)) {
Compressor compressor = spi.getInstance();
compressors.put(compressor.getEncoding(), compressor);
}

View File

@ -5,16 +5,16 @@
package io.opentelemetry.exporter.internal.grpc;
import static java.util.Objects.requireNonNull;
import io.grpc.Channel;
import io.grpc.ManagedChannel;
import io.opentelemetry.api.GlobalOpenTelemetry;
import io.opentelemetry.api.internal.ConfigUtil;
import io.opentelemetry.api.metrics.MeterProvider;
import io.opentelemetry.common.ComponentLoader;
import io.opentelemetry.exporter.internal.ExporterBuilderUtil;
import io.opentelemetry.exporter.internal.TlsConfigHelper;
import io.opentelemetry.exporter.internal.compression.Compressor;
import io.opentelemetry.exporter.internal.compression.CompressorProvider;
import io.opentelemetry.exporter.internal.compression.CompressorUtil;
import io.opentelemetry.exporter.internal.marshal.Marshaler;
import io.opentelemetry.sdk.common.InternalTelemetryVersion;
@ -29,7 +29,6 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.ServiceLoader;
import java.util.StringJoiner;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeUnit;
@ -70,7 +69,8 @@ public class GrpcExporterBuilder<T extends Marshaler> {
private Supplier<MeterProvider> meterProviderSupplier = GlobalOpenTelemetry::getMeterProvider;
private InternalTelemetryVersion internalTelemetryVersion = InternalTelemetryVersion.LEGACY;
private ClassLoader serviceClassLoader = GrpcExporterBuilder.class.getClassLoader();
private ComponentLoader componentLoader =
ComponentLoader.forClassLoader(GrpcExporterBuilder.class.getClassLoader());
@Nullable private ExecutorService executorService;
// Use Object type since gRPC may not be on the classpath.
@ -124,9 +124,8 @@ public class GrpcExporterBuilder<T extends Marshaler> {
* methods is available by implementing {@link Compressor} and {@link CompressorProvider}.
*/
public GrpcExporterBuilder<T> setCompression(String compressionMethod) {
requireNonNull(compressionMethod, "compressionMethod");
Compressor compressor =
CompressorUtil.validateAndResolveCompressor(compressionMethod, serviceClassLoader);
CompressorUtil.validateAndResolveCompressor(compressionMethod, componentLoader);
return setCompression(compressor);
}
@ -173,8 +172,8 @@ public class GrpcExporterBuilder<T extends Marshaler> {
return this;
}
public GrpcExporterBuilder<T> setServiceClassLoader(ClassLoader serviceClassLoader) {
this.serviceClassLoader = serviceClassLoader;
public GrpcExporterBuilder<T> setComponentLoader(ComponentLoader componentLoader) {
this.componentLoader = componentLoader;
return this;
}
@ -283,7 +282,7 @@ public class GrpcExporterBuilder<T extends Marshaler> {
if (grpcChannel != null) {
joiner.add("grpcChannel=" + grpcChannel);
}
joiner.add("serviceClassLoader=" + serviceClassLoader);
joiner.add("componentLoader=" + componentLoader);
if (executorService != null) {
joiner.add("executorService=" + executorService);
}
@ -317,8 +316,7 @@ public class GrpcExporterBuilder<T extends Marshaler> {
*/
private GrpcSenderProvider resolveGrpcSenderProvider() {
Map<String, GrpcSenderProvider> grpcSenderProviders = new HashMap<>();
for (GrpcSenderProvider spi :
ServiceLoader.load(GrpcSenderProvider.class, serviceClassLoader)) {
for (GrpcSenderProvider spi : componentLoader.load(GrpcSenderProvider.class)) {
grpcSenderProviders.put(spi.getClass().getName(), spi);
}

View File

@ -5,14 +5,14 @@
package io.opentelemetry.exporter.internal.http;
import static java.util.Objects.requireNonNull;
import io.opentelemetry.api.GlobalOpenTelemetry;
import io.opentelemetry.api.internal.ConfigUtil;
import io.opentelemetry.api.metrics.MeterProvider;
import io.opentelemetry.common.ComponentLoader;
import io.opentelemetry.exporter.internal.ExporterBuilderUtil;
import io.opentelemetry.exporter.internal.TlsConfigHelper;
import io.opentelemetry.exporter.internal.compression.Compressor;
import io.opentelemetry.exporter.internal.compression.CompressorProvider;
import io.opentelemetry.exporter.internal.compression.CompressorUtil;
import io.opentelemetry.exporter.internal.marshal.Marshaler;
import io.opentelemetry.sdk.common.InternalTelemetryVersion;
@ -27,7 +27,6 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.ServiceLoader;
import java.util.StringJoiner;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeUnit;
@ -67,7 +66,8 @@ public final class HttpExporterBuilder<T extends Marshaler> {
@Nullable private RetryPolicy retryPolicy = RetryPolicy.getDefault();
private Supplier<MeterProvider> meterProviderSupplier = GlobalOpenTelemetry::getMeterProvider;
private InternalTelemetryVersion internalTelemetryVersion = InternalTelemetryVersion.LEGACY;
private ClassLoader serviceClassLoader = HttpExporterBuilder.class.getClassLoader();
private ComponentLoader componentLoader =
ComponentLoader.forClassLoader(HttpExporterBuilder.class.getClassLoader());
@Nullable private ExecutorService executorService;
public HttpExporterBuilder(
@ -104,9 +104,8 @@ public final class HttpExporterBuilder<T extends Marshaler> {
* methods is available by implementing {@link Compressor} and {@link CompressorProvider}.
*/
public HttpExporterBuilder<T> setCompression(String compressionMethod) {
requireNonNull(compressionMethod, "compressionMethod");
Compressor compressor =
CompressorUtil.validateAndResolveCompressor(compressionMethod, serviceClassLoader);
CompressorUtil.validateAndResolveCompressor(compressionMethod, componentLoader);
return setCompression(compressor);
}
@ -158,8 +157,8 @@ public final class HttpExporterBuilder<T extends Marshaler> {
return this;
}
public HttpExporterBuilder<T> setServiceClassLoader(ClassLoader serviceClassLoader) {
this.serviceClassLoader = serviceClassLoader;
public HttpExporterBuilder<T> setComponentLoader(ComponentLoader componentLoader) {
this.componentLoader = componentLoader;
return this;
}
@ -280,7 +279,7 @@ public final class HttpExporterBuilder<T extends Marshaler> {
if (retryPolicy != null) {
joiner.add("retryPolicy=" + retryPolicy);
}
joiner.add("serviceClassLoader=" + serviceClassLoader);
joiner.add("componentLoader=" + componentLoader);
if (executorService != null) {
joiner.add("executorService=" + executorService);
}
@ -314,8 +313,7 @@ public final class HttpExporterBuilder<T extends Marshaler> {
*/
private HttpSenderProvider resolveHttpSenderProvider() {
Map<String, HttpSenderProvider> httpSenderProviders = new HashMap<>();
for (HttpSenderProvider spi :
ServiceLoader.load(HttpSenderProvider.class, serviceClassLoader)) {
for (HttpSenderProvider spi : componentLoader.load(HttpSenderProvider.class)) {
httpSenderProviders.put(spi.getClass().getName(), spi);
}

View File

@ -8,12 +8,16 @@ package io.opentelemetry.exporter.internal.compression;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import io.opentelemetry.common.ComponentLoader;
import java.net.URL;
import java.net.URLClassLoader;
import org.junit.jupiter.api.Test;
class CompressorUtilTest {
private final ComponentLoader componentLoader =
ComponentLoader.forClassLoader(CompressorUtilTest.class.getClassLoader());
@Test
void validateAndResolveCompressor_none() {
assertThat(CompressorUtil.validateAndResolveCompressor("none")).isNull();
@ -34,21 +38,19 @@ class CompressorUtilTest {
@Test
void validateAndResolveCompressor_withClassLoader_none() {
ClassLoader classLoader = CompressorUtilTest.class.getClassLoader();
assertThat(CompressorUtil.validateAndResolveCompressor("none", classLoader)).isNull();
assertThat(CompressorUtil.validateAndResolveCompressor("none", componentLoader)).isNull();
}
@Test
void validateAndResolveCompressor_withClassLoader_gzip() {
ClassLoader classLoader = CompressorUtilTest.class.getClassLoader();
assertThat(CompressorUtil.validateAndResolveCompressor("gzip", classLoader))
assertThat(CompressorUtil.validateAndResolveCompressor("gzip", componentLoader))
.isEqualTo(GzipCompressor.getInstance());
}
@Test
void validateAndResolveCompressor_withClassLoader_invalid() {
ClassLoader classLoader = CompressorUtilTest.class.getClassLoader();
assertThatThrownBy(() -> CompressorUtil.validateAndResolveCompressor("invalid", classLoader))
assertThatThrownBy(
() -> CompressorUtil.validateAndResolveCompressor("invalid", componentLoader))
.isInstanceOf(IllegalArgumentException.class)
.hasMessageContaining("Unsupported compressionMethod");
}
@ -56,18 +58,19 @@ class CompressorUtilTest {
@Test
void validateAndResolveCompressor_emptyClassLoader() {
// Create a class loader that cannot load CompressorProvider services
ClassLoader emptyClassLoader = new URLClassLoader(new URL[0], null);
ComponentLoader emptyComponentLoader =
ComponentLoader.forClassLoader(new URLClassLoader(new URL[0], null));
// Gzip should still work because it's hardcoded
assertThat(CompressorUtil.validateAndResolveCompressor("gzip", emptyClassLoader))
assertThat(CompressorUtil.validateAndResolveCompressor("gzip", emptyComponentLoader))
.isEqualTo(GzipCompressor.getInstance());
// None should still work because it doesn't require loading services
assertThat(CompressorUtil.validateAndResolveCompressor("none", emptyClassLoader)).isNull();
assertThat(CompressorUtil.validateAndResolveCompressor("none", emptyComponentLoader)).isNull();
// Any SPI-based compressor should not be available
assertThatThrownBy(
() -> CompressorUtil.validateAndResolveCompressor("base64", emptyClassLoader))
() -> CompressorUtil.validateAndResolveCompressor("base64", emptyComponentLoader))
.isInstanceOf(IllegalArgumentException.class)
.hasMessageContaining("Unsupported compressionMethod");
}
@ -76,13 +79,9 @@ class CompressorUtilTest {
void validateAndResolveCompressor_delegatesCorrectly() {
// Test that single-parameter method delegates to two-parameter method
assertThat(CompressorUtil.validateAndResolveCompressor("gzip"))
.isEqualTo(
CompressorUtil.validateAndResolveCompressor(
"gzip", CompressorUtil.class.getClassLoader()));
.isEqualTo(CompressorUtil.validateAndResolveCompressor("gzip", componentLoader));
assertThat(CompressorUtil.validateAndResolveCompressor("none"))
.isEqualTo(
CompressorUtil.validateAndResolveCompressor(
"none", CompressorUtil.class.getClassLoader()));
.isEqualTo(CompressorUtil.validateAndResolveCompressor("none", componentLoader));
}
}

View File

@ -8,6 +8,7 @@ package io.opentelemetry.exporter.internal.grpc;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import io.opentelemetry.common.ComponentLoader;
import io.opentelemetry.exporter.internal.compression.Compressor;
import io.opentelemetry.exporter.internal.compression.GzipCompressor;
import io.opentelemetry.exporter.internal.marshal.Marshaler;
@ -83,8 +84,9 @@ class GrpcExporterBuilderTest {
@Test
void compressionString_usesServiceClassLoader() {
// Create a class loader that cannot load CompressorProvider services
ClassLoader emptyClassLoader = new URLClassLoader(new URL[0], null);
builder.setServiceClassLoader(emptyClassLoader);
ComponentLoader emptyComponentLoader =
ComponentLoader.forClassLoader(new URLClassLoader(new URL[0], null));
builder.setComponentLoader(emptyComponentLoader);
// This should still work because gzip compressor is hardcoded
builder.setCompression("gzip");

View File

@ -8,6 +8,7 @@ package io.opentelemetry.exporter.internal.http;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import io.opentelemetry.common.ComponentLoader;
import io.opentelemetry.exporter.internal.compression.Compressor;
import io.opentelemetry.exporter.internal.compression.GzipCompressor;
import io.opentelemetry.exporter.internal.marshal.Marshaler;
@ -78,8 +79,9 @@ class HttpExporterBuilderTest {
@Test
void compressionString_usesServiceClassLoader() {
// Create a class loader that cannot load CompressorProvider services
ClassLoader emptyClassLoader = new URLClassLoader(new URL[0], null);
builder.setServiceClassLoader(emptyClassLoader);
ComponentLoader emptyComponentLoader =
ComponentLoader.forClassLoader(new URLClassLoader(new URL[0], null));
builder.setComponentLoader(emptyComponentLoader);
// This should still work because gzip compressor is hardcoded
builder.setCompression("gzip");

View File

@ -9,11 +9,10 @@ import static java.util.Collections.emptyMap;
import static java.util.Collections.singletonMap;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
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;
@ -29,10 +28,10 @@ import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ServiceLoader;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
import javax.annotation.Nullable;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
@ -306,7 +305,7 @@ abstract class AbstractOtlpStdoutExporterTest<T> {
@Test
void componentProviderConfig() {
DeclarativeConfigProperties properties = mock(DeclarativeConfigProperties.class);
DeclarativeConfigProperties properties = spy(DeclarativeConfigProperties.empty());
T exporter = exporterFromComponentProvider(properties);
assertThat(exporter).extracting("wrapperJsonObject").isEqualTo(true);
@ -330,22 +329,22 @@ abstract class AbstractOtlpStdoutExporterTest<T> {
@SuppressWarnings("unchecked")
protected T exporterFromComponentProvider(DeclarativeConfigProperties properties) {
return (T)
((ComponentProvider<?>)
loadSpi(ComponentProvider.class)
.filter(
p -> {
ComponentProvider<?> c = (ComponentProvider<?>) p;
return "otlp_file/development".equals(c.getName())
&& c.getType().equals(componentProviderType);
})
.findFirst()
.orElseThrow(() -> new IllegalStateException("No provider found")))
StreamSupport.stream(
properties.getComponentLoader().load(ComponentProvider.class).spliterator(), false)
.filter(
p -> {
ComponentProvider<?> c = p;
return "otlp_file/development".equals(c.getName())
&& c.getType().equals(componentProviderType);
})
.findFirst()
.orElseThrow(() -> new IllegalStateException("No provider found"))
.create(properties);
}
@SuppressWarnings("unchecked")
protected T exporterFromProvider(ConfigProperties config) {
Object provider = loadProvider();
Object provider = loadProvider(config);
try {
return (T)
@ -358,8 +357,9 @@ abstract class AbstractOtlpStdoutExporterTest<T> {
}
}
private Object loadProvider() {
return loadSpi(providerClass)
private Object loadProvider(ConfigProperties config) {
return StreamSupport.stream(
config.getComponentLoader().load(providerClass).spliterator(), false)
.filter(
p -> {
try {
@ -372,8 +372,4 @@ abstract class AbstractOtlpStdoutExporterTest<T> {
.findFirst()
.orElseThrow(() -> new IllegalStateException("No provider found"));
}
protected static Stream<?> loadSpi(Class<?> type) {
return Streams.stream(ServiceLoader.load(type, type.getClassLoader()).iterator());
}
}

View File

@ -7,7 +7,7 @@ package io.opentelemetry.exporter.logging.otlp;
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatCode;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
import com.google.common.collect.ImmutableMap;
@ -81,7 +81,7 @@ class OtlpStdoutMetricExporterTest
@Test
void componentProviderMetricConfig() {
DeclarativeConfigProperties properties = mock(DeclarativeConfigProperties.class);
DeclarativeConfigProperties properties = spy(DeclarativeConfigProperties.empty());
when(properties.getString("temporality_preference")).thenReturn("DELTA");
when(properties.getString("default_histogram_aggregation"))
.thenReturn("BASE2_EXPONENTIAL_BUCKET_HISTOGRAM");

View File

@ -10,6 +10,7 @@ import static java.util.Objects.requireNonNull;
import io.opentelemetry.api.GlobalOpenTelemetry;
import io.opentelemetry.api.metrics.MeterProvider;
import io.opentelemetry.common.ComponentLoader;
import io.opentelemetry.exporter.internal.compression.Compressor;
import io.opentelemetry.exporter.internal.http.HttpExporterBuilder;
import io.opentelemetry.exporter.internal.marshal.Marshaler;
@ -114,6 +115,7 @@ public final class OtlpHttpLogRecordExporterBuilder {
* supported by providing custom {@link Compressor} implementations via the service loader.
*/
public OtlpHttpLogRecordExporterBuilder setCompression(String compressionMethod) {
requireNonNull(compressionMethod, "compressionMethod");
delegate.setCompression(compressionMethod);
return this;
}
@ -241,13 +243,20 @@ public final class OtlpHttpLogRecordExporterBuilder {
}
/**
* Set the {@link ClassLoader} used to load the sender API.
* Set the {@link ClassLoader} used to load the sender API. Variant of {@link
* #setComponentLoader(ComponentLoader)}.
*
* @since 1.48.0
*/
public OtlpHttpLogRecordExporterBuilder setServiceClassLoader(ClassLoader serviceClassLoader) {
requireNonNull(serviceClassLoader, "serviceClassLoader");
delegate.setServiceClassLoader(serviceClassLoader);
return setComponentLoader(ComponentLoader.forClassLoader(serviceClassLoader));
}
/** Set the {@link ComponentLoader} used to load the sender API. */
public OtlpHttpLogRecordExporterBuilder setComponentLoader(ComponentLoader componentLoader) {
requireNonNull(componentLoader, "componentLoader");
delegate.setComponentLoader(componentLoader);
return this;
}

View File

@ -10,6 +10,7 @@ import static java.util.Objects.requireNonNull;
import io.opentelemetry.api.GlobalOpenTelemetry;
import io.opentelemetry.api.metrics.MeterProvider;
import io.opentelemetry.common.ComponentLoader;
import io.opentelemetry.exporter.internal.compression.Compressor;
import io.opentelemetry.exporter.internal.http.HttpExporterBuilder;
import io.opentelemetry.exporter.internal.marshal.Marshaler;
@ -301,13 +302,20 @@ public final class OtlpHttpMetricExporterBuilder {
}
/**
* Set the {@link ClassLoader} used to load the sender API.
* Set the {@link ClassLoader} used to load the sender API. Variant of {@link
* #setComponentLoader(ComponentLoader)}.
*
* @since 1.48.0
*/
public OtlpHttpMetricExporterBuilder setServiceClassLoader(ClassLoader serviceClassLoader) {
requireNonNull(serviceClassLoader, "serviceClassLoader");
delegate.setServiceClassLoader(serviceClassLoader);
return setComponentLoader(ComponentLoader.forClassLoader(serviceClassLoader));
}
/** Set the {@link ComponentLoader} used to load the sender API. */
public OtlpHttpMetricExporterBuilder setComponentLoader(ComponentLoader componentLoader) {
requireNonNull(componentLoader, "componentLoader");
delegate.setComponentLoader(componentLoader);
return this;
}

View File

@ -10,6 +10,7 @@ import static java.util.Objects.requireNonNull;
import io.opentelemetry.api.GlobalOpenTelemetry;
import io.opentelemetry.api.metrics.MeterProvider;
import io.opentelemetry.common.ComponentLoader;
import io.opentelemetry.exporter.internal.compression.Compressor;
import io.opentelemetry.exporter.internal.http.HttpExporterBuilder;
import io.opentelemetry.exporter.internal.marshal.Marshaler;
@ -243,13 +244,20 @@ public final class OtlpHttpSpanExporterBuilder {
}
/**
* Set the {@link ClassLoader} used to load the sender API.
* Set the {@link ClassLoader} used to load the sender API. Variant of {@link
* #setComponentLoader(ComponentLoader)}.
*
* @since 1.48.0
*/
public OtlpHttpSpanExporterBuilder setServiceClassLoader(ClassLoader serviceClassLoader) {
requireNonNull(serviceClassLoader, "serviceClassLoader");
delegate.setServiceClassLoader(serviceClassLoader);
return setComponentLoader(ComponentLoader.forClassLoader(serviceClassLoader));
}
/** Set the {@link ComponentLoader} used to load the sender API. */
public OtlpHttpSpanExporterBuilder setComponentLoader(ComponentLoader componentLoader) {
requireNonNull(componentLoader, "componentLoader");
delegate.setComponentLoader(componentLoader);
return this;
}

View File

@ -5,6 +5,7 @@
package io.opentelemetry.exporter.otlp.internal;
import io.opentelemetry.common.ComponentLoader;
import io.opentelemetry.exporter.internal.ExporterBuilderUtil;
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
import io.opentelemetry.sdk.autoconfigure.spi.ConfigurationException;
@ -53,6 +54,7 @@ public final class OtlpConfigUtil {
public static void configureOtlpExporterBuilder(
String dataType,
ConfigProperties config,
Consumer<ComponentLoader> setComponentLoader,
Consumer<String> setEndpoint,
BiConsumer<String, String> addHeader,
Consumer<String> setCompression,
@ -61,6 +63,8 @@ public final class OtlpConfigUtil {
BiConsumer<byte[], byte[]> setClientTls,
Consumer<RetryPolicy> setRetryPolicy,
Consumer<MemoryMode> setMemoryMode) {
setComponentLoader.accept(config.getComponentLoader());
String protocol = getOtlpProtocol(dataType, config);
boolean isHttpProtobuf = protocol.equals(PROTOCOL_HTTP_PROTOBUF);
URL endpoint =

View File

@ -11,6 +11,7 @@ import static io.opentelemetry.exporter.otlp.internal.OtlpConfigUtil.readFileByt
import static io.opentelemetry.exporter.otlp.internal.OtlpConfigUtil.validateEndpoint;
import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties;
import io.opentelemetry.common.ComponentLoader;
import io.opentelemetry.exporter.internal.IncubatingExporterBuilderUtil;
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
import io.opentelemetry.sdk.autoconfigure.spi.ConfigurationException;
@ -47,6 +48,7 @@ public final class OtlpDeclarativeConfigUtil {
public static void configureOtlpExporterBuilder(
String dataType,
DeclarativeConfigProperties config,
Consumer<ComponentLoader> setComponentLoader,
Consumer<String> setEndpoint,
BiConsumer<String, String> addHeader,
Consumer<String> setCompression,
@ -56,6 +58,8 @@ public final class OtlpDeclarativeConfigUtil {
Consumer<RetryPolicy> setRetryPolicy,
Consumer<MemoryMode> setMemoryMode,
boolean isHttpProtobuf) {
setComponentLoader.accept(config.getComponentLoader());
URL endpoint = validateEndpoint(config.getString("endpoint"), isHttpProtobuf);
if (endpoint != null) {
setEndpoint.accept(endpoint.toString());

View File

@ -39,6 +39,7 @@ public class OtlpGrpcLogRecordExporterComponentProvider
OtlpDeclarativeConfigUtil.configureOtlpExporterBuilder(
DATA_TYPE_LOGS,
config,
builder::setComponentLoader,
builder::setEndpoint,
builder::addHeader,
builder::setCompression,

View File

@ -39,6 +39,7 @@ public class OtlpGrpcMetricExporterComponentProvider implements ComponentProvide
OtlpDeclarativeConfigUtil.configureOtlpExporterBuilder(
DATA_TYPE_METRICS,
config,
builder::setComponentLoader,
builder::setEndpoint,
builder::addHeader,
builder::setCompression,

View File

@ -38,6 +38,7 @@ public class OtlpGrpcSpanExporterComponentProvider implements ComponentProvider<
OtlpDeclarativeConfigUtil.configureOtlpExporterBuilder(
DATA_TYPE_TRACES,
config,
builder::setComponentLoader,
builder::setEndpoint,
builder::addHeader,
builder::setCompression,

View File

@ -39,6 +39,7 @@ public class OtlpHttpLogRecordExporterComponentProvider
OtlpDeclarativeConfigUtil.configureOtlpExporterBuilder(
DATA_TYPE_LOGS,
config,
builder::setComponentLoader,
builder::setEndpoint,
builder::addHeader,
builder::setCompression,

View File

@ -39,6 +39,7 @@ public class OtlpHttpMetricExporterComponentProvider implements ComponentProvide
OtlpDeclarativeConfigUtil.configureOtlpExporterBuilder(
DATA_TYPE_METRICS,
config,
builder::setComponentLoader,
builder::setEndpoint,
builder::addHeader,
builder::setCompression,

View File

@ -38,6 +38,7 @@ public class OtlpHttpSpanExporterComponentProvider implements ComponentProvider<
OtlpDeclarativeConfigUtil.configureOtlpExporterBuilder(
DATA_TYPE_TRACES,
config,
builder::setComponentLoader,
builder::setEndpoint,
builder::addHeader,
builder::setCompression,

View File

@ -45,6 +45,7 @@ public class OtlpLogRecordExporterProvider
OtlpConfigUtil.configureOtlpExporterBuilder(
DATA_TYPE_LOGS,
config,
builder::setComponentLoader,
builder::setEndpoint,
builder::addHeader,
builder::setCompression,
@ -62,6 +63,7 @@ public class OtlpLogRecordExporterProvider
OtlpConfigUtil.configureOtlpExporterBuilder(
DATA_TYPE_LOGS,
config,
builder::setComponentLoader,
builder::setEndpoint,
builder::addHeader,
builder::setCompression,

View File

@ -38,6 +38,7 @@ public class OtlpMetricExporterProvider implements ConfigurableMetricExporterPro
OtlpConfigUtil.configureOtlpExporterBuilder(
DATA_TYPE_METRICS,
config,
builder::setComponentLoader,
builder::setEndpoint,
builder::addHeader,
builder::setCompression,
@ -58,6 +59,7 @@ public class OtlpMetricExporterProvider implements ConfigurableMetricExporterPro
OtlpConfigUtil.configureOtlpExporterBuilder(
DATA_TYPE_METRICS,
config,
builder::setComponentLoader,
builder::setEndpoint,
builder::addHeader,
builder::setCompression,

View File

@ -44,6 +44,7 @@ public class OtlpSpanExporterProvider
OtlpConfigUtil.configureOtlpExporterBuilder(
DATA_TYPE_TRACES,
config,
builder::setComponentLoader,
builder::setEndpoint,
builder::addHeader,
builder::setCompression,
@ -61,6 +62,7 @@ public class OtlpSpanExporterProvider
OtlpConfigUtil.configureOtlpExporterBuilder(
DATA_TYPE_TRACES,
config,
builder::setComponentLoader,
builder::setEndpoint,
builder::addHeader,
builder::setCompression,

View File

@ -11,6 +11,7 @@ import static java.util.Objects.requireNonNull;
import io.grpc.ManagedChannel;
import io.opentelemetry.api.GlobalOpenTelemetry;
import io.opentelemetry.api.metrics.MeterProvider;
import io.opentelemetry.common.ComponentLoader;
import io.opentelemetry.exporter.internal.compression.Compressor;
import io.opentelemetry.exporter.internal.grpc.GrpcExporterBuilder;
import io.opentelemetry.exporter.internal.marshal.Marshaler;
@ -271,13 +272,20 @@ public final class OtlpGrpcLogRecordExporterBuilder {
}
/**
* Set the {@link ClassLoader} used to load the sender API.
* Set the {@link ClassLoader} used to load the sender API. Variant of {@link
* #setComponentLoader(ComponentLoader)}.
*
* @since 1.48.0
*/
public OtlpGrpcLogRecordExporterBuilder setServiceClassLoader(ClassLoader serviceClassLoader) {
requireNonNull(serviceClassLoader, "serviceClassLoader");
delegate.setServiceClassLoader(serviceClassLoader);
return setComponentLoader(ComponentLoader.forClassLoader(serviceClassLoader));
}
/** Set the {@link ComponentLoader} used to load the sender API. */
public OtlpGrpcLogRecordExporterBuilder setComponentLoader(ComponentLoader componentLoader) {
requireNonNull(componentLoader, "componentLoader");
delegate.setComponentLoader(componentLoader);
return this;
}

View File

@ -11,6 +11,7 @@ import static java.util.Objects.requireNonNull;
import io.grpc.ManagedChannel;
import io.opentelemetry.api.GlobalOpenTelemetry;
import io.opentelemetry.api.metrics.MeterProvider;
import io.opentelemetry.common.ComponentLoader;
import io.opentelemetry.exporter.internal.compression.Compressor;
import io.opentelemetry.exporter.internal.grpc.GrpcExporterBuilder;
import io.opentelemetry.exporter.internal.marshal.Marshaler;
@ -329,13 +330,20 @@ public final class OtlpGrpcMetricExporterBuilder {
}
/**
* Set the {@link ClassLoader} used to load the sender API.
* Set the {@link ClassLoader} used to load the sender API. Variant of {@link
* #setComponentLoader(ComponentLoader)}.
*
* @since 1.48.0
*/
public OtlpGrpcMetricExporterBuilder setServiceClassLoader(ClassLoader serviceClassLoader) {
requireNonNull(serviceClassLoader, "serviceClassLoader");
delegate.setServiceClassLoader(serviceClassLoader);
return setComponentLoader(ComponentLoader.forClassLoader(serviceClassLoader));
}
/** Set the {@link ComponentLoader} used to load the sender API. */
public OtlpGrpcMetricExporterBuilder setComponentLoader(ComponentLoader componentLoader) {
requireNonNull(componentLoader, "componentLoader");
delegate.setComponentLoader(componentLoader);
return this;
}

View File

@ -11,6 +11,7 @@ import static java.util.Objects.requireNonNull;
import io.grpc.ManagedChannel;
import io.opentelemetry.api.GlobalOpenTelemetry;
import io.opentelemetry.api.metrics.MeterProvider;
import io.opentelemetry.common.ComponentLoader;
import io.opentelemetry.exporter.internal.compression.Compressor;
import io.opentelemetry.exporter.internal.grpc.GrpcExporterBuilder;
import io.opentelemetry.exporter.internal.marshal.Marshaler;
@ -268,13 +269,20 @@ public final class OtlpGrpcSpanExporterBuilder {
}
/**
* Set the {@link ClassLoader} used to load the sender API.
* Set the {@link ClassLoader} used to load the sender API. Variant of {@link
* #setComponentLoader(ComponentLoader)}.
*
* @since 1.48.0
*/
public OtlpGrpcSpanExporterBuilder setServiceClassLoader(ClassLoader serviceClassLoader) {
requireNonNull(serviceClassLoader, "serviceClassLoader");
delegate.setServiceClassLoader(serviceClassLoader);
return setComponentLoader(ComponentLoader.forClassLoader(serviceClassLoader));
}
/** Set the {@link ComponentLoader} used to load the sender API. */
public OtlpGrpcSpanExporterBuilder setComponentLoader(ComponentLoader componentLoader) {
requireNonNull(componentLoader, "componentLoader");
delegate.setComponentLoader(componentLoader);
return this;
}

View File

@ -371,6 +371,7 @@ class OtlpConfigUtilTest {
OtlpConfigUtil.configureOtlpExporterBuilder(
dataType,
DefaultConfigProperties.createFromMap(properties),
value -> {},
endpoint::set,
(value1, value2) -> {},
value -> {},

View File

@ -122,6 +122,7 @@ class OtlpLogRecordExporterProviderTest {
provider.createExporter(DefaultConfigProperties.createFromMap(Collections.emptyMap()))) {
assertThat(exporter).isInstanceOf(OtlpGrpcLogRecordExporter.class);
verify(grpcBuilder, times(1)).build();
verify(grpcBuilder).setComponentLoader(any());
verify(grpcBuilder, never()).setEndpoint(any());
verify(grpcBuilder, never()).addHeader(any(), any());
verify(grpcBuilder, never()).setCompression(any());
@ -154,6 +155,7 @@ class OtlpLogRecordExporterProviderTest {
provider.createExporter(DefaultConfigProperties.createFromMap(config))) {
assertThat(exporter).isInstanceOf(OtlpGrpcLogRecordExporter.class);
verify(grpcBuilder, times(1)).build();
verify(grpcBuilder).setComponentLoader(any());
verify(grpcBuilder).setEndpoint("https://localhost:443/");
verify(grpcBuilder).addHeader("header-key", "header-value");
verify(grpcBuilder).setCompression("gzip");
@ -189,6 +191,7 @@ class OtlpLogRecordExporterProviderTest {
provider.createExporter(DefaultConfigProperties.createFromMap(config))) {
assertThat(exporter).isInstanceOf(OtlpGrpcLogRecordExporter.class);
verify(grpcBuilder, times(1)).build();
verify(grpcBuilder).setComponentLoader(any());
verify(grpcBuilder).setEndpoint("https://localhost:443/");
verify(grpcBuilder).addHeader("header-key", "header-value");
verify(grpcBuilder).setCompression("gzip");

View File

@ -121,6 +121,7 @@ class OtlpMetricExporterProviderTest {
provider.createExporter(DefaultConfigProperties.createFromMap(Collections.emptyMap()))) {
assertThat(exporter).isInstanceOf(OtlpGrpcMetricExporter.class);
verify(grpcBuilder, times(1)).build();
verify(grpcBuilder).setComponentLoader(any());
verify(grpcBuilder, never()).setEndpoint(any());
verify(grpcBuilder, never()).addHeader(any(), any());
verify(grpcBuilder, never()).setCompression(any());
@ -149,6 +150,7 @@ class OtlpMetricExporterProviderTest {
provider.createExporter(DefaultConfigProperties.createFromMap(config))) {
assertThat(exporter).isInstanceOf(OtlpGrpcMetricExporter.class);
verify(grpcBuilder, times(1)).build();
verify(grpcBuilder).setComponentLoader(any());
verify(grpcBuilder).setEndpoint("https://localhost:443/");
verify(grpcBuilder).addHeader("header-key", "header-value");
verify(grpcBuilder).setCompression("gzip");
@ -184,6 +186,7 @@ class OtlpMetricExporterProviderTest {
provider.createExporter(DefaultConfigProperties.createFromMap(config))) {
assertThat(exporter).isInstanceOf(OtlpGrpcMetricExporter.class);
verify(grpcBuilder, times(1)).build();
verify(grpcBuilder).setComponentLoader(any());
verify(grpcBuilder).setEndpoint("https://localhost:443/");
verify(grpcBuilder).addHeader("header-key", "header-value");
verify(grpcBuilder).setCompression("gzip");
@ -205,6 +208,7 @@ class OtlpMetricExporterProviderTest {
"otel.exporter.otlp.metrics.protocol", "http/protobuf")))) {
assertThat(exporter).isInstanceOf(OtlpHttpMetricExporter.class);
verify(httpBuilder, times(1)).build();
verify(httpBuilder).setComponentLoader(any());
verify(httpBuilder, never()).setEndpoint(any());
verify(httpBuilder, never()).addHeader(any(), any());
verify(httpBuilder, never()).setCompression(any());
@ -234,6 +238,7 @@ class OtlpMetricExporterProviderTest {
provider.createExporter(DefaultConfigProperties.createFromMap(config))) {
assertThat(exporter).isInstanceOf(OtlpHttpMetricExporter.class);
verify(httpBuilder, times(1)).build();
verify(httpBuilder).setComponentLoader(any());
verify(httpBuilder).setEndpoint("https://localhost:443/v1/metrics");
verify(httpBuilder).addHeader("header-key", "header-value");
verify(httpBuilder).setCompression("gzip");
@ -271,6 +276,7 @@ class OtlpMetricExporterProviderTest {
provider.createExporter(DefaultConfigProperties.createFromMap(config))) {
assertThat(exporter).isInstanceOf(OtlpHttpMetricExporter.class);
verify(httpBuilder, times(1)).build();
verify(httpBuilder).setComponentLoader(any());
verify(httpBuilder).setEndpoint("https://localhost:443/v1/metrics");
verify(httpBuilder).addHeader("header-key", "header-value");
verify(httpBuilder).setCompression("gzip");

View File

@ -123,6 +123,7 @@ class OtlpSpanExporterProviderTest {
provider.createExporter(DefaultConfigProperties.createFromMap(Collections.emptyMap()))) {
assertThat(exporter).isInstanceOf(OtlpGrpcSpanExporter.class);
verify(grpcBuilder, times(1)).build();
verify(grpcBuilder).setComponentLoader(any());
verify(grpcBuilder, never()).setEndpoint(any());
verify(grpcBuilder, never()).addHeader(any(), any());
verify(grpcBuilder, never()).setCompression(any());
@ -155,6 +156,7 @@ class OtlpSpanExporterProviderTest {
provider.createExporter(DefaultConfigProperties.createFromMap(config))) {
assertThat(exporter).isInstanceOf(OtlpGrpcSpanExporter.class);
verify(grpcBuilder, times(1)).build();
verify(grpcBuilder).setComponentLoader(any());
verify(grpcBuilder).setEndpoint("https://localhost:443/");
verify(grpcBuilder).addHeader("header-key", "header-value");
verify(grpcBuilder).setCompression("gzip");
@ -190,6 +192,7 @@ class OtlpSpanExporterProviderTest {
provider.createExporter(DefaultConfigProperties.createFromMap(config))) {
assertThat(exporter).isInstanceOf(OtlpGrpcSpanExporter.class);
verify(grpcBuilder, times(1)).build();
verify(grpcBuilder).setComponentLoader(any());
verify(grpcBuilder).setEndpoint("https://localhost:443/");
verify(grpcBuilder).addHeader("header-key", "header-value");
verify(grpcBuilder).setCompression("gzip");
@ -210,6 +213,7 @@ class OtlpSpanExporterProviderTest {
Collections.singletonMap("otel.exporter.otlp.traces.protocol", "http/protobuf")))) {
assertThat(exporter).isInstanceOf(OtlpHttpSpanExporter.class);
verify(httpBuilder, times(1)).build();
verify(httpBuilder).setComponentLoader(any());
verify(httpBuilder, never()).setEndpoint(any());
verify(httpBuilder, never()).addHeader(any(), any());
verify(httpBuilder, never()).setCompression(any());
@ -240,6 +244,7 @@ class OtlpSpanExporterProviderTest {
provider.createExporter(DefaultConfigProperties.createFromMap(config))) {
assertThat(exporter).isInstanceOf(OtlpHttpSpanExporter.class);
verify(httpBuilder, times(1)).build();
verify(httpBuilder).setComponentLoader(any());
verify(httpBuilder).setEndpoint("https://localhost:443/v1/traces");
verify(httpBuilder).addHeader("header-key1", "header value1");
verify(httpBuilder).addHeader("header-key2", "header value2");
@ -279,6 +284,7 @@ class OtlpSpanExporterProviderTest {
provider.createExporter(DefaultConfigProperties.createFromMap(config))) {
assertThat(exporter).isInstanceOf(OtlpHttpSpanExporter.class);
verify(httpBuilder, times(1)).build();
verify(httpBuilder).setComponentLoader(any());
verify(httpBuilder).setEndpoint("https://localhost:443/v1/traces");
verify(httpBuilder).addHeader("header-key", "header-value");
verify(httpBuilder).setCompression("gzip");

View File

@ -10,6 +10,7 @@ import static java.util.Objects.requireNonNull;
import io.grpc.ManagedChannel;
import io.opentelemetry.api.metrics.MeterProvider;
import io.opentelemetry.common.ComponentLoader;
import io.opentelemetry.exporter.internal.compression.Compressor;
import io.opentelemetry.exporter.internal.grpc.GrpcExporterBuilder;
import io.opentelemetry.exporter.internal.marshal.Marshaler;
@ -201,10 +202,19 @@ public final class OtlpGrpcProfilesExporterBuilder {
return this;
}
/** Set the {@link ClassLoader} used to load the sender API. */
/**
* Set the {@link ClassLoader} used to load the sender API. Variant of {@link
* #setComponentLoader(ComponentLoader)}.
*/
public OtlpGrpcProfilesExporterBuilder setServiceClassLoader(ClassLoader serviceClassLoader) {
requireNonNull(serviceClassLoader, "serviceClassLoader");
delegate.setServiceClassLoader(serviceClassLoader);
return setComponentLoader(ComponentLoader.forClassLoader(serviceClassLoader));
}
/** Set the {@link ComponentLoader} used to load the sender API. */
public OtlpGrpcProfilesExporterBuilder setComponentLoader(ComponentLoader componentLoader) {
requireNonNull(componentLoader, "componentLoader");
delegate.setComponentLoader(componentLoader);
return this;
}

View File

@ -7,6 +7,7 @@ package io.opentelemetry.sdk.autoconfigure.spi;
import static io.opentelemetry.api.internal.ConfigUtil.defaultIfNull;
import io.opentelemetry.common.ComponentLoader;
import java.time.Duration;
import java.util.List;
import java.util.Map;
@ -204,4 +205,9 @@ public interface ConfigProperties {
Map<String, String> value = getMap(name);
return value.isEmpty() ? defaultValue : value;
}
/** Return a {@link ComponentLoader} that should be used to load SPIs. */
default ComponentLoader getComponentLoader() {
return ComponentLoader.forClassLoader(ConfigProperties.class.getClassLoader());
}
}

View File

@ -10,6 +10,7 @@ import static java.util.stream.Collectors.joining;
import io.opentelemetry.api.internal.ConfigUtil;
import io.opentelemetry.api.internal.StringUtils;
import io.opentelemetry.common.ComponentLoader;
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
import io.opentelemetry.sdk.autoconfigure.spi.ConfigurationException;
import java.time.Duration;
@ -38,6 +39,7 @@ import javax.annotation.Nullable;
public final class DefaultConfigProperties implements ConfigProperties {
private final Map<String, String> config;
private final ComponentLoader componentLoader;
/**
* Creates a {@link DefaultConfigProperties} by merging system properties, environment variables,
@ -46,9 +48,10 @@ public final class DefaultConfigProperties implements ConfigProperties {
* <p>Environment variables take priority over {@code defaultProperties}. System properties take
* priority over environment variables.
*/
public static DefaultConfigProperties create(Map<String, String> defaultProperties) {
public static DefaultConfigProperties create(
Map<String, String> defaultProperties, ComponentLoader componentLoader) {
return new DefaultConfigProperties(
ConfigUtil.safeSystemProperties(), System.getenv(), defaultProperties);
ConfigUtil.safeSystemProperties(), System.getenv(), defaultProperties, componentLoader);
}
/**
@ -56,13 +59,18 @@ public final class DefaultConfigProperties implements ConfigProperties {
* properties and environment variables.
*/
public static DefaultConfigProperties createFromMap(Map<String, String> properties) {
return new DefaultConfigProperties(properties, Collections.emptyMap(), Collections.emptyMap());
return new DefaultConfigProperties(
properties,
Collections.emptyMap(),
Collections.emptyMap(),
ComponentLoader.forClassLoader(DefaultConfigProperties.class.getClassLoader()));
}
private DefaultConfigProperties(
Map<?, ?> systemProperties,
Map<String, String> environmentVariables,
Map<String, String> defaultProperties) {
Map<String, String> defaultProperties,
ComponentLoader componentLoader) {
Map<String, String> config = new HashMap<>();
defaultProperties.forEach(
(name, value) -> config.put(ConfigUtil.normalizePropertyKey(name), value));
@ -73,6 +81,7 @@ public final class DefaultConfigProperties implements ConfigProperties {
config.put(ConfigUtil.normalizePropertyKey(key.toString()), value.toString()));
this.config = config;
this.componentLoader = componentLoader;
}
private DefaultConfigProperties(
@ -82,6 +91,7 @@ public final class DefaultConfigProperties implements ConfigProperties {
overrides.forEach((name, value) -> config.put(ConfigUtil.normalizePropertyKey(name), value));
this.config = config;
this.componentLoader = previousProperties.componentLoader;
}
@Override
@ -233,6 +243,11 @@ public final class DefaultConfigProperties implements ConfigProperties {
Map.Entry::getKey, Map.Entry::getValue, (first, next) -> next, LinkedHashMap::new));
}
@Override
public ComponentLoader getComponentLoader() {
return componentLoader;
}
/**
* Return a new {@link DefaultConfigProperties} by overriding the {@code previousProperties} with
* the {@code overrides}.

View File

@ -11,6 +11,7 @@ import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.assertj.core.api.Assertions.entry;
import io.opentelemetry.common.ComponentLoader;
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
import io.opentelemetry.sdk.autoconfigure.spi.ConfigurationException;
import java.time.Duration;
@ -22,6 +23,9 @@ import org.junit.jupiter.api.Test;
class ConfigPropertiesTest {
private static final ComponentLoader COMPONENT_LOADER =
ComponentLoader.forClassLoader(ConfigPropertiesTest.class.getClassLoader());
@Test
void allValid() {
Map<String, String> properties = makeTestProps();
@ -246,7 +250,7 @@ class ConfigPropertiesTest {
expectedMap.put("bear", "growl");
Map<String, String> map = makeTestProps();
ConfigProperties properties = DefaultConfigProperties.create(map);
ConfigProperties properties = DefaultConfigProperties.create(map, COMPONENT_LOADER);
assertThat(properties.getBoolean("test.boolean", false)).isTrue();
assertThat(properties.getString("test.string", "nah")).isEqualTo("str");
assertThat(properties.getDouble("test.double", 65.535)).isEqualTo(5.4);
@ -260,7 +264,7 @@ class ConfigPropertiesTest {
@Test
void defaultMethodsFallBack() {
ConfigProperties properties = DefaultConfigProperties.create(emptyMap());
ConfigProperties properties = DefaultConfigProperties.create(emptyMap(), COMPONENT_LOADER);
assertThat(properties.getBoolean("foo", true)).isTrue();
assertThat(properties.getString("foo", "bar")).isEqualTo("bar");
assertThat(properties.getDouble("foo", 65.535)).isEqualTo(65.535);
@ -271,7 +275,7 @@ class ConfigPropertiesTest {
@Test
void defaultCollectionTypes() {
ConfigProperties properties = DefaultConfigProperties.create(emptyMap());
ConfigProperties properties = DefaultConfigProperties.create(emptyMap(), COMPONENT_LOADER);
assertThat(properties.getList("foo", Arrays.asList("1", "2", "3")))
.containsExactly("1", "2", "3");
assertThat(properties.getList("foo")).isEmpty();

View File

@ -8,11 +8,11 @@ package io.opentelemetry.sdk.autoconfigure;
import static java.util.Objects.requireNonNull;
import io.opentelemetry.api.GlobalOpenTelemetry;
import io.opentelemetry.common.ComponentLoader;
import io.opentelemetry.context.propagation.ContextPropagators;
import io.opentelemetry.context.propagation.TextMapPropagator;
import io.opentelemetry.sdk.OpenTelemetrySdk;
import io.opentelemetry.sdk.OpenTelemetrySdkBuilder;
import io.opentelemetry.sdk.autoconfigure.internal.ComponentLoader;
import io.opentelemetry.sdk.autoconfigure.internal.SpiHelper;
import io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizer;
import io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizerProvider;
@ -117,7 +117,7 @@ public final class AutoConfiguredOpenTelemetrySdkBuilder implements AutoConfigur
Function.identity();
private ComponentLoader componentLoader =
SpiHelper.serviceComponentLoader(AutoConfiguredOpenTelemetrySdk.class.getClassLoader());
ComponentLoader.forClassLoader(AutoConfiguredOpenTelemetrySdk.class.getClassLoader());
private boolean registerShutdownHook = true;
@ -410,12 +410,12 @@ public final class AutoConfiguredOpenTelemetrySdkBuilder implements AutoConfigur
public AutoConfiguredOpenTelemetrySdkBuilder setServiceClassLoader(
ClassLoader serviceClassLoader) {
requireNonNull(serviceClassLoader, "serviceClassLoader");
this.componentLoader = SpiHelper.serviceComponentLoader(serviceClassLoader);
this.componentLoader = ComponentLoader.forClassLoader(serviceClassLoader);
return this;
}
/** Sets the {@link ComponentLoader} to be used to load SPI implementations. */
AutoConfiguredOpenTelemetrySdkBuilder setComponentLoader(ComponentLoader componentLoader) {
public AutoConfiguredOpenTelemetrySdkBuilder setComponentLoader(ComponentLoader componentLoader) {
requireNonNull(componentLoader, "componentLoader");
this.componentLoader = componentLoader;
return this;
@ -628,7 +628,8 @@ public final class AutoConfiguredOpenTelemetrySdkBuilder implements AutoConfigur
}
private ConfigProperties computeConfigProperties() {
DefaultConfigProperties properties = DefaultConfigProperties.create(propertiesSupplier.get());
DefaultConfigProperties properties =
DefaultConfigProperties.create(propertiesSupplier.get(), componentLoader);
for (Function<ConfigProperties, Map<String, String>> customizer : propertiesCustomizers) {
Map<String, String> overrides = customizer.apply(properties);
properties = properties.withOverrides(overrides);

View File

@ -8,8 +8,8 @@ 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.common.ComponentLoader;
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;
@ -51,8 +51,9 @@ final class IncubatingUtil {
Class<?> sdkConfigProvider =
Class.forName("io.opentelemetry.sdk.extension.incubator.fileconfig.SdkConfigProvider");
Method createFileConfigProvider =
sdkConfigProvider.getMethod("create", openTelemetryConfiguration);
ConfigProvider configProvider = (ConfigProvider) createFileConfigProvider.invoke(null, model);
sdkConfigProvider.getMethod("create", openTelemetryConfiguration, ComponentLoader.class);
ConfigProvider configProvider =
(ConfigProvider) createFileConfigProvider.invoke(null, model, componentLoader);
// Note: can't access file configuration resource without reflection so setting a dummy
// resource
return AutoConfiguredOpenTelemetrySdk.create(

View File

@ -8,6 +8,7 @@ package io.opentelemetry.sdk.autoconfigure;
import io.opentelemetry.api.common.AttributeKey;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.common.AttributesBuilder;
import io.opentelemetry.common.ComponentLoader;
import io.opentelemetry.sdk.autoconfigure.internal.SpiHelper;
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
import io.opentelemetry.sdk.autoconfigure.spi.ConfigurationException;
@ -59,7 +60,10 @@ public final class ResourceConfiguration {
* @return the resource.
*/
public static Resource createEnvironmentResource() {
return createEnvironmentResource(DefaultConfigProperties.create(Collections.emptyMap()));
return createEnvironmentResource(
DefaultConfigProperties.create(
Collections.emptyMap(),
ComponentLoader.forClassLoader(ResourceConfiguration.class.getClassLoader())));
}
/**

View File

@ -59,22 +59,6 @@ public final class AutoConfigureUtil {
}
}
/** Sets the {@link ComponentLoader} to be used in the auto-configuration process. */
public static AutoConfiguredOpenTelemetrySdkBuilder setComponentLoader(
AutoConfiguredOpenTelemetrySdkBuilder builder, ComponentLoader componentLoader) {
try {
Method method =
AutoConfiguredOpenTelemetrySdkBuilder.class.getDeclaredMethod(
"setComponentLoader", ComponentLoader.class);
method.setAccessible(true);
method.invoke(builder, componentLoader);
return builder;
} catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) {
throw new IllegalStateException(
"Error calling setComponentLoader on AutoConfiguredOpenTelemetrySdkBuilder", e);
}
}
/** Sets the {@link ConfigProperties} customizer to be used in the auto-configuration process. */
public static AutoConfiguredOpenTelemetrySdkBuilder setConfigPropertiesCustomizer(
AutoConfiguredOpenTelemetrySdkBuilder builder,

View File

@ -10,14 +10,9 @@ package io.opentelemetry.sdk.autoconfigure.internal;
*
* <p>This class is internal and is hence not for public use. Its APIs are unstable and can change
* at any time.
*
* @deprecated Use {@link io.opentelemetry.common.ComponentLoader} instead
*/
public interface ComponentLoader {
/**
* Load implementations of an SPI.
*
* @param spiClass the SPI class
* @param <T> the SPI type
* @return iterable of SPI implementations
*/
<T> Iterable<T> load(Class<T> spiClass);
}
@Deprecated
// TODO(jack-berg): delete after 1.54.0 release
public interface ComponentLoader extends io.opentelemetry.common.ComponentLoader {}

View File

@ -5,6 +5,7 @@
package io.opentelemetry.sdk.autoconfigure.internal;
import io.opentelemetry.common.ComponentLoader;
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
import io.opentelemetry.sdk.autoconfigure.spi.Ordered;
import io.opentelemetry.sdk.autoconfigure.spi.internal.AutoConfigureListener;
@ -15,7 +16,6 @@ import java.util.HashMap;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import java.util.ServiceLoader;
import java.util.Set;
import java.util.function.BiFunction;
import java.util.function.Function;
@ -37,7 +37,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(serviceComponentLoader(classLoader));
return new SpiHelper(ComponentLoader.forClassLoader(classLoader));
}
/** Create a {@link SpiHelper} which loads SPIs using the {@code componentLoader}. */
@ -45,11 +45,6 @@ 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;
@ -125,17 +120,4 @@ public final class SpiHelper {
public Set<AutoConfigureListener> getListeners() {
return Collections.unmodifiableSet(listeners);
}
private static class ServiceLoaderComponentLoader implements ComponentLoader {
private final ClassLoader classLoader;
private ServiceLoaderComponentLoader(ClassLoader classLoader) {
this.classLoader = classLoader;
}
@Override
public <T> Iterable<T> load(Class<T> spiClass) {
return ServiceLoader.load(spiClass, classLoader);
}
}
}

View File

@ -28,6 +28,7 @@ import io.opentelemetry.api.trace.Span;
import io.opentelemetry.api.trace.SpanId;
import io.opentelemetry.api.trace.TraceId;
import io.opentelemetry.api.trace.propagation.W3CTraceContextPropagator;
import io.opentelemetry.common.ComponentLoader;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.ContextKey;
import io.opentelemetry.context.propagation.TextMapGetter;
@ -35,7 +36,6 @@ import io.opentelemetry.context.propagation.TextMapPropagator;
import io.opentelemetry.internal.testing.slf4j.SuppressLogger;
import io.opentelemetry.sdk.OpenTelemetrySdk;
import io.opentelemetry.sdk.autoconfigure.internal.AutoConfigureUtil;
import io.opentelemetry.sdk.autoconfigure.internal.ComponentLoader;
import io.opentelemetry.sdk.autoconfigure.internal.SpiHelper;
import io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizerProvider;
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
@ -65,6 +65,7 @@ import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Properties;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CountDownLatch;
@ -255,22 +256,28 @@ class AutoConfiguredOpenTelemetrySdkTest {
SpiHelper spiHelper =
SpiHelper.create(AutoConfiguredOpenTelemetrySdkBuilder.class.getClassLoader());
AutoConfigureUtil.setComponentLoader(
builder,
new ComponentLoader() {
@SuppressWarnings("unchecked")
@Override
public <T> Iterable<T> load(Class<T> spiClass) {
if (spiClass.equals(AutoConfigurationCustomizerProvider.class)) {
return Collections.singletonList((T) customizerProvider);
}
return spiHelper.load(spiClass);
}
})
.build();
ComponentLoader componentLoader =
new ComponentLoader() {
@SuppressWarnings("unchecked")
@Override
public <T> Iterable<T> load(Class<T> spiClass) {
if (spiClass.equals(AutoConfigurationCustomizerProvider.class)) {
return Collections.singletonList((T) customizerProvider);
}
return spiHelper.load(spiClass);
}
};
AutoConfiguredOpenTelemetrySdk autoConfiguredOpenTelemetrySdk =
builder.setComponentLoader(componentLoader).build();
assertThat(
Objects.requireNonNull(autoConfiguredOpenTelemetrySdk.getConfig()).getComponentLoader())
.isSameAs(componentLoader);
verify(customizerProvider).customize(any());
verifyNoMoreInteractions(customizerProvider);
autoConfiguredOpenTelemetrySdk.getOpenTelemetrySdk().shutdown().join(10, TimeUnit.SECONDS);
}
@Test

View File

@ -12,6 +12,7 @@ import static java.util.Collections.singletonMap;
import com.google.common.collect.ImmutableMap;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.common.ComponentLoader;
import io.opentelemetry.sdk.autoconfigure.internal.SpiHelper;
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
import io.opentelemetry.sdk.autoconfigure.spi.internal.DefaultConfigProperties;
@ -25,6 +26,9 @@ import org.mockito.junit.jupiter.MockitoExtension;
@ExtendWith(MockitoExtension.class)
class ResourceConfigurationTest {
private static final ComponentLoader componentLoader =
ComponentLoader.forClassLoader(ResourceConfigurationTest.class.getClassLoader());
@Test
void customConfigResourceWithDisabledKeys() {
Map<String, String> props = new HashMap<>();
@ -35,7 +39,7 @@ class ResourceConfigurationTest {
assertThat(
ResourceConfiguration.configureResource(
DefaultConfigProperties.create(props),
DefaultConfigProperties.create(props, componentLoader),
SpiHelper.create(ResourceConfigurationTest.class.getClassLoader()),
(r, c) -> r))
.isEqualTo(

View File

@ -15,6 +15,7 @@ import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import io.opentelemetry.common.ComponentLoader;
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
import io.opentelemetry.sdk.autoconfigure.spi.ResourceProvider;
import io.opentelemetry.sdk.autoconfigure.spi.internal.DefaultConfigProperties;

View File

@ -9,6 +9,7 @@ import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.asser
import io.opentelemetry.api.common.AttributeKey;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.common.ComponentLoader;
import io.opentelemetry.internal.testing.slf4j.SuppressLogger;
import io.opentelemetry.sdk.autoconfigure.internal.SpiHelper;
import io.opentelemetry.sdk.autoconfigure.spi.internal.DefaultConfigProperties;
@ -29,14 +30,15 @@ import org.junit.jupiter.params.provider.MethodSource;
@SuppressLogger(ResourceConfiguration.class)
class ResourceConfigurationTest {
private final SpiHelper spiHelper =
SpiHelper.create(ResourceConfigurationTest.class.getClassLoader());
private final ComponentLoader componentLoader =
ComponentLoader.forClassLoader(ResourceConfigurationTest.class.getClassLoader());
private final SpiHelper spiHelper = SpiHelper.create(componentLoader);
@Test
void configureResource_EmptyClassLoader() {
Attributes attributes =
ResourceConfiguration.configureResource(
DefaultConfigProperties.create(Collections.emptyMap()),
DefaultConfigProperties.create(Collections.emptyMap(), componentLoader),
SpiHelper.create(new URLClassLoader(new URL[0], null)),
(r, c) -> r)
.getAttributes();
@ -66,7 +68,7 @@ class ResourceConfigurationTest {
}
Attributes attributes =
ResourceConfiguration.configureResource(
DefaultConfigProperties.create(config), spiHelper, (r, c) -> r)
DefaultConfigProperties.create(config, componentLoader), spiHelper, (r, c) -> r)
.getAttributes();
attributeAssertion.accept(assertThat(attributes));

View File

@ -7,6 +7,7 @@ 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.assertThat;
import static org.assertj.core.api.Assertions.assertThatCode;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.mockito.ArgumentMatchers.any;
@ -23,6 +24,7 @@ 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.common.ComponentLoader;
import io.opentelemetry.exporter.logging.LoggingSpanExporter;
import io.opentelemetry.internal.testing.CleanupExtension;
import io.opentelemetry.internal.testing.slf4j.SuppressLogger;
@ -40,7 +42,7 @@ 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 java.util.Optional;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
@ -134,17 +136,39 @@ class DeclarativeConfigurationTest {
builder.setConfig(config).build();
cleanup.addCloseable(autoConfiguredOpenTelemetrySdk.getOpenTelemetrySdk());
Assertions.assertThat(autoConfiguredOpenTelemetrySdk.getOpenTelemetrySdk().toString())
assertThat(autoConfiguredOpenTelemetrySdk.getOpenTelemetrySdk().toString())
.isEqualTo(expectedSdk.toString());
// AutoConfiguredOpenTelemetrySdk#getResource() is set to a dummy value when configuring from
// file
Assertions.assertThat(autoConfiguredOpenTelemetrySdk.getResource())
.isEqualTo(Resource.getDefault());
assertThat(autoConfiguredOpenTelemetrySdk.getResource()).isEqualTo(Resource.getDefault());
verify(builder, times(1)).shutdownHook(autoConfiguredOpenTelemetrySdk.getOpenTelemetrySdk());
Assertions.assertThat(Runtime.getRuntime().removeShutdownHook(thread)).isTrue();
assertThat(Runtime.getRuntime().removeShutdownHook(thread)).isTrue();
logCapturer.assertContains("Autoconfiguring from configuration file: " + configFilePath);
}
@Test
void configFile_setComponentLoader() {
ComponentLoader componentLoader =
ComponentLoader.forClassLoader(DeclarativeConfigurationTest.class.getClassLoader());
ConfigProperties config =
DefaultConfigProperties.createFromMap(
Collections.singletonMap("otel.experimental.config.file", configFilePath.toString()));
AutoConfiguredOpenTelemetrySdk autoConfiguredOpenTelemetrySdk =
AutoConfiguredOpenTelemetrySdk.builder()
.setConfig(config)
.setComponentLoader(componentLoader)
.build();
cleanup.addCloseable(autoConfiguredOpenTelemetrySdk.getOpenTelemetrySdk());
assertThat(
Optional.ofNullable(AutoConfigureUtil.getConfigProvider(autoConfiguredOpenTelemetrySdk))
.map(ConfigProvider::getInstrumentationConfig)
.map(DeclarativeConfigProperties::getComponentLoader)
.orElse(null))
.isSameAs(componentLoader);
}
@Test
void configFile_NoShutdownHook() {
ConfigProperties config =
@ -171,9 +195,7 @@ class DeclarativeConfigurationTest {
OpenTelemetrySdk openTelemetrySdk = autoConfiguredOpenTelemetrySdk.getOpenTelemetrySdk();
cleanup.addCloseable(openTelemetrySdk);
Assertions.assertThat(GlobalOpenTelemetry.get())
.extracting("delegate")
.isNotSameAs(openTelemetrySdk);
assertThat(GlobalOpenTelemetry.get()).extracting("delegate").isNotSameAs(openTelemetrySdk);
assertThat(GlobalConfigProvider.get())
.isNotSameAs(autoConfiguredOpenTelemetrySdk.getConfigProvider());
}
@ -189,9 +211,7 @@ class DeclarativeConfigurationTest {
OpenTelemetrySdk openTelemetrySdk = autoConfiguredOpenTelemetrySdk.getOpenTelemetrySdk();
cleanup.addCloseable(openTelemetrySdk);
Assertions.assertThat(GlobalOpenTelemetry.get())
.extracting("delegate")
.isSameAs(openTelemetrySdk);
assertThat(GlobalOpenTelemetry.get()).extracting("delegate").isSameAs(openTelemetrySdk);
assertThat(GlobalConfigProvider.get())
.isSameAs(autoConfiguredOpenTelemetrySdk.getConfigProvider());
}

View File

@ -11,8 +11,8 @@ import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.opentelemetry.api.incubator.config.DeclarativeConfigException;
import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties;
import io.opentelemetry.common.ComponentLoader;
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.internal.ComponentProvider;
import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.OpenTelemetryConfigurationModel;
@ -57,7 +57,7 @@ public final class DeclarativeConfiguration {
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(DeclarativeConfiguration.class.getClassLoader());
ComponentLoader.forClassLoader(DeclarativeConfigProperties.class.getClassLoader());
// Visible for testing
static final ObjectMapper MAPPER;

View File

@ -7,6 +7,7 @@ package io.opentelemetry.sdk.extension.incubator.fileconfig;
import io.opentelemetry.api.incubator.config.ConfigProvider;
import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties;
import io.opentelemetry.common.ComponentLoader;
import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.OpenTelemetryConfigurationModel;
import javax.annotation.Nullable;
@ -15,9 +16,10 @@ public final class SdkConfigProvider implements ConfigProvider {
@Nullable private final DeclarativeConfigProperties instrumentationConfig;
private SdkConfigProvider(OpenTelemetryConfigurationModel model) {
private SdkConfigProvider(
OpenTelemetryConfigurationModel model, ComponentLoader componentLoader) {
DeclarativeConfigProperties configProperties =
DeclarativeConfiguration.toConfigProperties(model);
DeclarativeConfiguration.toConfigProperties(model, componentLoader);
this.instrumentationConfig = configProperties.getStructured("instrumentation/development");
}
@ -28,7 +30,19 @@ public final class SdkConfigProvider implements ConfigProvider {
* @return the {@link SdkConfigProvider}
*/
public static SdkConfigProvider create(OpenTelemetryConfigurationModel model) {
return new SdkConfigProvider(model);
return create(model, ComponentLoader.forClassLoader(SdkConfigProvider.class.getClassLoader()));
}
/**
* Create a {@link SdkConfigProvider} from the {@code model}.
*
* @param model the configuration model
* @param componentLoader the component loader used to load SPIs
* @return the {@link SdkConfigProvider}
*/
public static SdkConfigProvider create(
OpenTelemetryConfigurationModel model, ComponentLoader componentLoader) {
return new SdkConfigProvider(model, componentLoader);
}
@Nullable

View File

@ -38,7 +38,8 @@ public class ServiceResourceDetector implements ComponentProvider<Resource> {
public Resource create(DeclarativeConfigProperties config) {
ResourceBuilder builder = Resource.builder();
ConfigProperties properties = DefaultConfigProperties.create(Collections.emptyMap());
ConfigProperties properties =
DefaultConfigProperties.create(Collections.emptyMap(), config.getComponentLoader());
String serviceName = properties.getString("otel.service.name");
if (serviceName != null) {
builder.put(SERVICE_NAME, serviceName).build();

View File

@ -10,7 +10,7 @@ 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.common.ComponentLoader;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedHashMap;
@ -303,6 +303,7 @@ public final class YamlDeclarativeConfigProperties implements DeclarativeConfigP
}
/** Return the {@link ComponentLoader}. */
@Override
public ComponentLoader getComponentLoader() {
return componentLoader;
}

View File

@ -13,9 +13,9 @@ 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.common.ComponentLoader;
import io.opentelemetry.internal.testing.CleanupExtension;
import io.opentelemetry.sdk.OpenTelemetrySdk;
import io.opentelemetry.sdk.autoconfigure.internal.SpiHelper;
import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.OpenTelemetryConfigurationModel;
import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.SpanProcessorModel;
import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.TracerProviderModel;
@ -161,7 +161,7 @@ class DeclarativeConfigurationCreateTest {
DeclarativeConfiguration.create(
model,
// customizer is TestDeclarativeConfigurationCustomizerProvider
SpiHelper.serviceComponentLoader(
ComponentLoader.forClassLoader(
DeclarativeConfigurationCreateTest.class.getClassLoader()));
assertThat(sdk.toString())
.contains(

View File

@ -28,6 +28,7 @@ include(":api:incubator")
include(":api:testing-internal")
include(":bom")
include(":bom-alpha")
include(":common")
include(":context")
include(":custom-checks")
include(":dependencyManagement")