Symfony日志通道需禁用console handler、仅保留rotating_file或stream处理器,并在monolog.yaml中声明所有通道、为各通道绑定专属文件处理器,确保日志写入文件而非被FrankenPHP捕获。

FrankenPHP 本身不接管 Symfony 的日志通道逻辑,它只负责将 PHP 进程的标准输出/错误(stdout/stderr)转发给系统日志或控制台。Symfony 的 monolog 配置完全独立运行,但两者衔接的关键在于:**别让 FrankenPHP 吞掉你配好的文件日志,也别让 Monolog 错误地往 php://stdout 写而污染容器日志流**。
FrankenPHP 默认会捕获所有 PHP stdout/stderr
当你用 frankenphp serve 或 systemd 启动时,PHP 的 error_log()、var_dump()、未配置处理器的 Monolog console handler,都会被 FrankenPHP 拦截并转为结构化日志行(含时间戳、level、request_id)。这会导致两个问题:
- 你配了
rotating_file,但同时又没禁用consolehandler → 日志既写文件又刷到 FrankenPHP 控制台,重复且冗余 - 开发时习惯用
$logger->info()却没指定通道,结果落到app通道 → 若该通道绑了consolehandler,就会被 FrankenPHP 收走,var/log/dev.log仍为空 - FrankenPHP 的日志级别默认是
info,而 Monolog 的debug级别会被静默丢弃(除非显式调高 FrankenPHP 的--log-level)
如何让 Symfony 日志通道真正写入文件而不被 FrankenPHP 干扰
核心原则:**禁用所有指向 php://stdout 或 console 的 handler,只保留 stream 或 rotating_file,并确保 channels 列表准确覆盖你的业务通道**。
- 检查
config/packages/dev/monolog.yaml和config/packages/prod/monolog.yaml,删掉或注释掉console:块 - 为每个自定义通道(如
payment)单独配rotating_filehandler,并严格限定channels: [payment] - 主通道
app必须显式绑定一个文件 handler,例如:app_file: type: rotating_file path: '%kernel.logs_dir%/app.log' level: debug max_files: 7 channels: ['app']
- 确认
monolog.yaml顶层的channels列表包含你用到的所有通道名,比如channels: ['app', 'payment', 'api']—— 缺一个,$this->get('monolog.logger.payment')就会报服务未定义
在 FrankenPHP 环境下验证通道日志是否生效
别只看终端输出,要直接查文件 + 容器日志双验证:
立即学习“PHP免费学习笔记(深入)”;
- 执行
bin/console debug:container --tag=monolog.logger,确认monolog.logger.payment这类服务存在且状态为 “active” - 在控制器里临时加一行:
$this->get('monolog.logger.payment')->info('test from frankenphp'); - 触发请求后,立刻检查:
ls -l var/log/payment.log是否有内容、修改时间是否匹配;同时用journalctl -u frankenphp -n 20看是否有意外的stdout日志混入 - 如果
payment.log有内容但journalctl也有重复条目,说明某个 handler 还没关干净,重点检查handlers下是否漏掉了console或whatwhy类型的 handler
为什么 rotating_file 在 FrankenPHP 下有时不轮转
不是 FrankenPHP 的锅,而是 PHP 进程生命周期导致的常见陷阱:FrankenPHP 默认以 long-running 方式复用 PHP worker,RotatingFileHandler 依赖 stream 文件句柄的打开/关闭时机来判断是否需要切分日期文件。如果进程不重启,句柄一直开着,就不会触发轮转。
- 解决方案一:改用
type: stream+ 外部 logrotate(推荐用于生产),在/etc/logrotate.d/里配好var/log/*.log规则 - 解决方案二:强制每日 reload FrankenPHP(如 systemd timer 调用
systemctl reload frankenphp),触发 worker 重启 - 不要依赖
filename_format参数 —— Symfony 6+ 已移除该选项,轮转格式固定为{filename}-{date}.{extension},无法自定义
最易被忽略的一点:FrankenPHP 的 --log-level 只影响它自己捕获的 stderr 和未被 Monolog 处理的裸输出,对 Monolog 已经绑定好 rotating_file 的通道日志毫无影响。别指望调高这个参数能让 payment.log 多出 debug 行 —— 那得去调 monolog.handlers.payment_file.level。



















