Describe the bug
When the tracer provider is not set in the OpenTelemetrySdk builder, spans claim they are recorded, but there is no exporter defined for them.
Therefore, despite appropriately guarding the attribute creation code with if (!span.isRecording()), an application can waste resources generating spans with attributes that will never be exported anyway.
Steps to reproduce
This reduced test case shows the problem:
@Test
public void shouldNotEmitSpansWhenTracingDisabledButLoggingEnabled2() {
// Given
OpenTelemetry openTelemetry = OpenTelemetrySdk.builder().setTracerProvider(null).build();
// Same as OpenTelemetry openTelemetry = OpenTelemetrySdk.builder().build();
// When
Span span = openTelemetry.getTracer("test").spanBuilder("test-span").startSpan();
// Then
assertThat(span.isRecording()).isFalse(); // Fails!
}
Of course our setup code is a bit more complicated: we have a real logger provider, but no tracer provider, and we ended up passing null for the tracer provider.
What did you expect to see?
In the test above, the assertion assertThat(span.isRecording()).isFalse(); should pass. As a result, all the application span code would be a noop.
What did you see instead?
In the test above, the assertion assertThat(span.isRecording()).isFalse(); does not pass. As a result, all the application span code does a lot of useless work.
Workaround
First attempt:
Do the following instead:
SdkTracerProvider noopTracer = SdkTracerProvider.builder().setSampler(alwaysOff()).build();
OpenTelemetry openTelemetry = OpenTelemetrySdk.builder().setTracerProvider(noopTracer).build();
But even that is not optimal, it will run through a lot of code on this line:
openTelemetry.getTracer("test").spanBuilder("test-span").startSpan();
I was hoping I could find a way to setSdkTracer#tracerEnabled to false, but I could not.
I wanted to override SdkTracerProviderBuilder#tracerConfiguratorBuilder, but there is no public API to do so either.
Second attempt:
In the end, the best workaround I found is the following:
SdkTracerProvider noopTracer = SdkTracerProvider.builder().build();
noopTracer.close();
OpenTelemetry openTelemetry = OpenTelemetrySdk.builder().setTracerProvider(noopTracer).build();
This ensures we take one of the fast paths in io.opentelemetry.sdk.trace.SdkTracer#spanBuilder, which returns a NoopSpanBuilder, and then the call to io.opentelemetry.api.trace.SpanBuilder.startSpan does as little as possible, and the returned io.opentelemetry.api.trace.PropagatedSpan isRecording() method returns false.
Which what we want.
Conclusion:
To cut a long story short, the default is surprising, and making it to behave as noop required a surprising amount of work, and it is a bit "heavy". i.e. doing the "right thing" in this case is not exactly trivial.
What version and what artifacts are you using?
io.opentelemetry:opentelemetry-sdk-trace:1.62.0
Environment
Compiler: Amazon Correto 17.0.19
OS: Ubuntu 24.04.4 LTS
Additional context
n/a
Describe the bug
When the tracer provider is not set in the OpenTelemetrySdk builder, spans claim they are recorded, but there is no exporter defined for them.
Therefore, despite appropriately guarding the attribute creation code with
if (!span.isRecording()), an application can waste resources generating spans with attributes that will never be exported anyway.Steps to reproduce
This reduced test case shows the problem:
Of course our setup code is a bit more complicated: we have a real logger provider, but no tracer provider, and we ended up passing
nullfor the tracer provider.What did you expect to see?
In the test above, the assertion
assertThat(span.isRecording()).isFalse();should pass. As a result, all the application span code would be a noop.What did you see instead?
In the test above, the assertion
assertThat(span.isRecording()).isFalse();does not pass. As a result, all the application span code does a lot of useless work.Workaround
First attempt:
Do the following instead:
But even that is not optimal, it will run through a lot of code on this line:
openTelemetry.getTracer("test").spanBuilder("test-span").startSpan();I was hoping I could find a way to set
SdkTracer#tracerEnabledto false, but I could not.I wanted to override
SdkTracerProviderBuilder#tracerConfiguratorBuilder, but there is no public API to do so either.Second attempt:
In the end, the best workaround I found is the following:
This ensures we take one of the fast paths in
io.opentelemetry.sdk.trace.SdkTracer#spanBuilder, which returns aNoopSpanBuilder, and then the call toio.opentelemetry.api.trace.SpanBuilder.startSpandoes as little as possible, and the returnedio.opentelemetry.api.trace.PropagatedSpanisRecording()method returnsfalse.Which what we want.
Conclusion:
To cut a long story short, the default is surprising, and making it to behave as noop required a surprising amount of work, and it is a bit "heavy". i.e. doing the "right thing" in this case is not exactly trivial.
What version and what artifacts are you using?
io.opentelemetry:opentelemetry-sdk-trace:1.62.0
Environment
Compiler: Amazon Correto 17.0.19
OS: Ubuntu 24.04.4 LTS
Additional context
n/a