Логирование в 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

Чек-лист

  1. Разделить уровни логов.
  2. Использовать категории для форм, заказов, cron и интеграций.
  3. Добавить request ID для связанных событий.
  4. Писать ID сущностей и результат операции.
  5. Не писать пароли, токены и секреты.
  6. Логировать HTTP-код и длительность внешних API.
  7. Настроить ротацию логов.
  8. Проверить права на директорию логов.

Хороший лог не обязан быть подробным во всём. Он должен быть достаточным, чтобы быстро ответить: операция была, с какой сущностью, чем завершилась и где искать следующую причину.

Комментарии (0)

Пока нет комментариев. Будьте первым!