读 request_logger 与 schedule_simulator#
这章解决什么问题#
前面的代码导读已经把请求主线、cache 树、输出尾部和运行时控制面都落回了源码,但还有一条很有工程价值、同时也很像“大书后半程才会出现”的闭环没有被放进代码导读:
- 线上请求日志怎样真正落盘
- 这些日志怎样再被离线调度模拟器消费
也就是说,如果你已经抓到了线上 request 日志,源码里下一步该去哪里读,才能把它变成调度策略实验材料?
这章的目标,就是把:
srt/utils/request_logger.pyTokenizerManager.configure_logging(...)debug_utils/schedule_simulator/
收成一条正式的源码阅读路径。
为什么这条链值得放进代码导读#
因为它不是纯维护技巧,也不是纯工具手册,而是一条真实的源码工作流:
- 运行时怎样决定日志口径
- 日志里哪些字段会被保留下来
- 这些字段怎样被 loader 缩成模拟请求
- 模拟器又怎样把它们送进 router / scheduler policy
从技术书角度看,这条链特别有价值,因为它说明:
- 线上证据并不是终点
- 还可以继续变成离线实验输入
一张图:这不是两件工具,而是一条连续链#
这张图解决的理解障碍是:很多读者会把 request logger 和 simulator 看成两套完全分开的工具,实际上它们在源码里是可以接起来的。
flowchart LR
Req["runtime request"] --> Log["RequestLogger.log_finished_request(...)"]
Log --> File["request.finished JSON lines"]
File --> Loader["load_from_request_logger(...)"]
Loader --> Sim["schedule_simulator"]
Sim --> Policy["router / scheduler policy replay"]图比纯文字多解释的一点是:日志不是只给人看,也可以成为策略实验的输入格式。
第一棵树:RequestLogger 应该怎么读#
如果要稳,最先抓的其实只有三处:
configure(...)log_received_request(...)log_finished_request(...)
这样读的好处是,你会立刻看清:
- 运行中日志行为是否可变
- 首尾日志分别保留了什么
- finished request 为什么会成为后续模拟的样本源
metadata 为什么是 RequestLogger 的真正核心#
RequestLogger 初始化时会立刻算:
metadata = self._compute_metadata()
这组 metadata 里最重要的是:
max_lengthskip_namesout_skip_names
这说明日志器本质上不是“把对象直接打印出来”,而是先决定:
- 哪些字段保留
- 哪些字段裁掉
- 哪些字段需要做截断
从工程角度看,这特别重要,因为后面的 simulator loader 只会看到被保留下来的 finished request 记录。
也就是说,logger 本身就在定义“未来离线实验能看见什么”。
log_finished_request(...) 为什么比 log_received_request(...) 更关键#
因为离线 simulator 默认只吃:
event == "request.finished"
而 finished log 里才会有真正稳定的:
prompt_tokenscompletion_tokensrid
这说明 request logger 的 finished 分支,不只是排障日志出口,也是后续离线分析的数据出口。
从技术书角度看,这是一处很值得点出来的“同一机制在不同层级重复利用”的设计。
configure_logging(...) 怎样把运行时和 logger 接起来#
在 TokenizerManager 里:
configure_logging(obj)
会直接调用:
self.request_logger.configure(...)
同时还可能更新:
dump_requests_folderdump_requests_threshold
这说明日志控制面不是 request logger 这棵树自己的附属功能,而是被正式拉进运行时控制面里的。
换句话说:
- 你在线上调日志粒度
- 其实就是在改变后续会进入“离线可分析材料”的请求样本口径
第二棵树:load_from_request_logger(...) 为什么设计得这么克制#
debug_utils/schedule_simulator/data_source/data_loader.py 很值得读,因为它透露出非常清楚的边界:
- 只读
request.finished - 只抽
request_id input_len = prompt_tokensoutput_len = completion_tokens
这说明 simulator 的目标不是复刻完整请求语义,而是构造:
- 最小够用的调度样本
这是一个非常值得技术书强调的取舍:
- 如果把完整请求语义都带进来,模拟器会过重
- 只取长度信息,则更适合做路由 / 调度策略实验
schedule_simulator/entrypoint.py 应该怎么读#
这份入口文件的阅读价值不在 argparse 本身,而在它把模拟问题明确切成了几组独立维度:
- 数据来源:真实 request logger 或 synthetic
- router:
random/round_robin/sticky - scheduler:当前至少是
fifo num_gpus_per_enginenum_enginesmax_total_tokens
这说明 simulator 的重点不是“完全复现线上”,而是:
- 给定某批请求形状,比较不同策略在同一资源模型上的相对行为
从系统阅读角度看,这是一种非常清晰的职责边界。
为什么这条链对整本书特别有回扣价值#
它会把很多前文重新接起来:
- request logger:解释线上 finished request 记录从哪里来
- 调度与内存:解释模拟器试的到底是哪类资源和策略问题
- 运行时控制面:解释你怎样在线上调整采样窗口和日志粒度
- codebase walkthrough:解释这些工具最终还是回到具体 router / scheduler policy 实现
也就是说,它不只是“再多一个工具介绍”,而是把线上维护和离线实验正式连成了一条工程闭环。
这一层最容易出现的误判#
1. 以为 request logger 只是值班日志#
它同样在给离线调度实验准备样本。
2. 以为 simulator 能复刻完整 runtime correctness#
它更专注于长度和策略维度,不是功能 replay。
3. 以为 logger 粒度只影响人类阅读体验#
它也会影响后续被离线工具消费的样本质量。
如果你要顺着源码读这条链,推荐顺序是什么#
建议按下面顺序:
RequestLogger._compute_metadata()log_finished_request(...)TokenizerManager.configure_logging(...)load_from_request_logger(...)schedule_simulator/entrypoint.py- 再回头看 simulator 的 router / scheduler policy 实现
这样读,你先搞清样本是怎样产生的,再搞清样本如何被压缩成模拟请求,最后才看策略实验本身。
小结#
这一章真正要补齐的,是代码导读里一条很有工程价值的闭环:
- request logger 负责把线上请求结束事实变成结构化记录
- schedule simulator 则负责把这些记录缩成调度实验输入
到这里,代码导读就不只是在教你“线上系统怎么跑”,也开始教你“线上证据怎样继续被拿去做离线策略分析”。
叶王 © 2013-2026 版权所有。如果本文档对你有所帮助,可以请作者喝饮料。