Laravel查询日志默认仅对当前请求有效且不捕获PDO直连、队列等查询;SQL被截断因预处理模拟,需手动拼接bindings;推荐用Telescope或Clockwork替代硬编码调试。

开启 Laravel 查询日志后没看到 SQL?
默认情况下 DB::enableQueryLog() 只对当前请求生命周期有效,且仅记录通过 DB:: 门面或查询构造器执行的语句,Eloquent 模型操作会记录,但原生 PDO 调用、第三方包直连数据库、队列任务里的查询都不会被捕捉。
常见错误现象:DB::getQueryLog() 返回空数组,或只在 Tinker 里生效,Web 请求中始终为空。
- 确保在查询发生前调用
DB::enableQueryLog()(比如在中间件、控制器方法开头) - 不要在
AppServiceProvider::boot()里全局启用——它早于请求上下文,日志对象会被丢弃 - 如果用了读写分离,
DB::connection('write')和DB::connection('read')是独立日志,需分别启用 - 注意:开启后有性能损耗,生产环境禁用
Laravel 日志里 SQL 被截断或显示为 ? 占位符
这是 Laravel 默认使用 PDO 的预处理模拟(PDO::ATTR_EMULATE_PREPARES = true)导致的,日志中只保留带问号的模板,参数单独存在 bindings 字段里,不会自动拼接。
要看到完整可执行 SQL,得手动组合:
use Illuminate\Support\Str;
$queries = DB::getQueryLog();
foreach ($queries as $q) {
$sql = Str::replaceArray('?', $q['bindings'], $q['query']);
\Log::info("Full SQL: {$sql}");
}
- 别依赖日志文件直接搜
INSERT INTO—— 实际写入的是带?的字符串 -
DB::listen()回调里的$query参数结构相同,同样需要手动拼接 - MySQL 服务端真实执行的 SQL 可通过
SHOW PROFILES或慢日志查看,和 Laravel 日志不是一回事
想实时看 SQL 但不想改代码?用 Telescope 或 Clockwork
硬编码 DB::enableQueryLog() + dd() 只适合本地调试;真要持续观察,得靠工具链。Telescope 是 Laravel 官方推荐,Clockwork 更轻量,两者都能捕获完整查询、绑定参数、执行时长、甚至 N+1 提示。
- Telescope 需运行
php artisan telescope:install,然后访问/telescope;默认不记录生产环境请求,修改TELESCOPE_ENABLED环境变量可强制开启(慎用) - Clockwork 不依赖 Laravel 服务容器,通过 Composer 安装后自动注入,访问任意页面底部会出现小图标,点开即见 SQL 标签页
- 两者都支持过滤:按连接名、执行时间 >100ms、是否含
SELECT *等条件筛选
DB::listen() 里怎么安全地 dump SQL?
DB::listen() 是监听所有查询最灵活的方式,但它在每次查询后同步触发,若在里面调用 dd() 或未加判断地 var_dump(),会导致整个请求中断,尤其在 API 或前端资源加载场景下非常危险。
- 只在开发环境启用:
if (app()->environment('local')) { DB::listen(...); } - 加条件过滤:比如只监听某个表
str_contains($event->sql, 'users'),或跳过迁移/健康检查类查询 - 避免阻塞:用
\Log::debug()写日志,而不是echo或dump(),否则响应头可能已发送,导致报错 - 注意事件对象字段:
$event->sql是原始语句,$event->bindings是参数数组,$event->time是毫秒数
真正难的不是“怎么打出来”,而是“什么时候该停手”——SQL 日志容易滚雪球,一个分页接口带关联加载,一次请求能打出三四十条语句。盯住慢查询和意外全表扫描,比数总数重要得多。











