Como generar logs que registren las consultas a base de datos en Laravel
Tags: php, laravel, castellano, debug
Published: 2020-10-15 10:18:37

---
title: Como generar logs que registren las consultas a base de datos en Laravel
published: true
description: Crea un log con las peticiones a base de datos para poder realizar debug y descubrir problemas N+1
tags: PHP, Laravel, castellano, Debug
cover_image: https://dev-to-uploads.s3.amazonaws.com/i/mmw9f9qq0wcee2hmz0sw.png
---

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
<?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
<?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`:

```php
    ...

    'providers' => [
        ...

        /*
         * Application Service Providers...
         */
        App\Providers\Debug::class,
    ],
];
```

También añadimos su opción de configuración en `config/logging.php`:

```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!