Lab / Laravel

Laravel: Пишем кастомный драйвер для логирования в базу данных

На одном из проектов по созданию CRM системы мне понадобилось сделать логи удобными для пользователей и более того, добавить по ним поиск и пред заготовленные фильтры.

Конечно можно добиться подобного поведения и просто читая файл логов, но зачем такие муки, особенно когда файл очень большой и чтение с диска потребляет неприлично много ресурсов сервера. Конечно же я решил переложить эту нагрузку на PostgreSQL.

Создание драйвера

Механизм логирования встроенный в Laravel использует Monolog под капотом. Расширением на его основе мы и займёмся в этой заметке.

Что бы создать новый драйвер легирования просто создаём новый инстанс от Monolog Logger и создаём в нем обработчик.

namespace App\Services;
 
use Monolog\Logger;
 
class DatabaseLogger
{

    public function __invoke(array $config)
    {
        return new Logger('Database', [
            new DatabaseHandler(),
        ]);
    }
}

В обработке мы создаём новое сообщение, которое по сути является обычной моделью Eloquent. Теперь у вас есть поля по которым вы можете сделать поиск, группировку и прочие полезности для вашего пользователя.

namespace App\Services;
 
use Monolog\Handler\AbstractProcessingHandler;
use App\Models\LogMessage;
 
class DatabaseHandler extends AbstractProcessingHandler
{
    protected function write(array $record): void
    {
        LogMessage::create([
            'level' => $record['level'],
            'level_name' => $record['level_name'],
            'message' => $record['message'],
            'logged_at' => $record['datetime'],
            'context' => $record['context'],
            'extra' => $record['extra'],
        ]);
    }
}

Подключение драйвера

Что бы подключить только что созданный драйвер, подключите новый канал в config/logging.php и вуаля, ваш кастомный драйвер готов к использованию.

use App\Services\DatabaseLogger;
 
return [
    'channels' => [
        'db' => [
            'driver' => 'custom',
            'via'    => DatabaseLogger::class,
        ],
    ]
]

Не забудьте создать вашу модель LogMessage и её миграцию:

php artisan make:model LogMessage -m

Теперь вы просто можете вызывать запись в этот канал вот таким образом:

Log::channel('db')->info('Your message');

Из неочивидных профитов — вы можете конвертировать тело сообщения в JSON что сделает поиск по логам ещё более удобным при помощи встроенных в PostgreSQL механизмов работы с JSON данными.