thinkphp sql日志不生效的主因是app_debug为false、log.level未包含'sql'或log.type非'file';需三者同时满足,且apart_level中级别必须先在level中声明。

ThinkPHP 的日志级别配置不生效,或者 SQL 日志始终不写入,八成是 level 和 apart_level 混用了,或者 APP_DEBUG 为 false 时还指望 sql 日志被记录——这根本不可能。
为什么开了 APP_DEBUG 却没 SQL 日志?
SQL 日志不是独立开关,它依赖调试模式 + 日志级别 + 驱动支持三者同时满足。缺一不可。
-
APP_DEBUG必须为true(环境变量或配置中),否则框架直接跳过 SQL 记录逻辑 -
log.level必须显式包含'sql',例如['error', 'sql'];只写['error']不会记录 SQL - ThinkPHP 6.x 中,
log.type为'File'时才支持sql级别落盘;'trace'或'socket'驱动不记录 SQL 到文件 - 5.1+ 版本废弃了
LOG_RECORD_LEVEL全局数组,改用log.level,旧配置残留会导致失效
log.level 和 apart_level 到底怎么配?
log.level 控制“哪些日志允许产生”,apart_level 控制“哪些级别单独成文件”。两者不是替代关系,而是先后过滤。
- 若只要错误日志,配
'level' => ['error']即可,apart_level可不设 - 若想让 SQL 日志单独写进
sql.log,必须同时设:'level' => ['error', 'sql']+'apart_level' => ['sql'] -
apart_level里写的级别,必须先在level里声明过,否则不会被分离——比如level没写sql,只在apart_level写了sql,结果就是:无 SQL 日志,也无sql.log - 路径末尾必须带斜杠:
'path' => RUNTIME_PATH . 'log/',否则File驱动会把log当成文件名,导致日志写入失败
Db::listen() 为什么一用 SQL 日志就没了?
Db::listen() 是接管式监听,一旦注册,框架默认的 SQL 日志记录就被禁用——它假设你打算自己处理日志。
- 如果你只是临时调试,用
Db::listen(function($sql, $time) { dump($sql . ' [' . $time . 's]'); });就够了 - 如果要保留原有日志文件 + 额外监听,必须手动触发写入:
Log::record($sql, 'sql');(注意:需确保log.level包含'sql') -
$explain参数只在db.sql_explain => true且查询实际执行了EXPLAIN时才有值;单纯开sql_explain不等于每条 SQL 都自动分析,得配合慢查询阈值或手动调用 - 别用
->fetchSql(true)替代监听——它不执行、不绑定参数、不测耗时,看到的 SQL 很可能和真实执行的不一致
生产环境怎么安全留 SQL 线索?
生产环境绝不能开 APP_DEBUG,但可以有限度捕获异常 SQL。
- 强制
APP_DEBUG => false,然后在数据库配置中设'slowlog' => true(需自定义中间件支持)或用Db::listen()+ 条件判断:if ($time > 1.0) { Log::record("SLOW: $sql", 'warning'); } - 日志路径必须隔离:
'path' => RUNTIME_PATH . 'log_prod/',避免和开发日志混在一起 - 加
'max_files' => 30防止磁盘打满,尤其在高并发慢查场景下 - SQL 日志永远不该包含敏感字段值(如密码、token),监听回调里要做脱敏处理,例如用正则替换
/password\s*=\s*['"].*?['"]/i
最易忽略的一点:ThinkPHP 的 .env 不支持嵌套语法,LOG_LEVEL=sql,error 这种字符串不会自动转成数组,必须在代码里用 env('APP_ENV') === 'dev' ? ['sql','error'] : ['error'] 动态返回——硬编码配置看似省事,换环境就掉坑里。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











