LiteLLM 全链路追踪(Trace ID / Session ID)文档¶
本系列文档完整描述 LiteLLM 代理服务中 trace_id 的流动路径:从 HTTP 请求到达、Header / Body 解析,到 access log、应用 JSON 日志、PostgreSQL SpendLogs.session_id、S3 完整日志正文、UI Logs 页搜索的全部出口与一一对应关系。
适用场景:
- 接入方(公司内部 mirror / model-adapter 服务):透传
X-Trace-Id后能在哪里看到自己的请求。 - 运维:拿到一个 trace_id 后该去哪几个地方查(pgsql、UI、S3、ELK)。
- 排查 bug:当 access log 有 trace_id 但 SpendLogs.session_id 是 UUID 时如何定位。
文档目录¶
| 文件 | 内容 |
|---|---|
| 01-input-channels.md | trace_id 的 4 种输入入口(x-litellm-trace-id / x-trace-id / body litellm_trace_id / 自动 UUID)及其优先级、代码位置 |
| 02-output-destinations.md | trace_id 的 6 个出口:响应头、access log、应用 JSON 日志、SpendLogs.session_id、S3 日志正文、UI Logs 页 |
| 03-ui-session-search.md | UI Logs 页 Session ID 过滤搜索(前缀匹配)、抽屉 session 模式与"顶部搜索框只搜当前页"的坑 |
| 04-debugging.md | end-to-end 验证脚本 scripts/trace-id/test_trace_id_behavior.py、SQL 速查、常见故障定位 checklist |
整体链路一览¶
flowchart TD
subgraph 上游
U1[mirror / model-adapter] -->|"X-Trace-Id: T123"| HTTP
end
HTTP[HTTP 请求到达 LiteLLM]
subgraph 链路A["链路 A:access log 通道(应用日志层)"]
HTTP --> A1[AccessLogMiddleware<br/>middleware/access_log_middleware.py:50]
A1 -->|"x-litellm-trace-id 或<br/>x-trace-id 或<br/>uuid()"| A2[set_request_trace_id<br/>request_context.py ContextVar]
A2 --> A3[litellm/_logging.py:166<br/>注入到 JSON 日志]
A3 --> A4[(应用 JSON 日志<br/>stdout / litellm.log)]
A1 --> A5[(access log 文件<br/>access.log)]
A1 -->|"响应头回写"| RESP1[x-litellm-trace-id<br/>x-trace-id]
end
subgraph 链路B["链路 B:SpendLogs 通道(计费层)"]
HTTP --> B1[litellm_pre_call_utils.py:572<br/>同样读两个 header]
B1 -->|"data['litellm_trace_id'] = T123"| B2[Logging.litellm_trace_id<br/>litellm_logging.py:349]
B2 --> B3[StandardLoggingPayload.trace_id]
B3 --> B4[_get_session_id_for_spend_log<br/>spend_tracking_utils.py:499]
B4 --> B5[("SpendLogs.session_id<br/>(pgsql)")]
B3 --> B6[("S3 日志正文<br/>trace_id 字段")]
B5 --> B7[UI Logs 页<br/>Session ID 列 / 过滤]
end
style 链路A fill:#e8f4ff,stroke:#5b9bd5
style 链路B fill:#fff4e8,stroke:#ed7d31
关键认知:链路 A 与链路 B 是两条独立的传播通道。
历史上踩过的坑:早期版本 litellm_pre_call_utils.py 只把 header 写进 metadata_from_headers["trace_id"] 而没有写 data["litellm_trace_id"],导致链路 A 一切正常(响应头有 trace_id、access log 有 trace_id),但链路 B 拿不到,SpendLogs.session_id 永远是随机 UUID。详见 04-debugging.md。
速查表:拿到 trace_id 之后去哪查¶
| 出口 | 位置 | 命令 / 操作 |
|---|---|---|
| 响应头 | HTTP response | curl -i ... \| grep -i trace-id |
| access log | 容器/Pod 文件 access.log |
grep T123 /var/log/litellm/access.log |
| 应用 JSON 日志 | stdout / litellm.log | ELK 按 trace_id: "T123" 检索 |
| SpendLogs(精确) | PostgreSQL | SELECT * FROM "LiteLLM_SpendLogs" WHERE session_id = 'T123' |
| SpendLogs(前缀) | UI Logs 页 | Filter → Session ID → 输入前缀 |
| S3 日志正文 | S3 兼容存储 | 用 s3_path_components 配置定位文件,正文 JSON 含 trace_id 字段 |
速查表:trace_id 输入优先级¶
按 litellm_pre_call_utils.py 的实际查找顺序(决定 SpendLogs.session_id):
- HTTP header
x-litellm-trace-id(LiteLLM 原生) - HTTP header
x-trace-id(公司内部 mirror / Confluence 规范) - 请求 body 字段
litellm_trace_id - 自动 UUID(最终兜底)
如果业务方既要满足公司规范又要带备选,统一推荐发 X-Trace-Id——这是公司 Confluence 文档约定的 header 名。
⚠️ 链路 A(access log)的优先级与链路 B 相同,且代码各自独立读取 header;但请避免一个请求里
x-litellm-trace-id和x-trace-id传不同的值,否则两条链路会各取所长。
想做某件事?快速入口¶
| 你想做的事 | 看哪篇 |
|---|---|
| 接入方调用前要怎么发 header | 01-input-channels.md |
| 运维拿到 trace_id 想全链路查询 | 02-output-destinations.md |
| 在 UI 上根据 Session ID 过滤日志 | 03-ui-session-search.md |
| 接入完成后想验证全链路通了 | 04-debugging.md |
| 修改了 trace_id 相关代码想本地回归 | 04-debugging.md |