ThinkPHP日志记录失败主因是runtime/log目录不可写、APP_DEBUG=true、日志驱动被覆盖、级别配置错误或异常未进框架;须逐层排查权限、调试模式、配置项及刷盘机制。

ThinkPHP日志记录失败,通常不是“没配置”,而是几个关键环节被悄悄绕过或卡住——目录不可写、APP_DEBUG开着、驱动被覆盖、级别设太高、异常根本没进框架。排查要从最底层开始,而不是翻配置文件。
检查 runtime/log 目录是否真正可写
90% 的日志空白问题出在这里。ThinkPHP 默认写入 runtime/log/,但这个路径必须同时满足:目录存在、父目录 runtime 可写、Web 进程用户(如 www-data 或 nginx)有写权限。
- 执行 ls -ld runtime runtime/log,确认权限位包含 drwxr-xr-x 或更宽松,且属组是 Web 用户
- 运行 ps aux | grep php-fpm 或 ps aux | grep nginx 查实际进程用户,再用 chown -R www-data:www-data runtime 修正归属(按实际用户调整)
- 容器部署时,确保 volume 挂载的 UID/GID 与容器内 Web 用户一致,否则 chmod 无效
- 别只建 log 目录,runtime 本身也必须可写,否则连子目录都创建不了
确认 APP_DEBUG = false 是硬性前提
ThinkPHP 6/8 在 APP_DEBUG = true 时会跳过日志落盘逻辑,所有错误直接输出到页面,runtime/log/ 和 PHP error_log 都不会记录。这不是 bug,是设计行为。
- 检查 .env 文件,必须有 app_debug=false(config/app.php 中的设置会被 .env 覆盖)
- 加一行代码验证:echo config('app.app_debug') ? 'on' : 'off';,输出必须为 off
- Fatal error、Parse error 等底层错误,仅在 app_debug=false 时由 register_shutdown_function 捕获并写入日志
核对 log.php 中三个生效项是否全对
光改 'level' => 'debug' 不够。以下三项缺一不可,且位置和格式不能错:
- 'default' => 'file':不能是空字符串、test、trace,必须是真实落盘驱动名
-
'channels' => ['file' => ['level' => ['error', 'warning']]]:level 必须是数组,
'level' => 'error'字符串写法无效 - 'close' => false:全局和 channel 里都要确认为 false;设成 true 就等于关掉整个日志系统
排除驱动被覆盖或日志未刷盘
手动调用 Log::record() 后看不到内容?可能是日志还在内存缓存,或被中间件/命令行中途切换了驱动。
- 检查是否在中间件、common.php 或命令行中调用了 Log::driver('socket') 或 Log::driver('test'),这会覆盖默认 file 驱动,且 socket/test 都不落盘
- 开启 'realtime_write' => true(在 channels.file 下),避免日志积压数秒才写入,调试时更直观
- 想立刻看到效果,改用 Log::save() 强制刷盘(注意性能影响,仅临时排查用)
- 用 Log::getLog() 查当前内存中已记录但未刷盘的日志条目,确认是“没写”还是“没刷”
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











