Logs Bridge API
Status: Experimental
Note: this document defines a log backend API. The API is not intended to be called by application developers directly. It is provided for logging library authors to build Appenders, which use this API to bridge between existing logging libraries and the OpenTelemetry log data model.
The Logs Bridge API consist of these main classes:
- LoggerProvider is the entry point of the API. It provides access to
Logger
s. - Logger is the class responsible for emitting logs as LogRecords.
graph TD A[LoggerProvider] -->|Get| B(Logger) B -->|Emit| C(LogRecord)
LoggerProvider
Logger
s can be accessed with a LoggerProvider
.
In implementations of the API, the LoggerProvider
is expected to be the stateful
object that holds any configuration.
Normally, the LoggerProvider
is expected to be accessed from a central place.
Thus, the API SHOULD provide a way to set/register and access a global default
LoggerProvider
.
Notwithstanding any global LoggerProvider
, some applications may want to or have
to use multiple LoggerProvider
instances, e.g. to have different configuration
(like LogRecordProcessors) for each (and
consequently for the Logger
s obtained from them), or because it’s easier with
dependency injection frameworks. Thus, implementations of LoggerProvider
SHOULD
allow creating an arbitrary number of instances.
LoggerProvider operations
The LoggerProvider
MUST provide the following functions:
- Get a
Logger
Get a Logger
This API MUST accept the following parameters:
name
(required): This name SHOULD uniquely identify the instrumentation scope, such as the instrumentation library (e.g.io.opentelemetry.contrib.mongodb
), package, module or class name. If an application or library has built-in OpenTelemetry instrumentation, both Instrumented library and Instrumentation library may refer to the same library. In that scenario, thename
denotes a module name or component name within that library or application. In case an invalid name (null or empty string) is specified, a working Logger implementation MUST be returned as a fallback rather than returning null or throwing an exception, itsname
property SHOULD be set to an empty string, and a message reporting that the specified value is invalid SHOULD be logged. A library implementing the OpenTelemetry API may also ignore this name and return a default instance for all calls, if it does not support “named” functionality (e.g. an implementation which is not even observability-related). ALoggerProvider
could also return a no-opLogger
here if application owners configure the SDK to suppress telemetry produced by this library.version
(optional): Specifies the version of the instrumentation scope if the scope has a version (e.g. a library version). Example value: 1.0.0.schema_url
(optional): Specifies the Schema URL that should be recorded in the emitted telemetry.include_trace_context
(optional): Specifies whether the Trace Context should automatically be passed on to theLogRecord
s emitted by theLogger
. This SHOULD be true by default.attributes
(optional): Specifies the instrumentation scope attributes to associate with emitted telemetry.
Logger
s are identified by name
, version
, and schema_url
fields. When more
than one Logger
of the same name
, version
, and schema_url
is created, it
is unspecified whether or under which conditions the same or different Logger
instances are returned. It is a user error to create Loggers with different
attributes but the same identity.
The term identical applied to Logger
s describes instances where all
identifying fields are equal. The term distinct applied to Logger
s describes
instances where at least one identifying field has a different value.
Implementations MUST NOT require users to repeatedly obtain a Logger
again with
the same name+version+schema_url+include_trace_context+attributes
to pick up configuration changes. This can be achieved either by allowing to
work with an outdated configuration or by ensuring that new configuration
applies also to previously returned Logger
s.
Note: This could, for example, be implemented by storing any mutable
configuration in the LoggerProvider
and having Logger
implementation objects
have a reference to the LoggerProvider
from which they were obtained.
If configuration must be stored per-Logger (such as disabling a certain Logger
),
the Logger
could, for example, do a look-up with its name+version+schema_url+include_trace_context+attributes
in a map in the LoggerProvider
, or the LoggerProvider
could maintain a registry
of all returned Logger
s and actively update their configuration if it changes.
The effect of associating a Schema URL with a Logger
MUST be that the telemetry
emitted using the Logger
will be associated with the Schema URL, provided that
the emitted data format is capable of representing such association.
Logger
The Logger
is responsible for emitting LogRecord
s.
Note that Logger
s should not be responsible for configuration. This should be
the responsibility of the LoggerProvider
instead.
Logger operations
The Logger
MUST provide functions to:
Emit LogRecord
Emit a LogRecord
to the processing pipeline.
This function MAY be named logRecord
.
Parameters:
logRecord
- the LogRecord to emit.
LogRecord
The API emits LogRecords using the LogRecord
data model.
A function receiving this as an argument MUST be able to set the following fields:
- Timestamp
- Observed Timestamp
- Context that contains the TraceContext
- Severity Number
- Severity Text
- Body
- Attributes
Usage
How to Create Log4J Style Appender
An Appender implementation can be used to allow emitting logs via OpenTelemetry LogRecordExporters. This approach is typically used for applications which are fine with changing the log transport and is one of the supported log collection approaches.
The Appender implementation will typically acquire a Logger from the
global LoggerProvider at startup time, then construct
LogRecord
s for each log received from the application, and then call
Emit LogRecord.
Implicit Context Injection
and Explicit Context Injection describe how an
Appender injects TraceContext
into LogRecord
s.

This same approach can be also used for example for:
- Python logging library by creating a Handler.
- Go zap logging library by implementing the Core interface. Note that since there is no implicit Context in Go it is not possible to get and use the active Span.
Appenders can be created in OpenTelemetry language libraries by OpenTelemetry maintainers, or by 3rd parties for any logging library that supports a similar extension mechanism. This specification recommends each OpenTelemetry language library to include out-of-the-box Appender implementation for at least one popular logging library.
Implicit Context Injection
When Context is implicitly available (e.g. in Java) the log library extension
can rely on automatic context propagation
by obtaining a Logger with include_trace_context=true
.
Some log libraries have mechanisms specifically tailored for injecting contextual
information into logs, such as MDC in Log4j. When available such mechanisms may
be the preferable place to fetch the TraceContext
and inject it into
the LogRecord
, since it usually allows fetching of the context to work
correctly even when log records are emitted asynchronously, which otherwise can
result in the incorrect implicit context being fetched.
TODO: clarify how works or doesn’t work when the log statement call site and the log appender are executed on different threads.
Explicit Context Injection
In languages where the Context must be provided explicitly (e.g. Go) the end
user must capture the context and explicitly pass it to the logging subsystem in
order for TraceContext
to be recorded in LogRecord
s.
Support for OpenTelemetry for logging libraries in these languages typically can
be implemented in the form of logger wrappers that can capture the context once,
when the span is created and then use the wrapped logger to execute log
statements in a normal way. The wrapper will be responsible for injecting the
captured context in the LogRecord
s.
This specification does not define how exactly it is achieved since the actual mechanism depends on the language and the particular logging library used. In any case the wrappers are expected to make use of the Trace Context API to get the current active span.
See an example of how it can be done for zap logging library for Go.