要让 Symfony2 的 Monolog 输出完整请求 URL 路径,必须使用 RequestUriProcessor:通过 RequestStack 获取当前请求,在 __invoke() 中注入 request_uri 到 $record['extra'],并在 services.yml 中为对应 channel 打上 monolog.logger_processor 标签。

如何让 Symfony2 的 Monolog 输出完整的请求 URL 路径
默认情况下,Symfony2 的 monolog 日志处理器(如 stream 或 rotating_file)不会自动记录 HTTP 请求的完整路径(GET /api/users?limit=10),只可能有简单上下文或空字段。要稳定输出请求路径,必须手动注入当前 Request 对象,并确保日志处理器在请求生命周期内可访问它。
为什么不能直接在 Handler 中调用 $request->getRequestUri()
Monolog 的 Handler 是服务容器中单例对象,而 Request 是每次请求新建的;Handler 初始化时 $request 还不存在,直接依赖会导致 ServiceCircularReferenceException 或空值。正确做法是使用 LoggerProcessor —— 它在每次日志写入前执行,能安全获取当前请求。
- Processor 必须实现
__invoke()方法,返回带request_uri键的数组 - 必须在
services.yml中声明为public: false并打上monolog.logger_processor标签 - 不能用
kernel.request事件监听器“提前存 Request”,因为日志可能在事件触发前就已写入(如异常堆栈)
配置自定义 Processor 并注入到 monolog channel
在 src/AppBundle/Log/RequestUriProcessor.php 中定义:
namespace AppBundle\Log;
use Symfony\Component\HttpFoundation\RequestStack;
use Monolog\Processor\ProcessorInterface;
class RequestUriProcessor implements ProcessorInterface
{
private $requestStack;
public function __construct(RequestStack $requestStack)
{
$this->requestStack = $requestStack;
}
public function __invoke(array $record): array
{
$request = $this->requestStack->getCurrentRequest();
if ($request) {
$record['extra']['request_uri'] = $request->getMethod() . ' ' . $request->getRequestUri();
}
return $record;
}
}
在 app/config/services.yml 注册:
services:
app.log_processor.request_uri:
class: AppBundle\Log\RequestUriProcessor
arguments: ['@request_stack']
tags: [{ name: monolog.logger_processor, channel: main }]
此时日志行会多出类似 [2024-01-01 12:00:00] app.INFO: something [] {"request_uri":"GET /api/v1/posts?id=5"}
避免日志格式被覆盖或丢失 extra 字段
Symfony2 默认使用的 LineFormatter 不会自动序列化 extra 数组内容,除非显式启用 include_stacktraces 或自定义格式。若发现 request_uri 没出现在日志文件中,请检查以下三点:
- 确认
monolog配置中对应 channel(如main)的formatter是monolog.formatter.line,且未被其他 formatter 覆盖 - 确保
app/config/config_prod.yml和config_dev.yml中都启用了该 processor(dev 环境常被忽略) - 如果用了
json格式输出,需改用JsonFormatter,否则extra会被丢弃或转成空字符串
真正容易被忽略的是:Processor 只对它绑定的 channel 生效;如果你在控制器里用 $this->get('logger')->info(),它走的是 main channel;但如果你用了 @logger 注入并指定了不同 channel(如 security),就必须给那个 channel 单独打标签。


















