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-IDresponse header, including on validation, error, and streaming responses; - adds
request_id,model,stream, and, for interrupt operations, the LGOSoperation_idthrough 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 withsafety_identifieror the deprecateduser.
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.