必须严格对齐通道声明、处理器绑定和级别过滤三者,漏一环日志即静默丢弃或误入其他通道;需在monolog.yaml中显式声明channels、为handler配置channels白名单及level,并正确注入对应服务id。

直接按 channel 分流日志,关键不是“加个配置就行”,而是必须确保通道声明、处理器绑定、级别过滤三者严格对齐——漏掉任意一环,日志就会静默丢弃或误入其他通道。
monolog.yaml 里必须显式声明 channel
没出现在 channels 列表里的名字,容器根本不会注册对应的服务 ID,monolog.logger.payment 这类注入会直接报错 ServiceNotFoundException。
- 在
config/packages/monolog.yaml的顶层加channels键,例如:channels: ['payment', 'api', 'audit'] - 已有默认通道(如
app、security)可跳过声明,但自定义名必须列出来 - 别用
monolog.additional_channels参数动态加——它只在编译期生效,运行时新增 channel 无效
每个 handler 必须用 channels: [] 严格限定归属
channels 字段是白名单,不是通配符。写成 channels: [payment] 表示“只处理 payment 通道来的日志”,哪怕其他通道的日志也打到这个 handler,也会被静默忽略。
- 一个 handler 可以绑定多个 channel,例如:
channels: [payment, api] - 同一 channel 可绑定多个 handler,比如
payment_file写文件 +payment_sentry推 Sentry - 别漏掉
level:如果 handler 设了level: warning,$logger->info()就不会触发它
代码里用对服务 ID 或标签,否则拿不到目标 channel
三种方式本质不同,选错就拿不到你想要的 logger:
- 依赖注入别名最稳:
LoggerInterface $paymentLogger+ 构造函数参数类型提示,前提是服务 IDmonolog.logger.payment已存在(MonologBundle 自动注册) - 服务标签自动注入要配对:
tags: [{ name: monolog.logger, channel: payment }],且服务类构造函数必须接收LoggerInterface类型参数 - 容器直取
$this->get('monolog.logger.payment')在 Symfony 6+ 已不推荐,容易绕过 DI 检查
Handler 执行顺序影响日志是否被截断
Monolog 是责任链模式:handler 按 YAML 中定义的顺序入栈,但执行是后进先出(LIFO)。如果某个 handler 返回 false 或抛异常,后续 handler 就不会执行。
- 比如
payment_sentry失败时不想阻塞本地写入,就把它放在payment_file后面 - RotatingFileHandler 默认不中断传播,StreamHandler 也是;但自定义 handler 要显式返回
true确保继续传递 - 生产环境慎用
type: consolehandler——它只在 CLI 或 debug 模式下生效,Web 请求里看不到输出
最容易被忽略的是 handler 的 level 和 channel 的 channels 白名单双重过滤:两者都得匹配,日志才真正落地。调试时先检查 bin/console debug:container | grep monolog.logger 确认服务是否存在,再看 handler 配置里有没有拼错 channel 名——大小写和下划线都算错。











