Eloquent Guard: как ловить N+1 и медленные запросы в Laravel, не зарываясь в vendor

в 11:15, , рубрики: database monitoring, database optimization, database performance, eloquent, laravel, mysql, php, postgresql, sql

Проблема N+1 стара как мир. Инструментов много: Debugbar хорош локально, Telescope тяжеловат для продакшена. Мне хотелось решения, которое будет «стучать» в Slack или Telegram именно тогда, когда проблема случилась на проде, и при этом сразу показывать пальцем на виновную строку кода.

Так появился Eloquent Guard

Как это работает под капотом

Основная магия происходит через подписку на события базы данных в Laravel. В сервис-провайдере пакета мы слушаем QueryExecuted:

DB::listen(function (QueryExecuted $query) {
    // 1. Проверяем дубликаты (N+1) по хэшу SQL
    // 2. Замеряем время выполнения (Slow Query)
    // 3. Если порог превышен — запускаем репортеры
});

Проблема «мусорного» бэктрейса

Самое сложное в автоматических алертах — понять, откуда пришел запрос. Если просто вызвать debug_backtrace(), вы получите 50+ кадров (frames), где 90% — это внутренности ядра Laravel (Illuminate...).

В Eloquent Guard я реализовал Smart Backtrace. Логика простая, но эффективная: мы фильтруем стек вызовов, отсекая всё, что находится в директории vendor.

// Упрощенный пример логики из пакета
public static function getCaller(): array
{
    $stack = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);

    foreach ($stack as $frame) {
        if (isset($frame['file']) && !str_contains($frame['file'], 'vendor')) {
            return [
                'file' => $frame['file'],
                'line' => $frame['line'] ?? 0,
            ];
        }
    }
}

Благодаря этому в уведомлении (Slack/Telegram) разработчик видит не абстрактную ошибку, а конкретный файл:source: /app/Http/Controllers/UserController.php:24

Интеграция с Laravel Pulse

Поскольку Pulse сейчас — стандарт для мониторинга Laravel-приложений, я добавил кастомную карточку. Она агрегирует данные о «нарушителях» и выводит их в удобном виде.

Eloquent Guard: как ловить N+1 и медленные запросы в Laravel, не зарываясь в vendor - 1

Мультиканальные алерты: где ловить уведомления

лавная фишка Eloquent Guard — вам не нужно сидеть в дашбордах. Пакет сам придет к вам туда, где вы работаете. Все уведомления снабжены тем самым Smart Backtrace, о котором я писал выше.

💬 Slack: Командный центр

Алерт в Slack выглядит максимально информативно. Мы сразу видим:

  • Тип проблемы: N+1 или Slow Query.

  • SQL-запрос: Текст запроса, который вызвал подозрение.

  • Счетчик: Сколько раз повторился запрос (для N+1).

  • Точное место: Файл и строка в вашем проекте.

Eloquent Guard: как ловить N+1 и медленные запросы в Laravel, не зарываясь в vendor - 2

🤖 Telegram: Оперативность в кармане

Если вы используете Telegram для мониторинга, пакет пришлет лаконичное сообщение через бота. Это идеально для небольших команд или личных проектов, когда нужно быстро отреагировать на просадку производительности.

Eloquent Guard: как ловить N+1 и медленные запросы в Laravel, не зарываясь в vendor - 3

🎯 Sentry: Трекинг как для ошибок

Интеграция с Sentry позволяет превратить проблемы производительности в полноценные Issues.

  • Вы можете назначить ответственного за оптимизацию конкретного запроса.

  • Видеть частоту появления проблемы на графиках.

  • Sentry подтянет окружение и дополнительные теги.

    Eloquent Guard: как ловить N+1 и медленные запросы в Laravel, не зарываясь в vendor - 4

📜 Стандартные логи Laravel

Если внешние сервисы не подключены, пакет пишет в стандартный лог (например, storage/logs/laravel.log). Это выглядит так:

[2026-03-09 21:27:16] local.WARNING: Eloquent Guard: N+1 detected! {"sql":"select * from users where users.id = ? limit 1","count":5,"source":"/var/www/html/routes/web.php:9"}
[2026-03-09 21:27:17] local.CRITICAL: Eloquent Guard: Slow Query! {"sql":"SELECT SLEEP(1)","duration":"1000.47ms","source":"/var/www/html/routes/web.php:13"}

Настройка репортеров

В конфиге config/eloquent-guard.php это настраивается одним массивом. Вы можете включить всё сразу или оставить только то, что нужно:

'reporters' => [
    MaxisEloquentGuardReportersLogReporter::class,      // Логи
    MaxisEloquentGuardReportersSlackReporter::class,    // Вебхуки Slack
    MaxisEloquentGuardReportersTelegramReporter::class, // Telegram Bot
    MaxisEloquentGuardReportersSentryReporter::class,   // Sentry SDK
],

Важно: Все «тяжелые» репортеры (Slack, Telegram, Sentry) автоматически используют Laravel Queue. Ваше приложение не будет ждать ответа от API мессенджера — отчет уйдет в фоновом режиме.

Заключение: зачем это всё?

Eloquent Guard — это не замена полноценным APM-системам вроде New Relic или Datadog. Это легкий, «свой» инструмент для Laravel-разработчика, который хочет держать руку на пульсе производительности без лишних затрат и сложных настроек.

Когда проект растет, N+1 и тяжелые запросы неизбежны. Главное — узнавать о них не от разгневанных пользователей, а из лаконичного уведомления в Slack или на дашборде Laravel Pulse.

🛠️ Планы на будущее

Пакет активно развивается, и в планах:

  • Поддержка Discord и Microsoft Teams.

  • Более глубокая аналитика по конкретным моделям Eloquent.

  • Игнорирование целых неймспейсов (не только vendor).

🤝 Присоединяйтесь!

Проект полностью открыт под лицензией MIT. Буду рад вашим Pull Requests, баг-репортам и, конечно, звездам на GitHub — это лучший стимул развивать инструмент дальше.

А как вы боретесь с «прожорливыми» запросами в своих проектах? Используете стандартный Telescope, сторонние сервисы или написали свой велосипед? Поделитесь опытом в комментариях!

Автор: Maxis88

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js