Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Spec compliance: OTEL_PROPAGATORS should still work when OTEL_SDK_DISABLED #7062

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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,7 +67,9 @@
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.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
Expand Down Expand Up @@ -391,7 +393,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 +421,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 +465,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 +553,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 +664,49 @@ 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");

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

@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);
}
}
Loading