Логирование в PHP-проекте: что писать, чтобы потом найти ошибку
Логирование в PHP-проекте часто появляется слишком поздно: когда заявка потерялась, интеграция не отправила данные, cron не выполнился, а в коде нет ни одной записи, по которой можно восстановить сценарий. Хороший лог не должен быть огромным. Он должен отвечать на вопросы: что произошло, где, когда, с какой сущностью и чем закончилось.
Логи нужны не для разработчика в момент написания кода, а для диагностики через неделю, когда ошибка повторится у реального пользователя.
Уровни логов
Не все события одинаковы. Ошибку отправки платежа и успешное открытие страницы нельзя писать с одним уровнем важности.
- debug — подробная отладка на тесте;
- info — нормальное значимое событие;
- warning — проблема, но процесс продолжился;
- error — операция не выполнена;
- critical — серьёзный сбой, требующий реакции.
На production debug обычно либо выключен, либо включается точечно и временно.
Категории
Категории помогают отделить интеграции, формы, заказы, cron и платежи. Один общий app.log быстро превращается в шум.
Yii::info([
'order_id' => $orderId,
'status' => 'created',
], 'orders');
Yii::error([
'lead_id' => $leadId,
'error' => $e->getMessage(),
], 'crm');
Если проект не на Yii2, подход тот же: лог должен содержать тип события и контекст.
Request ID
Для веб-запросов полезно иметь request ID. Тогда можно связать несколько записей в один сценарий: пользователь отправил форму, данные сохранились, письмо не ушло, CRM вернула ошибку.
$requestId = bin2hex(random_bytes(8));
error_log(json_encode([
'request_id' => $requestId,
'event' => 'form_received',
], JSON_UNESCAPED_UNICODE));
В больших проектах request ID также передают во внешние сервисы или хотя бы пишут в каждый лог интеграции.
Что обязательно писать
- ID локальной сущности: order_id, user_id, lead_id;
- название операции;
- результат операции;
- код ошибки внешнего сервиса;
- время выполнения долгих операций;
- номер попытки при retry;
- краткий текст ошибки.
Лог должен помогать найти запись в базе и повторить сценарий.
Что нельзя писать
В логах не должно быть паролей, токенов, приватных ключей, полных данных банковских карт и лишних персональных данных. Логи часто доступны большему числу людей, чем production-база.
// плохо
'password' => $password,
'token' => $apiToken,
// лучше
'token_present' => $apiToken !== '',
Email или телефон иногда нужны для диагностики, но их стоит писать только если это действительно необходимо и допустимо по правилам проекта.
Логи интеграций
Для внешних API полезно логировать метод, длительность, HTTP-код и краткий ответ. Полное тело запроса и ответа лучше писать только временно или в обезличенном виде.
$startedAt = microtime(true);
try {
$response = $client->send($request);
Yii::info([
'method' => 'crm.lead.add',
'http_code' => $response->statusCode,
'duration' => round(microtime(true) - $startedAt, 3),
], 'external-api');
} catch (Throwable $e) {
Yii::error([
'method' => 'crm.lead.add',
'error' => $e->getMessage(),
], 'external-api');
}
Ротация логов
Логи не должны бесконечно расти. Иначе однажды сайт упадёт из-за заполненного диска.
/var/www/site.ru/runtime/logs/*.log {
daily
rotate 14
compress
missingok
notifempty
}
Настройка зависит от сервера, но сама идея обязательна: логи ограничены по сроку и размеру.
Проверять наличие логов
Логирование бесполезно, если директория недоступна на запись. После деплоя и переноса нужно проверить права.
ls -la runtime/logs
tail -n 50 runtime/logs/app.log
Чек-лист
- Разделить уровни логов.
- Использовать категории для форм, заказов, cron и интеграций.
- Добавить request ID для связанных событий.
- Писать ID сущностей и результат операции.
- Не писать пароли, токены и секреты.
- Логировать HTTP-код и длительность внешних API.
- Настроить ротацию логов.
- Проверить права на директорию логов.
Хороший лог не обязан быть подробным во всём. Он должен быть достаточным, чтобы быстро ответить: операция была, с какой сущностью, чем завершилась и где искать следующую причину.
Комментарии (0)
Пока нет комментариев. Будьте первым!