Хочу поделиться своими рассуждениями на тему логирования и тем к чему они привели.
Возможно по причине некоторого недостатка теоретических изысканий, логирование всегда было некой зоной турбулентности в Java мире. С течением времени, это вызвало появление нескольких библиотек для логирования, таких как:
- Log4j
- Java Util Logging
- Commons Logging
- Logback
- Log4j2
Пытаясь сузить ограничения остальных, к сожалению каждая из них привносила свои недостатки.
И если с точки зрения стандартизации кода, ситуация улучшилась после появления Slf4j — как слоя абстракции для логирования, в существующих реализациях все еще присутствуют неразрешенные проблемы.
Как Open Source сообщество, мы предпринимаем инициативу чтобы придти к новому, революционному подходу — и создать легковесный (но в тоже время функционально-насыщенный) логгер, используя последние наработки, такие как скриптинг.
Проблемы
— Существующие реализации предоставляют лишь частичную поддержку скриптов в настройках
Это приводит к декларативному программированию в конфигурационных файлах логгера (XML, JSON, YAML), хотя гораздо проще было бы динамически интерпретировать конфигурационные значения во время исполнения, используя императивный скриптинг.
Возьмём пример конфигурации фильтра в Logback, для логирования только сообщений с уровнем логирования INFO:
<filter class="ch.qos.logback.classic.filter.LevelFilter"> <level>INFO</level> <onMatch>ACCEPT</onMatch> <onMismatch>DENY</onMismatch> </filter>
Это типичный пример декларативного XML программирования.
(да, Logback поддерживает фильтр использующий Groovy, но он применим только к конкретным аппендерам, а не к логгеру)
А вот поддержка скриптинга для форматирования строки отсутствует полностью.
— Усложнённая и переудлинённая конфигурация
Возьмём Logback и Log4j2:
Отсутствует возможность настроить уровень логирования для конкретного аппендера.
Аппендеры настраиваются отдельно от логгеров и логгеры ссылаются на аппендеры используя атрибут «AppenderRef» — при этом только логгеры поддерживают настройку уровня логирования и имён классов.
Допустим, нам надо исключить Debug сообщения от одного класса Foo из конкретного лог файла, не повлияв на другие лог файлы и классы.
В Logback это возможно используя Groovy Script фильтр на аппендере — но если у нас есть множество аппендеров — размер конфигурации растёт экспоненциально.
— Каждому уровню логирования — отдельный файл!
Мы не смогли найти возможность такой настройки, при которой сообщения группируются в файлы по уровню сообщения (debug, info, и т.д.)
Существующие возможности требуют дупликации аппендеров на каждый уровень логирования.
— Настройка фильтрации по имени класса в самом Root логгере
Root логгер поддерживает настройку только уровня логирования, но отсутствует возможность централизованного контроля того, какие классы должны логироваться.
— Существует концептуальное разъединение между тем, как производятся лог данные в приложении и тем, как эти данные потребляются логгером
Историческая практика такова, что логгеры (и их конфигурация) являются более классо-центричными, чем если бы файло-центричными.
Это противоречит человеческому восприятию, которое более логично воспринимает ожидания вокруг конечного содержимого лог файлов, а не беспокоится о настройке каждого отдельного класса.
На практике этот парадокс является причиной функциональных ограничений существующих реализаций:
- Усложнённая конфигурация имён файлов
- Иррациональная настройка логгера, например:
Logback поддерживает максимум 1 «discriminator» в «SiftingAppender».
SiftingAppender имеет ограничения в настройках полиси для архивирования
Переусложнённая настройка «RoutingAppender» в Log4j2
Решение
— Полная поддержка скриптинга в конфигурации
Bobbin (Бобина) использует конфигурацию как местодержатель для скриптов Groovy, которые определяют поведение логгера во времени исполнения приложения.
Вот так выглядит вышеприведенный пример «фильтра»:
{ "levels": "['info'].contains(level)" }
Каждый аспект логгера поддерживает настройку, используя скрипты:
- Уровни логирования
- Имена классов
- Формат сообщения
- Имена файлов
— Простая и краткая настройка
Bobbin не требует Энкодеры, Паттерны, Фильтры, Дискриминаторы и много других лишних вещей.
Он настраивается всего-лишь несколькими основными параметрами:
- Уровни
- Классы
- Файлы
- Формат строки
Отдельные файлы для каждого уровня логирования: просто разместите "${level}" в маску имени файла в Bobbin.json (файл настройки).
Пример файла настройки:
{ "levels": "['debug', 'info', 'warn', 'error'].contains(level)", "destinations": [ { "name": "io.infinite.bobbin.destinations.FileDestination", "properties": { "fileName": "\"./LOGS/PLUGINS/INPUT/${className}/${level}/${className}_${level}.log\"" }, "classes": "className.contains('conf.plugins.input')" }, { "name": "io.infinite.bobbin.destinations.FileDestination", "properties": { "fileName": "\"./LOGS/PLUGINS/OUTPUT/${className}/${level}/${threadName}_${level}_${date}.log\"" }, "classes": "className.contains('conf.plugins.output')" }, { "name": "io.infinite.bobbin.destinations.FileDestination", "properties": { "fileName": "\"./LOGS/THREADS/${threadGroupName}/${threadName}/${level}/${threadName}_${level}_${date}.log\"" }, "classes": "className.contains('io.infinite.')" }, { "name": "io.infinite.bobbin.destinations.FileDestination", "properties": { "fileName": "\"./LOGS/ALL/WARNINGS_AND_ERRORS_${date}.log\"" }, "levels": "['warn', 'error'].contains(level)" }, { "name": "io.infinite.bobbin.destinations.ConsoleDestination", "levels": "['warn', 'error'].contains(level)" } ] }
Попробуйте Bobbin сейчас:
Gradle: compile "io.infinite:bobbin:2.0.0"
*Bobbin является Open Source проектом под лицензией Apache.

