Standard-output LogRecordExporter for debugging
(logs/sdk_exporters/stdout.md).
Outputs each Otel.SDK.Logs.LogRecord to stdout in a
human-readable single-line format. Spec L16-L18 — "This
exporter is intended for debugging and learning purposes.
It is not recommended for production use. The output format
is not standardized and can change at any time."
Spec L29-L34 — recommended pairing is
Otel.SDK.Logs.LogRecordProcessor.Simple. The spec SHOULD
is conditional on the SDK providing an auto-configuration
mechanism (e.g. OTEL_LOGS_EXPORTER); this SDK does not
currently, so the pairing is informational rather than
mandated.
Output format
Single line per record, prefixed with [otel]. Fields are
joined with single spaces; absent fields (empty scope)
are dropped entirely so no double-space gaps appear.
[otel] <severity> [scope=<name>] trace=<hex> span=<hex> body=<inspect> attributes=<inspect>- severity — combined display per
data-model.md§Displaying Severity L365-L372: short name derived fromseverity_number(e.g.INFO), withseverity_textappended in parentheses when both are present (INFO (info)). Falls back toseverity_textalone whenseverity_number == 0, the short name alone whenseverity_textis empty, andUNSPECIFIEDwhen both are the proto3 zero value. TheUNSPECIFIEDchoice followsdata-model.mdL298-L302 option 1 (distinct display of missing severity) over option 2 (interpret as INFO) — a debug exporter benefits from preserving the source signal rather than fabricating a level. - scope — emitted only when
scope.nameis non-empty. - trace context — always rendered as 32-hex
trace_idand 16-hexspan_id. When no Context is active,Otel.SDK.Logs.LogRecorddefaults both to the all-zeros invalid sentinel; the field is still emitted so the absence is visible at a glance, matchingOtel.SDK.Trace.SpanExporter.Console.
Concurrency
Spec logs/sdk.md L559-L560 — "Each implementation MUST
document the concurrency characteristics the SDK requires
of the exporter." Console is stateless (the user's config
is opaque, never mutated):
export/2writes to:stdioviaIO.puts/1. The OTel SDK's bundled processors (Simple,Batch) serialiseexport/2calls per spec L572-L573, so Console assumes a singleexport/2in flight at a time.IO.puts/1itself is thread-safe (the IO server serialises writes), so even concurrent invocation by a non-bundled processor would not corrupt output, only interleave records.force_flush/1andshutdown/1are no-ops and therefore safe to call concurrently withexport/2per spec L659-L660.
Shutdown semantics
Spec L637-L639 says "After the call to Shutdown
subsequent calls to Export are not allowed and SHOULD
return a Failure result." Console does not enforce this
internally — shutdown/1 returns :ok without mutating
state, and export/2 continues to work afterward. The
LogRecordExporter behaviour returns :ok | {:error, term()} from shutdown/1 (no new state), so tracking
the shut-down flag would require side-channel storage
(process dictionary, ETS, or a GenServer wrapper) that
is overkill for a debug stdout exporter. The bundled
processors never call export/2 after shutdown/1 by
construction, so the SHOULD is satisfied at the processor
layer in practice.
Public API
| Function | Role |
|---|---|
init/1, export/2, force_flush/1, shutdown/1 | SDK (Console implementation) |
References
- OTel Logs SDK Stdout:
opentelemetry-specification/specification/logs/sdk_exporters/stdout.md - SeverityNumber short-name table:
opentelemetry-specification/specification/logs/data-model.md§Displaying Severity L338-L363 - Parent behaviour:
Otel.SDK.Logs.LogRecordExporter