Перейти к содержанию

Настроить лог

Событие ушло, а что было дальше — неизвестно. Кто его получил? Кто упал? Сколько это заняло? Без лога остаётся гадать. Роутер умеет писать это сам: в файл, по дням и уровням, одной строкой на запись.

Указать, куда писать

Путь к логу задаётся при создании роутера ключом log_path. Это не просто путь, а шаблон:

$events = EventRouterFactory::create($container, [
    'log_path' => '/var/www/local/logs/events-router/{level}/{date}.log',
]);
Подстановка Что подставляется Пример
{date} дата записи, Y-m-d 2026-10-10
{level} уровень PSR-3 error, info

С таким шаблоном ошибки окажутся в …/error/2026-10-10.log, а журнал рассылок — в …/info/2026-10-10.log. Каждый день — новый файл, папки создаются сами.

Подстановки необязательны. '/var/log/events-router.log' — тоже рабочий путь, просто всё будет в одном файле.

Без log_path лог пишется во временную папку системы: <sys_get_temp_dir()>/events-router/{date}.log. Для разработки этого хватает, для боевого сервера — нет: укажите путь явно.

Писать каждую рассылку

По умолчанию в лог попадают только ошибки слушателей. Чтобы видеть все события, включите log_dispatch:

use Selyusize\EventsRouter\Event;
use Selyusize\EventsRouter\EventRouterFactory;

$events = EventRouterFactory::create(config: [
    'log_path' => $logs . '/{level}/{date}.log',   // ошибки и рассылки — в разные папки, файл на каждый день
    'log_dispatch' => true,                        // писать каждую рассылку
]);

$events->listen('shop.order.{order_id}.paid', MarkOrderPaid::class);
$events->listen('shop.order.{order_id}.paid', AccrueBonuses::class);

$events->dispatch(new Event('shop.order.42.paid', ['amount' => 1500]));
info/2026-10-10.log
[2026-10-10 14:03:12] INFO events-router: shop.order.42.paid, слушателей: 2, 0.4 мс {"listeners":[{"listener":"MarkOrderPaid","status":"Handled","ms":0.1,"attributes":{"order_id":"42"}},{"listener":"AccrueBonuses","status":"Failed","ms":0.1,"attributes":{"order_id":"42"},"error":"RuntimeException: сервис бонусов недоступен"}]}

error/2026-10-10.log
[2026-10-10 14:03:12] ERROR events-router: слушатель AccrueBonuses упал на событии shop.order.42.paid: RuntimeException: сервис бонусов недоступен в … {"exception":{"class":"RuntimeException","message":"сервис бонусов недоступен","file":"…","trace":"…"},"event":"shop.order.42.paid","attributes":{"order_id":"42"},"listener":"AccrueBonuses"}

На каждое событие — одна запись уровня info: имя, сколько слушателей, общее время. В контексте — каждый слушатель со статусом (Handled, Failed, Skipped), временем и атрибутами, которые он получил, а для упавшего — ещё и ошибка.

Что важно знать:

  • Событие без слушателей тоже пишется — слушателей: 0. Это самая частая находка: топик ни с чем не совпал из-за опечатки в шаблоне или в имени события.
  • Payload не пишется. Он бывает большим и содержит персональные данные. В лог попадают имя события и атрибуты.
  • При стратегии ErrorStrategyEnum::Throw записи о рассылке нет: исключение уходит наружу раньше, чем собран отчёт. Ошибку логирует вызывающий код.

Удобная схема для проектов: log_dispatch включён на разработке и выключен в проде, где событий много, а важны только ошибки.

Писать в свой логгер

Если в проекте уже настроен Monolog или другой PSR-3 логгер, подключите его вместо файла:

$events->setLogger($logger);   // любой Psr\Log\LoggerInterface

Теперь ошибки слушателей и журнал рассылок идут в ваш логгер, а log_path не используется. log_dispatch продолжает работать.

Если файл не записывается

Нет прав на папку или кончилось место — рассылка от этого не падает. Логгер выдаёт предупреждение PHP (E_USER_WARNING), а слушатели продолжают работать. Предупреждение попадёт туда, куда в вашем окружении уходят ошибки PHP.

Если ошибся в конфиге

Неизвестный ключ (logPath вместо log_path), пустой путь или log_dispatch не типа bool — исключение InvalidConfig сразу при создании роутера. Опечатка в настройке не останется незамеченной.

Все ключи конфига — в справочнике.