導入
Laravelで開発をしていると、「あれ、このページの表示が遅いな…」「意図しないデータが取得されているな…」といったデータベース関連の問題に直面することがありますよね。
そんな時、多くの開発者がまず試すのが「SQLクエリのログ出力」です。しかし、標準的な方法でログを有効にすると、関係ないクエリまで大量に出力されてしまい、肝心の見たいクエリがログの海に埋もれてしまう…なんて経験はありませんか?
この記事では、そんな悩みを解決する、特定の処理で実行されたSQLクエリだけをピンポイントでログに出力する、非常に「時間効率」の良いデバッグテクニックをご紹介します。
なぜ「特定のクエリだけ」ログ出力したいのか?
Laravelは非常に便利なフレームワークですが、裏側では私たちが意識しない多くのSQLクエリが実行されています。例えば、ユーザー認証、セッション管理、設定情報の読み込みなどです。
デバッグのためにクエリログを有効にすると、これらのクエリが全てstorage/logs/laravel.log
に出力されます。
これでは、自分が調査したい特定の機能(例えば、ある商品の検索機能)で発行されているクエリを見つけ出すのは一苦労です。
今回紹介する方法を使えば、このノイズを排除し、調査したいSQLだけをクッキリと浮かび上がらせることができます。
解決策:DB::listen() を使ったピンポイント・ロギング
Laravelには、データベースイベントを「聞く(listen)」ためのDB::listen()
という便利なメソッドが用意されています。これを使うと、SQLクエリが実行されるたびに、指定した処理を割り込ませることができます。
これを利用して、**「特定の処理の直前に聞き耳を立て始め、処理が終わったら聞き耳を立てるのをやめる」**というような実装が可能です。
具体的な実装コード
例えば、あるコントローラーのindex
メソッドが発行するクエリだけを調査したい場合、以下のように記述します。
<?php
namespace App\Http\Controllers;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log; // Logファサードをインポート
use App\Models\Product;
class ProductController extends Controller
{
public function index()
{
// ▼▼▼ ここからSQLの監視を開始 ▼▼▼
DB::listen(function ($query) {
// toSql()でSQL文を取得
$sql = $query->sql;
// getBindings()でバインドされている値を取得
$bindings = $query->bindings;
// クエスチョンマーク(?)を実際の値に置き換える(簡易的な処理)
foreach ($bindings as $binding) {
$sql = preg_replace('/\\?/', is_numeric($binding) ? $binding : "'".$binding."'", $sql, 1);
}
// laravel.log に出力
Log::info($sql);
});
// ▲▲▲ ここまでが監視用のコード ▲▲▲
// === ここに調査したい処理を書く ===
$products = Product::where('price', '>', 1000)
->where('is_published', true)
->orderBy('created_at', 'desc')
->get();
// ===================================
return view('products.index', ['products' => $products]);
}
}
どう動くのか?
DB::listen()
が実行されると、それ以降の全てのSQLクエリが実行されるたびに、クロージャ(無名関数)の中の処理が呼び出されます。- クロージャ内では、実行されたクエリの情報(
$query
オブジェクト)から、SQL文やバインドされている値を取り出します。 - 取り出した情報を
Log::info()
を使って、storage/logs/laravel.log
に書き出します。
このコードはindex
メソッド内で完結しているため、他のページや処理のクエリがログに出力されることは一切ありません。
このテクニックの利点
- 導入が手軽:
Laravel Debugbar
のような追加パッケージをインストールする必要がなく、数行のコードを追加するだけですぐに試せます。 - ノイズがない: 本当に必要なクエリだけが出力されるため、ログの可読性が劇的に向上します。
- 本番環境でも応用可能: 特定のユーザーの操作時だけログを出力するなど、応用が効きます。
まとめ
今回は、DB::listen()
を使って特定のSQLクエリだけをログに出力する、効率的なデバッグ方法をご紹介しました。
大量のログにうんざりしていた方は、ぜひこのテクニックを活用してみてください。問題の早期発見に繋がり、あなたの貴重な開発時間を節約できるはずです。
これが、未来のあなたのための「ヒント」になれば幸いです。
コメント