Saludos, queridos amigos!
Quiero compartir mis pensamientos sobre el tema de la tala y a qué condujeron.
Quizás debido a la falta de investigación teórica, la tala siempre ha sido una zona de turbulencia en el mundo de Java. Con el tiempo, esto ha llevado a la aparición de varias bibliotecas para el registro, como:
- Log4j
- Java Util Logging
- Registro de Commons
- Logback
- Log4j2
Tratando de reducir el resto del resto, desafortunadamente cada uno de ellos presentó sus propias deficiencias.
Y si desde el punto de vista de la estandarización del código, la situación mejoró después de la aparición de Slf4j, como una capa de abstracción para el registro, los problemas no resueltos todavía están presentes en las implementaciones existentes.
Como comunidad de código abierto, estamos tomando la iniciativa de crear un enfoque nuevo y revolucionario, y crear un registrador liviano (pero al mismo tiempo rico en funciones), utilizando los últimos desarrollos, como las secuencias de comandos.
Los problemas
- Las implementaciones existentes proporcionan solo soporte parcial para scripts en la configuración
Esto conduce a la programación declarativa en los archivos de configuración del registrador (XML, JSON, YAML), aunque sería mucho más simple interpretar dinámicamente los valores de configuración en tiempo de ejecución utilizando secuencias de comandos imperativas.
Tomemos un ejemplo de una configuración de filtro en Logback, para registrar solo mensajes con el nivel de registro INFO:
<filter class="ch.qos.logback.classic.filter.LevelFilter"> <level>INFO</level> <onMatch>ACCEPT</onMatch> <onMismatch>DENY</onMismatch> </filter>
Este es un ejemplo típico de programación declarativa XML.
(sí, Logback admite un filtro con Groovy, pero se aplica solo a los apéndices específicos, no al registrador)
Pero falta el soporte de secuencias de comandos para formatear la cadena.
- Configuración complicada y demasiado larga
Tome Logback y Log4j2:
No hay forma de configurar el nivel de registro para un apéndice específico.
Los anexos se configuran por separado de los registradores y los registradores se refieren a los anexos utilizando el atributo "AppenderRef", mientras que solo los registradores admiten establecer el nivel de registro y los nombres de clase.
Supongamos que necesitamos excluir los mensajes de depuración de una clase Foo de un archivo de registro específico sin afectar otros archivos y clases de registro.
En Logback, esto es posible usando el filtro Groovy Script en el apéndice, pero si tenemos muchos apéndices, el tamaño de la configuración aumenta exponencialmente.
- Para cada nivel de registro: ¡un archivo separado!
No pudimos encontrar la posibilidad de tal configuración en la que los mensajes se agrupan en archivos por nivel de mensaje (depuración, información, etc.)
Las características existentes requieren la duplicación de los apéndices en cada nivel de registro.
- Configuración del filtrado por nombre de clase en el registrador raíz
Root logger admite establecer solo el nivel de registro, pero no existe la posibilidad de un control centralizado de qué clases deben registrarse.
- Existe una desconexión conceptual entre cómo se generan los datos de registro en la aplicación y cómo los registra el consumidor.
La práctica histórica es tal que los registradores (y su configuración) están más centrados en la clase que si estuvieran centrados en el archivo.
Esto contradice la percepción humana, que percibe de manera más lógica las expectativas en torno al contenido final de los archivos de registro, en lugar de preocuparse por configurar cada clase individual.
En la práctica, esta paradoja es la razón de las limitaciones funcionales de las implementaciones existentes:
- Configuración de nombre de archivo complicado
- Configuración irracional del registrador, por ejemplo:
Logback admite un máximo de 1 "discriminador" en "SiftingAppender".
SiftingAppender tiene limitaciones en la configuración de políticas para archivar
Rediseñado RoutingAppender en Log4j2
Solución
- Soporte completo de secuencias de comandos en la configuración
Bobbin usa la configuración como un localizador para scripts Groovy que determinan el comportamiento del registrador en el tiempo de ejecución de la aplicación.
Así es como se ve el ejemplo de "filtro":
{ "levels": "['info'].contains(level)" }
Cada aspecto del registrador admite la personalización mediante scripts:
- Niveles de registro
- Nombres de clase
- Formato de mensaje
- Nombres de archivo
- Configuración simple y concisa
La bobina no requiere codificadores, patrones, filtros, discriminadores y muchas otras cosas innecesarias.
Se configura con solo unos pocos parámetros básicos:
- Niveles
- Clases
- Archivos
- Formato de línea
Archivos separados para cada nivel de registro: simplemente ponga "$ {level}" en la máscara de nombre de archivo en Bobbin.json (archivo de configuración).
Archivo de configuración de ejemplo:
{ "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)" } ] }
Prueba Bobbin ahora:
Gradle: compile "io.infinite:bobbin:2.0.0"
* Bobbin es un proyecto de código abierto con licencia de Apache.