📜

Laravelで特定のSQLクエリだけを簡単にログ出力する方法

に公開

はじめに

Laravelで開発をしていると、「この部分だけ、実際にどんなSQLが発行されているか確認したい…」と思う場面がよくありますよね。toSql()メソッドで見ることもできますが、もっと手軽に、実行されたクエリをログファイルに書き出して後から確認したいケースも多いはずです。

今回は、Laravelのlistenメソッドを使って、特定の処理ブロック内だけでSQLクエリログを有効にする、シンプルで便利な方法を紹介します。

特定の処理内だけでクエリログを有効にする

DB::listenを使うと、アプリケーションで実行される全てのクエリイベントをリッスンできます。これを応用し、特定の処理の前後でリスナーを有効化・無効化します。

以下のコードをapp/Providers/AppServiceProvider.phpbootメソッドなどに記述します。

app/Providers/AppServiceProvider.php
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;

public function boot()
{
    // クエリログを有効にする
    DB::listen(function ($query) {
        // SQL文
        $sql = $query->sql;
        // バインディングされた値
        $bindings = $query->bindings;
        // 実行時間
        $time = $query->time;

        // バインディングを?に埋め込む
        foreach ($bindings as $binding) {
            $sql = preg_replace('/\\?/', is_numeric($binding) ? $binding : "'".$binding."'", $sql, 1);
        }

        Log::debug('SQL Query:', ['sql' => $sql, 'time' => $time]);
    });
}

これで、全てのSQLクエリがstorage/logs/laravel.logに出力されるようになります。

しかし、これだとアプリケーション全体でログが出力され続けてしまいます。特定の処理ブロックだけでログを取りたい場合は、以下のようにクロージャ(無名関数)を使って処理を囲みます。

// SQLログを収集するためのクロージャ
$queryLog = function ($query) {
    $sql = $query->sql;
    foreach ($query->bindings as $binding) {
        $sql = preg_replace('/\\?/', is_numeric($binding) ? $binding : "'".$binding."'", $sql, 1);
    }
    Log::info($sql);
};

// リスナーを登録
DB::listen($queryLog);

// ここにSQL発行の処理を書く
$users = App\Models\User::where('id', '>', 100)->get();

// リスナーを解除
DB::getEventDispatcher()->forget('Illuminate\Database\Events\QueryExecuted');

この方法を使えば、デバッグしたい箇所をピンポイントで囲むだけで、必要なSQLログだけを取得でき、開発効率がぐっと上がります。

おわりに

Laravelのデバッグは奥が深いですが、DB::listenは特にSQL関連の問題を解決する上で非常に強力なツールです。ぜひ活用してみてください。

この記事で紹介した内容を元に、今後はこちらのブログでより詳細な技術情報を発信していきます。よろしければブックマークをお願いします!

MyNote
https://mynote.meantix.com/

Discussion