ThinkPHP异常日志不写入等根本原因在于日志初始化时机滞后和异常捕获层级缺失:PDOException发生在Db::connect()阶段,Log驱动未初始化;Fatal error(如Parse/Class not found)在PHP早期触发,绕过框架流程;需在public/index.php顶部用register_shutdown_function+error_get_last()兜底,并重写Mysql::connect()捕获PDO异常。

ThinkPHP 异常日志不写入、写错位置、内容太简略或根本看不到——这些问题基本都卡在「日志初始化时机」和「异常捕获层级」上,而不是配置没开。
为什么 PDOException 和 Fatal error 不进 runtime/log/?
因为这两类错误根本没机会走到 ThinkPHP 的日志系统里:
-
PDOException在Db::connect()时由 PDO 原生抛出,而此时Log驱动尚未初始化(依赖数据库配置,配置又依赖连接,死循环) - Fatal error(如
Parse error、Class not found)发生在 PHP 解析或运行早期,register_shutdown_function虽被 ThinkPHP 注册,但前提是APP_DEBUG = false;开发时设为true,错误直接打屏,不落盘 - 即使你写了
Log::error($e->getMessage()),若异常发生在app/common.php加载前(比如 composer autoloader 失败),这行代码压根不会执行
如何让关键异常至少留下一条可查记录?
不能等框架兜底,得在最上游做拦截:
- 在
public/index.php顶部立即注册register_shutdown_function,用error_get_last()捕获未捕获的 fatal 错误,并用file_put_contents直写到绝对路径(如/tmp/think-fatal.log),绕过Log初始化依赖 - 重写
think\db\connector\Mysql::connect(),在try { new PDO(...) }外层加catch (\PDOException $e),调用Log::record('DB connect fail: ' . $e->__toString(), 'error') - 确保
config/exception.php中'log' => true已开启,否则未捕获的Throwable不会进日志 - 别用
Log::write()记录异常主体——它只记消息字符串;改用Log::info($e->__toString()),能完整输出堆栈、文件、行号、上下文变量
日志写进去了,但 tail -f 看不到实时内容?
不是没写,是缓存或路径没对上:
- 检查
config/log.php中'realtime_write' => true,否则日志可能积压数秒才刷盘 - 运行
php -r "var_dump(ini_get('error_log'));"确认 PHP 自身错误日志路径,Fatal error 会去那儿,不在runtime/log/ - 用
ls -l runtime/log/看目录属主是否为 Web 进程用户(如www-data),权限不足时静默失败,无报错 - 别信
date('Y-m') . '/' . date('d') . '.log这种路径——ThinkPHP 不自动创建多级目录,runtime/log/2026-05/15.log若目录不存在,日志直接丢弃
真正难排查的,永远是那些“没日志”的时刻:PDO 连接超时卡住进程、事务未回滚留脏数据、中间件里 throw new Exception 却被 $ignoreReport 吃掉——这些不会生成明显错误行,但会让后续请求集体变慢或错乱。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











