kernel.request 事件监听器必须注册为带 kernel.event_subscriber 标签的服务,实现 eventsubscriberinterface 并在 getsubscribedevents() 中声明事件;优先级需合理设置以确保路由信息可用,且应过滤非业务请求并记录完整 uri。

kernel.request 事件监听器必须注册为服务并标记为 event_subscriber
直接在控制器里写逻辑无法响应全局请求,必须通过事件订阅器(EventSubscriber)机制介入内核流程。关键不是“写了类”,而是这个类是否被 Symfony 的容器识别为事件订阅器——它需要在 services.yml 中显式声明,并添加 tags: [{ name: 'kernel.event_subscriber' }]。
常见错误是只定义了服务但漏掉 tag,结果订阅器完全不触发;或者用了 kernel.event_listener 标签却没实现 getSubscribedEvents() 方法,导致报错 The "kernel.request" event listener ... does not exist。
- 订阅器类必须实现
EventSubscriberInterface -
getSubscribedEvents()必须返回数组,键为事件名(如'kernel.request'),值为回调方法名或 [方法名, 优先级] - 优先级默认为 0;负数优先级更早执行(比如你想在 RouterListener 之前拦截请求)
从 Request 对象中安全提取路由信息
路由信息只有在 RouterListener 执行后才写入 $request->attributes,所以监听 kernel.request 时不能假设 _route 或 _controller 一定存在。如果你的订阅器优先级高于 RouterListener(比如设为 10),$request->attributes->get('_route') 就会返回 null,导致后续日志写入空值或抛出异常。
正确做法是:要么把优先级设为 -10(确保在路由解析之后),要么加判空:
public function onKernelRequest(GetResponseEvent $event)
{
$request = $event->getRequest();
$routeName = $request->attributes->get('_route', 'unknown');
$controller = $request->attributes->get('_controller', 'none');
// 记录到 monolog 或自定义 logger
$this->logger->info('Route accessed', [
'route' => $routeName,
'controller' => $controller,
'uri' => $request->getRequestUri(),
'method' => $request->getMethod(),
]);
}
避免日志爆炸:过滤 CLI、健康检查和静态资源请求
默认情况下,kernel.request 会捕获所有 HTTP 请求,包括 app/console 命令触发的 HTTP 内部调用、/healthz 探针、/bundles/ 资源路径等。这些请求进日志既无业务价值,又快速撑爆磁盘。
- 用
$event->isMasterRequest()过滤子请求(如嵌入的 ESI 或 render()) - 检查
$request->getPathInfo()是否匹配^/(health|ping|bundles|css|js|images) - 跳过 CLI 环境:
if (PHP_SAPI === 'cli') return;
注意:不要依赖 $request->isXmlHttpRequest() 来过滤,它只判断 X-Requested-With: XMLHttpRequest 头,对普通 API 调用无效。
记录地址时别只记 path,要保留 query 和 host 差异
同一个路由(如 blog_show)可能对应多个实际访问地址:https://example.com/blog/123、https://admin.example.com/blog/456?ref=mail、http://localhost:8000/blog/789。如果只记录 $request->getPathInfo(),就丢失了协议、host、query 等关键上下文,排查问题时无法还原真实请求来源。
建议统一记录 $request->getUri()(返回完整 URI 字符串),或至少组合:
$fullUrl = $request->getSchemeAndHttpHost() . $request->getRequestUri();
另外,若应用部署在反向代理后(如 Nginx + PHP-FPM),需确认 trusted_proxies 配置已设置,否则 getUri() 可能返回 http://localhost:8000 而非真实客户端地址。











