ThinkPHP 6 日志需切换为 Monolog 驱动以支持按大小切割;配置 RotatingFileHandler 时须显式指定时区、关闭缓冲、传入 maxSize 字符串参数,并用闭包创建实例绕过类型校验。

ThinkPHP 6 日志默认不支持按大小切割,得换驱动
ThinkPHP 6 内置的 FileHandler 只支持按天分割(daily),不支持按文件大小滚动(如 10MB 后新建日志)。硬改源码或强行重写 FileHandler 容易在升级时被覆盖,也不稳定。
正确做法是切换为 Monolog 驱动——TP6 原生兼容它,且 Monolog\Handler\RotatingFileHandler 同时支持按天(daily)和按大小(max_files + max_size)两种策略。
- 确认已安装
monolog/monolog:运行composer require monolog/monolog - 确保 TP6 版本 ≥ v6.0.13(低版本
MonologHandler初始化有 bug) - 不要删掉
config/log.php,而是在里面改default和对应通道的type
配置 daily 模式:按天生成,但要注意时区和命名规则
TP6 的 daily 看似简单,实际容易因服务器时区、日志写入延迟导致「当天日志跑到第二天文件里」。比如服务器用 UTC,而你业务逻辑里用 date('Y-m-d') 判断,就会错位。
关键不是“设成 daily 就完事”,而是让 RotatingFileHandler 的切割时机和你的业务时间对齐:
立即学习“PHP免费学习笔记(深入)”;
- 在
config/log.php中把通道的type改为'monolog' - 通过
handler选项传入RotatingFileHandler实例,显式指定$maxFiles = 30(保留 30 天) - 用
new \DateTimeZone('Asia/Shanghai')显式传入时区,避免依赖系统默认 - 日志文件名默认带
.log后缀,但RotatingFileHandler会自动加日期后缀,如think-2024-05-20.log,无需手动拼接
配置 max_size 模式:必须关掉日志缓冲,否则切不准
按大小切割(比如单个文件不超过 5MB)听着合理,但在 TP6 + Monolog 组合下,如果开启日志缓冲(buffer),RotatingFileHandler 无法实时感知文件体积变化,会导致:明明写了 6MB,却还在往旧文件里写,直到下次 flush 才切——这完全违背初衷。
所以必须关缓冲,并接受「每次写日志都触发一次磁盘 I/O」的代价(线上高并发场景慎用):
- 设置
'buffer' => false(在通道配置里) -
RotatingFileHandler构造参数中传$maxSize = '5M'(注意是字符串,不是字节数) -
$maxFiles控制保留几个历史文件,比如7表示最多存 7 个 5MB 的日志 - 别用
mb_convert_encoding()或其他编码处理函数包裹日志内容——Monolog 对非 UTF-8 字符串处理不稳定,可能报Notice: mb_strlen(): Unknown encoding "auto"
自定义 Handler 要绕开 TP6 的 handler 参数校验
想自己写个更灵活的切割逻辑?TP6 的 MonologHandler 类内部会对传入的 handler 实例做类型检查,只认 Monolog\Handler\HandlerInterface,但如果你 extends RotatingFileHandler 加了新方法,它会直接抛出 InvalidArgumentException。
绕过方式很直接,但容易被忽略:
- 不要在
config/log.php的handler键里 new 实例——TP6 会尝试 clone 它,失败就崩 - 改用
create闭包,在里面 new 并 return 实例,TP6 不校验闭包返回值类型 - 闭包里可以安全调用
setFilenameFormat()或重写rotate(),但别动write()——TP6 日志上下文(trace、extra)依赖原逻辑注入 - 测试时用
Log::info('test', ['a' => 1])而不是echo,否则看不到上下文字段是否正常写入
最麻烦的不是怎么切,而是切完之后没人清理旧文件——RotatingFileHandler 只管生成和轮转,不负责删除超期文件。如果业务跑半年没人工干预,/runtime/log/ 下可能积压几百个 G 的历史日志。



















