ak-flash / laravel-max-logger
Laravel logging channel for MAX with deduplication and rate limiting
Requires
- php: ^8.2
- ext-json: *
- ext-mbstring: *
- guzzlehttp/guzzle: ^7.8
- illuminate/cache: ^12.0
- illuminate/config: ^12.0
- illuminate/contracts: ^12.0
- illuminate/http: ^12.0
- illuminate/log: ^12.0
- illuminate/support: ^12.0
- monolog/monolog: ^3.0
Requires (Dev)
- laravel/pint: ^1.26
- orchestra/testbench: ^10.0
- phpstan/phpstan: ^2.0
- phpunit/phpunit: ^11.0
Suggests
None
Provides
None
Conflicts
None
Replaces
None
README
Канал логирования Laravel/Monolog, который отправляет ошибки приложения в мессенджер MAX через MAX Bot API.
Packagist · GitHub · CHANGELOG
Требования: PHP 8.2+, Laravel 12, Monolog 3.
Что это и для чего
Пакет добавляет в Laravel канал логирования max: записи лога заданного уровня (по умолчанию error и выше) уходят сообщением в чат или личный диалог MAX. Это способ узнавать об ошибках production-приложения там, где команда уже общается, без развертывания отдельного мониторинга.
Возможности:
- Отправка ошибок в MAX — репортируемые исключения Laravel и записи
Log::error()доставляются в чат или личный диалог; при ответе APIUnknown recipientавтоматически выполняется повторная отправка черезuser_id. - Дедупликация — одинаковые ошибки не заспамят чат: числа, UUID, email, IPv4 и длинные hex-подстроки нормализуются, повторная отправка блокируется на
dedup_ttlсекунд, а в следующем уведомлении выводится счётчикduplicates. - Ограничение частоты — не более
rate_limitпопыток доставки заrate_windowсекунд; подавленные записи накапливаются и отображаются в следующем уведомлении какsuppressed. - Маскирование — чувствительные ключи контекста (password, token, secret и др.), известный токен бота и явно заданные секретные строки не покидают приложение; email и IPv4 заменяются плейсхолдерами.
- Защита от гонок — счётчики и доставка защищены cache-блокировкой; ключи изолированы по приложению, окружению и получателю.
- Отказоустойчивость — сбой MAX API или cache не ломает логирование приложения и не создаёт рекурсию; без credentials канал безопасно отключается через
NullHandler. - Информативный формат — URL приложения, окружение, уровень, текст, класс и место исключения, ограниченный контекст; длина сообщения контролируется в Unicode-символах.
- Настраиваемый транспорт — API URL, таймауты, CA-сертификат (системное хранилище или PEM-файл, например Russian Trusted Root CA).
Отправка синхронная: это best-effort уведомления, а не гарантированная очередь доставки.
Установка
Packagist
composer require ak-flash/laravel-max-logger
Из GitHub (VCS)
Добавьте в composer.json приложения:
{
"repositories": [
{
"type": "vcs",
"url": "https://github.com/ak-flash/laravel-max-logger.git"
}
],
"require": {
"ak-flash/laravel-max-logger": "^0.1.0"
}
}
composer update ak-flash/laravel-max-logger
Локальная копия (разработка пакета)
Склонируйте репозиторий, например в packages/laravel-max-logger рядом с вашим приложением, и подключите по относительному пути:
{
"repositories": [
{
"type": "path",
"url": "packages/laravel-max-logger",
"options": {
"symlink": true,
"versions": {"ak-flash/laravel-max-logger": "dev-main"}
}
}
],
"require": {
"ak-flash/laravel-max-logger": "dev-main"
}
}
composer update ak-flash/laravel-max-logger
Настройка канала
Провайдер регистрируется через Laravel auto-discovery. В config/logging.php добавьте канал:
'max' => [ 'driver' => 'custom', 'via' => \AkFlash\MaxLogger\MaxLoggerFactory::class, ],
При необходимости опубликуйте конфигурацию (необязательно):
php artisan vendor:publish --tag=max-logger-config
В .env задайте:
MAX_LOGGER_TOKEN=your-bot-token MAX_LOGGER_RECIPIENT_ID=12345 MAX_LOG_LEVEL=error
Добавьте max в список channels нужного стека, например ['single', 'max'].
Репортируемые Laravel исключения и записи Log::error() будут проходить через стек.
Для отдельного уведомления: Log::channel('max')->error('Ошибка обработки', ['job_id' => 123]);.
Получите токен бота в MAX, узнайте chat_id/user_id (например, через GET /updates Bot API после сообщения боту) и убедитесь, что серверу доступен platform-api2.max.ru.
Настройки
Все значения из config/max-logger.php можно переопределить в описании канала.
| Ключ | По умолчанию | Назначение |
|---|---|---|
enabled |
true |
Включение канала; env MAX_LOGGER_ENABLED |
token |
env | MAX_LOGGER_TOKEN, fallback MAX_BOT_TOKEN |
recipient_id |
env | MAX_LOGGER_RECIPIENT_ID, fallback MAX_CHAT_ID; строковый числовой ID |
recipient_type |
auto |
chat, user или fallback chat → user при Unknown recipient |
level |
error |
Минимальный уровень; env MAX_LOG_LEVEL |
api_url |
https://platform-api2.max.ru |
env MAX_LOGGER_API_URL |
timeout / connect_timeout |
10 / 3 |
Таймауты в секундах |
ca_path |
null |
Системное CA-хранилище или путь к PEM; env MAX_LOGGER_CA_PATH |
dedup_ttl |
600 |
Срок дедупликации; 0 отключает; env MAX_LOG_DEDUP_TTL |
rate_limit / rate_window |
5 / 300 |
Попытки доставки за окно; rate_limit=0 отключает |
cache_store |
null |
Cache приложения или именованное хранилище; env MAX_LOGGER_CACHE_STORE |
cache_prefix |
max-logger |
Префикс ключей |
message_limit / context_limit |
3900 / 400 |
Максимальная длина текста и контекста в Unicode-символах |
redact_keys / redact_values |
см. config / [] |
Маскируемые ключи и явно заданные секретные строки |
app_url / environment |
config приложения | Подпись уведомления |
Старый ключ канала chat_id поддерживается. Явный recipient_id имеет приоритет.
Пустые credentials или enabled=false дают NullHandler.
Доставка и cache
Отправка синхронная, без фонового worker. Одна попытка включает максимум два HTTP-запроса при fallback получателя. Автоматических повторов после сетевого сбоя нет: повторная запись ошибки может попробовать доставку заново, в пределах rate limit.
Дедупликация нормализует числа, UUID, email, IPv4 и длинные hex-значения, учитывает уровень и место исключения. Успешная доставка запускает TTL. Сводки duplicates и suppressed очищаются после успешной отправки.
Хранилище должно поддерживать Laravel LockProvider: стандартные array/file/Redis/database поддерживают locks при правильной настройке. На недоступном или неподдерживаемом cache отправка пропускается. Для нескольких серверов используйте общее хранилище, например Redis. Array действует только внутри процесса; file — в пределах общего файлового пути.
Блокировка получателя защищает счётчики и отправку. Конкурирующая запись при занятой блокировке пропускается без ожидания и не входит в сводку suppressed — это ограничивает задержку обработки исходной ошибки. Ключи изолированы по URL приложения, окружению, токену и получателю. Многопроцессная работа проверена на file и Redis (6 параллельных процессов), реальная доставка подтверждена на staging.
Форматирование и расширение
Сообщение содержит URL приложения, окружение, уровень, текст, класс и место исключения и ограниченный контекст. Вложенные чувствительные ключи маскируются, объекты не сериализуются, глубина и количество элементов ограничены. Токен бота и redact_values удаляются из текста, email/IPv4 заменяются. Маскирование не является универсальным распознаванием персональных данных в свободном тексте.
Можно переопределить container binding MaxClientContract или MessageFormatterContract. При разрешении контракта фабрика передаёт объединённую конфигурацию в параметре options. Важно: фабрика вызывает make() с параметрами, поэтому контейнер игнорирует instance()-биндинги — переопределяйте через bind()-closure, при необходимости читая options из второго аргумента. Для ручной отправки доступен app(MaxClientContract::class)->sendMessage('...'); эта отправка обходит форматирование и лимиты handler.
Диагностика
Если уведомление отсутствует, проверьте credentials, уровень, состав stack, TTL/лимиты, доступность cache и CA-файл. Транспорт возвращает false при ошибке; handler не пишет свои сбои в тот же лог. Для диагностики транспорта используйте контракт клиента и его bool-результат. URL и сырые ответы API с токеном не выводятся.
Отложенные улучшения
- Retry после сетевого сбоя, метрики недоставки, фоновая отправка через очереди.
- Универсальное распознавание персональных данных в свободном тексте (сейчас — маскирование по ключам и известным значениям).
- Совместимость с Laravel 11 и более ранними версиями (matrix-проверка).
- Оценка поведения «пропуск при занятой блокировке» под боевой нагрузкой.
Разработка
В каталоге пакета:
composer install
composer validate --strict
composer test
composer lint
composer analyse
Тесты используют Orchestra Testbench, fake HTTP и изолированное приложение. CI (.github/workflows/tests.yml) прогоняет проверки на PHP 8.2–8.5.