别用 kernel.request / kernel.response 做耗时分析,该用 stopwatch 组件 + profiler 集成,否则数据不准、漏阶段、难对比;stopwatch 是 symfony 官方设计的、被 profiler 深度集成的计时工具,所有 timeline 数据都基于它。

直接结论:别用 kernel.request / kernel.response 做耗时分析,该用 Stopwatch 组件 + Profiler 集成,否则数据不准、漏阶段、难对比。
Stopwatch 是唯一靠谱的计时入口
kernel.request 和 kernel.response 事件之间的时间 ≠ 实际路由处理耗时。它漏掉了模板渲染、事件分发、响应发送等关键阶段,且无法区分“内核调度开销”和“业务逻辑耗时”。Stopwatch 是 Symfony 官方设计的、被 Profiler 深度集成的计时工具,所有 timeline 数据都基于它。
- 必须在
kernel.request之前启动(比如在自定义 HTTP 内核或早期监听器中),否则第一段耗时为空 - 推荐在
App\Kernel::handle()开头调用$stopwatch->start('request'),确保覆盖完整生命周期 - 多个标记可嵌套:
$stopwatch->start('controller')、$stopwatch->start('db.query'),Profiler 的 Timeline 标签页会自动展开层级 - 不要手动
microtime(true)—— 它不兼容 Profiler 数据采集,也不会出现在 Web Debug Toolbar 里
如何让自定义耗时出现在 Profiler Timeline 里
Stopwatch 记录本身不会自动显示在 Profiler 的 Timeline 页面,需确保两件事:
当代理已经知道网站路由或内容URL,并且在启动前需要有效的sitemap XML、sitemap索引或robots.txt引用时,请使用sitemap。这是一个发布构件技能,而不是爬虫或SEO平台。
- 服务配置中启用
stopwatch:检查config/packages/dev/web_profiler.yaml是否包含enabled: true,且未被覆盖 - Profiler 必须已启用:确认
APP_ENV=dev且debug: true,否则stopwatch数据不会被收集 - 标记名要语义化:用
'controller.action'、'service.cache.get'这类命名,避免泛用'main'或'total',否则在 Timeline 中无法区分 - 别在 CLI 场景下依赖 Web Toolbar:命令行任务需手动调用
$profiler->collect(...)并确保stopwatch已 start,否则 Timeline 为空
常见错误:监听器里自己算时间,结果对不上 Profiler
很多人在 onKernelRequest 里记开始时间、在 onKernelResponse 里算差值,发现比 Profiler 显示的总耗时短 20–50ms。这是因为:
-
onKernelRequest触发时,请求刚进内核,但路由匹配、控制器解析还没开始 -
onKernelResponse触发时,响应已生成,但还没经过事件分发、HTTP 缓存中间件、甚至 gzip 压缩 - Stopwatch 的
start('request')实际插在HttpKernel::handle()入口,stop()在handle()返回前,覆盖更全 - Profiler 的 “Total time” 是从 PHP
$_SERVER['REQUEST_TIME_FLOAT']开始算的,Stopwatch 对齐这个起点;手算时间用microtime(true)会有微秒级偏差累积
真正要分析某段路由耗时,就老实用 Stopwatch 打点、看 Profiler Timeline。想加业务维度(比如“用户登录耗时”),也该用 $stopwatch->start('auth.login'),而不是另起一套计时逻辑——Profiler 不认你写的变量名,只认 Stopwatch 实例里的标记。










