PSR-3 — это стандарт интерфейса (interface), а не готовая библиотека. Он определяет контракт между вашим кодом и системой логирования, позволяя менять движок без переписывания логики приложения. Внедрение стандарта решает задачу централизации логов, упрощает отладку и позволяет масштабировать систему. Основной принцип: код работает с интерфейсом PSR-3, а конкретная реализация (например, Monolog) подключается отдельно.

1. Понимание роли PSR-3 в PHP: стандарт вместо реализации

PSR-3 (PHP Standard Recommendation 3) определяет интерфейс LoggerInterface. Это набор методов, которые должна реализовать любая система логирования. Ключевые методы: emergency, alert, critical, error, warning, notice, info, debug.

Встроенный в PHP класс ErrorLog не является реализацией PSR-3 и не поддерживает уровни приоритета или форматирование по стандарту. Использование кастомных решений без стандарта приводит к тому, что при смене провайдера логирования (например, переход от syslog к файлу или базе данных) весь код логики нужно переписывать.

Стандартизация позволяет легко менять движок логирования без переписывания кода. Если вы пишете модуль, который использует PSR-3, вы можете подключить его к Monolog, PsrLog или любой другой библиотеке, реализующей интерфейс, просто изменив один файл конфигурации.

Аналогия с искусственным интеллектом: качество ответа модели зависит от точности инструкции. Так и здесь — код приложения не должен знать, куда именно писать логи (в файл, в базу, в консоль). Он должен знать только *как* записать сообщение (уровень, контекст). Выбор реализации делегируется конфигурации.

Персонализация подхода к задаче повышает эффективность решения. В случае с логгерами это означает настройку разных форматов вывода для разных сред: отладочный режим в консоли и структурированный JSON для мониторинга в продакшене.

2. Подготовка проекта к внедрению логгера по стандарту

Перед заменой существующего логгера необходимо провести инвентаризацию всех вызовов функций записи логов. Найдите все места, где используется ErrorLog или кастомные функции.

Создайте абстракцию уровня приложения. Обычно это делается через создание класса-обертки (Wrapper), который реализует интерфейс PSR-3 и внутри делегирует вызовы выбранной библиотеке. Это позволяет изолировать логику логирования от бизнес-логики.

Определите каналы отправки логов заранее. Будут ли они записываться в файлы, отправляться через HTTP (например, в ELK Stack или Datadog) или выводиться на stderr? Выбор каналов влияет на конфигурацию библиотеки, но не на код приложения.

Убедитесь, что у вас есть права на запись в целевые директории для файловых логов. Если используется отправка в базу данных, проверьте подключение и структуру таблиц.

3. Выбор и установка библиотеки-реализации (Monolog)

De facto стандартом реализации PSR-3 является библиотека Monolog. Она поддерживает множество обработчиков (handlers): файлы, syslog, email, HTTP, консоль и другие.

Установка производится через Composer: composer require monolog/monolog. Это добавит пакет в зависимости проекта и создаст файл autoload.json.

Альтернативы существуют, но Monolog наиболее полно реализует возможности стандарта и имеет активное сообщество. Если вы используете фреймворк (Laravel, Symfony), они уже используют Monolog по умолчанию, что упрощает задачу.

Важно: библиотека сама по себе не является логгером. Это просто набор обработчиков. Вы должны собрать их в цепочку (Processor -> Handler).

4. Настройка конфигурации через файл или код

Создайте экземпляр логгера, используя статический метод Factory::create(). Метод принимает массив конфигурации.

В массиве укажите обработчики (handlers). Например: ['type' => 'stream', 'path' => '/var/log/app.log']. Можно добавить несколько обработчиков одновременно: один в файл, другой в syslog.

Настройте уровни приоритета. По умолчанию используется 'debug'. В продакшене часто ставят 'info' или 'warning', чтобы не засорять логи отладочными сообщениями.

Добавьте процессоры (processors) для обогащения логов. Например, добавление IP-адреса клиента, ID сессии или имени пользователя в каждое сообщение. Это критически важно для анализа проблем в продакшене.

Пример конфигурации в коде:
$logger = new \Monolog\Logger('app');
$handler = new \Monolog\Handler\StreamHandler('/var/log/app.log', Monolog\Logger::INFO);
$logger->pushHandler($handler);

Для сложных сценариев используйте файл конфигурации (например, YAML или JSON), который загружается в код. Это позволяет менять настройки без изменения исходного кода.

5. Интеграция с существующим кодом без нарушения совместимости

Если у вас уже есть код, использующий ErrorLog или кастомные функции, создайте класс-адаптер. Этот класс реализует интерфейс PSR-3 и внутри вызывает старые методы.

Пример адаптера:
class LegacyLogger implements Psr\Log\LoggerInterface {
private $legacyHandler;
public function emergency($message) { $this->legacyHandler('emergency', $message); }
// … остальные методы
}

Постепенно заменяйте вызовы ErrorLog на вызовы вашего нового логгера. Сначала в новых файлах, затем в старых, проверяя работу после каждого изменения.

Не удаляйте старые вызовы сразу. Оставьте их работать через адаптер до тех пор, пока все модули не будут перенесены на новый стандарт.

6. Определение форматов вывода и каналов отправки логов

Формат вывода зависит от потребителя логов. Для консольной отладки подходит простой текстовый формат с уровнем приоритета.
[<level>] <message>
Для систем мониторинга (ELK, Splunk) требуется JSON-формат.

Используйте обработчик StreamHandler для файлов и SyslogUdpHandler для отправки в систему syslog. Для отправки в HTTP-сервисы используйте HttpHandler.

Настройте ротацию логов через RotatingFileHandler, чтобы файлы не росли бесконечно. Укажите максимальный размер файла (например, 10 МБ) и количество файлов для хранения (например, 5).

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

7. Тестирование корректности записи и фильтрации сообщений

Тестируйте логгер в разных уровнях приоритета. Запустите скрипт, который вызывает emergency, alert, critical, error, warning, notice, info, debug.

Проверьте, что сообщения попадают в правильные файлы или каналы. Убедитесь, что фильтрация работает: сообщения уровня debug не должны попадать в файл, настроенный на INFO.

Используйте тестовые данные для проверки форматирования. Вставьте в логи JSON-объекты, чтобы убедиться, что структура сохраняется корректно.

Проверьте работу в разных средах: локальная машина (консоль), тестовый сервер (файл), продакшен (syslog/HTTP).

Если используется отправка логов во внешнюю систему, проверьте задержки и возможность потери сообщений при сбоях сети.

8. Поддержка и масштабирование системы логирования

Документируйте конфигурацию логгера. Укажите, какие уровни приоритета используются, куда пишутся логи и как их ротировать.

Мониторьте размер логов и частоту записи. Если файлы растут слишком быстро, увеличьте ротацию или снизьте уровень приоритета в продакшене.

При масштабировании приложения (добавлении новых серверов) убедитесь, что конфигурация логгера одинакова на всех узлах. Используйте централизованное управление конфигурацией (например, Ansible или Chef).

Регулярно очищайте старые логи и проверяйте их целостность. Если используется отправка во внешнюю систему, следите за статусом соединения.

Вопросы и ответы

Можно ли использовать PSR-3 без Composer?

Технически да, если вы вручную подключите файлы интерфейса и реализации. Однако это неудобно и требует ручного управления зависимостями. Composer упрощает установку и обновление библиотек.

Нужно ли менять код приложения при переходе на PSR-3?

Нет, если вы используете абстракцию уровня приложения. Код бизнес-логики продолжает вызывать методы интерфейса (например, $logger->error()), не зная, какая библиотека реализует этот интерфейс.

Как выбрать уровень приоритета для продакшена?

Рекомендуется использовать 'info' или 'warning'. Уровень 'debug' обычно отключается в продакшене, чтобы не засорять логи и не нагружать систему. Критические ошибки (error, critical) должны всегда записываться.

По теме

<?php
use Monolog\Logger;
use Monolog\Handler\StreamHandler;
use Monolog\Formatter\LineFormatter;

// Создание логгера
$logger = new Logger('app');

// Настройка обработчика для файла
$fileHandler = new StreamHandler('/var/log/app.log', Logger::INFO);
$fileHandler->setFormatter(new LineFormatter('[%datetime%] [%channel%] %level_name%: %message%', 'Y-m-d H:i:s'));
$logger->pushHandler($fileHandler);

// Настройка обработчика для syslog
$syslogHandler = new StreamHandler('php://stderr', Logger::WARNING);
$logger->pushHandler($syslogHandler);

// Использование
$logger->info('Приложение запущено');
$logger->error('Произошла ошибка: ' . $exception->getMessage());
?>