6.2 The Logging Bridge API & Log Appenders

Key Takeaways

  • OpenTelemetry does not require rewriting existing log statements: the recommended pattern keeps idiomatic frameworks such as Logback, Log4j2, SLF4J, Python logging, Winston, Zap, or Serilog and installs an OpenTelemetry appender.

  • The Logs API (formerly called the Logs Bridge API) exists mainly for appender authors; the current specification also lets instrumentation libraries and applications call it directly, most often to emit structured events.

  • OpenTelemetry Log Appenders bridge native logging events into the OpenTelemetry pipeline in-process, automatically extracting active TraceId and SpanId from ambient context and populating standardized severity and attribute structures.

  • The LoggerProvider orchestrates log processing pipelines via LogRecordProcessors: SimpleLogRecordProcessor delivers synchronous, blocking exports suitable for local debugging and serverless runtimes, while BatchLogRecordProcessor provides asynchronous buffering and batching for high-throughput production workloads.

  • When in-process appenders cannot be deployed, the OpenTelemetry Collector enables non-invasive out-of-process log ingestion using the filelog receiver, parsing operators, and the k8sattributes processor, trading in-process context propagation fidelity for zero application modifications.

Last updated: September 2026

6.2 The Logging Bridge API & Log Appenders

Quick Answer: Developers do not need to rewrite existing log statements against the OpenTelemetry Logs API. They keep their preferred, idiomatic logging frameworks (e.g., Logback, Log4j2, Python logging, Winston, Zap, Serilog). OpenTelemetry provides Log Appenders / Bridges that plug into these native frameworks as handlers. The appender automatically intercepts log events in-process, extracts the active TraceId and SpanId from ambient context, transforms the event into an OpenTelemetry Log Record, and forwards it to the LoggerProvider for processing and OTLP export.

A frequent misconception is that adopting OpenTelemetry requires replacing all existing logger.info() or logger.error() calls with OpenTelemetry API methods. For distributed traces and metrics, developers directly invoke tracer.spanBuilder() or meter.createCounter(). For logs, however, OpenTelemetry enforces a completely different architectural philosophy.


The OpenTelemetry Logging Philosophy

Logging frameworks have evolved over more than twenty-five years. Modern logging libraries—such as Log4j2 and Logback in Java, Winston and Pino in Node.js, standard logging and Structlog in Python, Zap and Zerolog in Go, and Serilog in .NET—are extraordinarily mature. They offer sophisticated capabilities:

  • Highly optimized memory allocation and zero-garbage serialization buffers.
  • Granular hierarchical logger configuration and dynamic runtime level filtering.
  • Rich formatting layouts, custom appenders, and asynchronous ring-buffer queues (e.g., LMAX Disruptor in Log4j2).
  • Extensive community adoption and millions of existing lines of code.

Requiring developers to abandon these battle-tested frameworks to learn a new, generic logging API would impose massive migration friction for zero technical benefit.

The Role of the OpenTelemetry Log API

KEY CONCEPT: The stable Logs API specification (the API was previously called the Logs Bridge API) states two things:

  1. Its primary audience is logging library authors and appender implementers, who use it to bridge existing logging libraries into the OpenTelemetry log data model.
  2. It can also be called directly by instrumentation libraries, instrumented libraries, and applications, and a language may offer a more ergonomic API for that purpose.

In practice this gives a simple rule. Keep ordinary application logging in your existing framework and add an appender, because that is what gives you trace correlation without code churn. Call the Logs API directly when you are writing an appender or an instrumentation library, or when you need to emit a structured event (a log record with an EventName).


How the OpenTelemetry Logging Bridge Works

OpenTelemetry bridges the gap between idiomatic application logging and the OpenTelemetry telemetry pipeline through Log Appenders (also referred to as Log Bridges or Log Handlers).

An OpenTelemetry Log Appender is a native plugin that implements the appender/handler interface of the underlying logging framework:

  • Java: OpenTelemetryAppender for Logback (ch.qos.logback.core.Appender) or Log4j2 (org.apache.logging.log4j.core.Appender).
  • Python: LoggingHandler (logging.Handler) registered with Python's standard logging root logger.
  • Node.js: Custom transports for Winston (winston-transport) or stream destinations for Pino.
  • Go: Custom core for Uber Zap (zapcore.Core) or handler for Go standard library slog (slog.Handler).
  • .NET: OpenTelemetryLoggerProvider implementing Microsoft.Extensions.Logging.ILoggerProvider.

In-Process Execution Flow

When an application logs a message during execution, the bridge operates entirely in-process through a deterministic five-step lifecycle:

Application Code -> logger.info("Order processed", kv("order_id", 1024))
       │
       ▼
Native Logging Framework (Logback / Log4j2 / Winston / Python logging)
       │
       ▼
OpenTelemetry Log Appender (Plugin / Handler)
  ├── 1. Intercepts native log event
  ├── 2. Reads ambient in-process context (ThreadLocal / ContextVars)
  │      └── Extracts active TraceId, SpanId, and TraceFlags
  ├── 3. Maps native level to OpenTelemetry SeverityNumber & SeverityText
  ├── 4. Extracts message body, parameters, and MDC / structured keys into Attributes
  └── 5. Constructs OpenTelemetry LogRecord
       │
       ▼
OpenTelemetry LoggerProvider (SDK Pipeline Factory)
       │
       ▼
LogRecordProcessor (SimpleLogRecordProcessor or BatchLogRecordProcessor)
       │
       ▼
LogRecordExporter (OTLP Exporter over gRPC or HTTP/Protobuf)

Step-by-Step Breakdown

  1. Interception: The application executes standard logging code (e.g., logger.error("Payment gateway timeout", ex)). The native logging framework evaluates level filters and dispatches the event to all configured appenders, including the OpenTelemetry appender.
  2. Context Extraction: The OpenTelemetry appender accesses the ambient in-process context storage (such as ThreadLocal in Java, ContextVars in Python, AsyncLocalStorage in Node.js, or context.Context in Go). It checks whether an active OpenTelemetry distributed span exists. If present, it extracts the current TraceId, SpanId, and TraceFlags.
  3. Severity Normalization: The appender converts the framework's native log level into the standardized OpenTelemetry SeverityNumber (e.g., Logback Level.ERROR -> SeverityNumber.ERROR = 17) and stores the original level name in SeverityText ("ERROR").
  4. Attribute & Payload Mapping: The appender maps structured MDC (Mapped Diagnostic Context), thread names, logger category names, and method arguments into OpenTelemetry Attributes. The primary log message or template is assigned to Body.
  5. SDK Dispatch: The appender obtains a Logger instance from the global LoggerProvider and calls emit(logRecord). The log record now enters the standard OpenTelemetry SDK processing pipeline.

The LoggerProvider and LogRecordProcessors

The LoggerProvider is the central factory interface in the OpenTelemetry SDK for managing the logging pipeline. Similar to TracerProvider for traces and MeterProvider for metrics, the LoggerProvider:

  • Associates common Resource metadata (service.name, service.version, deployment.environment.name) with all emitted log records.
  • Manages one or more registered LogRecordProcessor implementations.
  • Exposes lifecycle control methods (forceFlush(), shutdown()).

LogRecordProcessor Implementations

When a log record is submitted to the LoggerProvider, it passes sequentially through registered LogRecordProcessor instances. The OpenTelemetry SDK provides two standard processors with distinct operational profiles:

1. SimpleLogRecordProcessor

The SimpleLogRecordProcessor processes and exports each log record synchronously on the caller's thread the moment it is emitted:

  • When logger.info() is invoked, the application execution thread blocks until the configured LogRecordExporter finishes transmitting the record over the network or writing it to the console.
  • Use Cases: Local debugging, unit and integration tests, command-line utilities, and serverless environments (e.g., AWS Lambda, Google Cloud Run).
  • Serverless Significance: In serverless runtimes, the cloud provider freezes execution threads immediately after the function handler returns. Background worker threads cannot reliably export buffered telemetry. Using SimpleLogRecordProcessor (or calling forceFlush() before function exit) guarantees that logs are dispatched before the runtime freezes.
  • Production Warning: SimpleLogRecordProcessor is an anti-pattern for high-throughput production microservices. Blocking application request threads on synchronous network I/O dramatically inflates request latency and degrades throughput.

2. BatchLogRecordProcessor

The BatchLogRecordProcessor is the standard processor for high-throughput production workloads:

  • It decouples application execution from telemetry export by buffering incoming log records in an internal, bounded in-memory queue.
  • A dedicated background worker thread periodically drains the queue and dispatches batches of log records to the configured exporter.
  • Configurable Parameters:
    • Maximum queue size (OTEL_BLRP_MAX_QUEUE_SIZE, default 2048 records). If the queue fills due to downstream backpressure, new records are dropped to prevent application out-of-memory (OOM) crashes.
    • Schedule delay (OTEL_BLRP_SCHEDULE_DELAY, default 1000 ms): the interval between export attempts. Note that this is shorter than the span processor's 5000 ms default.
    • Maximum export batch size (OTEL_BLRP_MAX_EXPORT_BATCH_SIZE, default 512 records; must not exceed the queue size).
    • Export timeout (OTEL_BLRP_EXPORT_TIMEOUT, default 30000 ms).

Ingesting Logs via the OpenTelemetry Collector

While deploying in-process OpenTelemetry log appenders is the recommended approach for modern services, organizations frequently operate legacy commercial off-the-shelf (COTS) applications, pre-compiled third-party containers, or environments where modifying application code or dependencies is prohibited.

In these environments, OpenTelemetry provides out-of-process log ingestion through the OpenTelemetry Collector:

  1. filelog Receiver — Deployed as an agent or DaemonSet on the host or Kubernetes node, the filelog receiver tails container log files (e.g., /var/log/pods/*/*/*.log), handles log rotation, and persists its read offsets across restarts when a storage extension such as file_storage is configured.
  2. Parsing Operators — The receiver applies an internal pipeline of parsing operators:
    • regex_parser / json_parser: Decodes structured JSON or extracts regex capture groups into distinct attributes.
    • timestamp parser: Extracts event time strings from the log body to populate Timestamp, while the Collector automatically populates ObservedTimestamp with the current arrival time.
    • severity_parser: Translates textual level strings ("warning", "err") into normalized SeverityNumber values.
  3. k8sattributes Processor — Intercepts the raw log records, extracts the container ID or pod IP, queries the local Kubernetes API server, and automatically enriches the log record with rich metadata (k8s.pod.name, k8s.namespace.name, k8s.node.name, k8s.container.name).

Architectural Comparison: In-Process Appender vs. Out-of-Process Collector Scraping

Choosing between in-process log appenders and out-of-process Collector log scraping is a classic architectural trade-off:

Architectural DimensionIn-Process Log Appender / BridgeOut-of-Process Collector File Scraping
Distributed Context CorrelationAutomatic & 100% reliable. Directly extracts TraceId, SpanId, and TraceFlags from in-memory ambient context (ThreadLocal).Complex & indirect. Application must be configured to format trace IDs into text/JSON strings; Collector must use regex/JSON parsers to reconstruct correlation.
Application Performance OverheadModerate. Requires in-memory buffering and CPU cycles for serialization and OTLP network dispatch within the application process.Zero application telemetry overhead. Application writes plain text to stdout/stderr; OS kernel buffers and independent Collector process handle all parsing.
Code & Dependency ModificationRequires adding the OpenTelemetry appender dependency to the application build and updating logging configuration (e.g., logback.xml).Non-invasive. Zero application code, dependency, or configuration changes required; operates transparently on any container.
Structured Attribute FidelityPreserves native data types (numbers, booleans, nested maps, arrays) directly in AnyValue without serialization loss.Must serialize attributes to text strings or JSON lines, requiring downstream deserialization and type inference.
Process Crash ResilienceIn-flight logs buffered in application memory may be lost if the container process abruptly crashes or is killed (SIGKILL).Highly resilient. Logs written to stdout are captured by the container engine and persisted to disk independently of application process survival.
Legacy & COTS CompatibilityIncompatible with closed-source, pre-compiled binaries or languages without OpenTelemetry SDK support.Universal compatibility. Ingests logs from any binary, shell script, or legacy operating system daemon capable of writing to disk or stdout.
Resource Attribution FidelityExplicitly enriched with SDK Resource attributes defined at application initialization time.Relies on the Collector's k8sattributes processor or host environment to discover and append entity metadata.
Loading diagram...
In-Process Logging Bridge Architecture and Export Pipeline
Test Your Knowledge

A senior engineer reviews a pull request for a large Java microservice. The pull request replaces thousands of SLF4J calls (log.info, log.error) with direct calls to the OpenTelemetry Logs API, arguing that this is required for trace correlation. How should the reviewer respond?

A

Approve it, because OpenTelemetry only attaches TraceId and SpanId to records emitted through its own Logs API

B

Reject it and require the team to stop logging entirely and convert every statement into a span event

C

Request changes: keep SLF4J and add an OpenTelemetry Logback or Log4j2 appender, which provides the same correlation; the Logs API is mainly for appender authors, so the rewrite adds churn without benefit

D

Approve it only for ERROR and FATAL statements while INFO and DEBUG stay on SLF4J

Test Your Knowledge

A DevOps team is deploying an event-driven serverless application on AWS Lambda instrumented with the OpenTelemetry SDK. The Lambda functions handle low-frequency webhooks, executing in under 150 milliseconds before the cloud runtime freezes execution. During initial testing, the team notices that log records emitted immediately before the Lambda handler returns are consistently lost or delayed until subsequent invocations. Which configuration adjustment directly eliminates this telemetry loss in serverless runtimes?

A

Increase the BatchLogRecordProcessor max_queue_size to 50,000 to prevent buffer overflow during execution spikes

B

Switch the export protocol from OTLP/gRPC to uncompressed OTLP/JSON over plain HTTP/1.0

C

Replace the in-process appender with an asynchronous local disk file writer that flushes to ephemeral container storage

D

Configure the LoggerProvider with a SimpleLogRecordProcessor or explicitly invoke forceFlush() before the serverless handler terminates

Test Your Knowledge

An architecture team is comparing two strategies for capturing container logs in a Kubernetes cluster: (Strategy 1) embedding the OpenTelemetry Logback Appender within each container runtime, versus (Strategy 2) deploying the OpenTelemetry Collector as a DaemonSet using the filelog receiver to scrape container stdout log files from the node filesystem. Which statement accurately describes a major operational difference between these two approaches?

A

Strategy 1 automatically injects the active in-process TraceId and SpanId into every log record without requiring custom log formatting, whereas Strategy 2 requires the application to explicitly serialize trace identifiers into the log string and relies on regex/JSON parsing in the Collector to reconstruct correlation

B

Strategy 1 requires zero application dependencies or configuration changes, whereas Strategy 2 requires modifying container source code to integrate the Kubernetes downward API

C

Strategy 1 prevents memory exhaustion during downstream network outages, whereas Strategy 2 causes container process crashes if the node collector runs out of memory

D

Strategy 1 completely bypasses the OpenTelemetry LoggerProvider, whereas Strategy 2 requires every container to run an embedded OpenTelemetry SDK

Sections you finish are checked off in the contents.