Kernelで全ルートに効かせてログを取らない時はKernelからコメントアウト
アーキテクチャありの大規模PJでソースが追いきれない場合に有効
外部キーとか張っていない等
どこにどんなデータができたのかをサクッと把握できる
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
class LogDbWritesOnce
{
public function handle($request, Closure $next)
{
$writes = [];
DB::listen(function ($q) use (&$writes) {
$sql = $q->sql;
if (preg_match('/^\s*(insert|update|delete|replace|truncate|create|alter|drop)\b/i', $sql)) {
$writes[] = [
'op' => strtoupper(strtok(trim($sql), ' ')),
'sql' => self::interpolate($sql, $q->bindings),
];
}
});
$res = $next($request);
foreach ($writes as $w) Log::channel('dbwrites')->info('DB write', $w);
return $res;
}
private static function interpolate(string $sql, array $b): string
{
if (!$b) return $sql;
$parts = explode('?', $sql);
$out = array_shift($parts);
foreach ($b as $v) $out .= self::q($v) . array_shift($parts);
return $out;
}
private static function q($v): string
{
if ($v === null) return 'NULL';
if (is_bool($v)) return $v ? '1' : '0';
if (is_int($v) || is_float($v)) return (string)$v;
$s = str_replace("'", "''", (string)$v);
if (strlen($s) > 200) $s = substr($s, 0, 200) . '…';
return "'{$s}'";
}
}
config/logging.php
return [
'channels' => [
// 既存の channels がたくさん並んでいるはず…
// ↓↓↓ これを末尾に追加 ↓↓↓
'dbwrites' => [
// 日毎に分けたいなら "daily"、1ファイル固定なら "single"
'driver' => 'daily',
'path' => storage_path('logs/db-writes.log'),
'level' => 'info',
'days' => 14,
],
// ↑↑↑ ここまで追加 ↑↑↑
],
];
app/Http/Kernel.php
protected $routeMiddleware = [
// 既存…
'log.writes' => \App\Http\Middleware\LogDbWritesOnce::class,
];
