03 — UI Logs 页 Session ID 搜索¶
LiteLLM Dashboard 的 /ui → Logs 页提供两种基于 Session ID 的能力:
- 过滤搜索(本次新增):在高级 Filter 里输入 Session ID 前缀,全表过滤。
- 抽屉 session 模式(旧功能):点击表格里某行的 Session ID 单元格,右侧抽屉展开同 session 的全部 request。
本篇说清两者的区别、参数语义、以及"为什么顶部那个白色搜索框搜不到"的常见困惑。
一图看清三种搜索机制¶
flowchart LR
UI[Logs 页] --> S1[顶部搜索框<br/>'Search by Request ID']
UI --> S2[高级 Filter<br/>Session ID]
UI --> S3[Session ID 单元格<br/>点击]
S1 -. 纯前端 .-> P1["仅当前 50 条<br/>match: request_id, model, user"]
S2 -->|GET /spend/logs/ui<br/>?session_id=前缀| P2[("LiteLLM_SpendLogs<br/>WHERE session_id LIKE 'xxx%'")]
S3 -->|GET /spend/logs/session/ui<br/>?session_id=精确| P3[("LiteLLM_SpendLogs<br/>WHERE session_id = 'xxx'")]
style S1 fill:#ffe8e8,stroke:#c00
style S2 fill:#e8ffe8,stroke:#080
style S3 fill:#e8f4ff,stroke:#06c
| 机制 | 范围 | 后端调用 | 匹配方式 |
|---|---|---|---|
| 顶部搜索框 | 仅当前页 50 条 | 无 | 子串匹配 request_id / model / user |
| 高级 Filter → Session ID | 全表 | /spend/logs/ui?session_id=前缀 |
LIKE 'xxx%' |
| 点击 Session ID 单元格 | 全表 | /spend/logs/session/ui?session_id=精确 |
等值 |
1. 高级 Filter → Session ID(推荐)¶
操作步骤:
- 进入
/ui→ 左侧导航 Logs - 点顶栏右上角的 Filter 按钮,弹出过滤面板
- 选 Session ID 字段
- 粘贴你拿到的 trace_id(完整值或前缀均可)
- Apply
前端代码位置: ui/litellm-dashboard/src/components/view_logs/log_filter_logic.tsx(FILTER_KEYS.SESSION_ID)。
后端代码位置: spend_management_endpoints.py:1641-1644 的 ui_view_spend_logs query 参数 session_id。
匹配语义: 前缀匹配(LIKE 'xxx%'),不是等值。原因:上游 mirror 服务的 trace_id 常带 <trace>-<span> 结构,运维拿到 trace 部分时可以一次找到该 trace 下所有 span。
SQL 等价物:
SELECT * FROM "LiteLLM_SpendLogs"
WHERE session_id LIKE 'SPAN-2026-04-30-%'
AND "startTime" BETWEEN ... AND ...
ORDER BY "startTime" DESC
LIMIT 50 OFFSET 0;
注意:
- 时间窗仍以 Logs 页顶部的快捷范围(默认最近 24h)为准。如果你的请求是 3 天前的,记得把范围拉到 7 天。
- Session ID 与其他 Filter(Team ID / Status / Model 等)可以叠加使用。
2. 抽屉 session 模式(点击单元格)¶
适用场景:你已经在表格里看到某条 request 行,想看"同一 session 的其他 request"。
操作: 直接点表格某行的 Session ID 单元格(可点链接样式)。
行为:
- 触发
onSessionClick(sessionId)→ 打开右侧抽屉 - 抽屉内 query:
GET /spend/logs/session/ui?session_id=<精确值> - 抽屉内拉取该 session 的全部 request,时间正序排列
与 Filter 的差异:
| 抽屉 session 模式 | Filter Session ID | |
|---|---|---|
| 匹配 | 等值 | 前缀 |
| 范围 | 不受时间窗约束(接口内固定取整段) | 受 Logs 页时间窗约束 |
| 上下文 | 抽屉内独立视图,主表格不变 | 主表格替换 |
| 入口 | 点单元格 | 顶部 Filter |
3. 顶部搜索框 = 坑(待优化)¶
Logs 页表格上方有个白色 "Search by Request ID" 搜索框(view_logs/index.tsx:537-543)。
它的真实行为(index.tsx:302-307):
const matchesSearch =
!searchTerm ||
log.request_id.includes(searchTerm) ||
log.model.includes(searchTerm) ||
(log.user && log.user.includes(searchTerm));
也就是说:
- ✗ 纯前端,不发后端请求
- ✗ 只在当前页 50 条已拉到的数据里子串匹配
- ✗ 不匹配
session_id - ✗ 跨页搜索一律失败
- ✓ 仅当目标确实在当前 50 条内时才管用
结论:搜 trace_id / session_id 一律用高级 Filter,不要用顶部那个搜索框。
待办:未来把顶部搜索框改造成统一走后端的智能搜索框(同时支持 request_id 与 session_id),目前未做。
4. 后端接口约束¶
/spend/logs/ui 是分页接口,单次返回上限 100 条。如果你按 Session ID 前缀搜出 1000 条记录,需要翻页(页号在 query 参数 page、page_size)。
鉴权与可见范围:
- Admin 视图:可见全表
- 普通用户:受
_can_user_view_spend_log限制,只能看到自己 user_id 名下的行 - Team 视图:受 team 成员关系约束
详见 spend_management_endpoints.py:1832-1853 的鉴权分支。
5. 一个完整的全链路定位示例¶
业务方反馈:"2026-04-30 上午用户 X 的请求异常",给了 trace_id SPAN-2026-04-30-abc123-span04。
- 打开 Logs 页,时间范围调到当天 8:00-12:00
- Filter → Session ID 输入
SPAN-2026-04-30-abc123(只输 trace 部分,省略 span 后缀) - 表格出现该 trace 下所有 span:8 行
- 点其中任一行的 Session ID 单元格,抽屉打开看到完整 8 个 span 的时序
- 找到失败那条,点 request_id 看 messages / response 详情
- 如需 S3 完整日志,复制 request_id 按 02-output-destinations.md ⑤ 拼路径下载