⬆️ ⬇️

Alternate Slf4j Logger "Reel"

Greetings, dear friends!



I want to share my reasoning on the topic of logging and what they led to.



Perhaps due to some lack of theoretical research, logging has always been a kind of turbulence zone in the Java world. Over time, this caused the emergence of several logging libraries, such as:

')



Trying to narrow down the restraints, unfortunately each of them introduced its own shortcomings.



And if from the point of view of code standardization, the situation has improved after the appearance of Slf4j - as an abstraction layer for logging, there are still unresolved problems in existing implementations.



As an open source community, we are taking the initiative to come up with a new, revolutionary approach - and create a lightweight (but at the same time functionally rich) logger using the latest developments, such as scripting.



Problems



- Existing implementations provide only partial support for scripts in settings



This leads to declarative programming in the logger configuration files (XML, JSON, YAML), although it would be much easier to dynamically interpret the configuration values ​​at runtime using imperative scripting.



Let's take an example of a filter configuration in Logback, for logging messages only with the INFO logging level:



<filter class="ch.qos.logback.classic.filter.LevelFilter"> <level>INFO</level> <onMatch>ACCEPT</onMatch> <onMismatch>DENY</onMismatch> </filter> 


This is a typical example of declarative XML programming.



(yes, Logback supports a filter using Groovy, but it applies only to specific appenders, not to a logger)



But support for scripting to format the string is completely missing.



- Advanced and over-extended configuration



Take Logback and Log4j2:



There is no possibility to configure the logging level for a specific appender.



Appenders are configured separately from loggers and loggers refer to appenders using the “AppenderRef” attribute — only loggers support customization of logging level and class names.



Suppose we need to exclude Debug messages from one Foo class from a specific log file, without affecting other log files and classes.



In Logback, this is possible using the Groovy Script filter on the appender - but if we have a lot of appenders, the size of the configuration grows exponentially.



- Each logging level is a separate file!



We could not find the possibility of such a setting, in which messages are grouped into files by message level (debug, info, etc.)



Existing features require duplication of appenders for each level of logging.



- Setting up filtering by class name in the Root logger itself



Root logger supports setting only the logging level, but there is no possibility of centralized control of which classes should be logged.



- There is a conceptual separation between how log data is produced in the application and how this data is consumed by the logger.



Historical practice is such that loggers (and their configuration) are more class-centric than file-centric ones.



This is contrary to human perception, which more logically perceives expectations around the final contents of the log files, and does not worry about setting up each individual class.



In practice, this paradox causes the functional limitations of existing implementations:





The logback supports a maximum of 1 "discriminator" in the "SiftingAppender".

SiftingAppender has limitations in policy settings for archiving

Re-configured “RoutingAppender” setting in Log4j2



Decision



- Full support for scripting in the configuration



Bobbin uses the configuration as a placeholder for Groovy scripts that determine the behavior of the logger during the execution time of the application.



This is what the above “filter” example looks like:



 { "levels": "['info'].contains(level)" } 


Every aspect of the logger supports customization using scripts:





- Simple and short setup



Bobbin does not require Encoders, Patterns, Filters, Discriminators and many other extra things.



It is configured with only a few basic parameters:





Separate files for each logging level: just place the "$ {level}" in the file name mask in Bobbin.json (configuration file).



Example configuration file:



 { "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)" } ] } 


Try Bobbin now:



 Gradle: compile "io.infinite:bobbin:2.0.0" 


* Bobbin is an open source project under the Apache license.

Source: https://habr.com/ru/post/442232/



All Articles