Журнал корисний, лише якщо за ним можна відновити, що сталося з конкретним запитом чи завданням.
1. Контекст у кожному записі:
// middleware
Context::add('request_id', (string) Str::uuid());
Context::add('user_id', $request->user()?->id);
Log::info('Замовлення оформлено', ['order_id' => $order->id]);
Дані з Context автоматично додаються до кожного запису журналу й передаються в завдання черги, які цей запит поставив. Тоді один request_id зв'язує запит, завдання й виклики API.
2. Структурований формат - JSON (JsonFormatter) замість рядків, щоб журнал фільтрувався в Loki, ELK чи Datadog за полями.
3. Рівні з розумом: error - те, що вимагає уваги; warning - підозріле, але оброблене; info - бізнес-події; debug - вимкнений на продакшені. Якщо все error, сигнал губиться.
4. Канали: stack з кількох каналів - stderr для платформи, окремий канал для платежів, Slack лише для critical.
5. Винятки - у сервіс відстеження помилок (Sentry, Flare, Nightwatch) з групуванням і стектрейсом; throttle() у withExceptions(), щоб шторм однакових помилок не з'їв квоту.
Чого не писати: паролі, токени, повні номери карток, персональні дані без потреби. Витік журналу - теж витік.
Локально журнал зручно читати через php artisan pail з фільтрами за рівнем і користувачем.