Otel.SDK.Logs.LoggerProvider (otel v0.2.0)

Copy Markdown View Source

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

FunctionRole
start_link/1SDK (lifecycle) — start the provider GenServer
get_logger/2SDK (OTel API MUST) — logs/api.md §Get a Logger L60-L97
shutdown/2SDK (OTel API MUST) — logs/sdk.md §Shutdown
force_flush/2SDK (OTel API MUST) — logs/sdk.md §ForceFlush
resource/1SDK (introspection) — read provider resource
config/1SDK (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.md L36-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

config()

@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_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_logger/2 returns the noop logger and force_flush/2 / shutdown/2 reply with {:error, :already_shutdown}.
  • :processors_key is the :persistent_term key under which Otel.SDK.Logs.Logger reads the projected [{module, callback_config}] list on every emit.

processor_entry()

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

A registered processor in the provider state.

  • :module — the LogRecordProcessor 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 LoggerProvider 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 (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.

get_logger(server, instrumentation_scope)

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

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

start_link(opts)

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

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