Monolog опционален
Winter Logger — тонкий слой поверх Monolog, — и всё же Monolog лишь рекомендуемая зависимость. Без него каждый логгер становится no-op. Это проектное решение, а не недосмотр, и оно формирует поведение библиотеки.
Гейт
LoggerManager::build() проверяет наличие Monolog прежде всего остального. Если класс не
загружается, он возвращает PSR-3-совместимый NullLogger и останавливается:
private function build(string $channel): LoggerInterface
{
if (!class_exists(MonologLogger::class)) {
return new NullLogger();
}
// ... иначе строим настоящий канал Monolog
}Поскольку channel() кэширует по имени, проверка выполняется один раз на канал. Каждый вызов
логирования дальше попадает в NullLogger, чьи методы пусты — стоимость логирования при
отсутствии Monolog фактически нулевая, и ничто не бросает исключений.
Почему suggest, а не require
Monolog объявлен в composer.json в секции suggest, поэтому один лишь
composer require flytachi/winter-logger никогда его не подтянет. Это даёт потребителям
реальный выбор:
- Библиотеки, поддерживающие логирование опционально, могут зависеть от Winter Logger, не навязывая Monolog каждому нижестоящему приложению.
- Микросервисы могут включать/выключать логирование по окружению, включая или исключая один пакет — без флага в конфиге, без мёртвого кода.
- Тесты могут убрать Monolog, чтобы полностью заглушить вывод.
monolog/monolog установлен → настоящие каналы → форматированный вывод
monolog/monolog отсутствует → NullLogger → молчаливый no-op, без ошибкиКомпромисс: молча, а не громко
Цена принципа «логирование выключается тем, что пакет не установлен» в том, что забытая установка выглядит идентично намеренной — логи просто не появляются, без предупреждения. Это принятый компромисс: библиотека трактует «нет Monolog» как валидное, поддерживаемое состояние, а не как ошибку, о которой надо кричать.
Если ваши логи пропали
Ни вывода, ни ошибки — обычно значит, что Monolog не установлен. Запустите
composer require monolog/monolog, и тот же код начнёт выдавать строки — без иных правок.
Как это вписывается в философию
Это зеркалит центральное правило библиотеки: она не делает предположений о своём окружении. Она не предполагает, что Monolog присутствует, — как не читает env-переменные и не определяет рантайм. Оба решения выталкивают ответственность наружу — к приложению и фреймворку, которые знают своё окружение, — и держат сам логгер маленьким, переносимым и тривиально тестируемым.
LoggerFactory уважает тот же гейт: когда
нижележащий канал — не настоящий Logger (то есть NullLogger или своя PSR-3-реализация),
getLogger() возвращает его как есть, не оборачивая — так что поклассовое именование тоже
вырождается корректно.