Skip to content

Commit

Permalink
Spec compliance: OTEL_PROPAGATORS should still work when OTEL_SDK_DIS…
Browse files Browse the repository at this point in the history
…ABLED=false
  • Loading branch information
mikeblum committed Feb 3, 2025
1 parent cb64451 commit 137ee88
Show file tree
Hide file tree
Showing 2 changed files with 231 additions and 62 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -443,65 +443,19 @@ public AutoConfiguredOpenTelemetrySdk build() {
List<Closeable> closeables = new ArrayList<>();

try {
OpenTelemetrySdk openTelemetrySdk = OpenTelemetrySdk.builder().build();
boolean sdkEnabled = !config.getBoolean("otel.sdk.disabled", false);
OpenTelemetrySdkBuilder sdkBuilder = OpenTelemetrySdk.builder();

// The propagation system is part of the API and functions in the absence of an SDK.
ContextPropagators propagators =
PropagatorConfiguration.configurePropagators(config, spiHelper, propagatorCustomizer);
sdkBuilder.setPropagators(propagators);

boolean sdkEnabled = !config.getBoolean("otel.sdk.disabled", false);
if (sdkEnabled) {
SdkMeterProviderBuilder meterProviderBuilder = SdkMeterProvider.builder();
meterProviderBuilder.setResource(resource);
MeterProviderConfiguration.configureMeterProvider(
meterProviderBuilder,
config,
spiHelper,
metricReaderCustomizer,
metricExporterCustomizer,
closeables);
meterProviderBuilder = meterProviderCustomizer.apply(meterProviderBuilder, config);
SdkMeterProvider meterProvider = meterProviderBuilder.build();
closeables.add(meterProvider);

SdkTracerProviderBuilder tracerProviderBuilder = SdkTracerProvider.builder();
tracerProviderBuilder.setResource(resource);
TracerProviderConfiguration.configureTracerProvider(
tracerProviderBuilder,
config,
spiHelper,
meterProvider,
spanExporterCustomizer,
spanProcessorCustomizer,
samplerCustomizer,
closeables);
tracerProviderBuilder = tracerProviderCustomizer.apply(tracerProviderBuilder, config);
SdkTracerProvider tracerProvider = tracerProviderBuilder.build();
closeables.add(tracerProvider);

SdkLoggerProviderBuilder loggerProviderBuilder = SdkLoggerProvider.builder();
loggerProviderBuilder.setResource(resource);
LoggerProviderConfiguration.configureLoggerProvider(
loggerProviderBuilder,
config,
spiHelper,
meterProvider,
logRecordExporterCustomizer,
logRecordProcessorCustomizer,
closeables);
loggerProviderBuilder = loggerProviderCustomizer.apply(loggerProviderBuilder, config);
SdkLoggerProvider loggerProvider = loggerProviderBuilder.build();
closeables.add(loggerProvider);

ContextPropagators propagators =
PropagatorConfiguration.configurePropagators(config, spiHelper, propagatorCustomizer);

OpenTelemetrySdkBuilder sdkBuilder =
OpenTelemetrySdk.builder()
.setTracerProvider(tracerProvider)
.setLoggerProvider(loggerProvider)
.setMeterProvider(meterProvider)
.setPropagators(propagators);

openTelemetrySdk = sdkBuilder.build();
configureSdk(sdkBuilder, config, resource, spiHelper, closeables);
}

OpenTelemetrySdk openTelemetrySdk = sdkBuilder.build();
maybeRegisterShutdownHook(openTelemetrySdk);
maybeSetAsGlobal(openTelemetrySdk);
callAutoConfigureListeners(spiHelper, openTelemetrySdk);
Expand All @@ -526,6 +480,62 @@ public AutoConfiguredOpenTelemetrySdk build() {
}
}

// Visible for testing
void configureSdk(
OpenTelemetrySdkBuilder sdkBuilder,
ConfigProperties config,
Resource resource,
SpiHelper spiHelper,
List<Closeable> closeables) {
SdkMeterProviderBuilder meterProviderBuilder = SdkMeterProvider.builder();
meterProviderBuilder.setResource(resource);

MeterProviderConfiguration.configureMeterProvider(
meterProviderBuilder,
config,
spiHelper,
metricReaderCustomizer,
metricExporterCustomizer,
closeables);
meterProviderBuilder = meterProviderCustomizer.apply(meterProviderBuilder, config);
SdkMeterProvider meterProvider = meterProviderBuilder.build();
closeables.add(meterProvider);

SdkTracerProviderBuilder tracerProviderBuilder = SdkTracerProvider.builder();
tracerProviderBuilder.setResource(resource);
TracerProviderConfiguration.configureTracerProvider(
tracerProviderBuilder,
config,
spiHelper,
meterProvider,
spanExporterCustomizer,
spanProcessorCustomizer,
samplerCustomizer,
closeables);
tracerProviderBuilder = tracerProviderCustomizer.apply(tracerProviderBuilder, config);
SdkTracerProvider tracerProvider = tracerProviderBuilder.build();
closeables.add(tracerProvider);

SdkLoggerProviderBuilder loggerProviderBuilder = SdkLoggerProvider.builder();
loggerProviderBuilder.setResource(resource);
LoggerProviderConfiguration.configureLoggerProvider(
loggerProviderBuilder,
config,
spiHelper,
meterProvider,
logRecordExporterCustomizer,
logRecordProcessorCustomizer,
closeables);
loggerProviderBuilder = loggerProviderCustomizer.apply(loggerProviderBuilder, config);
SdkLoggerProvider loggerProvider = loggerProviderBuilder.build();
closeables.add(loggerProvider);

sdkBuilder
.setTracerProvider(tracerProvider)
.setLoggerProvider(loggerProvider)
.setMeterProvider(meterProvider);
}

@Nullable
private static AutoConfiguredOpenTelemetrySdk maybeConfigureFromFile(
ConfigProperties config, ComponentLoader componentLoader) {
Expand Down Expand Up @@ -607,7 +617,7 @@ void callAutoConfigureListeners(SpiHelper spiHelper, OpenTelemetrySdk openTeleme
}

@SuppressWarnings("deprecation") // Support deprecated SdkTracerProviderConfigurer
private void mergeSdkTracerProviderConfigurer() {
void mergeSdkTracerProviderConfigurer() {
for (io.opentelemetry.sdk.autoconfigure.spi.traces.SdkTracerProviderConfigurer configurer :
componentLoader.load(
io.opentelemetry.sdk.autoconfigure.spi.traces.SdkTracerProviderConfigurer.class)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@
import java.util.Properties;
import java.util.concurrent.TimeUnit;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Supplier;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
Expand Down Expand Up @@ -391,7 +392,15 @@ void builder_registersShutdownHook() {
}

@Test
void shutdownHook() throws InterruptedException {
void builder_customizes() {
builder = spy(builder);
OpenTelemetrySdk sdk = builder.build().getOpenTelemetrySdk();
assertThat(sdk).isNotNull();
verify(builder, times(1)).mergeSdkTracerProviderConfigurer();
}

@Test
void builder_shutdownHook() throws InterruptedException {
OpenTelemetrySdk sdk = mock(OpenTelemetrySdk.class);

Thread thread = builder.shutdownHook(sdk);
Expand All @@ -411,7 +420,7 @@ void builder_CallAutoConfigureListeners() {
}

@Test
void callAutoConfigureListeners() {
void builder_callAutoConfigureListeners() {
AutoConfigureListener listener = mock(AutoConfigureListener.class);
SpiHelper spiHelper = mock(SpiHelper.class);
when(spiHelper.getListeners()).thenReturn(Collections.singleton(listener));
Expand Down Expand Up @@ -455,6 +464,39 @@ void disableSdk() {
verify(logCustomizer, never()).apply(any(), any());
}

@Test
void disableSdk_PropagatorCustomizer() {
Context extracted = Context.root().with(ContextKey.named("animal"), "bear");

when(propagator2.extract(any(), any(), any())).thenReturn(extracted);

AutoConfiguredOpenTelemetrySdk autoConfiguredSdk =
AutoConfiguredOpenTelemetrySdk.builder()
.addPropertiesSupplier(() -> singletonMap("otel.sdk.disabled", "true"))
.addPropertiesSupplier(() -> singletonMap("otel.propagators", "tracecontext"))
.addPropagatorCustomizer(
(previous, config) -> {
assertThat(previous).isSameAs(W3CTraceContextPropagator.getInstance());
return propagator1;
})
.addPropagatorCustomizer(
(previous, config) -> {
assertThat(previous).isSameAs(propagator1);
return propagator2;
})
.build();

// When the SDK is disabled, propagators are still configured
assertThat(autoConfiguredSdk.getOpenTelemetrySdk().getPropagators()).isNotNull();
Consumer<TextMapPropagator> propagatorConsumer =
propagator -> {
assertThat(propagator.extract(Context.root(), Collections.emptyMap(), getter))
.isEqualTo(extracted);
};
assertThat(autoConfiguredSdk.getOpenTelemetrySdk().getPropagators().getTextMapPropagator())
.isInstanceOfSatisfying(TextMapPropagator.class, propagatorConsumer);
}

@Test
void tracerProviderCustomizer() {
InMemorySpanExporter spanExporter = InMemorySpanExporter.create();
Expand Down Expand Up @@ -510,6 +552,88 @@ void testNonStringProperties() {
});
}

@Test
@SuppressLogger(AutoConfiguredOpenTelemetrySdkBuilder.class)
void configurationError_propagators() {
BiFunction<SdkTracerProviderBuilder, ConfigProperties, SdkTracerProviderBuilder>
traceCustomizer = getTracerProviderBuilderSpy();
BiFunction<SdkMeterProviderBuilder, ConfigProperties, SdkMeterProviderBuilder>
metricCustomizer = getMeterProviderBuilderSpy();
BiFunction<SdkLoggerProviderBuilder, ConfigProperties, SdkLoggerProviderBuilder> logCustomizer =
getLoggerProviderBuilderSpy();

assertThatThrownBy(
() ->
// Override the provider builders with mocks which we can verify are closed
AutoConfiguredOpenTelemetrySdk.builder()
.addTracerProviderCustomizer(traceCustomizer)
.addMeterProviderCustomizer(metricCustomizer)
.addLoggerProviderCustomizer(logCustomizer)
.addPropertiesSupplier(() -> singletonMap("otel.metrics.exporter", "none"))
.addPropertiesSupplier(() -> singletonMap("otel.traces.exporter", "none"))
.addPropertiesSupplier(() -> singletonMap("otel.logs.exporter", "none"))
.addPropertiesSupplier(() -> singletonMap("otel.propagators", "foo"))
.addPropertiesSupplier(() -> singletonMap("otel.sdk.disabled", "true"))
.build())
.isInstanceOf(ConfigurationException.class)
.hasMessageContaining("Unrecognized value for otel.propagators");

// When the SDK is disabled and propagators are mis-configured, none of the customizers are
// called
verify(traceCustomizer, never()).apply(any(), any());
verify(metricCustomizer, never()).apply(any(), any());
verify(logCustomizer, never()).apply(any(), any());

assertThatThrownBy(
() ->
// Override the provider builders with mocks which we can verify are closed
AutoConfiguredOpenTelemetrySdk.builder()
.addTracerProviderCustomizer(traceCustomizer)
.addMeterProviderCustomizer(metricCustomizer)
.addLoggerProviderCustomizer(logCustomizer)
.addPropertiesSupplier(() -> singletonMap("otel.metrics.exporter", "none"))
.addPropertiesSupplier(() -> singletonMap("otel.traces.exporter", "none"))
.addPropertiesSupplier(() -> singletonMap("otel.logs.exporter", "none"))
.addPropertiesSupplier(() -> singletonMap("otel.propagators", "foo"))
.addPropertiesSupplier(() -> singletonMap("otel.sdk.disabled", "false"))
.build())
.isInstanceOf(ConfigurationException.class)
.hasMessageContaining("Unrecognized value for otel.propagators");

// When the SDK is enabled and propagators are mis-configured, none of the customizers are
// called
verify(traceCustomizer, never()).apply(any(), any());
verify(metricCustomizer, never()).apply(any(), any());
verify(logCustomizer, never()).apply(any(), any());
}

@Test
@SuppressLogger(AutoConfiguredOpenTelemetrySdkBuilder.class)
void configurationError_runtime() {
BiFunction<SdkTracerProviderBuilder, ConfigProperties, SdkTracerProviderBuilder>
traceCustomizer = getTracerProviderBuilderSpy();
BiFunction<SdkMeterProviderBuilder, ConfigProperties, SdkMeterProviderBuilder>
metricCustomizer = getMeterProviderBuilderSpy();
BiFunction<SdkLoggerProviderBuilder, ConfigProperties, SdkLoggerProviderBuilder> logCustomizer =
getLoggerProviderBuilderSpy();

doThrow(new RuntimeException()).when(traceCustomizer).apply(any(), any());

assertThatThrownBy(
() ->
AutoConfiguredOpenTelemetrySdk.builder()
.addTracerProviderCustomizer(traceCustomizer)
.addMeterProviderCustomizer(metricCustomizer)
.addLoggerProviderCustomizer(logCustomizer)
.addPropertiesSupplier(() -> singletonMap("otel.metrics.exporter", "none"))
.addPropertiesSupplier(() -> singletonMap("otel.traces.exporter", "none"))
.addPropertiesSupplier(() -> singletonMap("otel.logs.exporter", "none"))
.addPropertiesSupplier(() -> singletonMap("otel.sdk.disabled", "false"))
.build())
.isInstanceOf(ConfigurationException.class)
.hasMessageContaining("Unexpected configuration error");
}

@Test
@SuppressLogger(AutoConfiguredOpenTelemetrySdkBuilder.class)
void configurationError_ClosesResources() {
Expand Down Expand Up @@ -539,16 +663,51 @@ void configurationError_ClosesResources() {
.addLoggerProviderCustomizer((u1, u2) -> loggerProviderBuilder)
.addPropertiesSupplier(() -> singletonMap("otel.metrics.exporter", "none"))
.addPropertiesSupplier(() -> singletonMap("otel.traces.exporter", "none"))
.addPropertiesSupplier(() -> singletonMap("otel.logs.exporter", "none"))
.addPropertiesSupplier(() -> singletonMap("otel.propagators", "foo"))
.addPropertiesSupplier(() -> singletonMap("otel.logs.exporter", "foo"))
.addPropertiesSupplier(() -> singletonMap("otel.sdk.disabled", "false"))
.build())
.isInstanceOf(ConfigurationException.class)
.hasMessageContaining("Unrecognized value for otel.propagators");
.hasMessageContaining("Unrecognized value for otel.logs.exporter: foo");

verify(tracerProvider).close();
verify(meterProvider).close();
verify(loggerProvider).close();

logs.assertContains("Error closing io.opentelemetry.sdk.trace.SdkTracerProvider: Error!");
}

@Test
@SuppressLogger(AutoConfiguredOpenTelemetrySdkBuilder.class)
void configurationError_fileNotFound() {
assertThatThrownBy(
() ->
AutoConfiguredOpenTelemetrySdk.builder()
.addPropertiesSupplier(() -> singletonMap("otel.config.file", "foo"))
.addPropertiesSupplier(
() -> singletonMap("otel.experimental.config.file", "foo"))
.addPropertiesSupplier(() -> singletonMap("otel.sdk.disabled", "true"))
.build())
.isInstanceOf(ConfigurationException.class)
.hasMessageContaining("Configuration file not found");

assertThatCode(
() ->
AutoConfiguredOpenTelemetrySdk.builder()
.addPropertiesSupplier(() -> singletonMap("otel.experimental.config.file", ""))
.addPropertiesSupplier(() -> singletonMap("otel.sdk.disabled", "true"))
.build())
.doesNotThrowAnyException();
;
}

@Test
@SuppressLogger(AutoConfiguredOpenTelemetrySdkBuilder.class)
void serviceClassLoader() {
ClassLoader classLoader = null;
assertThatThrownBy(
() -> AutoConfiguredOpenTelemetrySdk.builder().setServiceClassLoader(classLoader))
.isInstanceOf(NullPointerException.class)
.hasMessageContaining("serviceClassLoader");
ClassLoader mockClassLoader = mock(ClassLoader.class);
AutoConfiguredOpenTelemetrySdk.builder().setServiceClassLoader(mockClassLoader);
}
}

0 comments on commit 137ee88

Please sign in to comment.