Skip to content

Production Logging and Request Correlation

LGOS emits standard-library logging.LogRecord objects. The host application chooses how those records are formatted, collected, retained, and exported.

LGOS responsibilities

For HTTP requests handled by the mounted OpenAI-compatible application, LGOS:

  • accepts X-Request-ID, or generates a UUID4 when the header is missing or unusable;
  • returns the request ID in the X-Request-ID response header, including on validation, error, and streaming responses;
  • adds request_id, model, stream, and, for interrupt operations, the LGOS operation_id through request-scoped context; and
  • logs unexpected request failures and application-level server errors with the request context.

LGOS does not emit one generic log record for every successful or client-error request. The ASGI server or ingress proxy owns access logs, including method, path, status, and request duration. This prevents duplicate access records and keeps latency measurement in the server or observability layer that owns it.

LGOS does not deliberately attach prompts, messages, responses, tool data, credentials, full headers, client metadata, or LangGraph state as structured log fields. That is not a data-safety guarantee: accepted X-Request-ID values are caller-controlled, and exception messages or tracebacks from graphs and dependencies can contain arbitrary application data. Treat exported records as potentially sensitive. request_id identifies one HTTP invocation; it is not a LangGraph run_id, a session ID, or a tracing ID.

Deployment responsibilities

The application or deployment configures:

  • text or JSON formatting;
  • stdout/stderr routing and collection;
  • trusted request-ID policy, retention, redaction, and access controls;
  • ASGI server or ingress access logs; and
  • the OpenTelemetry SDK, OTLP export, and server or client instrumentation. LGOS reports graph runs through the OpenTelemetry API; see OpenTelemetry. The demo's optional Demo OpenTelemetry overlay shows one Collector-based deployment pattern.

For latency percentiles and distributed request timing, use the deployment's metrics and tracing system rather than adding a per-request application log.

LGOS does not configure the root logger, install output handlers, select a formatter, write log files, or configure an OpenTelemetry SDK.

If browser code needs to read the returned request ID, expose X-Request-ID in the host application's CORS configuration. Restrict accepted upstream IDs to a trusted proxy or redact them before export when callers must not control retained telemetry.

Application formatting

Configure logging in the host application. lgos serve does this for you; langgraph_openai_serve/server/logging.py shows how it formats LGOS, Uvicorn, and application records together as JSON, or as readable lines at a terminal. It disables Uvicorn access logs unless you pass --access-log; prefer access logging at the ingress layer that owns request timing and retention.

The formatter can include LGOS context fields such as request_id, model, stream, and operation_id. If OpenTelemetry's Python logging instrumentation is enabled, its otelTraceID and otelSpanID fields can coexist with these application fields.

LGOS adds these fields to its own records. To add them to every record handled during a request, including logs from graph nodes, dependencies, and the host, install RequestContextFilter on the output handler:

import logging

from langgraph_openai_serve import RequestContextFilter

handler = logging.StreamHandler()
handler.addFilter(RequestContextFilter())
logging.getLogger().addHandler(handler)

A field that a record already carries keeps its value.

Set severity floors on each output handler, not only on the root logger. A propagated record is offered directly to ancestor handlers, so an ancestor logger's level does not filter a child logger that explicitly emits a lower level.

Langfuse correlation

LGOS passes the following correlation metadata through LangChain's RunnableConfig on every run. Graph code reads it with LangGraph's get_config(), with or without Langfuse, and Langfuse records it as observation metadata when LGOS_ENABLE_LANGFUSE=True:

  • lgos.request_id, when an HTTP request context exists;
  • lgos.operation_id, for an interrupt operation;
  • lgos.model, the registered OpenAI-compatible graph model;
  • lgos.user, the end user, when the request names one with safety_identifier or the deprecated user.

With LGOS_ENABLE_LANGFUSE=True, LGOS also names the end user and the request's non-empty metadata.conversation_id string as Langfuse's native user and session on every observation of the run, so model calls and their cost count toward that user, and names each trace after its registered model. It applies them with Langfuse's propagate_attributes() where the run starts: LangChain runs Langfuse's handler on worker threads with a copy of the caller's context, so trace attributes passed as callback metadata reach only the root observation (langfuse#16177). Langfuse records through a tracer provider of its own, so its observations, with their prompts and outputs, never reach the application's OpenTelemetry exporters; a Langfuse trace and the run's OpenTelemetry trace share one ID.

Both endpoints use the stable graph run name lgos.graph_run. LangGraph propagates primitive configurable fields to callback metadata during execution, so an interrupt-enabled run also exposes the derived checkpoint thread_id to callbacks. That checkpoint identifier is operation state, not conversation identity. LGOS does not set LangChain's native execution run_id or Langfuse's generated trace ID.

Langfuse's LangChain integration creates a trace for each invocation by default. Treat one Responses or Chat Completions request as one trace. When several requests belong to one conversation, send the same UI-owned value as metadata.conversation_id; LGOS sets it as the Langfuse session, which groups the independent traces into one session. This is correlation only: LGOS remains stateless and the client must still send the input items needed by each request. The Langfuse trace ID guidance supports deterministic custom trace IDs for a trusted external ID, but LGOS's X-Request-ID is a correlation header and may be client-supplied or reused. It is therefore metadata here, not a custom trace ID.

For end-to-end distributed tracing, configure the host application and proxy to propagate W3C/OpenTelemetry context. LGOS does not install a second tracing system or create a span around the HTTP request; its graph-run span joins the active trace.