CodeGym /Blog Java /Random-ES /Registro: ¿qué, cómo, dónde y con qué?
John Squirrels
Nivel 41
San Francisco

Registro: ¿qué, cómo, dónde y con qué?

Publicado en el grupo Random-ES
¡Hola a todos en la comunidad de CodeGym! Registro: ¿qué, cómo, dónde y con qué?  - 1 Hoy hablemos de registro:
  1. Qué es, por qué existe, cuándo debe usarlo, cuándo debe evitarlo.
  2. Qué implementaciones de registro están disponibles en Java y qué debe hacer con todas estas opciones de registro.
  3. Y niveles de registro. Discutiremos qué appender es y cómo configurarlo correctamente.
  4. Registro de nodos y cómo configurarlos correctamente para que todo funcione como queremos.
Este material está destinado a un público amplio. Quedará claro para cualquiera que esté empezando a conocer Java, así como para las personas que ya están trabajando pero que solo han explorado ¡ logger.info("log something"); Vamos!

¿Por qué necesita iniciar sesión?

Veamos algunos casos reales en los que el registro puede resolver un problema. Aquí hay un ejemplo de mi trabajo. Hay puntos donde una aplicación se integra con otros servicios. Utilizo el registro en estos puntos para establecer una especie de "coartada" : si la integración no funciona, entonces es fácil averiguar de qué lado tiene el problema. También es deseable registrar información importante almacenada en una base de datos. Por ejemplo, la creación de un usuario administrador. Este es precisamente el tipo de cosas que sería bueno registrar.

Herramientas para iniciar sesión en Java

Entre las conocidas soluciones de registro en Java, podemos destacar las siguientes:
  • Log4j
  • JUL — java.util.logging
  • JCL - registro de bienes comunes de Yakarta
  • Volver a iniciar sesión
  • SLF4J — Fachada de registro simple para Java
Daremos una visión general de cada uno de ellos. Luego tomaremos un enlace slf4j - log4j como base de una discusión práctica. Esto puede parecer extraño ahora, pero no te preocupes: al final del artículo, todo estará claro.

Sistema.err.println

Al principio, existía System.err.println (que mostraba las entradas de registro en la consola). Incluso hoy en día, esta técnica se utiliza para realizar rápidamente un registro durante la depuración. Por supuesto, no hay configuraciones para discutir aquí, así que recuerde este método y continuaremos.

Log4j

Esta es una solución completa que los desarrolladores crearon por necesidad. El resultado es una herramienta realmente interesante que puedes utilizar. Por diversas circunstancias, esta solución no terminó en el JDK, hecho que molestó mucho a toda la comunidad. Log4j tiene opciones de configuración que le permiten habilitar el inicio de sesión en el com.example.typepaquete y desactivarlo en el com.example.type.genericsubpaquete. Esto hace posible excluir rápidamente el código que no necesita ser registrado. Es importante señalar aquí que existen dos versiones de Log4j: 1.2.x y 2.xx, y son incompatibles entre sí . Log4j agregó los conceptos de appender(una herramienta utilizada para escribir registros) y diseño (formato de registro). Esto le permite registrar solo lo que necesita y registrarlo tal como lo necesita. Hablaremos más sobre appender un poco más tarde.

JUL — java.util.logging

Uno de los beneficios clave de esta solución es que JUL está incluido en el JDK (Java Development Kit). Lamentablemente, cuando se desarrolló, sus creadores no se basaron en la popular utilidad Log4j, sino en una solución de IBM. Esa decisión ha tenido consecuencias. La realidad es que nadie usa JUL ahora. Los niveles de registro en JUL difieren de los que tienen Logback, Log4j y Slf4j. Esto hace que sea más difícil para ellos entenderse unos a otros. Crear un registrador es más o menos similar. Para hacer esto, necesitas hacer una importación:

java.util.logging.Logger log = java.util.logging.Logger.getLogger(LoggingJul.class.getName());
Se pasa el nombre de la clase, por lo que sabemos de dónde vendrá nuestro registro. A partir de Java 8, puede pasar Supplier<String>. Esto nos ayuda a leer y crear una línea solo cuando realmente la necesitamos, en lugar de cada vez, como ocurría anteriormente. Solo con el lanzamiento de Java 8, los desarrolladores finalmente resolvieron problemas importantes e hicieron que JUL fuera realmente utilizable. Es decir, métodos con un Supplier<String> msgSupplierparámetro, como se muestra a continuación:

public void info(Supplier<String> msgSupplier) {
   log(Level.INFO, msgSupplier);
}

JCL - registro de bienes comunes de Yakarta

Debido a que durante mucho tiempo no hubo un estándar de la industria con respecto al registro y muchas personas crearon sus propios registradores personalizados, se tomó la decisión de lanzar JCL, un contenedor general que se podía usar sobre otros. ¿Por qué? A veces, las dependencias agregadas al proyecto usaban un registrador diferente al del proyecto. Debido a esto, se agregaron al proyecto como dependencias transitivas, y esto creó verdaderos problemas al intentar ponerlo todo junto. Desafortunadamente, el envoltorio no era muy funcional y no agregaba nada. Probablemente sería conveniente que todos usaran JCL. Pero eso no fue lo que sucedió, por lo que usar JCL no es la mejor idea en este momento.

Volver a iniciar sesión

El camino del código abierto es espinoso... El mismo desarrollador que escribió Log4j también escribió Logback como marco de registro sucesor. Se basó en la misma idea que Log4j. Las diferencias en Logback son:
  • desempeño mejorado
  • soporte nativo agregado para Slf4j
  • opciones de filtrado ampliadas
De manera predeterminada, Logback no requiere ninguna configuración y registra todos los eventos en el nivel DEBUG y superior. Si necesita alguna personalización, puede lograrla a través de una configuración XML:

<configuration> 
    <appender name="FILE" class="ch.qos.logback.core.FileAppender"> 
        <file>app.log</file> 
        <encoder> 
            <pattern>%d{HH:mm:ss,SSS} %-5p [%c] - %m%n</pattern> 
        </encoder> 
    </appender> 
    <logger name="org.hibernate.SQL" level="DEBUG" /> 
    <logger name="org.hibernate.type.descriptor.sql" level="TRACE" /> 
    <root level="info"> 
        <appender-ref ref="FILE" /> 
    </root> 
</configuration>

SLF4J — Fachada de registro simple para Java

En algún momento de 2006, uno de los padres fundadores de Log4j abandonó el proyecto y creó Slf4j (Simple Logging Facade for Java), un contenedor para Log4j, JUL, common-logging y Logback. Como puede ver, hemos avanzado hasta el punto de crear un contenedor sobre un contenedor... En este caso, se divide en dos partes: una API que se usa en la aplicación y una implementación que se agrega con separado dependencias para cada tipo de registro. Por ejemplo, slf4j-log4j12.jary slf4j-jdk14.jar. Debe conectar la implementación correcta y eso es todo: todo su proyecto lo usará. Slf4j admite todas las funciones más recientes, como el formato de cadenas para el registro. Anteriormente, había tal problema. Digamos que creamos una entrada de registro como esta:

log.debug("User " + user + " connected from " + request.getRemoteAddr());
Debido al operador de concatenación, el userobjeto se convierte silenciosamente en una cadena gracias a user.toString(). Esto lleva tiempo y ralentiza el sistema. Y eso puede estar bien si estamos depurando la aplicación. Empezamos a tener problemas si el nivel de registro de esta clase es INFO o superior. En otras palabras, no deberíamos escribir esta entrada de registro (para INFO o superior) y no deberíamos usar la concatenación de cadenas. En teoría, la propia biblioteca de registro debería abordar esto. Da la casualidad de que este resultó ser el mayor problema en la primera versión de Log4j. No entregó una solución decente, sino que sugirió hacer algo como esto:

if (log.isDebugEnabled()) {
    log.debug("User " + user + " connected from " + request.getRemoteAddr());
}
Es decir, en lugar de una línea de código para iniciar sesión, ¡sugirieron escribir 3! El registro debe minimizar los cambios de código y las tres líneas violan claramente ese enfoque general. Slf4j no tuvo problemas de compatibilidad con JDK y API, por lo que surgió una buena solución de inmediato:

log.debug("User {} connected from {}", user, request.getRemoteAddr());
donde {}denota marcadores de posición para los argumentos pasados ​​al método. Es decir, el primero {}corresponde a user, y el segundo {}corresponde a request.getRemoteAddr(). Al hacerlo de esta manera, realizaremos la concatenación de cadenas solo si el nivel de registro requiere que escribamos la entrada de registro. Después de eso, Sjf4j comenzó a crecer rápidamente en popularidad. Actualmente, es la mejor solución. En consecuencia, echemos un vistazo al registro mediante un slf4j-log4j12enlace.

Lo que necesita ser registrado

Por supuesto, no debe registrar todo. Esto a menudo no es necesario y, a veces, incluso peligroso. Por ejemplo, si registra los datos personales de alguien y de alguna manera se filtran, habrá problemas reales, especialmente en proyectos centrados en los mercados occidentales. Pero también hay cosas que definitivamente deberías registrar :
  1. Inicio/fin de la aplicación. Necesitamos saber si la aplicación realmente comenzó y finalizó como se esperaba.
  2. Temas de seguridad. Aquí sería bueno registrar los intentos de adivinar la contraseña de alguien, las instancias en las que los administradores inician sesión, etc.
  3. Ciertos estados de aplicación . Por ejemplo, la transición de un estado a otro en un proceso empresarial.
  4. Cierta información de depuración junto con el nivel de registro correspondiente.
  5. Ciertos scripts SQL. Hay casos del mundo real cuando esto es necesario. Pero, de nuevo, ajustando hábilmente los niveles de registro, puede lograr excelentes resultados.
  6. Los subprocesos en ejecución se pueden registrar al verificar que las cosas funcionan correctamente.

Errores populares en el registro

Hay muchos matices aquí, pero haremos una mención especial de algunos errores comunes:
  1. Registro excesivo. No debe registrar cada paso que teóricamente podría ser importante. Aquí una buena regla general: los registros no deben exceder el 10% de la carga. De lo contrario, habrá problemas de rendimiento.
  2. Registro de todos los datos en un archivo. En algún momento, esto hará que sea muy difícil leer/escribir el registro, sin mencionar el hecho de que ciertos sistemas tienen límites en el tamaño del archivo.
  3. Uso de niveles de registro incorrectos. Cada nivel de registro tiene límites claros y deben respetarse. Si un límite no está claro, puede llegar a un acuerdo sobre qué nivel usar.

Niveles de registro

x: Visible
FATAL ERROR ADVERTIR INFORMACIÓN DEPURAR RASTRO TODO
APAGADO
FATAL X
ERROR X X
ADVERTIR X X X
INFORMACIÓN X X X X
DEPURAR X X X X X
RASTRO X X X X X X
TODO X X X X X X X
¿Qué son los niveles de registro? Para crear de alguna manera una jerarquía de entradas de registro, son necesarias ciertas convenciones y delimitaciones. Esta es la razón por la que se introdujeron los niveles de registro. El nivel se establece en la aplicación. Si una entrada está por debajo de un nivel específico, no se registra. Por ejemplo, tenemos registros que usamos al depurar la aplicación. Durante el funcionamiento normal (cuando la aplicación se utiliza para el fin previsto), dichos registros no son necesarios. Por lo tanto, el nivel de registro es mayor que para la depuración. Veamos los niveles de registro usando Log4j. Además de JUL, otras soluciones utilizan los mismos niveles de registro. Aquí están en orden decreciente:
  • APAGADO: No se registran entradas de registro; todo se ignora.
  • FATAL: un error que impide que la aplicación continúe ejecutándose. Por ejemplo, "Error de memoria insuficiente de JVM".
  • ERROR: Los errores en este nivel indican problemas que deben resolverse. El error no detiene la aplicación como un todo. Otras solicitudes pueden funcionar correctamente.
  • WARN: Entradas de registro que representan una advertencia. Sucedió algo inesperado, pero el sistema pudo hacer frente y cumplió con la solicitud.
  • INFO: Entradas de registro que indican acciones importantes en la aplicación. Estos no son errores o advertencias. Son eventos esperados del sistema.
  • DEPURAR: las entradas de registros necesitan depurar la aplicación. Para asegurarse de que la aplicación hace exactamente lo que se espera, o para describir las acciones realizadas por la aplicación, es decir, "Método 1 ingresado".
  • TRACE: Entradas de registro de menor prioridad para la depuración. El nivel de registro más bajo.
  • TODO: un nivel de registro para escribir todas las entradas de registro de la aplicación.
En el nivel de registro INFO está habilitado en algún lugar de la aplicación, luego se registrarán las entradas para cada nivel, desde INFO hasta FATAL. Si se establece el nivel de registro FATAL, solo se escribirán las entradas de registro con ese nivel.

Registro y envío de registros: Appender

Consideremos cómo funciona todo esto cuando usamos Log4j, que brinda amplias oportunidades para escribir/enviar registros:
  • escribir en un archivo -DailyRollingFileAppender
  • para escribir información en la consola —ConsoleAppender
  • escribir registros en una base de datos:JDBCAppender
  • para administrar el envío de registros a través de TCP/IP:TelnetAppender
  • para garantizar que el registro no afecte negativamente al rendimiento:AsyncAppender
Hay algunas implementaciones más: una lista completa está disponible aquí . Por cierto, si el agregador que necesita no existe, no hay problema. Puede escribir su propio appender implementando la interfaz Appender , que admite Log4j.

Nodos de registro

Para fines de demostración, utilizaremos una interfaz Slf4j, con una implementación de Log4j. Crear un registrador es muy simple: en una clase llamada MainDemo, que realizará algunos registros, debemos agregar lo siguiente:

org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(MainDemo.class);
Esto creará un registrador para nosotros. Para realizar una entrada de registro, hay varios métodos disponibles cuyos nombres reflejan qué nivel de registro se utilizará. Por ejemplo:

logger.trace("Method 1 started with argument={}", argument);
logger.debug("Database updated with script = {}", script);
logger.info("Application has started on port = {}", port);
logger.warn("Log4j didn't find the log4j.properties file. Please fix this.");
logger.error("Connection refused to host = {}", host);
Aunque estamos pasando la clase, el nombre final es el nombre completo de la clase, incluidos los paquetes. Esto se hace para que luego pueda dividir el registro en nodos y configurar el nivel de registro y el agregador para cada nodo. Por ejemplo, el registrador se creó en la com.github.romankh3.logginglecture.MainDemoclase. El nombre proporciona la base para crear una jerarquía de nodos de registro. El nodo principal es RootLogger de nivel superior . Este es el nodo que recibe todas las entradas de registro de toda la aplicación. Los nodos restantes se pueden representar como se muestra a continuación: Registro: ¿qué, cómo, dónde y con qué?  - 3Los agregadores se configuran para nodos de registro específicos. Ahora vamos a ver el archivo log4j.properties para ver un ejemplo de cómo configurarlos.

Una guía paso a paso para el archivo log4j.properties

Configuraremos todo paso a paso y veremos qué es posible:

log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
Esta línea dice que estamos registrando el agregador CONSOLE, que usa la implementación org.apache.log4j.ConsoleAppender. Este appender escribe información en la consola. A continuación, registramos otro appender. Este escribirá en un archivo:

log4j.appender.FILE=org.apache.log4j.RollingFileAppender
Es importante tener en cuenta que los agregadores mismos aún deben configurarse. Una vez que hayamos registrado nuestros appenders, podemos determinar qué niveles de registro y qué appenders se utilizarán en los nodos.

log4j.rootLogger=DEBUG, CONSOLA, ARCHIVO

  • log4j.rootLogger significa que estamos configurando el nodo raíz, que contiene todas las entradas de registro
  • La primera palabra después del signo igual indica el nivel de registro mínimo para escribir (en nuestro caso, es DEBUG)
  • A continuación de la coma, indicamos todos los appenders a utilizar.
Para configurar un nodo de registro más específico, usaría una entrada como esta:

log4j.logger.com.github.romankh3.logginglecture=TRACE, OWN, CONSOLE
where log4j.logger.se utiliza para hacer referencia a un nodo específico. En nuestro caso, com.github.romankh3.logginglecture. ahora hablemos de configurar el agregador de CONSOLA:

# CONSOLE appender customization
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.CONSOLE.threshold=DEBUG
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
log4j.appender.CONSOLE.layout.ConversionPattern=[%-5p] : %c:%L : %m%n
Aquí vemos que es posible establecer el nivel específico en el que el agregador comenzará a funcionar. He aquí un ejemplo de lo que realmente sucede: supongamos que el nodo de registro recibe un mensaje con el nivel INFO y lo pasa al agregador asignado. Si el umbral del appender se establece en WARN, entonces recibe la entrada de registro pero no hace nada con ella. A continuación, debemos decidir qué diseño utilizará el mensaje. Yo uso PatternLayout en el ejemplo, pero hay muchas otras opciones. No los cubriremos en este artículo. Ejemplo de configuración del agregador de ARCHIVOS:

# File appender customization
log4j.appender.FILE=org.apache.log4j.RollingFileAppender
log4j.appender.FILE.File=./target/logging/logging.log
log4j.appender.FILE.MaxFileSize=1MB
log4j.appender.FILE.threshold=DEBUG
log4j.appender.FILE.MaxBackupIndex=2
log4j.appender.FILE.layout=org.apache.log4j.PatternLayout
log4j.appender.FILE.layout.ConversionPattern=[ %-5p] - %c:%L - %m%n
Puede configurar el archivo específico en el que se escribirán las entradas de registro, como se puede ver en esta línea:

log4j.appender.FILE.File=./target/logging/logging.log
La entrada se escribe en el logging.logarchivo. Para evitar problemas con el tamaño del archivo, puedes configurar el máximo, que en este caso es de 1 MB. MaxBackupIndexindica cuántos archivos de registro habrá. Si necesitamos crear más archivos que este, entonces se eliminará el primer archivo. Para ver un ejemplo real donde se configura el registro, puede ir al repositorio público en GitHub.

Reforzar lo que hemos discutido

Intente por su cuenta hacer todo lo que hemos descrito:
  • Cree su propio proyecto similar a nuestro ejemplo anterior.
  • Si sabe cómo usar Maven, úselo. Si no es así, lea este tutorial, que describe cómo conectar la biblioteca.

En resumen

  1. Hablamos de las soluciones de registro que existen en Java.
  2. Casi todas las bibliotecas de registro conocidas fueron escritas por una sola persona: D
  3. Aprendimos lo que debe y no debe registrarse.
  4. Descubrimos los niveles de registro.
  5. Nos presentaron a los nodos de registro.
  6. Vimos qué es un appender y para qué sirve.
  7. Creamos un archivo log4j.proterties paso a paso.

Materiales adicionales

  1. CodeGym: lección de registrador
  2. Weekly Geekly: registro de Java. Hola Mundo
  3. El horror de la codificación: el problema con el registro
  4. YouTube: comprensión del infierno de registro de Java: conceptos básicos. Infierno de registro de Java y cómo mantenerse al margen
  5. Log4j: agregador
  6. Log4j: Diseño
También vea mi otro artículo:
Comentarios
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION