Otel.SDK.Trace.TracerProvider (otel v0.2.0)

Copy Markdown View Source

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-183init_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

FunctionRole
start_link/1SDK (lifecycle)
get_tracer/2SDK (OTel API MUST) — trace/api.md §Get a Tracer L107-L157
shutdown/2SDK (OTel API MUST) — trace/sdk.md §Shutdown
force_flush/2SDK (OTel API MUST) — trace/sdk.md §ForceFlush

Deferred Development-status features

  • TracerConfig (enabled flag). Spec trace/sdk.md L197-L218 (Status: Development) defines a per-Tracer config with enabled: true | false. When enabled=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 of Tracer.enabled?/2 (spec L223-L227) IS honoured (see tracer.ex); the disabled-Tracer leg waits for spec stabilisation.

References

  • OTel Trace SDK §TracerProvider: opentelemetry-specification/specification/trace/sdk.md L36-L116
  • OTel Trace API §TracerProvider: opentelemetry-specification/specification/trace/api.md L88-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

config()

@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_limits come from the user's start_link/1 config (or defaults).
  • :shut_down is the lifecycle flag flipped by handle_call({:shutdown, _}, _, _). Once true, get_tracer/2 returns the noop tracer and force_flush/2 / shutdown/2 reply with {:error, :already_shutdown}.
  • :processors_key is the :persistent_term key under which Otel.SDK.Trace.Tracer reads the projected [{module, callback_config}] list on every start_span and end_span.

processor_entry()

@type processor_entry() :: %{
  module: module(),
  pid: pid() | nil,
  callback_config: Otel.SDK.Trace.SpanProcessor.config()
}

A registered processor in the provider state.

  • :module — the SpanProcessor implementation.
  • :pid — the PID returned by module.start_link/1, or nil for module-only processors that expose no start_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

child_spec(init_arg)

Returns a specification to start this module under a supervisor.

See Supervisor.

config(server)

@spec config(server :: GenServer.server()) :: config() | %{}

SDK (introspection) — Returns the current configuration snapshot, or an empty map when the provider isn't running.

force_flush(server, timeout \\ 30000)

@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.

get_tracer(server, instrumentation_scope)

@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.

resource(server)

@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.

shutdown(server, timeout \\ 30000)

@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.

start_link(opts)

@spec start_link(opts :: keyword()) :: GenServer.on_start()

SDK (lifecycle) — Starts the TracerProvider with the given configuration.