导读:本期聚焦于台湾程序员创作的《Laravel如何获取执行的SQL语句?监听SQL执行方法与监控实践》,敬请观看详情。想知道某次请求里 Laravel 到底向数据库发送了哪些 SQL、绑定参数是什么、每条语句耗时多久吗?Laravel 提供了 DB::listen 监听器和查询日志机制,可以方便地捕获所有执行的 SQL。本文从全局监听器注册入手,展示如何获取 SQL 模板与绑定值,并给出将问号占位符替换为真实参数、拼出可执行 SQL 的辅助方法。同时介绍 enableQueryLog 的适用场景,以及 laravel-debugbar、Telescope 等开发工具的差异。针对生产环境,还会说明基于慢查询阈值的告警策略、绑定数据脱敏和性能开销控制,帮助你既保留监控能力,又避免日志膨胀与敏感信息泄露。无论你是在排查 N+1 问题、优化索引,还是做数据库审计,都能从这套监听机制中找到合适的落地方式。

在排查接口慢查询、定位 N+1 问题或做数据库审计时,能够拿到 Laravel 实际发送给数据库的 SQL 语句非常关键。Laravel 的查询构建器和 Eloquent 最终都会经由 Illuminate\Database\Connection 执行 SQL,并在执行完成时派发 QueryExecuted 事件。开发者可以在 AppServiceProvider 中通过 DB::listen 注册一个全局监听器,捕获每一条 SQL 的模板、绑定参数和执行时间。这种机制既适合本地调试,也可以在经过裁剪后用于生产环境的慢查询监控。

Laravel如何获取执行的SQL语句?监听SQL执行方法与监控实践

使用 DB::listen 注册全局 SQL 监听器

Laravel 的 DB 门面提供了 listen 方法,接收一个闭包,闭包参数是 Illuminate\Database\Events\QueryExecuted 实例。该实例包含 sql、bindings、time 和 connectionName 属性,分别表示预编译 SQL 模板、绑定值数组、执行耗时毫秒数以及当前连接名称。通常会把监听逻辑放在 AppServiceProvider 的 boot 方法中,因为服务提供者启动时数据库组件已经可用,而且能覆盖应用生命周期内的所有数据库操作。

下面这段代码在应用启动时注册全局监听器,并把每一条 SQL 的原始模板与绑定参数写入 Laravel 日志。注意 bindings 数组里的值可能包含敏感字段,比如用户手机号、密码哈希或 token,开发环境可以接受,但生产环境需要做脱敏处理,后面会单独说明。

<?php

namespace App\Providers;

use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Illuminate\Database\Events\QueryExecuted;

class AppServiceProvider extends ServiceProvider
{
    public function boot(): void
    {
        DB::listen(function (QueryExecuted $query) {
            Log::debug('SQL 执行记录', [
                'sql' => $query->sql,
                'bindings' => $query->bindings,
                'time' => $query->time . 'ms',
                'connection' => $query->connectionName,
            ]);
        });
    }
}

注册完成后,每次执行查询都会触发闭包。比如调用 User::where('id', 1)->first() 时,日志中会出现 select * from users where id = ?,绑定参数显示为 [1]。这个日志粒度已经能帮助开发者确认 ORM 是否生成了预期的查询结构,也便于发现循环中反复查询导致的性能问题。

替换问号占位符生成可执行 SQL

DB::listen 拿到的 sql 属性是带问号占位符的预处理语句,不能直接复制到数据库客户端执行。为了调试方便,可以写一个辅助函数,按顺序把 bindings 中的值替换到问号位置。替换时要注意类型:整型、浮点型直接转字符串,字符串需要用单引号包裹并处理转义,null 要转换成 NULL,避免拼出来的 SQL 语法错误。

下面的 formatSql 函数实现了基础的绑定参数替换逻辑。它只用于日志输出,不建议把返回值直接交给数据库执行,因为手动拼接 SQL 存在注入风险,而且 Laravel 已经通过 PDO 预处理机制保证了安全。调试场景中复制到客户端执行是可以的,但不要在生产代码中依赖拼接结果作为查询入口。

function formatSql(string $sql, array $bindings): string
{
    foreach ($bindings as $binding) {
        if ($binding === null) {
            $value = 'NULL';
        } elseif (is_int($binding) || is_float($binding)) {
            $value = (string) $binding;
        } else {
            $value = "'" . addslashes((string) $binding) . "'";
        }

        $sql = preg_replace('/\?/', $value, $sql, 1);
    }

    return $sql;
}

DB::listen(function ($query) {
    $fullSql = formatSql($query->sql, $query->bindings);
    Log::debug('完整 SQL:' . $fullSql);
});

这个函数每次只替换一个问号,避免把同一个绑定值错误地填入所有占位符。对于日期对象或二进制数据,addslashes 处理不一定完善,可以根据业务需要扩展 Carbon 实例转换、JSON 字符串保留等逻辑。真正需要可读性好、可执行的 SQL 时,建议优先使用开发调试工具,因为它们已经处理了大量边界情况。

使用查询日志与开发期调试工具

除了全局 DB::listen,Laravel 还提供 DB::enableQueryLog 和 DB::getQueryLog 方法。它们适合在某个局部代码块中临时开启查询记录,执行完目标逻辑后立即取出日志并关闭,避免整站长期记录带来的内存占用。与 DB::listen 不同,查询日志返回的也是包含 query、bindings、time 的数组,但不会实时触发回调,需要手动取出。

DB::enableQueryLog();

$users = User::where('status', 1)->orderBy('created_at', 'desc')->take(10)->get();

$queries = DB::getQueryLog();

foreach ($queries as $query) {
    echo formatSql($query['query'], $query['bindings']) . PHP_EOL;
}

DB::disableQueryLog();

在本地开发阶段,更推荐使用 Laravel Debugbar 或 Laravel Telescope。Debugbar 会在页面底部展示当前请求执行的所有 SQL、耗时、调用来源,并自动把问号替换为实际参数,省去手动拼 SQL 的步骤。Telescope 则把 SQL 记录进独立的调试面板,支持按请求、按慢查询浏览,还可以查看某条 SQL 对应的调用栈。对团队协作排查问题非常有帮助,但要注意这些工具通常会记录绑定参数,不应默认部署到生产环境。

如果只想在命令行或测试环境中快速查看 SQL,也可以直接监听到 php://stdout 或者使用 dump 输出。关键在于根据场景选择监听方式:需要实时处理时用 DB::listen,需要局部快照时用 enableQueryLog,需要可视化和历史追溯时用 Debugbar、Telescope。

生产环境的慢查询监控与脱敏

把 DB::listen 直接搬到生产环境会带来两个问题:一是每条 SQL 都写日志会造成大量磁盘 IO 和存储成本;二是绑定参数可能包含手机号、身份证、密码哈希等敏感数据,明文写入日志不符合安全规范。因此生产监控应当设定筛选条件,比如只记录执行耗时超过 200 毫秒的 SQL,或者只记录写操作,并同步对绑定参数做脱敏。

下面的监听器只针对慢查询输出告警日志,并且用 maskBindings 函数把非数字类型的绑定值统一替换为星号,避免敏感信息进入日志。实际项目中还可以把告警发送到监控平台,或者配合 Laravel 的日志通道单独写入 slow_sql.log,方便后续分析。

function maskBindings(array $bindings): array
{
    return array_map(function ($value) {
        if (is_int($value) || is_float($value) || $value === null) {
            return $value;
        }

        return '***';
    }, $bindings);
}

DB::listen(function ($query) {
    if ($query->time > 200) {
        Log::warning('慢查询告警', [
            'sql' => $query->sql,
            'bindings' => maskBindings($query->bindings),
            'time' => $query->time . 'ms',
            'connection' => $query->connectionName,
        ]);
    }
});

阈值需要根据业务接口的响应时间目标来设定,例如 200 毫秒适合大多数 Web 请求,异步任务或报表服务可以放宽到 1000 毫秒。超过阈值的 SQL 日志应保留足够上下文,包括链接、绑定的脱敏值、耗时和发生时间,便于后续定位是缺少索引、数据量增长还是 ORM 查询结构不合理。即使只记录慢查询,也建议配置日志轮转和保留周期,防止长期运行后磁盘被占满。

综合来看,Laravel 获取执行 SQL 的关键手段是 DB::listen 和 QueryExecuted 事件,它们提供了原始模板、绑定参数和执行时间。开发环境可以追求完整和可读性,生产环境则要聚焦慢查询、做好脱敏和成本控制。把这两套策略结合起来,就能在不影响线上稳定性的前提下,持续观察应用的真实数据库行为。

LaravelSQL监听查询日志修改时间:2026-10-04 10:12:37

免责声明:已尽一切努力确保本网站所含信息的准确性。网站作品多为原创整理与精心创作,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们进行处理Email:chomcom@qq.com。
引用或转载本作品时,请注明当前出处:https://www.ipipp.com/html/1004/65512.html,基于非商业用途的前提下,欢迎转载或二创本作品。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。