Log::record不生效的根本原因是Log驱动未初始化,即Log::$driver为null,常见于App::initialize()前调用、path不可写、level未包含当前级别或LOG_RECORD未显式开启。

ThinkPHP 日志系统不是“调用 Log::record 就能写进文件”,而是由初始化时机、驱动加载顺序、请求生命周期钩子三者共同决定的;漏掉任意一环,日志就静默失效。
Log::record 为什么有时不生效?
常见现象是写了 Log::record('msg', 'info') 却没生成日志文件,甚至 Log::save() 也返回 false。
- 根本原因:Log 驱动未初始化,
Log::$driver为 null —— 这通常发生在App::initialize()之前调用记录逻辑(比如某些中间件或服务提前触发) - TP5 默认在请求结束时自动调用
Log::save(),但前提是Log::$log不为空且Log::$driver已实例化 - 若配置中
'type' => 'file'但'path'目录不可写,或'levels'里没包含当前日志级别(如只配了['error']却调用Log::record(..., 'info')),也会静默丢弃
日志驱动初始化的真实时机
Log 驱动不是框架启动就加载,而是在首次调用 Log::record 或显式调用 Log::init() 时才触发初始化。
-
Log::init()会读取config/log.php中的配置,并根据'type'实例化对应驱动(如think\log\driver\File) - 如果
App::$debug === false,且没手动调用Log::init(),某些低频日志(如debug级)可能压根不会初始化驱动 - 注意:环境变量
LOG_TYPE若覆盖了配置,会绕过 config/log.php,导致预期外的驱动类型(比如设成db但没配数据库连接)
request_id 如何真正注入到每条日志中?
单纯在中间件里 session('request_id', $id) 并不能让日志自动带上它 —— 日志格式由驱动控制,不自动读取 session 或 request。
立即学习“PHP免费学习笔记(深入)”;
- 必须重写日志格式:在
config/log.php中设置'format' => '[{datetime}] [{level}] {message} [rid:{request_id}]' - 但
{request_id}是自定义占位符,需扩展think\log\driver\File的write()方法,在写入前用Request::instance()->header('X-Request-Id')或全局变量替换 - 更稳妥的做法是:在中间件中把
request_id注入到Log::$config['extra']['request_id'],再让驱动从$config['extra']取值拼接
错误和异常日志为何有时“看不见”?
TP5 的异常日志依赖 set_exception_handler 和 register_shutdown_function 捕获,但它们只对未被 try/catch 拦截的异常生效。
- 控制器内
try { ... } catch (\Exception $e) { Log::error($e->getMessage()); }是主动记录,不受框架异常处理器影响 - 而
Log::record()不会自动捕获E_WARNING类错误,必须配合set_error_handler将其转为异常再交由 Log 处理 - 关键细节:
Log::save()在App::run()结束时执行,但如果异常导致 PHP 提前终止(如内存溢出),register_shutdown_function可能来不及写入 —— 此时日志会丢失
最易被忽略的一点:日志是否写入,不取决于你调用了多少次 Log::record,而取决于 Log::$driver 是否成功初始化、Log::$log 是否在 Log::save() 前仍保有数据、以及目标存储介质(文件权限/数据库连接)是否真实可用。调试时优先检查 Log::$driver 和 Log::$log 的状态,比翻配置文件更快定位问题。



















