Приветствую, дорогие друзья!
Хочу поделиться своими рассуждениями на тему логирования и тем к чему они привели.
Возможно по причине некоторого недостатка теоретических изысканий, логирование всегда было некой зоной турбулентности в Java мире. С течением времени, это вызвало появление нескольких библиотек для логирования, таких как:
Пытаясь сузить ограничения остальных, к сожалению каждая из них привносила свои недостатки.
И если с точки зрения стандартизации кода, ситуация улучшилась после появления Slf4j — как слоя абстракции для логирования, в существующих реализациях все еще присутствуют неразрешенные проблемы.
Как Open Source сообщество, мы предпринимаем инициативу чтобы придти к новому, революционному подходу — и создать легковесный (но в тоже время функционально-насыщенный) логгер, используя последние наработки, такие как скриптинг.
Это приводит к декларативному программированию в конфигурационных файлах логгера (XML, JSON, YAML), хотя гораздо проще было бы динамически интерпретировать конфигурационные значения во время исполнения, используя императивный скриптинг.
Возьмём пример конфигурации фильтра в Logback, для логирования только сообщений с уровнем логирования INFO:
Это типичный пример декларативного XML программирования.
(да, Logback поддерживает фильтр использующий Groovy, но он применим только к конкретным аппендерам, а не к логгеру)
А вот поддержка скриптинга для форматирования строки отсутствует полностью.
Возьмём Logback и Log4j2:
Отсутствует возможность настроить уровень логирования для конкретного аппендера.
Аппендеры настраиваются отдельно от логгеров и логгеры ссылаются на аппендеры используя атрибут «AppenderRef» — при этом только логгеры поддерживают настройку уровня логирования и имён классов.
Допустим, нам надо исключить Debug сообщения от одного класса Foo из конкретного лог файла, не повлияв на другие лог файлы и классы.
В Logback это возможно используя Groovy Script фильтр на аппендере — но если у нас есть множество аппендеров — размер конфигурации растёт экспоненциально.
Мы не смогли найти возможность такой настройки, при которой сообщения группируются в файлы по уровню сообщения (debug, info, и т.д.)
Существующие возможности требуют дупликации аппендеров на каждый уровень логирования.
Root логгер поддерживает настройку только уровня логирования, но отсутствует возможность централизованного контроля того, какие классы должны логироваться.
Историческая практика такова, что логгеры (и их конфигурация) являются более классо-центричными, чем если бы файло-центричными.
Это противоречит человеческому восприятию, которое более логично воспринимает ожидания вокруг конечного содержимого лог файлов, а не беспокоится о настройке каждого отдельного класса.
На практике этот парадокс является причиной функциональных ограничений существующих реализаций:
Logback поддерживает максимум 1 «discriminator» в «SiftingAppender».
SiftingAppender имеет ограничения в настройках полиси для архивирования
Переусложнённая настройка «RoutingAppender» в Log4j2
Bobbin (Бобина) использует конфигурацию как местодержатель для скриптов Groovy, которые определяют поведение логгера во времени исполнения приложения.
Вот так выглядит вышеприведенный пример «фильтра»:
Каждый аспект логгера поддерживает настройку, используя скрипты:
Bobbin не требует Энкодеры, Паттерны, Фильтры, Дискриминаторы и много других лишних вещей.
Он настраивается всего-лишь несколькими основными параметрами:
Отдельные файлы для каждого уровня логирования: просто разместите "${level}" в маску имени файла в Bobbin.json (файл настройки).
Пример файла настройки:
Попробуйте Bobbin сейчас:
*Bobbin является Open Source проектом под лицензией Apache.
Хочу поделиться своими рассуждениями на тему логирования и тем к чему они привели.
Возможно по причине некоторого недостатка теоретических изысканий, логирование всегда было некой зоной турбулентности в 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.