SDK implementation of the LoggerProvider.
A GenServer that owns log configuration (resource,
processors) and creates loggers. Registers itself as the
global LoggerProvider on start.
Provider-owned processor lifecycle
When a registered processor module exports start_link/1,
the LoggerProvider starts it as a linked child during
init/1, captures the resulting PID, and passes that PID to
every behaviour callback (on_emit/3, 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 LoggerProvider 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 LoggerProvider 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 Logger.emit walks —
graceful degradation, the other processors keep working.
Once removed, the processor is not re-added; if its
module is supervised by us, the LoggerProvider's own crash
takes its linked processors with it (no orphans).
All public functions are safe for concurrent use, satisfying
spec logs/api.md L172-L174 (Status: Stable, #4885) —
"LoggerProvider — 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) — start the provider GenServer |
get_logger/2 | SDK (OTel API MUST) — logs/api.md §Get a Logger L60-L97 |
shutdown/2 | SDK (OTel API MUST) — logs/sdk.md §Shutdown |
force_flush/2 | SDK (OTel API MUST) — logs/sdk.md §ForceFlush |
resource/1 | SDK (introspection) — read provider resource |
config/1 | SDK (introspection) — read full config snapshot |
References
- OTel Logs SDK §LoggerProvider:
opentelemetry-specification/specification/logs/sdk.md - OTel Logs API §LoggerProvider:
opentelemetry-specification/specification/logs/api.mdL36-L97
Summary
Types
Runtime state held by the LoggerProvider 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
(logs/sdk.md §ForceFlush).
SDK (OTel API MUST) — Get a Logger
(logs/api.md §Get a Logger L60-L97).
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
(logs/sdk.md §Shutdown).
SDK (lifecycle) — Starts the LoggerProvider with the given configuration.
Types
@type config() :: %{ resource: Otel.SDK.Resource.t(), processors: [processor_entry()], log_record_limits: Otel.SDK.Logs.LogRecordLimits.t(), shut_down: boolean(), processors_key: {module(), :processors, reference()} }
Runtime state held by the LoggerProvider GenServer.
:resource/:processors/:log_record_limitscome from the user'sstart_link/1config (or defaults).:shut_downis the lifecycle flag flipped byhandle_call({:shutdown, _}, _, _). Oncetrue,get_logger/2returns the noop logger andforce_flush/2/shutdown/2reply with{:error, :already_shutdown}.:processors_keyis the:persistent_termkey under whichOtel.SDK.Logs.Loggerreads the projected[{module, callback_config}]list on every emit.
@type processor_entry() :: %{ module: module(), pid: pid() | nil, callback_config: Otel.SDK.Logs.LogRecordProcessor.config() }
A registered processor in the provider state.
:module— theLogRecordProcessorimplementation.:pid— the PID returned bymodule.start_link/1, ornilfor module-only processors that expose nostart_link/1.:callback_config— what the LoggerProvider 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
(logs/sdk.md §ForceFlush).
Forces all registered processors to flush pending log records.
timeout is forwarded to each processor's force_flush/2 and
also bounds the outer GenServer call. Default 30_000ms.
@spec get_logger( server :: GenServer.server(), instrumentation_scope :: Otel.API.InstrumentationScope.t() ) :: Otel.API.Logs.Logger.t()
SDK (OTel API MUST) — Get a Logger
(logs/api.md §Get a Logger L60-L97).
Falls back to the Noop logger 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
(logs/sdk.md §Shutdown).
Invokes shutdown on all registered processors. After shutdown,
get_logger/2 returns the noop logger. 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 LoggerProvider with the given configuration.