Continuamos con la segunda parte de los principales consejos sobre como sacar el máximo provecho a tu log. Revisa la primera parte si aún no lo has hecho o descarga el código de ejemplo de este post de nuestro repositorio en GitHub

No te llenes de logs

Ok, tu aplicación ya crea archivos de log, muy bien!… pero unas semanas después de la salida a producción, tu servidor nuevamente está caído, esta vez, porque el disco está lleno de archivos de log viejos. Que hacer? Fácil: no te llenes de logs!

La manera más sencilla de hacerlo es con la configuración de tu logger. Tanto Log4J como Logback ofrecen la posibilidad de crear un número máximo de archivos de log, definir un tamaño máximo del archivo y eliminar archivos antiguos. Incluso Logback permite comprimir automáticamente los logs.

Otra opción que tienes y es aún mejor, es utilizar un concentrador de Logs como Logstash, Fluentd o Prometheus. Estos concentradores utilizan agentes, librerías de cliente u otros métodos para transmitir el contenido de tus archivos de log a una base de datos central en la cual puedes hacer consultas, análisis detallados y exponer tableros de mando (dashboards) que ilustren el comportamiento y patrones de tus aplicaciones. Así no sólo evitarás ingresar a cada servidor para analizar logs sino que tendrás las bases de una plataforma de monitoreo y analítica de eventos.

Registra información de contexto

Los niveles de log y la configuración del logger son útiles para filtrar los mensajes, pero una transacción de negocio normal de tu aplicación siempre va a cruzar por múltiples clases, capas e incluso componentes que pueden estar en otro servidor y los mensajes relacionados con esa transacción se van a mezclar en los archivos de log con los mensajes de otras transacciones. Mantener en el log información relacionada con la transacción, te ayudará a identificarla, depurar su procesamiento y diagnosticar problemas.

Parte de esta información relacionada pueden ser los valores de las variables por lo que es útil que todas tus clases, especialmente las que servirán como contenedores de datos, implementen el método toString y de esa manera, escribir mensajes simples como método log.trace("Carrito de compras: {}", carrito). Esta instrucción invocará el método toString de la variable carrito, dejando así información del contexto de la transcción en el log. Los IDE como Eclipse ofrecen funcionalidades para crear automáticamente el método toString (Menú: Source/Generate toString…)

Por otra parte, Logback ofrece la clase Mapped Diagnostic Context (MDC) de enorme utilidad principalmente en contextos web pues permite que cada instrucción de log automáticamente registre información como el id del usuario, el id de la transacción, entre otras que puedes agregar al contexto.

Utiliza un logger asíncrono

Cuando se registra información en un archivo o base de datos cientos o miles de veces por minuto, como ocurre con un log, la aplicación incurre en un gasto adicional de tiempo que puede poner lento al resto del procesamiento. Para mitigar este efecto puedes utilizar loggers (exactamente appenders) asíncronos. Tanto Log4J2 como Logback los ofrecen y funcionan así: reciben el mensaje a registrar y lo guardan en una cola interna, devolviendo el control inmediatamente al método de tu aplicación que invoco al logger.

Unos milisegundos o segundos después, el logger registrará el mensaje en su repositorio ya sea un archivo, base de datos o concentrador de logs. Esto normalmente se hace en un hilo independiente de tu aplicación.

Conoce tu logger

Finalmente, conoce tu logger, investiga que puede ofrecerte y que no. Seguramente encontrarás configuraciones muy útiles, así como problemas frecuentes que puedes evitar.

Un ejemplo de lo que puedes sufrir si no conoces tu logger: una instrucción como log.debug("La variable A tiene el valor " + variableA) repetida muchas veces incurre en una reducción importante de rendimiento en tu aplicación porque concatena cadenas de texto lo cual es muy costoso en lenguajes como Java y C# (trataremos en detalle la razón en un futuro post). La forma de remediar este problema podría ser utilizar un StringBuilder o utilizar técnicas propias de cada librería
Log4J

Utiliza la instrucción isXXXEnabled donde XXX es el nombre de cualquiera de los niveles o prioridades. La instrucción de log sólo se ejecutará si el nivel está activo en la configuración

if(log.isDebugEnabled()){
  log.debug("La variable A tiene el valor " + variableA);
}

Logback

Lo anterior puede llenar el código de instrucciones if innecesarias, haciéndolo más difícil de leer. Logback puede hacer lo anterior pero también ofrece una alternativa: utilizar un placeholder a manera de plantillas para ser reemplazados por valores de variables, así:

log.debug("La variable A tiene el valor {}", variableA);

Conclusión

Definitivamente utilizar System.out.println o printStackTrace, son las mejores formas de pegarte un tiro en el pie. Utiliza un logger real siempre que escribas código, que sea una precondición antes de escribir siquiera la primera línea.

Conoce y utiliza las prestaciones de tu librería de logging: appenders asíncronos, niveles de log, etc. y registra información contextual que te ayude en el diagnóstico de un problema, así estarás preparado para tu próximo e inevitable momento en que «the shit happens»