Con este post crearemos un fichero de log por cada día y URL que nos permitirá descubrir problemas como consultas N+1 y analizar si estamos ejecutando una carga excesiva a la base de datos.
1. Creamos el Logger propio de Base de Datos
Lo podemos dar de alta por ejemplo en app/Services/Database/Logger.php
:
<?php declare(strict_types=1);
namespace App\Services\Database;
use DateTime;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Request;
class Logger
{
/**
* @var string
*/
protected static string $file = '';
/**
* @return void
*/
public static function listen(): void
{
if (static::$file) {
return;
}
static::load();
static::write('['.date('Y-m-d H:i:s').'] ['.Request::method().'] '.Request::fullUrl());
DB::listen(static function ($sql) {
foreach ($sql->bindings as $i => $binding) {
if ($binding instanceof DateTime) {
$sql->bindings[$i] = $binding->format('Y-m-d H:i:s');
} elseif (is_string($binding)) {
$sql->bindings[$i] = "'${binding}'";
} elseif (is_bool($binding)) {
$sql->bindings[$i] = $binding ? 'true' : 'false';
}
}
static::write(vsprintf(str_replace(['%', '?'], ['%%', '%s'], $sql->sql), $sql->bindings));
});
}
/**
* @return void
*/
protected static function load(): void
{
static::file();
if (is_dir($dir = dirname(static::$file)) === false) {
mkdir($dir, 0755, true);
}
}
/**
* @return void
*/
protected static function file(): void
{
$file = array_filter(explode('-', preg_replace('/[^a-z0-9]+/i', '-', Request::path())));
$file = implode('-', array_map(static fn ($value) => substr($value, 0, 20), $file)) ?: '-';
static::$file = storage_path('logs/query/'.date('Y-m-d').'/'.substr($file, 0, 150).'.log');
}
/**
* @param string $message
*
* @return void
*/
protected static function write(string $message): void
{
file_put_contents(static::$file, "\n\n".$message, FILE_APPEND | LOCK_EX);
}
}
Ahora podemos crear un ServiceProvider
que gestione el debug. Lo haremos en app/Providers/Debug.php
:
<?php declare(strict_types=1);
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use App\Services\Database\Logger as LoggerDatabase;
class Debug extends ServiceProvider
{
/**
* @return void
*/
public function boot()
{
$this->logging();
}
/**
* @return void
*/
protected function logging(): void
{
$this->loggingDatabase();
}
/**
* @return void
*/
protected function loggingDatabase(): void
{
if (config('logging.channels.database.enabled')) {
LoggerDatabase::listen();
}
}
}
En este mismo ServiceProvider
podríamos, por ejemplo, añadir también un log que registre los envíos de correos (esto irá en un nuevo post).
Una vez tengamos el servicio, lo añadimos en config/app.php
en el array de providers
:
...
'providers' => [
...
/*
* Application Service Providers...
*/
App\Providers\Debug::class,
],
];
También añadimos su opción de configuración en config/logging.php
:
...
'channels' => [
...
'database' => [
'enabled' => env('LOG_DATABASE', false),
],
],
Y lo completamos con el valor en .env
:
LOG_DATABASE=true
De este modo nos creará una carpeta en storage/logs/query/
con cada día y dentro un fichero con el slug del path de cada petición.
El código completo lo podeis encontrar aquí https://gist.github.com/eusonlito/c0a87467b55165a9585a640f1ba4083e
Y ya sabes, si te ha parecido interesante, comparte!
Top comments (0)