应监听 kernel.controller 事件,此时路由已匹配完成、$request->attributes 中包含 '_route' 等键,适合日志记录与权限前检查;需使用 ControllerEvent、过滤子请求、避免手动重复匹配路由。

Symfony2 里没有“一键调取所有访问路由”的监听器封装工具类——监听器本身不负责收集路由,它只响应事件;而路由信息在 kernel.request 事件触发时还未完全解析,RouterListener 才是真正做路由匹配的那个监听器。
为什么不能在 kernel.request 监听器里直接拿到完整路由?
因为 kernel.request 是早期事件,此时请求刚进内核,RouterListener 还没执行。你拿到的 $request->attributes 是空的,$request->get('_route') 肯定为 null。真要取当前匹配的路由名,得等 RouterListener 跑完,也就是在 kernel.controller 或之后的事件里才行。
-
kernel.request:请求刚进来,Request对象可用,但路由、控制器都还没确定 -
kernel.controller:路由已匹配完成,$request->attributes里有_route、_controller等键 -
kernel.view/kernel.response:控制器已执行,响应正在生成或已生成
想记录每次访问的路由,该监听哪个事件?
用 kernel.controller 最稳妥。这时路由已确定,又还没执行控制器逻辑,适合做日志、审计、权限前检查(注意:不是替代 SecurityBundle 的授权)。
示例监听器:
开箱即用的技能链路由引擎。13 条预定义链覆盖搜索、开发、审查、MLOps、法律、创意等场景,三层路由架构(触发词→SAD反馈→DAG编排),recall@10=96.97%。配置驱动(chains.yaml),零代码扩展。pip install skill-weave-chains 一键安装。
class RouteLoggerListener
{
private $logger;
<pre class="brush:php;toolbar:false;">public function __construct(LoggerInterface $logger)
{
$this->logger = $logger;
}
public function onKernelController(ControllerEvent $event): void
{
$request = $event->getRequest();
$routeName = $request->attributes->get('_route', 'unknown');
$method = $request->getMethod();
$ip = $request->getClientIp();
$this->logger->info('Route accessed', [
'route' => $routeName,
'method' => $method,
'ip' => $ip,
'url' => $request->getUri(),
]);
}}
- 必须用
ControllerEvent(不是RequestEvent),否则拿不到_route - 别依赖
$request->getPathInfo()做路由识别——重写规则、子请求、API 前缀都可能让它和真实路由名不一致 - 如果用了 FOSRestBundle 或 API 平台,
_route可能是api_get_users这种,不是路径字符串
如何让监听器只对特定环境或路径生效?
别在监听器里写 if ($env !== 'prod') 这类判断——性能差、难测试、违反单一职责。应该靠容器配置控制是否注册:
# app/config/services.yml
services:
App\EventListener\RouteLoggerListener:
arguments: ['@logger']
tags:
- { name: kernel.event_listener, event: kernel.controller, method: onKernelController }
# 只在 dev 和 prod 注册,test 环境跳过
when: '@=container.getParameter("kernel.environment") in ["dev", "prod"]'- 用
when表达式比在 PHP 里判断更干净,且编译期就排除,不加载类 - 若只想记录某些路径,加个参数配置再注入:
%app.route_logging_patterns%,监听器里用fnmatch()匹配$request->getPathInfo() - 避免监听
kernel.request后手动调$router->matchRequest($request)——这会重复执行路由匹配,开销大,还可能因顺序问题匹配失败
真正容易被忽略的是:kernel.controller 事件在子请求中也会触发,比如 render(controller(...))。如果你的日志里突然冒出大量 _internal 路由,就是这个原因——得在监听器里加 if (!$event->isMainRequest()) return; 过滤掉。


















