Repository navigation
feat(logging): add opt-in session_id and trace_id correlation to JSON log records via contextvars - #33260
Conversation
… records via contextvars Adds two ContextVar instances (session_id_var, trace_id_var) to litellm/_logging.py and two setter functions (set_session_id, set_trace_id). Logging.__init__() now calls both setters after assigning litellm_trace_id so every JSON log record emitted within the async request context carries trace_id and, when provided, session_id — enabling log correlation in Loki, CloudWatch Logs Insights, and other structured-log sinks without any changes to individual log call sites. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
Deepanshu seems not to be a GitHub user. You need a GitHub account to be able to sign the CLA. If you have already a GitHub account, please add the email address used for this commit to your account. You have signed the CLA already but the status is still pending? Let us recheck it. |
Greptile SummaryThis PR adds opt-in
Confidence Score: 4/5Safe to merge for the proxy use-case, but the conditional The ContextVar injection in
|
| Filename | Overview |
|---|---|
| litellm/_logging.py | Adds two module-level ContextVars (session_id_var, trace_id_var) with setter helpers and extends JsonFormatter.format() to inject them into JSON log records when non-empty; properly guards with not in json_record to allow caller-supplied extra={} to take precedence. |
| litellm/litellm_core_utils/litellm_logging.py | Wires set_trace_id (unconditional) and set_session_id (conditional on litellm_session_id being present in kwargs) into Logging.__init__(); the conditional set_session_id path leaves session_id_var holding a stale value from a prior call when no session ID is provided (already flagged in earlier review thread). |
| tests/test_litellm/test_logging.py | Adds 7 tests covering ContextVar injection, absence, and asyncio task isolation; test_logging_init_does_not_set_session_id_when_absent asserts the cross-call contamination behaviour rather than the correct reset behaviour (already flagged in earlier review thread). Logger cleanup is also missing across tests. |
Reviews (4): Last reviewed commit: "fix(logging): guard session_id/trace_id ..." | Re-trigger Greptile
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
… caller-supplied extra fields
|
Addressed the review comment in the latest commit (b42250a): Greptile — overwrite guard for session_id/trace_id: added |
1 similar comment
|
Addressed Greptile feedback: fixed the stale session_id_var cross-call contamination bug. When litellm_session_id is absent, Logging.init now always resets session_id_var to "" instead of leaving it with a prior request's value. Updated the test to assert the correct reset behavior and renamed it to test_logging_init_resets_session_id_to_empty_when_absent. |
|
Both issues flagged in the Greptile review have been addressed. Comment 1 (session_id_var cross-call leak): the fix was already committed as b0b13c9. The condition that gated set_session_id was replaced with an unconditional call that passes an empty string when no litellm_session_id is present — so a prior request's session ID never bleeds into a subsequent call's log records in the same coroutine or thread. Comment 2 (test asserting the wrong value): the corresponding test test_logging_init_resets_session_id_to_empty_when_absent was updated in the same commit to assert session_id_var.get() == '' rather than the preexisting value, correctly capturing the intended reset behaviour as a regression guard. |
|
Superseding this PR with a new one built on top of |
Relevant issues
Closes #31873
None
Linear ticket
None
Pre-Submission checklist
Screenshots / Proof of Fix
This PR adds structured logging fields and does not change LLM provider behavior; proof of fix is via the test suite (see Changes).
Type
Refactoring / New Feature
Changes
Adds opt-in request correlation to litellm's JSON logging. When
litellm.request_correlation_in_logs = True, every JSON log record emitted through the standard litellm loggers will automatically includetrace_idandsession_idfields sourced from Python contextvars.The implementation follows the standard Python logging pattern: a
CorrelationContextFilter(logging.Filter)subclass stamps eachLogRecordwith the contextvar values before the formatter runs.JsonFormatteralready has a loop that promotes arbitraryrecord.__dict__entries to first-class JSON fields, so no formatter changes are needed. This mirrors the existingSecretRedactionFilterpattern in the same module.set_trace_id()andset_session_id()are the public API for callers to bind correlation IDs.litellm.litellm_core_utils.litellm_logging.Logging.__init__()already calls both at request start, so every log line emitted during a litellm call inherits the correct IDs with no per-call-site wiring. Contextvars provide natural async isolation: concurrent tasks each see only their own IDs._correlation_filteris registered on the module-level stream handler, the JSON exception handler created by_setup_json_exception_handlers(), and any handler passed to_initialize_loggers_with_handler(), so coverage is complete regardless of which code path activates JSON logging.The test helper
_JsonCaptureis updated to registerCorrelationContextFilteron itself so that unit tests exercise the same filter path production code uses.A Greptile review identified that
set_session_idwas only called whenlitellm_session_idwas present in kwargs, meaning a prior request's session ID could persist in the contextvar across sequential calls in the same coroutine. This is fixed:Logging.__init__()now unconditionally callsset_session_idwith an empty string as the fallback, so the var is always reset at the start of each request. The corresponding test was updated to assert the reset behavior rather than the previous (incorrect) preservation behavior.