必须调用DB::enableQueryLog()才能获取查询日志,它仅对当前请求有效;toSql()可查看原始SQL,结合getBindings()获取参数;DB::listen()更可靠地捕获含真实参数的SQL。

查不到 SQL?先确认是否启用了查询日志
默认情况下,Laravel 的 DB::getQueryLog() 返回空数组——不是 bug,是设计如此:查询日志只在 DB::enableQueryLog() 调用后才开始记录,且仅对当前请求生命周期有效。开发环境没开日志,自然什么都看不到。
- 必须在需要调试的代码前显式调用
DB::enableQueryLog() - 不要放在中间件或服务提供者里“一劳永逸”,它不跨请求,也不自动开启
-
APP_DEBUG=true并不会自动启用查询日志,这是常见误解 - 如果用的是队列任务或 API 测试(如 PHPUnit),需在对应上下文中单独启用
想看实时 SQL?用 toSql() 而不是执行
toSql() 是最轻量、最安全的调试方式,它跳过执行,只生成最终 SQL 字符串,适合检查绑定参数是否被正确替换、子查询结构是否符合预期。
-
DB::table('users')->where('id', 1)->toSql()输出:select * from `users` where `id` = ?(注意:? 不会被替换成 1) - 要看到真实值,得手动处理绑定:
DB::table('users')->where('id', 1)->getBindings()返回[1] - 别在生产环境或循环里滥用
toSql(),它仍会构建完整查询实例,有轻微开销 - 关联查询(
with())和聚合(groupBy())也支持toSql(),但嵌套太深时 SQL 可读性会下降
SQL 打印出来全是 ?,怎么看到真实参数值?
Laravel 查询构建器默认使用预处理语句,所以日志里的 SQL 总带问号。要拼出可直接复制到数据库客户端执行的语句,得自己做参数替换——但别手写正则,容易错位或漏转义。
- 推荐用 Laravel 自带的
DB::listen()回调,在每次查询触发时拿到$query、$bindings和$time - 简单拼接示例:
str_replace(['?', '?'], $bindings, $query)不可靠,因为字符串里也可能含? - 更稳妥的做法是用
Illuminate/Database/Query/Grammars/Grammar::compileBindings()的逻辑(实际项目中建议封装为辅助函数) - 注意:JSON 字段、二进制数据、NULL 值在绑定数组中是原样传递的,替换时需判断类型再加引号或保留
NULL
为什么 Telescope / Clockwork 显示的 SQL 和 getQueryLog() 不一致?
Telescope 和 Clockwork 是通过 DB::listen() 钩子捕获查询的,而 getQueryLog() 依赖内部日志数组。两者时机和范围不同,尤其在事务、多连接、延迟加载场景下差异明显。
- 事务中未提交的查询,
getQueryLog()会记录,但 Telescope 可能因监听时机略晚而漏掉 - 如果你切换了数据库连接(如
DB::connection('mysql2')),getQueryLog()默认只返回主连接日志 - 某些 ORM 操作(如 Eloquent 的
loadMissing())可能绕过查询构建器,导致toSql()不可用,只能靠监听 - 本地开发用 Telescope 最省心,但它的日志是异步写入的,极短生命周期请求(如健康检查)可能来不及捕获
真正难的不是打印 SQL,而是确认你看到的那条,确实是业务逻辑里真正执行的那条——连接、事务、连接池、读写分离都可能让“你以为的查询”和“实际发出的查询”错位。


















