Docker容器中Symfony Worker日志无法输出至supervisord/runit/stdout求助
我之前处理过类似的场景,结合Symfony Monolog的配置逻辑、Docker的输出捕获机制,以及supervisord/runit的特性,给你几个具体的排查和解决方向:
1. 修复Monolog Handler的通道匹配问题
你的console handler配置里排除了console通道,而Symfony Messenger Worker(默认的Symfony Worker实现)的日志默认会打到console通道。这就导致Worker的日志直接被console handler忽略,而如果main handler还是指向文件路径(哪怕你改成了php://stdout),也要确认它的channels是否包含Worker的日志通道。
推荐两种调整方式:
- 给Worker单独配置专属Handler:
handlers: main: type: stream path: "%kernel.logs_dir%/%kernel.environment%.log" level: debug channels: ["!event"] worker_stdout: type: stream path: php://stdout level: debug channels: ["console"] # 专门捕获Worker的console通道日志 console: type: console process_psr_3_messages: false channels: ["!event", "!doctrine", "!console"]
- 修改现有Console Handler的通道规则:
去掉console通道的排除,同时开启process_psr_3_messages确保PSR日志被处理:
console: type: console process_psr_3_messages: true channels: ["!event", "!doctrine"]
2. 关闭Monolog的输出缓冲
长生命周期的Worker进程中,PHP的输出缓冲会导致Monolog写入php://stdout的内容被滞留,无法即时输出到容器的stdout。给Stream Handler添加buffering: false参数强制实时输出:
handlers: worker_stdout: type: stream path: php://stdout level: debug channels: ["console"] buffering: false # 关键:关闭缓冲,日志实时输出
3. 确保Supervisord/Runit正确捕获输出
针对Supervisord:
检查你的program配置,必须开启标准输出/错误的重定向,并且禁用日志文件大小限制:
[program:symfony-worker] command=php /var/www/html/bin/console messenger:consume async -vv process_name=%(program_name)s_%(process_num)02d numprocs=1 autostart=true autorestart=true user=www-data redirect_stderr=true # 将stderr合并到stdout,方便统一捕获 stdout_logfile=/dev/stdout stdout_logfile_maxbytes=0 # 不切割日志,持续输出到stdout stderr_logfile=/dev/stderr stderr_logfile_maxbytes=0 catch_workers_output=true # 强制捕获子进程的所有输出
针对Runit:
确保你的run脚本使用exec命令启动Worker,这样Worker进程会直接替换shell进程,所有输出都会被runit的日志捕获机制接管:
#!/bin/sh cd /var/www/html # 使用exec让Worker成为主进程,避免输出被shell拦截 exec php bin/console messenger:consume async -vv
4. 调整PHP的输出配置
在PHP层面禁用输出缓冲,确保日志能即时输出:
- 在php.ini中设置:
output_buffering = Off implicit_flush = On
- 或者在启动Worker时通过命令行参数临时设置:
php -d output_buffering=0 bin/console messenger:consume async -vv
5. 验证Worker的Logger注入是否正确
检查你的Worker Handler类是否正确注入了Psr\Log\LoggerInterface,避免使用自定义的、未关联Monolog配置的日志实例:
use Psr\Log\LoggerInterface; class OrderProcessingHandler { private $logger; // 确保注入的是Symfony容器提供的Logger服务 public function __construct(LoggerInterface $logger) { $this->logger = $logger; } public function __invoke(ProcessOrder $message) { // 测试日志输出 $this->logger->info('开始处理订单', ['order_id' => $message->getOrderId()]); // ...业务逻辑 } }
6. 快速验证Monolog配置是否生效
可以在Worker中临时添加调试代码,确认Handler的配置是否正确加载:
// 在Worker Handler的__invoke方法中临时添加 $handlers = $this->logger->getHandlers(); foreach ($handlers as $handler) { if ($handler instanceof \Monolog\Handler\StreamHandler) { echo "当前Handler路径: " . $handler->getUrl() . "\n"; echo "当前Handler监听的通道: " . implode(',', $handler->getChannels()) . "\n"; } }
运行Worker后,查看stdout输出,就能确认Handler的路径和通道是否符合预期。
内容的提问来源于stack exchange,提问作者Kolesar

