在开发过程中,难免需要对 sql 语句的问题排查,我们可以把程序执行中运行的 sql 语句记录到日志中。

甚至,还可以记录 sql 语句的执行时间,对执行时间过长的 sql 的语句增加预警等。

Laravel 框架提供的 sql 监听事件,只需要在 Provider 的 boot 方法里增加监听回调即可。

监听 sql 执行

这里以 Laravel 11 为例,参考官方文档:Listening for Query Events

<?php

namespace App\Providers;

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

class AppServiceProvider extends ServiceProvider
{
    /**
     * Register any application services.
     */
    public function register(): void
    {
        // ...
    }

    /**
     * Bootstrap any application services.
     */
    public function boot(): void
    {
        DB::listen(function (QueryExecuted $query) {
            // 打印 sql 及执行耗时
            Log::debug($query->toRawSql() . sprintf(' (%.2fms)', $query->time));
        });
    }
}

如果是在低版本的 Laravel 中,如在 Laravel 10 中,没有提供 $query->toRawSql() ,那么需要手动将 sql 和参数拼接出来打印。

/**
 * Bootstrap any application services.
 */
public function boot(): void
{
    DB::listen(function (QueryExecuted $query) {
        // $query->sql;
        // $query->bindings;
        // $query->time;

        $bindings = $query->bindings;
        $sql = $query->sql;
        foreach ($bindings as $replace) {
            $value = is_numeric($replace) ? $replace : "'" . $replace . "'";
            $sql = preg_replace('/\?/', strval($value), $sql, 1);
        }

        Log::debug($sql . " ({$query->time}ms)");
    });
}

监视 sql 耗时

还可以监听 sql 执行时间,针对执行时间过长的 sql 的,发送通知等。

官方文档中也有介绍:Monitoring Cumulative Query Time

/**
 * Bootstrap any application services.
 */
public function boot(): void
{
    // 对超过 500ms 的 sql 增加回调监听
    DB::whenQueryingForLongerThan(500, function (Connection $connection, QueryExecuted $event) {
        // Notify development team...
    });
}

最后

这篇介绍了在 Laravel 中如何监听 sql 语句并打印 sql 到日志中,以及监视 sql 的执行耗时。这有助于开发者分析 sql 语句,并对慢 sql 做相应的优化,极大的提高了开发者的效率。