跳转至

01 — trace_id 的输入入口

LiteLLM 在收到一个请求后,会依次尝试 4 种方式拿到 trace_id。第一个非空命中即生效,后面的 fallback 不再覆盖。


优先级总览

顺序 来源 实际去向 备注
1 HTTP header x-litellm-trace-id 链路 A + 链路 B LiteLLM 上游原生支持的 header
2 HTTP header x-trace-id 链路 A + 链路 B 公司内部 mirror / model-adapter 规范,本仓库 fork 加入
3 请求 body 字段 litellm_trace_id 链路 B 上游原生路径;不进 access log
4 自动生成 UUID 链路 A 与链路 B 各生成各的 兜底;两条链路的 UUID 可能不同

链路 A、链路 B 的概念见 README.md


1. HTTP header x-litellm-trace-id(最高优先级)

链路 A(access log)—— access_log_middleware.py:51-55

trace_id = (
    request.headers.get("x-litellm-trace-id")
    or request.headers.get("x-trace-id")
    or str(uuid4())
)

链路 B(SpendLogs)—— litellm_pre_call_utils.py:572-590

trace_id_from_header = headers.get("x-litellm-trace-id") or headers.get("x-trace-id")
if trace_id_from_header:
    metadata_from_headers["trace_id"] = trace_id_from_header
    data["litellm_trace_id"] = trace_id_from_header

两条链路各自独立读 header,但读取顺序一致。


2. HTTP header x-trace-id(公司内部规范)

公司内部 mirror / model-adapter 服务按 Confluence 规范发的就是这个 header。本仓库 fork 在两个位置都加了对它的识别(见上面同两段代码)。

示例 curl:

curl -X POST https://litellm.example.com/v1/chat/completions \
  -H "Authorization: Bearer sk-..." \
  -H "Content-Type: application/json" \
  -H "X-Trace-Id: SPAN-2026-04-30-abc123" \
  -d '{"model": "gpt-4", "messages": [{"role": "user", "content": "hi"}]}'

响应头会返回(双名回写):

x-litellm-trace-id: SPAN-2026-04-30-abc123
x-trace-id: SPAN-2026-04-30-abc123

参见 access_log_middleware.py:58-60


3. 请求 body 字段 litellm_trace_id

LiteLLM 上游官方支持的方式。把 trace_id 直接放在请求 JSON 里:

{
  "model": "gpt-4",
  "messages": [{"role": "user", "content": "hi"}],
  "litellm_trace_id": "MY-TRACE-001"
}

特点:

  • 只走链路 B:能写进 SpendLogs.session_id、S3 日志正文。
  • 不进链路 A:AccessLogMiddleware 只看 header,所以 access log 里仍是自动 UUID。
  • 适用于:客户端 SDK 不方便加 header、但能改 body 的场景。

实现位置:litellm_logging.py:349-351 直接接收 litellm_trace_id 关键字参数赋给 Logging.litellm_trace_id


4. 自动 UUID(兜底)

什么 trace_id 都不传时,两条链路各生成各的 UUID

链路 UUID 生成位置 后果
A access_log_middleware.py:54 str(uuid4()) access log / 应用 JSON 日志 / 响应头 拿到 UUID-A
B _get_session_id_for_spend_log spend_tracking_utils.py:522litellm_logging.py:350 str(uuid.uuid4()) SpendLogs.session_id 拿到 UUID-B

两个 UUID 互相独立,绝大多数情况不相同。 想在 access log 与 SpendLogs 之间对齐请求,必须显式传 trace_id(推荐 header)。

⚠️ 业务方排查时常陷入这个坑:"为什么 access log 里这条请求的 trace_id 在 UI 上搜不到?"——答:因为没传 header,UI 用的 session_id 是另一条 UUID。


一个请求里同时传了 x-litellm-trace-idx-trace-id

代码里两个分支是 orx-litellm-trace-id 优先x-trace-id 仅在前者缺失时生效。

如果两个值不同,链路 A 与链路 B 都会取 x-litellm-trace-idx-trace-id 被丢弃——但响应头会按 trace_id 实际值(即 x-litellm-trace-id)覆盖回写到两个 header 名上,可能让接入方误以为对方支持了 x-trace-id 输入。

建议:业务方只发一个 header,按公司规范统一发 X-Trace-Id 即可。


关于响应头回写

无论用上面哪种方式拿到 trace_id(即使是兜底 UUID),AccessLogMiddleware 都会在响应里同时写两个 header:

x-litellm-trace-id: <值>
x-trace-id: <值>

参见 access_log_middleware.py:58-60。这让上游不论用哪个 header 名都能从响应头取回同一份 trace_id。

⚠️ 但响应头里的 trace_id 来自链路 A,可能与 SpendLogs.session_id(链路 B)不一致——前提是没显式传 trace_id 走兜底 UUID。