SDK implementation of the Otel.API.Trace.TracerProvider
behaviour (trace/sdk.md §TracerProvider L36-L116).
A GenServer that owns trace configuration (sampler,
processors, id_generator, resource, span_limits) and creates
tracers. Registers itself as the global TracerProvider on
start.
Provider-owned processor lifecycle
When a registered processor module exports start_link/1,
the TracerProvider starts it as a linked child during
init/1, captures the resulting PID, and passes that PID
to every behaviour callback (on_start/3, on_end/2,
shutdown/2, force_flush/2) via the %{pid: pid} config.
The user does not start processors separately and does not
provide an atom registration name — the TracerProvider owns
the lifecycle.
This mirrors the typical OTel SDK pattern (Java, Go, Python,
and erlang's otel_tracer_server.erl:158-183 —
init_processor starting children under a dedicated
supervisor).
Modules without a start_link/1 export are registered as
module-only processors (no process, no PID). Their callback
config is whatever the user supplied verbatim. This supports
pure-callback fixtures used in tests; production processors
(Simple, Batch) all expose start_link/1.
Crash handling
init/1 enables trap_exit so a processor crash is
delivered to the TracerProvider as {:EXIT, pid, reason}
rather than propagating along the link. The dead processor
is removed from both the in-memory list and the
:persistent_term fast path that Tracer.start_span and
Span.end_span walk — graceful degradation, the other
processors keep working. Once removed, the processor is
not re-added; if its module is supervised by us, the
TracerProvider's own crash takes its linked processors with
it (no orphans).
All public functions are safe for concurrent use, satisfying
spec trace/api.md L843-L853 (Status: Stable, #4887) —
"TracerProvider — all methods MUST be documented that
implementations need to be safe for concurrent use by
default."
Public API
| Function | Role |
|---|---|
start_link/1 | SDK (lifecycle) |
get_tracer/2 | SDK (OTel API MUST) — trace/api.md §Get a Tracer L107-L157 |
shutdown/2 | SDK (OTel API MUST) — trace/sdk.md §Shutdown |
force_flush/2 | SDK (OTel API MUST) — trace/sdk.md §ForceFlush |
Deferred Development-status features
- TracerConfig (
enabledflag). Spectrace/sdk.mdL197-L218 (Status: Development) defines a per-Tracer config withenabled: true | false. Whenenabled=false, the Tracer MUST behave equivalently to a No-op Tracer. Not implemented — every Tracer obtained from this provider is currently always active. The no-SpanProcessors leg ofTracer.enabled?/2(spec L223-L227) IS honoured (seetracer.ex); the disabled-Tracer leg waits for spec stabilisation.
References
- OTel Trace SDK §TracerProvider:
opentelemetry-specification/specification/trace/sdk.mdL36-L116 - OTel Trace API §TracerProvider:
opentelemetry-specification/specification/trace/api.mdL88-L157
Summary
Types
Runtime state held by the TracerProvider GenServer.
A registered processor in the provider state.
Functions
Returns a specification to start this module under a supervisor.
SDK (introspection) — Returns the current configuration snapshot, or an empty map when the provider isn't running.
SDK (OTel API MUST) — ForceFlush
(trace/sdk.md §ForceFlush).
SDK (OTel API MUST) — Get a Tracer
(trace/api.md §Get a Tracer L107-L157).
SDK (introspection) — Returns the resource associated with
this provider, or Otel.SDK.Resource.default/0 when the
provider isn't running.
SDK (OTel API MUST) — Shutdown
(trace/sdk.md §Shutdown).
SDK (lifecycle) — Starts the TracerProvider with the given configuration.
Types
@type config() :: %{ sampler: {module(), term()}, processors: [processor_entry()], id_generator: module(), resource: Otel.SDK.Resource.t(), span_limits: Otel.SDK.Trace.SpanLimits.t(), shut_down: boolean(), processors_key: {module(), :processors, reference()} }
Runtime state held by the TracerProvider GenServer.
:sampler/:processors/:id_generator/:resource/:span_limitscome from the user'sstart_link/1config (or defaults).:shut_downis the lifecycle flag flipped byhandle_call({:shutdown, _}, _, _). Oncetrue,get_tracer/2returns the noop tracer andforce_flush/2/shutdown/2reply with{:error, :already_shutdown}.:processors_keyis the:persistent_termkey under whichOtel.SDK.Trace.Tracerreads the projected[{module, callback_config}]list on everystart_spanandend_span.
@type processor_entry() :: %{ module: module(), pid: pid() | nil, callback_config: Otel.SDK.Trace.SpanProcessor.config() }
A registered processor in the provider state.
:module— theSpanProcessorimplementation.:pid— the PID returned bymodule.start_link/1, ornilfor module-only processors that expose nostart_link/1.:callback_config— what the TracerProvider passes to every behaviour callback.%{pid: pid}for process-backed processors, the user's original config map for module-only processors.
Functions
Returns a specification to start this module under a supervisor.
See Supervisor.
@spec config(server :: GenServer.server()) :: config() | %{}
SDK (introspection) — Returns the current configuration snapshot, or an empty map when the provider isn't running.
@spec force_flush(server :: GenServer.server(), timeout :: timeout()) :: :ok | {:error, term()}
SDK (OTel API MUST) — ForceFlush
(trace/sdk.md §ForceFlush).
Forces all registered processors to export pending spans.
timeout is forwarded to each processor's force_flush/2
and also bounds the outer GenServer call. Default 30_000ms.
@spec get_tracer( server :: GenServer.server(), instrumentation_scope :: Otel.API.InstrumentationScope.t() ) :: Otel.API.Trace.Tracer.t()
SDK (OTel API MUST) — Get a Tracer
(trace/api.md §Get a Tracer L107-L157).
Falls back to the Noop tracer if server is no longer alive.
@spec resource(server :: GenServer.server()) :: Otel.SDK.Resource.t()
SDK (introspection) — Returns the resource associated with
this provider, or Otel.SDK.Resource.default/0 when the
provider isn't running.
@spec shutdown(server :: GenServer.server(), timeout :: timeout()) :: :ok | {:error, term()}
SDK (OTel API MUST) — Shutdown
(trace/sdk.md §Shutdown).
Invokes shutdown on all registered processors. After shutdown,
get_tracer/2 returns the noop tracer. Can only be called
once; subsequent calls reply {:error, :already_shutdown}.
timeout is forwarded to each processor's shutdown/2 and
also bounds the outer GenServer call. Default 30_000ms.
@spec start_link(opts :: keyword()) :: GenServer.on_start()
SDK (lifecycle) — Starts the TracerProvider with the given configuration.