Настроить лог¶
Событие ушло, а что было дальше — неизвестно. Кто его получил? Кто упал? Сколько это заняло? Без лога остаётся гадать. Роутер умеет писать это сам: в файл, по дням и уровням, одной строкой на запись.
Указать, куда писать¶
Путь к логу задаётся при создании роутера ключом 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 логгер, подключите его вместо файла:
Теперь ошибки слушателей и журнал рассылок идут в ваш логгер, а log_path не используется. log_dispatch продолжает работать.
Если файл не записывается¶
Нет прав на папку или кончилось место — рассылка от этого не падает. Логгер выдаёт предупреждение PHP (E_USER_WARNING), а слушатели продолжают работать. Предупреждение попадёт туда, куда в вашем окружении уходят ошибки PHP.
Если ошибся в конфиге¶
Неизвестный ключ (logPath вместо log_path), пустой путь или log_dispatch не типа bool — исключение InvalidConfig сразу при создании роутера. Опечатка в настройке не останется незамеченной.
Все ключи конфига — в справочнике.