skip to content

OpenTelemetry calls its Logs API a bridge API rather than something application developers should call directly. What does that mean in practice, how does a log record acquire a trace id, and what are the ways of getting an existing application's logs into OTLP?

level: seniorimportance: should knowfreq 30%

answer

  1. bridge API = for appenders, not app code
  2. keep your logging library, attach an appender
  3. LoggerProvider → processor → OTLP, mirrors spans
  4. trace/span id attached from active context
  5. in-process OTLP vs stdout + collector

basics

~20 s

Applications keep using their existing logging library; an OpenTelemetry appender bridges records into a LoggerProvider, which processes and exports them as OTLP. Records emitted while a span is active pick up its trace and span ids automatically, correlating logs with traces.

solid answer

~50 s

Logs differ from traces and metrics: every language already has entrenched logging libraries, so OpenTelemetry did not try to replace them. The Logs API is aimed at **bridge implementations** — an appender or handler plugged into the existing logging framework that converts each record into an OTLP log record and hands it to a `LoggerProvider`. From there it flows through a log-record processor (batching, in practice) and an exporter, exactly mirroring the span pipeline. **Correlation** comes from context: when a record is bridged while a span is active, the trace and span ids are attached to the log record, so a backend can pivot from a trace to its logs and back. The same idea covers the file route — inject the ids into the log line's structured fields. Two deployment shapes: **in-process bridge** (direct OTLP, keeps structure, loses records if the process dies before flush) or **write structured logs to stdout/file and collect them** (survives crashes, decoupled, but needs a parsing pipeline).

go deeper

for a junior

Say that you keep your existing logging library and an OpenTelemetry appender forwards records, and that logs written during a span carry its trace id.

for a middle

Describe the LoggerProvider → processor → exporter pipeline and the log record's fields, including severity number versus text.

for a senior

Compare in-process OTLP against stdout collection on durability, fidelity and coupling, and diagnose missing trace ids as lost context.

for a principal

Decide the fleet's logging transport, budget for logs being the dominant signal by volume, and rule out double-shipping and exporter feedback loops.

## Why logs are the odd signal With traces and metrics OpenTelemetry could define a fresh API because most applications had no strongly entrenched alternative. Logging is the opposite: every language has one or two dominant libraries, wired into every file of every codebase, with configuration people rely on. Replacing them was never realistic, so the design goal became *interoperation*: define a log **data model** and a transport, and let existing libraries feed it. Hence the phrasing in the specification: the Logs API is a **bridge API**, intended for authors of appenders/handlers, not for application code. You keep calling your logger; someone attaches a bridge. ## The pipeline Structurally it mirrors tracing: - **LoggerProvider** — configured once by the application (or an agent), carrying the same resource that identifies the service. - **Logger** — obtained per instrumentation scope, as with tracers. - **LogRecordProcessor** — batching in production; a simple pass-through exists for debugging. - **Exporter** — OTLP to a collector or backend. The log record itself has a defined shape: timestamp and observed timestamp, severity number plus severity text, body, attributes, resource, instrumentation scope, and trace context fields (trace id, span id, trace flags). The separation of severity *number* from *text* matters: the number is the normalised, comparable value across libraries whose level names differ. ## How correlation happens When the bridge emits a record it reads the active context. If a span is current on that execution, its trace id and span id are written into the record's dedicated fields. That is what makes 'show the logs for this trace' a query rather than a manual timestamp hunt, and it is why the bridge must run where the context is live — a record handed to an async queue and bridged later on another thread loses the association unless the context travelled with it. If you are not exporting logs via OTLP at all, the same correlation is achievable by injecting the ids into the structured fields of the log line and letting your collection pipeline index them. Less integrated, equally effective for pivoting. ## Two deployment shapes **In-process bridge → OTLP.** Records leave the process directly, keeping full structure and typed attributes with no parsing. The costs: telemetry now shares the application's fate (records buffered in the batch processor are lost if it is killed) and its network dependencies, and you must ensure shutdown flushes. **Write to stdout or a file, collect out-of-process.** The classic container pattern: the application writes structured JSON, an agent or sidecar tails it and converts to OTLP. Survives process death, decouples telemetry from the application's networking, and is often mandated by platform logging. The costs are a parsing step, potential fidelity loss (types flattened to strings), and dependence on the collection layer being present. Many fleets run both: OTLP for services where structure matters, file collection as the baseline everywhere. ## Practical cautions - **Do not double-ship.** Bridging to OTLP while also writing the same records to a file that gets collected doubles cost and creates two slightly different copies. - **Severity mapping is a translation.** Check how your library's levels map onto severity numbers before writing alerts on them. - **Watch feedback loops.** If the exporter itself logs errors through the bridged logger, a backend outage can produce a log storm about failing to send logs. Bridges normally suppress their own output; verify it. - **Volume dominates.** Logs are usually the largest signal by bytes; the batching processor's queue and the collection pipeline, not the application, are where that has to be managed.

  • A log line written inside a request handler has no trace id. What do you check?
    Whether a span was actually active on that execution at the moment the record was emitted — a hand-off to a thread pool or an async continuation without context propagation is the usual cause. Then check that the bridge is installed for that logger and reads context, and that records are not being buffered and bridged later on a different thread.
  • Would you export logs directly over OTLP from the process, or write them out and collect them?
    Direct OTLP preserves structure and avoids parsing, but ties telemetry to the process lifetime and its network path, so records buffered at kill time are lost. File or stdout collection survives crashes and decouples the application, at the cost of a parsing stage and some fidelity. Pick per environment; many fleets run file collection as the floor and OTLP where structured attributes matter.

saying these in an interview costs you the question

  • Believing application code is supposed to call the OpenTelemetry Logs API directly instead of its normal logger
  • Expecting trace correlation on records emitted where no span is active
  • Shipping the same records both through the OTLP bridge and through file collection
  • Treating severity text as comparable across libraries instead of using the normalised severity number

context