Логирование в PHP приложениях: лучшие практики на практике

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Логирование в PHP приложениях: лучшие практики на практике

Логирование — это не просто запись ошибок в файл. Это стратегический инструмент мониторинга, отладки и аудита работы приложения. В этой статье мы разберём, как организовать логирование в PHP на практике, используя современные подходы и библиотеки. Вы узнаете, как избежать типичных ошибок и сделать логи действительно полезными.

1. Выбор библиотеки и стандарт PSR-3

В современном PHP-сообществе стандартом де-факто является интерфейс PSR-3 (Logger Interface). Он определяет единый набор методов для логирования, что позволяет легко менять библиотеку логирования без переписывания кода приложения.

Наиболее популярная реализация PSR-3 — библиотека Monolog. Она гибкая, производительная и имеет множество "обработчиков" (handlers) для отправки логов в файлы, базы данных, Slack, Telegram, Email и облачные сервисы.

Установка Monolog через Composer:

composer require monolog/monolog

Базовый пример использования:

<?php
require 'vendor/autoload.php';

use Monolog\Logger;
use Monolog\Handler\StreamHandler;

// Создаём канал (channel) логов — обычно по имени приложения или модуля
$log = new Logger('my_app');
// Добавляем обработчик: пишем логи уровня WARNING и выше в файл
$log->pushHandler(new StreamHandler(__DIR__ . '/logs/app.log', Logger::WARNING));

// Теперь можно логировать
$log->warning('Пользователь {username} попытался войти с неверным паролем', ['username' => 'ivan']);
$log->error('Не удалось подключиться к базе данных: {error}', ['error' => $e->getMessage()]);
?>

Почему это важно? Использование PSR-3 делает ваш код независимым от конкретной библиотеки. Если завтра вы захотите перейти с Monolog на другую реализацию (например, Log4php), вам нужно будет изменить только инициализацию логгера, а не все места, где вызываются $log->error().

2. Уровни логирования: когда что использовать

PSR-3 определяет 8 уровней логирования (от самого низкого приоритета к высокому): DEBUG, INFO, NOTICE, WARNING, ERROR, CRITICAL, ALERT, EMERGENCY. Правильное использование уровней — основа читаемых и полезных логов.

  • DEBUG — детальная отладочная информация, которая нужна только разработчику (значения переменных, трассировка вызовов). Включайте только на dev-окружении.
  • INFO — важные события работы приложения: пользователь зарегистрировался, заказ создан, cron-задача выполнена.
  • NOTICE — нормальные, но значимые события (например, использование устаревшего API).
  • WARNING — потенциальные проблемы, которые не остановили выполнение скрипта (неудачная попытка входа, медленный запрос к БД).
  • ERROR — ошибки, требующие вмешательства: исключения, сбои в работе внешних сервисов.
  • CRITICAL — критические сбои: потеря соединения с БД, нехватка памяти.
  • ALERT — требуется немедленное действие администратора (сайт недоступен).
  • EMERGENCY — вся система неработоспособна.

Практический совет: Настройте разные обработчики для разных уровней. Например, WARNING и выше пишите в файл, а ERROR и выше — ещё и отправляйте в Telegram/Slack.

// Пример: два обработчика для одного логгера
$log->pushHandler(new StreamHandler(__DIR__ . '/logs/all.log', Logger::DEBUG));
$log->pushHandler(new StreamHandler(__DIR__ . '/logs/errors.log', Logger::ERROR));
// Для критических ошибок — отправка в Telegram
$log->pushHandler(new TelegramBotHandler('TOKEN', 'CHAT_ID', Logger::CRITICAL));

3. Структурированные логи и контекст

Одна из самых частых ошибок — логирование строки с подстановкой переменных через конкатенацию:

// ПЛОХО: строку сложно парсить
$log->error('Ошибка с ID ' . $userId . ': ' . $errorMessage);

Правильный подход — использовать структурированные логи с контекстом (второй аргумент методов логгера):

// ХОРОШО: контекст сохраняется отдельно
$log->error('Ошибка обработки пользователя', [
    'user_id' => $userId,
    'error' => $errorMessage,
    'ip' => $_SERVER['REMOTE_ADDR'] ?? 'unknown',
    'trace' => debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 5)
]);

Преимущества структурированных логов:

  • Легко фильтровать и искать по полям (например, все ошибки с user_id = 42).
  • Можно передавать в системы агрегации (ELK, Graylog, Sentry) в формате JSON.
  • Не нужно экранировать спецсимволы в сообщениях.

Для вывода в файл в читаемом виде используйте LineFormatter:

use Monolog\Formatter\LineFormatter;

$formatter = new LineFormatter("[%datetime%] %channel%.%level_name%: %message% %context%\n");
$handler = new StreamHandler(__DIR__ . '/logs/app.log', Logger::DEBUG);
$handler->setFormatter($formatter);
$log->pushHandler($handler);

4. Ротация логов и управление размером

Если не управлять размером логов, файл может вырасти до гигабайтов, что вызовет проблемы с производительностью и дисковым пространством. Monolog предоставляет встроенный обработчик RotatingFileHandler, который автоматически создаёт новый файл каждый день (или каждый час) и удаляет старые.

use Monolog\Handler\RotatingFileHandler;

// Хранить логи за 30 дней
$handler = new RotatingFileHandler(__DIR__ . '/logs/app.log', 30, Logger::DEBUG);
$log->pushHandler($handler);

Дополнительные рекомендации:

  • Храните логи вне корня веб-сервера (не в public/), чтобы их нельзя было скачать через браузер.

Блоги

Рекомендаций по книгам