Make SPI factories instantiate implementations lazily (#4059)
* Make SPI factories instantiate implementations lazily Previously, this would load all SPI factories and then eagerly instantiate each implementation. This would potentially result in implementations that would get instantiated even if they didn't end up getting used. An example was if the AWS contrib jar was included, this would result in an AwsXrayRemoteSampler being instantiated eagerly despite the configuration not using it. This could then result in errors or unused resources depending on the implementation i.e. in the AwsXrayRemoteSampler example it spawns a thread and makes a network request. This change transitions the factories so that they only construct the implementation objects if the configuration specifically asks for it. Note the SPI factories themselves must still must be eagerly loaded to determine which factory names are available. This change applies to all auto-configuration areas following this pattern: Samplers, Metric Exporters, Text Map Propagators and Span Exporters. * Now that instantiating implementations is on-demand (only if used), allow any exceptions to propagate * format fix * Change NamedSpiManager from interface to class Co-authored-by: jack-berg <jberg@newrelic.com>
This commit is contained in:
parent
6aaebdd4b3
commit
586ac11803
|
|
@ -24,8 +24,6 @@ import io.opentelemetry.sdk.metrics.SdkMeterProviderBuilder;
|
||||||
import io.opentelemetry.sdk.metrics.export.MetricExporter;
|
import io.opentelemetry.sdk.metrics.export.MetricExporter;
|
||||||
import io.opentelemetry.sdk.metrics.export.PeriodicMetricReader;
|
import io.opentelemetry.sdk.metrics.export.PeriodicMetricReader;
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.Map;
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
final class MetricExporterConfiguration {
|
final class MetricExporterConfiguration {
|
||||||
|
|
@ -62,15 +60,14 @@ final class MetricExporterConfiguration {
|
||||||
@Nullable
|
@Nullable
|
||||||
static MetricExporter configureSpiExporter(
|
static MetricExporter configureSpiExporter(
|
||||||
String name, ConfigProperties config, ClassLoader serviceClassLoader) {
|
String name, ConfigProperties config, ClassLoader serviceClassLoader) {
|
||||||
Map<String, MetricExporter> spiExporters =
|
NamedSpiManager<MetricExporter> spiExportersManager =
|
||||||
SpiUtil.loadConfigurable(
|
SpiUtil.loadConfigurable(
|
||||||
ConfigurableMetricExporterProvider.class,
|
ConfigurableMetricExporterProvider.class,
|
||||||
Collections.singletonList(name),
|
|
||||||
ConfigurableMetricExporterProvider::getName,
|
ConfigurableMetricExporterProvider::getName,
|
||||||
ConfigurableMetricExporterProvider::createExporter,
|
ConfigurableMetricExporterProvider::createExporter,
|
||||||
config,
|
config,
|
||||||
serviceClassLoader);
|
serviceClassLoader);
|
||||||
return spiExporters.get(name);
|
return spiExportersManager.getByName(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void configureLoggingMetrics(
|
private static void configureLoggingMetrics(
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,48 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.opentelemetry.sdk.autoconfigure;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.concurrent.ConcurrentMap;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
class NamedSpiManager<T> {
|
||||||
|
|
||||||
|
private final Map<String, Supplier<T>> nameToProvider;
|
||||||
|
private final ConcurrentMap<String, Optional<T>> nameToImplementation = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
private NamedSpiManager(Map<String, Supplier<T>> nameToProvider) {
|
||||||
|
this.nameToProvider = nameToProvider;
|
||||||
|
}
|
||||||
|
|
||||||
|
static <T> NamedSpiManager<T> create(Map<String, Supplier<T>> nameToProvider) {
|
||||||
|
return new NamedSpiManager<>(nameToProvider);
|
||||||
|
}
|
||||||
|
|
||||||
|
static <T> NamedSpiManager<T> createEmpty() {
|
||||||
|
return create(Collections.emptyMap());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
T getByName(String name) {
|
||||||
|
return nameToImplementation
|
||||||
|
.computeIfAbsent(name, this::tryLoadImplementationForName)
|
||||||
|
.orElse(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Optional<T> tryLoadImplementationForName(String name) {
|
||||||
|
Supplier<T> provider = nameToProvider.get(name);
|
||||||
|
if (provider == null) {
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
return Optional.ofNullable(provider.get());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -15,7 +15,6 @@ import io.opentelemetry.sdk.autoconfigure.spi.ConfigurationException;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.LinkedHashSet;
|
import java.util.LinkedHashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.function.BiFunction;
|
import java.util.function.BiFunction;
|
||||||
|
|
||||||
|
|
@ -32,10 +31,9 @@ final class PropagatorConfiguration {
|
||||||
requestedPropagators = Arrays.asList("tracecontext", "baggage");
|
requestedPropagators = Arrays.asList("tracecontext", "baggage");
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<String, TextMapPropagator> spiPropagators =
|
NamedSpiManager<TextMapPropagator> spiPropagatorsManager =
|
||||||
SpiUtil.loadConfigurable(
|
SpiUtil.loadConfigurable(
|
||||||
ConfigurablePropagatorProvider.class,
|
ConfigurablePropagatorProvider.class,
|
||||||
requestedPropagators,
|
|
||||||
ConfigurablePropagatorProvider::getName,
|
ConfigurablePropagatorProvider::getName,
|
||||||
ConfigurablePropagatorProvider::getPropagator,
|
ConfigurablePropagatorProvider::getPropagator,
|
||||||
config,
|
config,
|
||||||
|
|
@ -43,14 +41,14 @@ final class PropagatorConfiguration {
|
||||||
|
|
||||||
for (String propagatorName : requestedPropagators) {
|
for (String propagatorName : requestedPropagators) {
|
||||||
propagators.add(
|
propagators.add(
|
||||||
propagatorCustomizer.apply(getPropagator(propagatorName, spiPropagators), config));
|
propagatorCustomizer.apply(getPropagator(propagatorName, spiPropagatorsManager), config));
|
||||||
}
|
}
|
||||||
|
|
||||||
return ContextPropagators.create(TextMapPropagator.composite(propagators));
|
return ContextPropagators.create(TextMapPropagator.composite(propagators));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static TextMapPropagator getPropagator(
|
private static TextMapPropagator getPropagator(
|
||||||
String name, Map<String, TextMapPropagator> spiPropagators) {
|
String name, NamedSpiManager<TextMapPropagator> spiPropagatorsManager) {
|
||||||
if (name.equals("tracecontext")) {
|
if (name.equals("tracecontext")) {
|
||||||
return W3CTraceContextPropagator.getInstance();
|
return W3CTraceContextPropagator.getInstance();
|
||||||
}
|
}
|
||||||
|
|
@ -58,7 +56,7 @@ final class PropagatorConfiguration {
|
||||||
return W3CBaggagePropagator.getInstance();
|
return W3CBaggagePropagator.getInstance();
|
||||||
}
|
}
|
||||||
|
|
||||||
TextMapPropagator spiPropagator = spiPropagators.get(name);
|
TextMapPropagator spiPropagator = spiPropagatorsManager.getByName(name);
|
||||||
if (spiPropagator != null) {
|
if (spiPropagator != null) {
|
||||||
return spiPropagator;
|
return spiPropagator;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -61,10 +61,9 @@ final class SpanExporterConfiguration {
|
||||||
exporterNames = Collections.singleton("otlp");
|
exporterNames = Collections.singleton("otlp");
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<String, SpanExporter> spiExporters =
|
NamedSpiManager<SpanExporter> spiExportersManager =
|
||||||
SpiUtil.loadConfigurable(
|
SpiUtil.loadConfigurable(
|
||||||
ConfigurableSpanExporterProvider.class,
|
ConfigurableSpanExporterProvider.class,
|
||||||
exporterNames,
|
|
||||||
ConfigurableSpanExporterProvider::getName,
|
ConfigurableSpanExporterProvider::getName,
|
||||||
ConfigurableSpanExporterProvider::createExporter,
|
ConfigurableSpanExporterProvider::createExporter,
|
||||||
config,
|
config,
|
||||||
|
|
@ -76,7 +75,7 @@ final class SpanExporterConfiguration {
|
||||||
Function.identity(),
|
Function.identity(),
|
||||||
exporterName ->
|
exporterName ->
|
||||||
spanExporterCustomizer.apply(
|
spanExporterCustomizer.apply(
|
||||||
configureExporter(exporterName, config, spiExporters, meterProvider),
|
configureExporter(exporterName, config, spiExportersManager, meterProvider),
|
||||||
config)));
|
config)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -84,7 +83,7 @@ final class SpanExporterConfiguration {
|
||||||
static SpanExporter configureExporter(
|
static SpanExporter configureExporter(
|
||||||
String name,
|
String name,
|
||||||
ConfigProperties config,
|
ConfigProperties config,
|
||||||
Map<String, SpanExporter> spiExporters,
|
NamedSpiManager<SpanExporter> spiExportersManager,
|
||||||
MeterProvider meterProvider) {
|
MeterProvider meterProvider) {
|
||||||
switch (name) {
|
switch (name) {
|
||||||
case "otlp":
|
case "otlp":
|
||||||
|
|
@ -100,7 +99,7 @@ final class SpanExporterConfiguration {
|
||||||
"opentelemetry-exporter-logging");
|
"opentelemetry-exporter-logging");
|
||||||
return LoggingSpanExporter.create();
|
return LoggingSpanExporter.create();
|
||||||
default:
|
default:
|
||||||
SpanExporter spiExporter = spiExporters.get(name);
|
SpanExporter spiExporter = spiExportersManager.getByName(name);
|
||||||
if (spiExporter == null) {
|
if (spiExporter == null) {
|
||||||
throw new ConfigurationException("Unrecognized value for otel.traces.exporter: " + name);
|
throw new ConfigurationException("Unrecognized value for otel.traces.exporter: " + name);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,41 +6,44 @@
|
||||||
package io.opentelemetry.sdk.autoconfigure;
|
package io.opentelemetry.sdk.autoconfigure;
|
||||||
|
|
||||||
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
|
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.ServiceLoader;
|
import java.util.ServiceLoader;
|
||||||
import java.util.function.BiFunction;
|
import java.util.function.BiFunction;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import java.util.logging.Level;
|
import java.util.function.Supplier;
|
||||||
import java.util.logging.Logger;
|
|
||||||
|
|
||||||
final class SpiUtil {
|
final class SpiUtil {
|
||||||
|
|
||||||
private static final Logger logger = Logger.getLogger(SpiUtil.class.getName());
|
interface ServiceLoaderFinder {
|
||||||
|
|
||||||
static <T, U> Map<String, T> loadConfigurable(
|
<S> Iterable<S> load(Class<S> spiClass, ClassLoader classLoader);
|
||||||
|
}
|
||||||
|
|
||||||
|
static <T, U> NamedSpiManager<T> loadConfigurable(
|
||||||
Class<U> spiClass,
|
Class<U> spiClass,
|
||||||
Collection<String> requestedNames,
|
|
||||||
Function<U, String> getName,
|
Function<U, String> getName,
|
||||||
BiFunction<U, ConfigProperties, T> getConfigurable,
|
BiFunction<U, ConfigProperties, T> getConfigurable,
|
||||||
ConfigProperties config,
|
ConfigProperties config,
|
||||||
ClassLoader serviceClassLoader) {
|
ClassLoader serviceClassLoader) {
|
||||||
Map<String, T> result = new HashMap<>();
|
return loadConfigurable(
|
||||||
for (U provider : ServiceLoader.load(spiClass, serviceClassLoader)) {
|
spiClass, getName, getConfigurable, config, serviceClassLoader, ServiceLoader::load);
|
||||||
|
}
|
||||||
|
|
||||||
|
// VisibleForTesting
|
||||||
|
static <T, U> NamedSpiManager<T> loadConfigurable(
|
||||||
|
Class<U> spiClass,
|
||||||
|
Function<U, String> getName,
|
||||||
|
BiFunction<U, ConfigProperties, T> getConfigurable,
|
||||||
|
ConfigProperties config,
|
||||||
|
ClassLoader serviceClassLoader,
|
||||||
|
ServiceLoaderFinder serviceLoaderFinder) {
|
||||||
|
Map<String, Supplier<T>> nameToProvider = new HashMap<>();
|
||||||
|
for (U provider : serviceLoaderFinder.load(spiClass, serviceClassLoader)) {
|
||||||
String name = getName.apply(provider);
|
String name = getName.apply(provider);
|
||||||
T configurable;
|
nameToProvider.put(name, () -> getConfigurable.apply(provider, config));
|
||||||
try {
|
|
||||||
configurable = getConfigurable.apply(provider, config);
|
|
||||||
} catch (Throwable t) {
|
|
||||||
Level level = requestedNames.contains(name) ? Level.WARNING : Level.FINE;
|
|
||||||
logger.log(
|
|
||||||
level, "Error initializing " + spiClass.getSimpleName() + " with name " + name, t);
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
result.put(name, configurable);
|
return NamedSpiManager.create(nameToProvider);
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private SpiUtil() {}
|
private SpiUtil() {}
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,6 @@ import io.opentelemetry.sdk.trace.export.SpanExporter;
|
||||||
import io.opentelemetry.sdk.trace.samplers.Sampler;
|
import io.opentelemetry.sdk.trace.samplers.Sampler;
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
@ -133,10 +132,9 @@ final class TracerProviderConfiguration {
|
||||||
// Visible for testing
|
// Visible for testing
|
||||||
static Sampler configureSampler(
|
static Sampler configureSampler(
|
||||||
String sampler, ConfigProperties config, ClassLoader serviceClassLoader) {
|
String sampler, ConfigProperties config, ClassLoader serviceClassLoader) {
|
||||||
Map<String, Sampler> spiSamplers =
|
NamedSpiManager<Sampler> spiSamplersManager =
|
||||||
SpiUtil.loadConfigurable(
|
SpiUtil.loadConfigurable(
|
||||||
ConfigurableSamplerProvider.class,
|
ConfigurableSamplerProvider.class,
|
||||||
Collections.singletonList(sampler),
|
|
||||||
ConfigurableSamplerProvider::getName,
|
ConfigurableSamplerProvider::getName,
|
||||||
ConfigurableSamplerProvider::createSampler,
|
ConfigurableSamplerProvider::createSampler,
|
||||||
config,
|
config,
|
||||||
|
|
@ -168,7 +166,7 @@ final class TracerProviderConfiguration {
|
||||||
return Sampler.parentBased(Sampler.traceIdRatioBased(ratio));
|
return Sampler.parentBased(Sampler.traceIdRatioBased(ratio));
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
Sampler spiSampler = spiSamplers.get(sampler);
|
Sampler spiSampler = spiSamplersManager.getByName(sampler);
|
||||||
if (spiSampler == null) {
|
if (spiSampler == null) {
|
||||||
throw new ConfigurationException(
|
throw new ConfigurationException(
|
||||||
"Unrecognized value for otel.traces.sampler: " + sampler);
|
"Unrecognized value for otel.traces.sampler: " + sampler);
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ class NotOnClasspathTest {
|
||||||
assertThatThrownBy(
|
assertThatThrownBy(
|
||||||
() ->
|
() ->
|
||||||
SpanExporterConfiguration.configureExporter(
|
SpanExporterConfiguration.configureExporter(
|
||||||
"otlp", EMPTY, Collections.emptyMap(), MeterProvider.noop()))
|
"otlp", EMPTY, NamedSpiManager.createEmpty(), MeterProvider.noop()))
|
||||||
.isInstanceOf(ConfigurationException.class)
|
.isInstanceOf(ConfigurationException.class)
|
||||||
.hasMessageContaining(
|
.hasMessageContaining(
|
||||||
"OTLP gRPC Trace Exporter enabled but opentelemetry-exporter-otlp not found on "
|
"OTLP gRPC Trace Exporter enabled but opentelemetry-exporter-otlp not found on "
|
||||||
|
|
@ -40,7 +40,7 @@ class NotOnClasspathTest {
|
||||||
assertThatThrownBy(
|
assertThatThrownBy(
|
||||||
() ->
|
() ->
|
||||||
SpanExporterConfiguration.configureExporter(
|
SpanExporterConfiguration.configureExporter(
|
||||||
"otlp", config, Collections.emptyMap(), MeterProvider.noop()))
|
"otlp", config, NamedSpiManager.createEmpty(), MeterProvider.noop()))
|
||||||
.isInstanceOf(ConfigurationException.class)
|
.isInstanceOf(ConfigurationException.class)
|
||||||
.hasMessageContaining(
|
.hasMessageContaining(
|
||||||
"OTLP HTTP Trace Exporter enabled but opentelemetry-exporter-otlp-http-trace not found on "
|
"OTLP HTTP Trace Exporter enabled but opentelemetry-exporter-otlp-http-trace not found on "
|
||||||
|
|
@ -52,7 +52,7 @@ class NotOnClasspathTest {
|
||||||
assertThatThrownBy(
|
assertThatThrownBy(
|
||||||
() ->
|
() ->
|
||||||
SpanExporterConfiguration.configureExporter(
|
SpanExporterConfiguration.configureExporter(
|
||||||
"jaeger", EMPTY, Collections.emptyMap(), MeterProvider.noop()))
|
"jaeger", EMPTY, NamedSpiManager.createEmpty(), MeterProvider.noop()))
|
||||||
.isInstanceOf(ConfigurationException.class)
|
.isInstanceOf(ConfigurationException.class)
|
||||||
.hasMessageContaining(
|
.hasMessageContaining(
|
||||||
"Jaeger gRPC Exporter enabled but opentelemetry-exporter-jaeger not found on "
|
"Jaeger gRPC Exporter enabled but opentelemetry-exporter-jaeger not found on "
|
||||||
|
|
@ -64,7 +64,7 @@ class NotOnClasspathTest {
|
||||||
assertThatThrownBy(
|
assertThatThrownBy(
|
||||||
() ->
|
() ->
|
||||||
SpanExporterConfiguration.configureExporter(
|
SpanExporterConfiguration.configureExporter(
|
||||||
"zipkin", EMPTY, Collections.emptyMap(), MeterProvider.noop()))
|
"zipkin", EMPTY, NamedSpiManager.createEmpty(), MeterProvider.noop()))
|
||||||
.isInstanceOf(ConfigurationException.class)
|
.isInstanceOf(ConfigurationException.class)
|
||||||
.hasMessageContaining(
|
.hasMessageContaining(
|
||||||
"Zipkin Exporter enabled but opentelemetry-exporter-zipkin not found on classpath");
|
"Zipkin Exporter enabled but opentelemetry-exporter-zipkin not found on classpath");
|
||||||
|
|
@ -75,7 +75,7 @@ class NotOnClasspathTest {
|
||||||
assertThatThrownBy(
|
assertThatThrownBy(
|
||||||
() ->
|
() ->
|
||||||
SpanExporterConfiguration.configureExporter(
|
SpanExporterConfiguration.configureExporter(
|
||||||
"logging", EMPTY, Collections.emptyMap(), MeterProvider.noop()))
|
"logging", EMPTY, NamedSpiManager.createEmpty(), MeterProvider.noop()))
|
||||||
.isInstanceOf(ConfigurationException.class)
|
.isInstanceOf(ConfigurationException.class)
|
||||||
.hasMessageContaining(
|
.hasMessageContaining(
|
||||||
"Logging Trace Exporter enabled but opentelemetry-exporter-logging not found on "
|
"Logging Trace Exporter enabled but opentelemetry-exporter-logging not found on "
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,136 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.opentelemetry.sdk.autoconfigure;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||||
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.never;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
|
||||||
|
import java.util.Collections;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
public class SpiUtilTest {
|
||||||
|
|
||||||
|
private static final ConfigProperties EMPTY =
|
||||||
|
DefaultConfigProperties.createForTest(Collections.emptyMap());
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void canRetrieveByName() {
|
||||||
|
SpiUtil.ServiceLoaderFinder mockFinder = mock(SpiUtil.ServiceLoaderFinder.class);
|
||||||
|
when(mockFinder.load(any(), any()))
|
||||||
|
.thenReturn(Collections.singletonList(new SpiExampleProviderImplementation()));
|
||||||
|
|
||||||
|
NamedSpiManager<SpiExample> spiProvider =
|
||||||
|
SpiUtil.loadConfigurable(
|
||||||
|
SpiExampleProvider.class,
|
||||||
|
SpiExampleProvider::getName,
|
||||||
|
SpiExampleProvider::createSpiExample,
|
||||||
|
EMPTY,
|
||||||
|
SpiUtilTest.class.getClassLoader(),
|
||||||
|
mockFinder);
|
||||||
|
|
||||||
|
assertNotNull(spiProvider.getByName(SpiExampleProviderImplementation.NAME));
|
||||||
|
assertNull(spiProvider.getByName("invalid-provider"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void instantiatesImplementationsLazily() {
|
||||||
|
SpiExampleProvider mockProvider = mock(SpiExampleProvider.class);
|
||||||
|
when(mockProvider.getName()).thenReturn("lazy-init-example");
|
||||||
|
|
||||||
|
SpiUtil.ServiceLoaderFinder mockFinder = mock(SpiUtil.ServiceLoaderFinder.class);
|
||||||
|
when(mockFinder.load(any(), any())).thenReturn(Collections.singletonList(mockProvider));
|
||||||
|
|
||||||
|
NamedSpiManager<SpiExample> spiProvider =
|
||||||
|
SpiUtil.loadConfigurable(
|
||||||
|
SpiExampleProvider.class,
|
||||||
|
SpiExampleProvider::getName,
|
||||||
|
SpiExampleProvider::createSpiExample,
|
||||||
|
EMPTY,
|
||||||
|
SpiUtilTest.class.getClassLoader(),
|
||||||
|
mockFinder);
|
||||||
|
|
||||||
|
verify(mockProvider, never()).createSpiExample(any()); // not requested yet
|
||||||
|
spiProvider.getByName("lazy-init-example");
|
||||||
|
verify(mockProvider).createSpiExample(EMPTY); // initiated upon request
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void onlyInstantiatesOnce() {
|
||||||
|
SpiUtil.ServiceLoaderFinder mockFinder = mock(SpiUtil.ServiceLoaderFinder.class);
|
||||||
|
when(mockFinder.load(any(), any()))
|
||||||
|
.thenReturn(Collections.singletonList(new SpiExampleProviderImplementation()));
|
||||||
|
|
||||||
|
NamedSpiManager<SpiExample> spiProvider =
|
||||||
|
SpiUtil.loadConfigurable(
|
||||||
|
SpiExampleProvider.class,
|
||||||
|
SpiExampleProvider::getName,
|
||||||
|
SpiExampleProvider::createSpiExample,
|
||||||
|
EMPTY,
|
||||||
|
SpiUtilTest.class.getClassLoader(),
|
||||||
|
mockFinder);
|
||||||
|
|
||||||
|
SpiExample first = spiProvider.getByName(SpiExampleProviderImplementation.NAME);
|
||||||
|
SpiExample second = spiProvider.getByName(SpiExampleProviderImplementation.NAME);
|
||||||
|
assertEquals(first, second);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void failureToInitializeThrows() {
|
||||||
|
String exceptionMessage = "failure to initialize should throw";
|
||||||
|
SpiExampleProvider mockProvider = mock(SpiExampleProvider.class);
|
||||||
|
when(mockProvider.getName()).thenReturn("init-failure-example");
|
||||||
|
when(mockProvider.createSpiExample(any())).thenThrow(new RuntimeException());
|
||||||
|
|
||||||
|
SpiUtil.ServiceLoaderFinder mockFinder = mock(SpiUtil.ServiceLoaderFinder.class);
|
||||||
|
when(mockFinder.load(any(), any())).thenReturn(Collections.singletonList(mockProvider));
|
||||||
|
|
||||||
|
NamedSpiManager<SpiExample> spiProvider =
|
||||||
|
SpiUtil.loadConfigurable(
|
||||||
|
SpiExampleProvider.class,
|
||||||
|
SpiExampleProvider::getName,
|
||||||
|
SpiExampleProvider::createSpiExample,
|
||||||
|
EMPTY,
|
||||||
|
SpiUtilTest.class.getClassLoader(),
|
||||||
|
mockFinder);
|
||||||
|
|
||||||
|
assertThrows(
|
||||||
|
RuntimeException.class,
|
||||||
|
() -> spiProvider.getByName("init-failure-example"),
|
||||||
|
exceptionMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
private interface SpiExampleProvider {
|
||||||
|
|
||||||
|
String getName();
|
||||||
|
|
||||||
|
SpiExample createSpiExample(ConfigProperties config);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class SpiExampleProviderImplementation implements SpiExampleProvider {
|
||||||
|
|
||||||
|
private static final String NAME = "spi-example";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return NAME;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SpiExample createSpiExample(ConfigProperties config) {
|
||||||
|
return new SpiExample() {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private interface SpiExample {}
|
||||||
|
}
|
||||||
|
|
@ -104,7 +104,7 @@ public class ConfigurableSpanExporterTest {
|
||||||
SpanExporterConfiguration.configureExporter(
|
SpanExporterConfiguration.configureExporter(
|
||||||
"catExporter",
|
"catExporter",
|
||||||
DefaultConfigProperties.createForTest(Collections.emptyMap()),
|
DefaultConfigProperties.createForTest(Collections.emptyMap()),
|
||||||
Collections.emptyMap(),
|
NamedSpiManager.createEmpty(),
|
||||||
MeterProvider.noop()))
|
MeterProvider.noop()))
|
||||||
.isInstanceOf(ConfigurationException.class)
|
.isInstanceOf(ConfigurationException.class)
|
||||||
.hasMessageContaining("catExporter");
|
.hasMessageContaining("catExporter");
|
||||||
|
|
|
||||||
|
|
@ -40,7 +40,7 @@ class SpanExporterConfigurationTest {
|
||||||
"otlp",
|
"otlp",
|
||||||
DefaultConfigProperties.createForTest(
|
DefaultConfigProperties.createForTest(
|
||||||
Collections.singletonMap("otel.exporter.otlp.timeout", "10")),
|
Collections.singletonMap("otel.exporter.otlp.timeout", "10")),
|
||||||
Collections.emptyMap(),
|
NamedSpiManager.createEmpty(),
|
||||||
MeterProvider.noop());
|
MeterProvider.noop());
|
||||||
try {
|
try {
|
||||||
assertThat(exporter)
|
assertThat(exporter)
|
||||||
|
|
@ -63,7 +63,7 @@ class SpanExporterConfigurationTest {
|
||||||
"jaeger",
|
"jaeger",
|
||||||
DefaultConfigProperties.createForTest(
|
DefaultConfigProperties.createForTest(
|
||||||
Collections.singletonMap("otel.exporter.jaeger.timeout", "10")),
|
Collections.singletonMap("otel.exporter.jaeger.timeout", "10")),
|
||||||
Collections.emptyMap(),
|
NamedSpiManager.createEmpty(),
|
||||||
MeterProvider.noop());
|
MeterProvider.noop());
|
||||||
try {
|
try {
|
||||||
assertThat(exporter)
|
assertThat(exporter)
|
||||||
|
|
@ -86,7 +86,7 @@ class SpanExporterConfigurationTest {
|
||||||
"zipkin",
|
"zipkin",
|
||||||
DefaultConfigProperties.createForTest(
|
DefaultConfigProperties.createForTest(
|
||||||
Collections.singletonMap("otel.exporter.zipkin.timeout", "5s")),
|
Collections.singletonMap("otel.exporter.zipkin.timeout", "5s")),
|
||||||
Collections.emptyMap(),
|
NamedSpiManager.createEmpty(),
|
||||||
MeterProvider.noop());
|
MeterProvider.noop());
|
||||||
try {
|
try {
|
||||||
assertThat(exporter).isNotNull();
|
assertThat(exporter).isNotNull();
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,6 @@ import io.opentelemetry.sdk.metrics.export.MetricExporter;
|
||||||
import io.opentelemetry.sdk.trace.data.SpanData;
|
import io.opentelemetry.sdk.trace.data.SpanData;
|
||||||
import io.opentelemetry.sdk.trace.export.SpanExporter;
|
import io.opentelemetry.sdk.trace.export.SpanExporter;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
@ -75,7 +74,7 @@ class OtlpGrpcConfigTest {
|
||||||
ConfigProperties properties = DefaultConfigProperties.createForTest(props);
|
ConfigProperties properties = DefaultConfigProperties.createForTest(props);
|
||||||
try (SpanExporter spanExporter =
|
try (SpanExporter spanExporter =
|
||||||
SpanExporterConfiguration.configureExporter(
|
SpanExporterConfiguration.configureExporter(
|
||||||
"otlp", properties, Collections.emptyMap(), MeterProvider.noop());
|
"otlp", properties, NamedSpiManager.createEmpty(), MeterProvider.noop());
|
||||||
MetricExporter metricExporter =
|
MetricExporter metricExporter =
|
||||||
MetricExporterConfiguration.configureOtlpMetrics(
|
MetricExporterConfiguration.configureOtlpMetrics(
|
||||||
properties, SdkMeterProvider.builder());
|
properties, SdkMeterProvider.builder());
|
||||||
|
|
@ -144,7 +143,7 @@ class OtlpGrpcConfigTest {
|
||||||
SpanExporterConfiguration.configureExporter(
|
SpanExporterConfiguration.configureExporter(
|
||||||
"otlp",
|
"otlp",
|
||||||
DefaultConfigProperties.createForTest(props),
|
DefaultConfigProperties.createForTest(props),
|
||||||
Collections.emptyMap(),
|
NamedSpiManager.createEmpty(),
|
||||||
MeterProvider.noop())) {
|
MeterProvider.noop())) {
|
||||||
assertThat(spanExporter)
|
assertThat(spanExporter)
|
||||||
.extracting("delegate.timeoutNanos")
|
.extracting("delegate.timeoutNanos")
|
||||||
|
|
@ -243,7 +242,7 @@ class OtlpGrpcConfigTest {
|
||||||
assertThatThrownBy(
|
assertThatThrownBy(
|
||||||
() ->
|
() ->
|
||||||
SpanExporterConfiguration.configureExporter(
|
SpanExporterConfiguration.configureExporter(
|
||||||
"otlp", properties, Collections.emptyMap(), MeterProvider.noop()))
|
"otlp", properties, NamedSpiManager.createEmpty(), MeterProvider.noop()))
|
||||||
.isInstanceOf(ConfigurationException.class)
|
.isInstanceOf(ConfigurationException.class)
|
||||||
.hasMessageContaining("Invalid OTLP certificate path:");
|
.hasMessageContaining("Invalid OTLP certificate path:");
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,6 @@ import io.opentelemetry.sdk.metrics.data.MetricData;
|
||||||
import io.opentelemetry.sdk.metrics.export.MetricExporter;
|
import io.opentelemetry.sdk.metrics.export.MetricExporter;
|
||||||
import io.opentelemetry.sdk.trace.data.SpanData;
|
import io.opentelemetry.sdk.trace.data.SpanData;
|
||||||
import io.opentelemetry.sdk.trace.export.SpanExporter;
|
import io.opentelemetry.sdk.trace.export.SpanExporter;
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
@ -61,7 +60,7 @@ class OtlpGrpcRetryTest {
|
||||||
SpanExporterConfiguration.configureExporter(
|
SpanExporterConfiguration.configureExporter(
|
||||||
"otlp",
|
"otlp",
|
||||||
DefaultConfigProperties.createForTest(props),
|
DefaultConfigProperties.createForTest(props),
|
||||||
Collections.emptyMap(),
|
NamedSpiManager.createEmpty(),
|
||||||
MeterProvider.noop())) {
|
MeterProvider.noop())) {
|
||||||
|
|
||||||
testRetryableStatusCodes(() -> SPAN_DATA, spanExporter::export, server.traceRequests::size);
|
testRetryableStatusCodes(() -> SPAN_DATA, spanExporter::export, server.traceRequests::size);
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,6 @@ import io.opentelemetry.sdk.metrics.data.AggregationTemporality;
|
||||||
import io.opentelemetry.sdk.metrics.export.MetricExporter;
|
import io.opentelemetry.sdk.metrics.export.MetricExporter;
|
||||||
import io.opentelemetry.sdk.trace.export.SpanExporter;
|
import io.opentelemetry.sdk.trace.export.SpanExporter;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
@ -64,7 +63,7 @@ class OtlpHttpConfigTest {
|
||||||
ConfigProperties properties = DefaultConfigProperties.createForTest(props);
|
ConfigProperties properties = DefaultConfigProperties.createForTest(props);
|
||||||
SpanExporter spanExporter =
|
SpanExporter spanExporter =
|
||||||
SpanExporterConfiguration.configureExporter(
|
SpanExporterConfiguration.configureExporter(
|
||||||
"otlp", properties, Collections.emptyMap(), MeterProvider.noop());
|
"otlp", properties, NamedSpiManager.createEmpty(), MeterProvider.noop());
|
||||||
MetricExporter metricExporter =
|
MetricExporter metricExporter =
|
||||||
MetricExporterConfiguration.configureOtlpMetrics(properties, SdkMeterProvider.builder());
|
MetricExporterConfiguration.configureOtlpMetrics(properties, SdkMeterProvider.builder());
|
||||||
LogExporter logExporter =
|
LogExporter logExporter =
|
||||||
|
|
@ -150,7 +149,7 @@ class OtlpHttpConfigTest {
|
||||||
SpanExporterConfiguration.configureExporter(
|
SpanExporterConfiguration.configureExporter(
|
||||||
"otlp",
|
"otlp",
|
||||||
DefaultConfigProperties.createForTest(props),
|
DefaultConfigProperties.createForTest(props),
|
||||||
Collections.emptyMap(),
|
NamedSpiManager.createEmpty(),
|
||||||
MeterProvider.noop());
|
MeterProvider.noop());
|
||||||
|
|
||||||
assertThat(spanExporter)
|
assertThat(spanExporter)
|
||||||
|
|
@ -269,7 +268,7 @@ class OtlpHttpConfigTest {
|
||||||
assertThatThrownBy(
|
assertThatThrownBy(
|
||||||
() ->
|
() ->
|
||||||
SpanExporterConfiguration.configureExporter(
|
SpanExporterConfiguration.configureExporter(
|
||||||
"otlp", properties, Collections.emptyMap(), MeterProvider.noop()))
|
"otlp", properties, NamedSpiManager.createEmpty(), MeterProvider.noop()))
|
||||||
.isInstanceOf(ConfigurationException.class)
|
.isInstanceOf(ConfigurationException.class)
|
||||||
.hasMessageContaining("Invalid OTLP certificate path:");
|
.hasMessageContaining("Invalid OTLP certificate path:");
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,6 @@ import io.opentelemetry.sdk.metrics.export.MetricExporter;
|
||||||
import io.opentelemetry.sdk.trace.data.SpanData;
|
import io.opentelemetry.sdk.trace.data.SpanData;
|
||||||
import io.opentelemetry.sdk.trace.export.SpanExporter;
|
import io.opentelemetry.sdk.trace.export.SpanExporter;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
@ -59,7 +58,7 @@ class OtlpHttpRetryTest {
|
||||||
SpanExporterConfiguration.configureExporter(
|
SpanExporterConfiguration.configureExporter(
|
||||||
"otlp",
|
"otlp",
|
||||||
DefaultConfigProperties.createForTest(props),
|
DefaultConfigProperties.createForTest(props),
|
||||||
Collections.emptyMap(),
|
NamedSpiManager.createEmpty(),
|
||||||
MeterProvider.noop());
|
MeterProvider.noop());
|
||||||
|
|
||||||
testRetryableStatusCodes(() -> SPAN_DATA, spanExporter::export, server.traceRequests::size);
|
testRetryableStatusCodes(() -> SPAN_DATA, spanExporter::export, server.traceRequests::size);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue