thinkphp 6 移除了全局 log 类,需改用 think\facade\log 或 app('log');socket 驱动失效,须集成 monolog;多应用需显式配置 log.path 隔离日志;log.trace 生产环境必须关闭。

ThinkPHP 6 升级后 Log::write() 不再生效
TP6 彻底移除了全局静态 Log 类,Log::write() 直接报 Class 'think\Log' not found。这不是配置问题,是类被删了——官方改用 think\facade\Log 门面 + 依赖注入方式记录日志。
实操建议:
- 把所有
Log::write('msg')替成\think\facade\Log::info('msg')或app('log')->info('msg') - 门面方法(如
info()、error())参数签名和 TP5 一致,但底层调用的是think\log\driver\File等驱动实例,不支持直接传数组级别参数(如['level' => 'error']) - 若项目中混用了
think\Log的自定义封装,需同步重写为基于LoggerInterface的实现,否则运行时抛出BadMethodCallException
TP5.1 日志配置 log.type = socket 在 TP6 中失效
TP6 移除了原生 socket 日志驱动,log.type = socket 配置会被忽略,日志静默降级到 File 驱动且无任何警告。
实操建议:
- 如需远程日志(比如发给 Logstash),必须手动引入
monolog/monolog并注册自定义驱动:新建app/log/SocketHandler.php继承Monolog\Handler\SocketHandler,再在app/provider.php中绑定think\log\driver\Custom实例 - TP6 默认的
File驱动开启按天分文件(log.file_format = Y-m-d),但不会自动清理旧日志;若沿用 TP5.1 的log.max_files = 30配置,该配置项已废弃,需改用log.file_size = 10485760+ 外部定时脚本清理 - 注意
log.level在 TP6 中默认是debug,生产环境务必显式设为error,否则大量调试日志会迅速打满磁盘
多应用模式下各应用日志路径混乱
TP6 多应用(app/multi)中,若未显式配置 log.path,所有应用共用 runtime/log/ 下同一套文件,无法按应用隔离。
实操建议:
- 在每个应用的
config/log.php中强制指定路径:'path' => runtime_path() . 'log/' . APP_NAME . '/',其中APP_NAME是应用目录名(如'admin'、'api') - 不要依赖
log.file配置项控制文件名——它只影响单个日志文件名,不改变目录结构;真正起作用的是path和file_format - 如果使用 Swoole 模式,
runtime_path()可能返回非预期路径(如/tmp/thinkphp/runtime),此时需用Env::get('RUNTIME_PATH')替代,并确保该环境变量在 Swoole 启动前已加载
TP6.3+ 引入 log.trace 后性能明显下降
开启 log.trace = true 后,每条日志都会记录 debug_backtrace(),CPU 占用飙升,尤其在高频请求接口中,单次 Log::info() 耗时从 0.2ms 增至 8ms+
实操建议:
-
log.trace仅建议在开发环境开启,生产环境必须关闭;CI/CD 流水线中应检查该配置是否被误提交 - 若需追踪特定错误,优先用
try-catch包裹关键块,手动在catch中调用Log::error($e->getMessage(), ['trace' => $e->getTraceAsString()]),避免全量采集 - TP6.3.5 起支持按日志等级开关 trace:
'trace_level' => ['error', 'alert'],但该配置对info()和notice()无效,别指望它“部分优化”
最麻烦的不是配置迁移,而是历史代码里散落的 file_put_contents(runtime_path().'log/test.log', ...) 这类直写操作——它们绕过整个日志系统,既不走格式化也不受等级控制,上线后才发现日志根本没进 ELK,还得逐个 grep 改。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!










