ThinkPHP生产环境日志不写入的主因是APP_DEBUG=false时默认禁用日志,且必须显式配置level数组(如['error','warning']);权限问题常导致静默丢弃,需确保PHP-FPM用户对runtime/log有写权限;trace未开启则无堆栈信息,apart_level可分流sql等日志但生产环境sql日志仍为空。

ThinkPHP 日志不是配了就能看到,生产环境默认不写、权限不对就静默丢弃、Log::info() 调用成功但文件里空空如也——这些问题基本都卡在配置组合和执行时机上。
日志根本没写入?先确认 APP_DEBUG 和 log.level 是否生效
生产环境(APP_DEBUG = false)下,ThinkPHP 默认跳过所有日志写入逻辑,哪怕 config/log.php 里写了 'type' => 'File' 也没用。必须显式配置 level 数组,且至少包含一个有效级别:
-
'level' => ['error', 'warning']—— 生产环境最低安全配置 -
'level' => ['debug', 'info', 'error']—— 仅限开发环境,上线前必须删掉debug和info - 别只改
app.php里的log配置,TP6+ 主配置已移到config/log.php,旧位置的设置会被忽略 -
LOG_LEVEL环境变量可覆盖配置,但必须在public/index.php加载框架前定义,例如:putenv('LOG_LEVEL=error,sql');
日志写了但找不到文件?检查 runtime/log 权限和路径归属
日志目录为空,90% 是 PHP-FPM 进程用户没有 runtime/log 写权限,而不是路径错或磁盘满。Linux 下常见表现是 file_put_contents(...): failed to open stream: Permission denied:
- 先查 PHP 进程用户:
ps aux | grep php-fpm,通常是www-data(Debian/Ubuntu)或nginx(CentOS/RHEL) - 再查目录归属:
ls -ld runtime/log,确保属主/属组匹配上述用户 - 修复命令示例:
chown -R www-data:www-data runtime/log - 权限设为
755(目录)和644(文件)足够,不要盲目chmod 777 - 注意:如果用了
'single' => true,日志会固定写入app.log,不会按日期生成2026_04/18.log
日志有内容但没堆栈?trace 开关没开,或级别不匹配
捕获到异常却只看到 [ERROR] SQLSTATE[...] 一行,没有文件名、行号、变量上下文——这是 trace 未启用的典型表现:
- 在
config/log.php中确认'trace' => true已设置(TP6.1+ 默认为false) -
trace只对error和notice级别生效,info或debug即使开了也不带堆栈 - 开启后单条日志体积可能增大 5–10 倍,线上排查期临时启用即可,切勿长期打开
- 若用
Log::record('msg', 'error')手动记录,仍需trace配合才能输出上下文;而框架自动捕获的异常默认走完整 trace 流程
想看 SQL 日志或独立错误文件?apart_level 和 sql 驱动要分清
apart_level 不是 level 的子集,它绕过全局过滤,专门分流特定类型日志。但 sql 是特例,受双重限制:
-
'apart_level' => ['error', 'sql']表示error和sql日志各自进独立文件(error.log、sql.log) -
sql日志不看level配置,但强依赖app_debug = true;生产环境即使开了apart_level,sql.log也永远为空 - SQL 日志默认级别是
notice,若你把level设成['error'],sql仍能写,因为apart_level优先级更高 - 自定义通道(如
Log::channel('db')->error())要求channels数组里真有对应键名,写错名不会报错,而是静默丢弃
最易被忽略的是:日志写入是请求结束时统一刷盘的,Log::record() 只是入队,Log::save() 才强制落盘——调试时加一句 Log::save() 能立刻验证是否真写进去了,但别在线上高频调用。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











