thinkphp 5.1+ 默认日志不支持按级别自动分流,需手动操作 monolog 实例添加多个 rotatingfilehandler 并设置 level 和 bubbling 参数,确保互斥写入且路径可写。

ThinkPHP 5.1+ 默认日志系统基于 Monolog,但 Log::record() 等调用默认走 default 通道,不自动按模块/业务类型分流;要实现「多处理器分级存储」(比如 error 写入单独文件、debug 只存本地、info 推送到 Slack),必须手动配置多个 Handler 并绑定到同一 Logger 实例,不能只靠改 log.php 里的 channel 配置。
为什么 Log::channel('xxx') 无法直接实现分级存储
调用 Log::channel('admin') 只是切换当前 logger 实例的「默认写入目标」,它背后仍是一个单一 Logger 对象,所有级别日志都走同一个 Handler。Monolog 的分级能力在 Handler 层——每个 Handler 可设置 $level 过滤阈值,且可共存于一个 Logger 中。
-
Log::channel('admin')不等于「创建新 Logger」,只是取缓存的实例或初始化一次 - 框架默认的
File驱动不支持 per-level 文件分离,需显式添加多个RotatingFileHandler - 若仅配置
'admin'=>['type'=>'File', 'level'=>['error']],实际无效:ThinkPHP 的原生 File 驱动不识别level数组,该字段被忽略
如何给一个通道绑定多个 Handler(如 error.log + info.log)
必须绕过 ThinkPHP 原生日志驱动封装,直接操作 Monolog 实例。推荐在 app\common\middleware\InitLogChannel 中完成:
- 先调用
Log::getLogger()获取底层Monolog\Logger实例(非 ThinkPHP 的think\Log封装类) - 清空默认
Handler:$logger->popHandler()(否则会和你新加的 handler 冲突) - 为 error 单独加一个
RotatingFileHandler:new RotatingFileHandler(LOG_PATH . 'admin_error.log', 7, Logger::ERROR) - 为 info/debug 加另一个
RotatingFileHandler:new RotatingFileHandler(LOG_PATH . 'admin_info.log', 30, Logger::INFO) - 注意顺序:Monolog 按 handler 添加顺序过滤,
ERRORhandler 必须放在INFOhandler 前面,否则 INFO handler 会把 ERROR 日志也吃掉
使用 RotatingFileHandler 时路径与权限的坑
RotatingFileHandler 不会自动创建父目录,且默认以 0644 权限创建文件,容易因目录不存在或权限不足导致日志静默丢失。
- 必须提前确保
LOG_PATH . 'admin_error.log'所在目录存在,建议在中间件中用mkdir($dir, 0755, true)创建 - 不要用
date('Ym/d')动态拼路径——RotatingFileHandler自带按天滚动逻辑,额外嵌套会导致文件名混乱(如admin_error.2026-04-29.log变成202604/admin_error.2026-04-29.log) - 若需按模块+日期双层目录,应继承
RotatingFileHandler重写getFilename(),而非在构造参数里硬塞路径
错误级别日志没进 error.log?检查 Handler 的 bubbling 行为
默认情况下,Monolog 的 Handler 是「冒泡」的:一个日志记录会依次经过所有 handler,除非显式关闭 bubbling。这意味着即使你设了 Logger::ERROR,INFO 级别日志仍可能被后续 handler 记录。
- 在添加 handler 时传入第四个参数:
new RotatingFileHandler(..., 7, Logger::ERROR, false)(false关闭冒泡) - 或者统一用
FilterHandler包裹:new FilterHandler($handler, Logger::ERROR),更清晰可控 - 验证方式:临时加一个
StreamHandler('php://stdout'),看 ERROR 日志是否重复输出两次
真正难的不是加几个 handler,而是确保每个 handler 的 level 范围互斥、路径可写、不因常驻进程(Swoole)复用旧实例而错乱——这些细节不验证,日志就只会安静地消失。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











