Affects every bootstrapper (FastAPI, Litestar, Free, and FastStream since #223), not one framework.
What happens
OpenTelemetryInstrument.bootstrap() builds a provider and installs it:
tracer_provider = TracerProvider(resource=self._build_resource(), sampler=config.opentelemetry_sampler)
set_tracer_provider(tracer_provider)
self._tracer_provider = tracer_provider
...
if config.opentelemetry_endpoint and (span_exporter := self._build_span_exporter()):
tracer_provider.add_span_processor(BatchSpanProcessor(span_exporter))
The OTel SDK enforces set_tracer_provider as set-once per process. When an application installed its
own provider first, the call is refused (the SDK logs "Overriding of current TracerProvider is not
allowed", and _silence_otel_loggers() has just disabled that logger). self._tracer_provider is then
not the global one. Every bootstrapper reads get_tracer_provider() when wiring its middleware or
instrumentor, so tracing itself is fine and goes to the application's provider. The provider
lite-bootstrap built is what is left over.
Why it is not merely a wasted object
It carries the configured resource, sampler and exporter, and nothing will ever feed it a span.
BatchSpanProcessor starts its worker on construction:
threads before/after BatchSpanProcessor: 1 2
after shutdown: 1
So an OTLP-configured service that loses the race runs an idle exporter worker thread and an open
connection to the collector for the process lifetime, until teardown() calls shutdown(). With
opentelemetry_log_traces=True there is also an orphaned SimpleSpanProcessor.
None of this is reported. The one signal the SDK emits is suppressed by _silence_otel_loggers(),
which runs immediately before.
Proposal
A diagnostic, not a behaviour change:
set_tracer_provider(tracer_provider)
if get_tracer_provider() is not tracer_provider:
warn_at_caller("a TracerProvider is already installed; the configured exporter, sampler and "
"resource will not be used")
Deliberately not proposed: skipping provider construction when one already exists. That would
silently discard an explicit opentelemetry_endpoint, which is worse than the leak.
The design cost, which is the real work here
The warning would fire on nearly every test in the suite, since the first test to bootstrap wins the
process global and all the others then lose it. It needs either a filter in tests/conftest.py or a
restructure of how the otel tests share the global. That decision is the substance of this issue; the
three-line diff is not.
Related
The set-once lifecycle is already documented in the OpenTelemetryInstrument docstring ("The supported
lifecycle is one OpenTelemetryInstrument per process"), but that covers bootstrapping twice. This is
the different case where the application owns the provider, which is legitimate and is exactly what
keeps the #223 fix from breaking existing FastStream users.
Affects every bootstrapper (FastAPI, Litestar, Free, and FastStream since #223), not one framework.
What happens
OpenTelemetryInstrument.bootstrap()builds a provider and installs it:The OTel SDK enforces
set_tracer_provideras set-once per process. When an application installed itsown provider first, the call is refused (the SDK logs "Overriding of current TracerProvider is not
allowed", and
_silence_otel_loggers()has just disabled that logger).self._tracer_provideris thennot the global one. Every bootstrapper reads
get_tracer_provider()when wiring its middleware orinstrumentor, so tracing itself is fine and goes to the application's provider. The provider
lite-bootstrap built is what is left over.
Why it is not merely a wasted object
It carries the configured resource, sampler and exporter, and nothing will ever feed it a span.
BatchSpanProcessorstarts its worker on construction:So an OTLP-configured service that loses the race runs an idle exporter worker thread and an open
connection to the collector for the process lifetime, until
teardown()callsshutdown(). Withopentelemetry_log_traces=Truethere is also an orphanedSimpleSpanProcessor.None of this is reported. The one signal the SDK emits is suppressed by
_silence_otel_loggers(),which runs immediately before.
Proposal
A diagnostic, not a behaviour change:
Deliberately not proposed: skipping provider construction when one already exists. That would
silently discard an explicit
opentelemetry_endpoint, which is worse than the leak.The design cost, which is the real work here
The warning would fire on nearly every test in the suite, since the first test to bootstrap wins the
process global and all the others then lose it. It needs either a filter in
tests/conftest.pyor arestructure of how the otel tests share the global. That decision is the substance of this issue; the
three-line diff is not.
Related
The set-once lifecycle is already documented in the
OpenTelemetryInstrumentdocstring ("The supportedlifecycle is one
OpenTelemetryInstrumentper process"), but that covers bootstrapping twice. This isthe different case where the application owns the provider, which is legitimate and is exactly what
keeps the #223 fix from breaking existing FastStream users.