运行观测:设计

跟踪:决定 146。依据:所有者 2026-11-04“这也是我们的 hub 应该能记录和显示的内容,方便 debug”“抄一抄 langfuse 的设计,看看他都能监控到什么”;所有者同日批准本设计。控制台页面先出截图稿,再写代码。

调研对象是 Langfuse 4.15:官方文档,加上它开源的主表定义 events_full。抄的是设计思路,不搬代码。

1. 要回答的四个问题

管理员不上服务器,在控制台就能回答:

  1. 这次调用慢在哪?
  2. 没人用的时候,Hub 在干什么?
  3. 现在有多忙,还能撑多少人?
  4. 崩溃之前,它在处理什么?

今天这四个问题都答不了。调用日志只有总耗时,控制台请求和后台任务没有记录,代码里没有读过内存和事件循环,崩溃时正在处理的请求不留痕迹。

2. Langfuse 能监控什么

层面 记录和显示的内容
每一步 父子层级、开始和结束时间、类型、级别和状态说明、输入输出及其长度、元数据
用量和花费 每一步一张用量表 usage_details(名称到数字,名称可以自定),一张花费表
谁、在哪 用户、会话、环境、发布、版本、标签、上报的服务和 SDK
聚合指标 延迟 p50 / p95 / p99、数量、花费、分数,按时间、用户、名称、版本切片
质量 分数、人工标注、模型评审
怎么看 过滤栏(level:ERROR latency:>2)、Pulse 时间条、表格转图表、追踪的树 / 时间线 / 图、会话回放、看板
告警 按聚合指标设阈值、窗口和严重级别,发通知
治理 采样、脱敏、保留期、审计日志
跨服务 W3C Trace Context,MCP 用 _meta 字段把客户端和服务端的追踪连起来

它的存储设计有四点值得抄:

  • 一张宽表:一行一个步骤,追踪级的属性(用户、会话、环境)抄到每一行上,查询不用连表。
  • 用量是映射:加一种用量不用改表结构。
  • 后台批量写入:记录不拖慢应用。
  • 名字当接口:动词开头,不含动态值,看板和告警都按名字引用。

3. 它看不到的

Langfuse 是请求层面的追踪,不是资源监控。它看不到:

  • 进程内存、CPU、事件循环;
  • 连接池、沙箱这类有限资源的排队;
  • 磁盘;
  • 进程崩溃;
  • 没有埋点的后台任务。

它的文档把基础设施监控交给 Datadog 这类工具,还把数据库查询算作追踪里的噪声。而 Hub 这次的问题恰恰在这些地方。所以 Hub 的做法是:追踪部分抄 Langfuse,资源和崩溃自己补。

4. 对照

Langfuse Hub
追踪 trace 一次 MCP 调用、一个控制台请求、一个同步请求、一轮后台任务、一条命令行命令
步骤 observation 认证、读来源、解析文档、跑沙箱函数、重跑检查、写记录
类型 用 span、event、tool(沙箱函数)、retriever(读来源和知识)、evaluator(重跑检查)、guardrail(边界校验);不用 generation、agent、chain、embedding,Hub 不调用模型
会话 session MCP 会话,一个终端一个;现有的 session_id
用户 user 账号
用量 usage_details 资源用量,见第 5 节
花费 cost_details 不抄,Hub 没有 token 费用
环境、发布 部署的环境类别和发布编号;比较部署前后是快了还是慢了
级别和状态说明 错误码和说明
输入输出 不存,只存长度。参数仍按 B08 的脱敏规则进调用日志
Pulse、过滤栏、时间线 抄,放在记录页
看板 只做固定的运行状况页,不做自定义看板
告警 抄阈值和窗口;先显示在控制台,通知渠道等 #126
MCP 追踪 Hub 已读 traceparent 请求头,加读 _meta

不抄:提示词管理、模型和 token、数据集与实验、模型评审、标注队列、纠正、公开分享。这些属于模型一侧,Hub 不调用模型;验证结论已经在运行记录里。

5. 记录什么

追踪和步骤

一张宽表,每行一个步骤:

字段 说明
trace_id、span_id、parent_span_id W3C 格式。根步骤和调用日志用同一个追踪号
name、type 名字动词开头,不含动态值,例如 read-source、parse-document、run-check、maintain-runs
started_at、ended_at 开始和结束
level、status_message 级别和说明
origin 入口:MCP、控制台、同步、后台、命令行
user_id、session_id、run_id 抄到每一行
client_name、client_version 客户端报的名字和版本,只作声明
release、environment 发布和环境
usage 用量映射

数据库查询不单独成步骤,只累加到所在步骤的用量里。超过 1 秒的查询记成 event。

用量

名称 含义
db.queries、db.rows、db.bytes、db.ms 查询次数、读出行数和字节、数据库耗时
db.wait_ms 等连接池的时间
sandbox.runs、sandbox.wait_ms、sandbox.ms 沙箱运行次数、排队时间、运行时间
source.reads、source.bytes、source.ms 读来源的次数、字节、耗时
answer.bytes 回答的大小
self.ms 总耗时减去上面各项

self.ms 不写成 CPU 时间。同一线程上交错的异步请求分不准,主线程忙不忙看下面“资源”一节的事件循环指标。db.rows、db.bytes 和 answer.bytes 放在一起,读了多少和答了多少一眼可比。

后台任务

每轮后台任务都是一条追踪,用量记法相同:运行维护、引用索引、知识同步、控制台实时轮询、备份。

资源

每 10 秒采样一次,按每种资源的利用率、排队和错误来记:

资源 利用率 排队 错误
主线程 进程 CPU、事件循环利用率 事件循环延迟 p99 —
内存 堆已用 / 上限、常驻内存 垃圾回收暂停 崩溃
沙箱 运行中 / 上限 排队数 超时、队列满
连接池 使用中 / 上限 等待数 连接失败
数据库 活动连接、缓存命中率 锁等待 死锁
磁盘 数据目录可用空间 I/O 等待(Linux) —
连接 MCP 会话、实时推送流,按客户端名字分 — —

进程事件

启动、正常停止、异常退出,带发布编号和运行时长。

  • 在途请求日志:每个请求进来时先写一行到本地文件,结束时再写一行。下次启动时,没有结束行的就是崩溃时正在处理的请求。文件放在 systemd 给服务的运行目录里,也就是内存:它只要活过进程,不用活过机器。实测放在生产的机械盘上,写入中位数 10 微秒,但 ext4 每 5 秒的日志提交会卡住一次写入 10 到 30 毫秒,而这是主线程;放在内存里最长 76 微秒。
  • 崩溃报告:打开 Node 的 --report-on-fatalerror,加 --report-exclude-env,下次启动时读进来:退出原因、堆状态、资源用量。实测堆耗尽时写出约 18 KB 的 JSON,不含环境变量;此时 JavaScript 调用栈不可用,所以还要靠在途请求日志。
  • 不留内存转储:进程内存里有密钥。服务单元同时设 LimitCORE=0 和 CoredumpFilter=0:实测只设前者没用,apport 照样把内存写进报告;两个都设后只剩约 23 KB 元数据。

6. 怎么写入

  • 请求路径上只往内存队列放,不等待。
  • 后台每 5 秒或攒够 500 行批量写一次,用单独的连接,不占业务连接池。
  • 命令行这类短命进程,退出前把队列写完。
  • 队列满了就丢弃并计数。丢了多少显示在运行状况页上,不悄悄丢。
  • 观测本身出错,不影响请求。
  • 保留 14 天,后台分批删除。这个量级用不上分区。
  • 备份不含诊断数据的行,只含表结构:否则 14 份每日备份会让数据活过保留期,备份也跟着访问量变大。

这些是诊断数据,不进只追加的记录链,不验摘要,不作依据,和 B08 R[0] 一致。

7. 怎么显示

记录页(现有页面加功能)

  • 时间条:表格上方,一格一个时间段,可切数量、p95、出错数。点一格,表格收窄到这一段。
  • 过滤栏:tool:get_nodes user:张三 latency:>2 level:ERROR,和现有筛选是同一份状态。
  • 表格:加一列占比条,显示数据库、沙箱、来源和自身各占多少。
  • 追踪详情:点一行看步骤的树和时间线,旁边是用量。控制台已有时间线图表。
  • 会话视图:一个终端的全部调用连起来看。

运行状况页(新页面,管理员)

  • 主线程、内存、沙箱、连接池、数据库五张卡:当前值和 24 小时曲线。
  • 后台任务表:每种任务的间隔、上次耗时、读的行数和字节,按占用排序。
  • 崩溃和重启列表:点开看当时在处理什么,以及之前 5 分钟的曲线。
  • 最慢的查询:来自 pg_stat_statements,按累计耗时和读出行数排序。
  • 按客户端:每种客户端的连接数、调用量和 p95,这是多终端、多平台的视图。
  • 按发布:每个发布的 p95,看部署前后的变化。
  • 观测自身:写入延迟和丢弃数。

告警

阈值和窗口照 Langfuse 的做法。先显示在控制台顶部和运行状况页。默认几条来自决定 146:

  • 堆超过上限的 80%;
  • 事件循环延迟 p99 超过 500 毫秒;
  • 交互类调用 p95 超过 2 秒;
  • 沙箱或连接池持续排队 1 分钟;
  • 进程异常退出。

权限

成员在记录页只看自己的调用,包括这些调用的步骤和用量(B08 R[4])。运行状况页只给管理员。

8. 模型改动

  • B08 的定义从“每次 MCP 调用”扩到“每次进入 Hub 的请求和每轮后台任务”,加步骤、用量和客户端声明三个字段。
  • B08 加一条规则:诊断数据保留 14 天,不进记录链,不作根据。
  • 词表加三个轴:步骤类型、级别、入口(step_type、step_level、trace_origin;入口是埋点任务里加的)。

9. 分步

  1. 埋点:追踪上下文,数据库、沙箱、来源三处累加用量,队列和宽表。
  2. 资源和崩溃:10 秒采样、进程事件、在途请求日志、崩溃报告。
  3. 控制台:记录页和运行状况页,先出截图稿。
  4. 告警和转发开关。字段和 OpenTelemetry 对齐,转发到外部默认关闭,诊断数据默认不出 Hub。

第 1、2 步上线后调优就能开始,不用等界面:用命令行先读这些数据。

10. 验收

用今天的三件真实事情,在合成数据上重现:

  • 空转的定时维护:后台任务表第一行就是它,读的行数和字节都写着。
  • 堆耗尽崩溃:崩溃列表能指出当时正在处理的请求。
  • 固定 5 秒以上的 resolve_reference:追踪详情说得出时间花在哪一步。

另外,观测本身不能成为负担:请求路径不等待写入,队列满时计数,观测的开销显示在页面上。

11. 已定

所有者 2026-11-04:

  1. 诊断数据保留 14 天。
  2. 成员能看自己调用的用量。
  3. 留一个把追踪转发到外部的开关,默认关闭。