Request metrics exporter 与证据落盘链#

扩展与调试部分前面已经讲了 metrics、trace、profiling、dump、watchdog 和 liveness,但如果只停在这些能力上,维护层仍然会少一条特别关键的证据落地链:请求完成后,那些 request-level 的性能与元信息怎样真正被导出到文件或其他 sink,而不是只停留在内存里的 meta_info

这章真正要补齐的,就是这条“请求完成 -> 结构化证据落盘”的链。它的重点不是介绍又一个 exporter,而是解释:为什么 request-level 证据不能只存在于日志流和 in-memory 对象里,为什么它必须有一条正式的、可离线消费的落盘路径。

先把它放回维护主线,而不是 observability 名词堆#

很多技术书在讲 observability 时会停在“系统有 metrics”“系统能 trace”。对 inference runtime 的维护者来说,这还不够。另一类真正值钱的能力是:一条请求结束之后,能否把它的 request parameters、finish reason、latency 和 token 统计持久化,供之后离线分析、批量对比或合规存档。

python/sglang/srt/observability/request_metrics_exporter.py 做的正是这件事,而且它不是外围脚本,而是已经挂进 TokenizerManager 请求收尾链里的正式能力。也就是说,这条链不是“导出一点额外日志”,而是运行时正式承认的一条 request-level evidence sink。

这条链真正长成什么样#

下面这张图的作用,是把请求完成之后的证据落地链重新压出来:

flowchart LR
    Done["request finished in _wait_one_response()"] --> Log["request_logger.log_finished_request()"]
    Log --> Export["RequestMetricsExporterManager.write_record()"]
    Export --> File["FileRequestMetricsExporter"]
    File --> Sink["sglang-request-metrics-*.log"]

这张图最重要的一点是:这不是 Prometheus scrape 路径的变体,而是请求收尾链上的另一条正式持久化分支。

真正的抽象重点不在“怎么写文件”,而在“什么算一条可持久化请求证据”#

request_metrics_exporter.py 最值得先看的,不是 FileRequestMetricsExporter,而是更高一层的抽象边界。因为基类已经把职责切得很清楚:

  • _format_output_data(...) 负责把 request parameters 和 meta_info 里可序列化的字段整理成一条记录
  • write_record(...) 负责把这条记录交给具体 exporter

这说明 request-level exporter 的真正核心不是文件 I/O,而是先定义“什么叫一条可长期保存、可离线消费的请求事实”。只要这一层概念稳住了,后面的 file sink、private sink 扩展和分析链就都更容易理解。

_format_output_data(...) 其实是在做证据裁剪,而不是简单 dump#

这也是这章最值得系统书主动指出的地方。这个函数并不会把整个 request 和 output 原样扔进文件,而是会:

  1. 遍历请求对象 dataclass 字段
  2. 跳过 obj_skip_names 和固定不可序列化字段
  3. out_dict["meta_info"] 里过滤掉 out_skip_names
  4. 最终只输出 request_parameters 加上筛过的 meta_info

这说明 request-level exporter 的设计哲学不是“证据越多越好”,而是“证据要足够结构化、足够轻、足够可复用”。这和前面 request logger、dump、schedule simulator 那几章会自然形成一条更成熟的维护主线:

  • logger 更偏首尾事实
  • dump 更偏窗口材料
  • exporter 更偏稳定、结构化、长期落盘

FileRequestMetricsExporter 真正和 request log 有什么区别#

当前公开实现是 FileRequestMetricsExporter。它会:

  • 根据 export_metrics_to_file_dir 创建目录
  • 按小时切文件,命名为 sglang-request-metrics-{hour_suffix}.log
  • 写每条请求的一行 JSON
  • 显式跳过 health check 请求

这说明它和 request log 的差别非常大:

  • request log 更偏面向人工阅读或值班排障
  • request metrics exporter 更偏结构化落盘和离线分析

因此它不是普通日志系统的重复实现,而是 request-level 证据链的另一条专门分支。把这层边界说清之后,维护者就不容易再把“有 request log”误当成“已经有了可分析的结构化请求事实”。

RequestMetricsExporterManager 真正说明了它是正式收尾链的一部分#

这一层如果只看文件目录,容易被误判成外围工具。更稳的证据来自 TokenizerManager 的接线方式。初始化阶段它会创建 RequestMetricsExporterManager,并把 request logger 的 metadata 过滤口径 obj_skip_names / out_skip_names 交给它。之后在 _wait_one_response(...) 的 finished 分支里,会按顺序:

  1. log_finished_request(...)
  2. 判断 exporter_enabled()
  3. 异步 create_task(self.request_metrics_exporter_manager.write_record(obj, out))

这条顺序非常关键,因为它说明 exporter 不是“事后再去读日志文件做 ETL”,而是在请求正式结束的那一刻就接上了收尾链。这也是为什么它更像 runtime 正式能力,而不是外部脚本。

为什么它放在 TokenizerManager 而不是 scheduler#

从架构上看,这个放置位置非常合理。因为到了 TokenizerManager finished 分支,系统已经能同时拿到:

  • 原始请求对象 obj
  • 回包整理后的 out
  • 完整的 finish_reason
  • 发送给客户端之前 / 之后的时间统计

Scheduler 更擅长理解 batch 和 token 推进;TokenizerManager 则更适合把“对调用方有意义的一条请求事实”重新收拢出来。只要这一层理解稳住,request-level exporter 的位置就不再像随意选择,而会显得几乎是唯一合理的落点。

health check 被显式排除,说明系统在保护证据集的纯度#

FileRequestMetricsExporter.write_record(...) 会跳过 HEALTH_CHECK_RID_PREFIX 请求。这件事特别值得点出来,因为它说明系统并不把 health check 当作真实业务请求的一部分,也不希望 request-level 证据被探活噪声污染。

这和前面 liveness 章节正好形成回扣:health check 是系统活性信号,不应和真实业务请求的长期证据混成同一批数据。这个边界一旦稳住,后面的离线分析也会干净很多。

这条链最容易被低估的三种价值#

第一,长期积累 request-level 事实。
dump / replay 更适合重现复杂问题;request metrics exporter 更适合长期积累大量请求级事实。

第二,做优化前后的批量对比。
如果你改了调度、cache、sampling 或 parser,这类结构化记录非常适合做前后行为对比,而不只是看一次 benchmark。

第三,作为私有 fork 的扩展面。
RequestMetricsExporterManager 还会尝试加载 private exporter factory,这说明它天生就是一条“可插拔 evidence sink”接口,而不是只服务当前 file sink。

真正验证这条 exporter 链时,更稳的顺序#

建议按下面顺序:

  1. 先看 server_args 是否真的启用了 export_metrics_to_file
  2. 再看 TokenizerManager 初始化时是否创建了 exporter manager。
  3. 再看 _wait_one_response(...) 进入 finished 分支后有没有真正调 write_record(...)
  4. 再看 export_metrics_to_file_dir 下是否生成了按小时分割的 .log 文件。
  5. 最后才检查被写出的字段是否符合你真正关心的分析维度。

这样做的价值在于,它先验证“链有没有通”,再验证“通出来的内容够不够好”,而不是一开始就盯文件格式。

最容易出现的三种误判#

第一,误以为它只是 metrics 的另一个表现形式。
更准确地说,它是一条 request-level 证据落盘链。

第二,误以为 request log 已经等于结构化请求事实。
logger 更偏人工阅读,exporter 更偏离线分析和长期积累。

第三,误以为 exporter 只是当前 file sink 的技术细节。
实际上 manager 设计已经把它做成了一条可插拔 evidence sink 扩展面。

小结#

这一章真正补齐的,是 observability 里经常被省略的一层:metrics 是连续观测,trace 是阶段证据,dump / replay 是重现材料,而 request metrics exporter 则是“请求完成后把证据正式落盘”的链。

把这一层讲清之后,扩展与调试部分就不只覆盖在线可见性,也开始覆盖离线证据怎样被持续积累下来。