ThinkPHP 6 的 SQL 日志需在调试模式下、单库配置(deploy=0)中启用 'log' => ['enable' => true],仅捕获真实执行的查询(如 Db::query、Db::name()->find),不记录 fetchSql() 构建语句,日志默认显示问号占位符,依赖 runtime 目录可写权限。

ThinkPHP 6 的 database.php 配置里怎么开 SQL 日志
默认不记录任何 SQL,必须手动开启,且仅在调试模式下生效。生产环境即使配了也无效,这是设计使然,不是 bug。
关键配置项是 'log' => ['enable' => true],但它必须嵌套在数据库连接的 'deploy' => 0(单库)分支下,不能写在顶层或 'deploy' => 1(多库)结构里。
-
'type' => 'mysql'等基础配置正常写 -
'log' => ['enable' => true, 'file' => runtime_path() . 'sql.log']——file可选,不设则走 ThinkPHP 默认日志通道 - 确保
APP_DEBUG为true,否则enable字段会被直接忽略
为什么 Db::table()->select() 没进日志,但 Db::query() 却有
ThinkPHP 6 的 SQL 日志只捕获由 Query 类触发的底层执行,而 Db::table()->select() 这类链式调用在未真正执行前只是构建查询对象,日志发生在 ->select() 返回结果那一刻;但若中间用了 ->fetchSql(true) 或 ->buildSql(),SQL 根本不会执行,自然不记日志。
-
Db::query('select * from user')和Db::execute('update ...')一定记日志(直连 PDO) -
Db::name('user')->where('id', 1)->find()会记,前提是没提前调fetchSql() - 使用模型(
UserModel::find(1))同样记日志,模型底层也是走 Query
日志内容里看不到绑定参数值,只有问号占位符
这是 PDO 预处理机制决定的,ThinkPHP 不做额外替换。日志中出现 SELECT * FROM user WHERE id = ? 是正常现象,不代表参数没传进去。
立即学习“PHP免费学习笔记(深入)”;
想看到真实 SQL,得关掉预处理——但不推荐。更稳妥的方式是:在日志输出前临时 patch 查询对象,或改用 Db::getLastSql() 在代码中打印。
- 临时查看:在查询后立刻加
echo Db::getLastSql(); die; - 全局替换(仅开发):在数据库配置里加
'params' => [PDO::ATTR_EMULATE_PREPARES => true],但会失去预处理安全保护 - 注意:
getLastSql()必须在查询执行后立即调,延迟调用可能返回空或上一条语句
日志文件权限被拒绝、写不进 runtime/log/
ThinkPHP 日志系统依赖 runtime/ 目录可写,但 SQL 日志默认复用框架日志通道,如果 log 配置里指定了自定义路径(如 'file' => '/var/log/tp-sql.log'),就必须保证该路径存在且 Web 进程用户(如 www-data)有写权限。
- 检查
runtime/目录权限:应为755,子目录和文件需644或664 - 避免绝对路径写错:比如
/runtime/sql.log少了开头的.,实际变成根目录下的 runtime - Apache/Nginx 用户组不一致时,可用
chown -R www-data:www-data runtime/修复(Linux)
SQL 日志本身不单独建目录,它完全依赖你配的路径和运行时权限,这点比想象中更“脆”。



















