Laravel 查询日志默认关闭,需在开发环境调用 DB::enableQueryLog() 启用;DB::getQueryLog() 返回带占位符的 SQL 与 bindings 数组;推荐用 DB::listen() 实时监听并记录完整查询信息。

如何开启 Laravel 的查询日志功能
默认情况下,Laravel 的查询日志是关闭的,DB::getQueryLog() 会返回空数组。必须先手动启用,否则拿不到任何 SQL 记录。
在开发环境(如 app/Providers/AppServiceProvider.php 的 boot() 方法中)添加:
if (app()->environment('local')) {
DB::enableQueryLog();
}
注意:生产环境严禁开启,会影响性能且暴露敏感查询逻辑。
- 只对当前请求生命周期有效,每次 HTTP 请求需重新启用(除非全局中间件里统一处理)
- 启用后,所有通过
DB::、Eloquent、Query Builder 发起的查询都会被记录 - 如果用了读写分离,主库和从库的日志是分开记录的,
DB::getQueryLog()只返回默认连接的日志
怎么获取「上一条」执行的 SQL 语句
所谓“上一条”,实际就是查询日志数组的最后一个元素。但要注意:它不一定是你刚写的那行代码触发的——中间可能穿插了 Laravel 自身的查询(如 Gate 权限检查、Session 存储、日志写入等)。
安全获取方式:
$queries = DB::getQueryLog(); $lastQuery = end($queries); dd($lastQuery['query'], $lastQuery['bindings']);
-
$lastQuery['query']是带问号占位符的原始 SQL -
$lastQuery['bindings']是参数数组,需手动替换才能看到真实值(Laravel 不自动拼接) - 直接用
DB::raw()或原生 PDO 查询绕过 Query Builder 时,不会被记录 - 如果启用了缓存(如
Cache::remember),且命中缓存,则无对应 SQL 日志
监听 DB 查询并实时输出(调试专用)
比起反复调用 getQueryLog(),更推荐用事件监听机制,在每次查询执行后立刻捕获——这对定位某段逻辑下的 SQL 尤其有用。
在 AppServiceProvider::boot() 中注册监听:
DB::listen(function ($event) {
\Log::debug('SQL:', [
'query' => $event->sql,
'bindings' => $event->bindings,
'time' => $event->time,
'connectionName' => $event->connectionName,
]);
});
-
$event是Illuminate\Database\Events\QueryExecuted实例,字段比getQueryLog()更全 - 输出到
storage/logs/laravel.log,配合tail -f storage/logs/laravel.log实时观察最方便 - 如果想只看慢查询,加判断:
if ($event->time > 100) - 该监听对所有数据库连接生效,包括测试时用的
sqlite::memory
常见踩坑点与替代方案
很多人以为 DB::getQueryLog() 能稳定拿到“上一条”,结果在中间件、模型事件、队列任务里失效——根本原因是日志在每次请求结束时被清空,而队列是独立进程,中间件顺序也可能导致日志未及时捕获。
- 不要在模型的
boot()或静态作用域里依赖getQueryLog(),时机不可控 - 使用
DB::transaction()时,日志仍按执行顺序追加,但事务回滚后 SQL 实际未生效,日志却已存在 - 若需完整 SQL(含参数值),可用
Str::replaceArray('?', $bindings, $sql)手动拼接,但注意字符串引号和NULL处理 - 更彻底的调试可配合
barryvdh/laravel-debugbar,它在浏览器底部展示所有查询,支持点击展开绑定参数
真正难的不是取到 SQL,而是确认这条 SQL 确实来自你关心的那行业务代码——尤其当项目用了大量 trait、global scope 或第三方包时,得结合上下文和连接名交叉验证。










