En el mundo Java una de las librerías que más se emplea para implementar logs es log4j (//logging.apache.org/log4j/). En nuestro caso vamos a emplear como fachada del sistema el Jakarta Commons Logging para eludir acoplarnos a una implementación concreta en un futuro.

Hasta aquí no descubro nada nuevo. El asunto puede venir cuando no configuramos de manera eficiente nuestro sistema pudiendo provocar ineficiencia y pérdidas de rendimiento. Vamos a ver un ejemplo bastante común de configuración de un sistema de logs.

El ejemplo emplea Spring Framework en su versión 3.0 para configurar las clases necesarias.

Paso 1 Escribimos el test del servicio que deseamos probar. Como es un ejemplo el test es fácil quedando así

package com.farmerdev.spring.logs

import javax.inject.Inject

import org.junit.Assert
import org.junit.Test
import org.junit.runner.RunWith
import org.springframework.test.context.ContextConfiguration
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner

import com.farmerdev.spring.logs.services.SimpleService

@ContextConfiguration(classpathspring/context-test.xml)
@RunWith(SpringJUnit4ClassRunner.class)
public class SimpleTradicionaLogTest {
@Inject
private SimpleService simpleService

@Test
public void simpleTraditionalLogging(){

simpleService.businessMethod()

Assert.assertNotNull(simpleService)
}

}
Paso 2 El fichero de configuración del test con spring es (context-test.xml)

?xml version=1.0 encoding=UTF-8?
beans xmlns=//.springframework.org/schema/beans
xmlnsxsi=//.w3.org/2001/XMLSchema-instance
xmlnscontext=//.springframework.org/schema/context
xmlnsp=//.springframework.org/schema/p
xsischemaLocation=//.springframework.org/schema/beans //.springframework.org/schema/beans/spring-beans.xsd
//.springframework.org/schema/context //.springframework.org/schema/context/spring-context-3.0.xsd

import resource=classpathspring/context-root.xml
beans
Este fichero a su vez importa el otro fichero global llamado context-root.xml

?xml version=1.0 encoding=UTF-8?
beans xmlns=//.springframework.org/schema/beans
xmlnsxsi=//.w3.org/2001/XMLSchema-instance
xmlnscontext=//.springframework.org/schema/context
xsischemaLocation=//.springframework.org/schema/beans //.springframework.org/schema/beans/spring-beans.xsd
//.springframework.org/schema/context //.springframework.org/schema/context/spring-context-3.0.xsd

contextcomponent-scan base-package=com.farmerdev.spring.logs
beans
Con la etiquetaconseguimos que Spring incluya todos los beans que se encuentren anotados con @Service dentro de su contexto es decir los pueda gestionar su contenedor.

Paso 3 Necesitamos el fichero de configuración del log4j

log4j.rootCategory=INFO stdout

log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - %n
Paso 4 Nos disponemos a escribir el código del servicio que estamos implementando

4.1 Primero la interfaz

package com.farmerdev.spring.logs.services

public interface SimpleService {

void businessMethod()

}
4.2 Ahora la implementación

package com.farmerdev.spring.logs.services

import org.apache.commons.logging.Log
import org.apache.commons.logging.LogFactory
import org.springframework.stereotype.Service

@Service
public class SimpleServiceImpl implements SimpleService {
private static final Log logger = LogFactory.getLog(SimpleServiceImpl.class)
public void businessMethod() {
logger.info( ----- Log trace example ---)

}
}
Podemos observar que la clase de log (Log) pertenece realmente a la librería Jakarta Commons Logging que utilizamos para eludir acoplar nuestro código a una implementación concreta. Será Commons Logging el que utilizará Log4J finalmente.

Si ejecutamos la aplicación en nuestro editor favorito obtendremos las próximos trazas (se han omitido determinadas para simplificar el resultado)

2011-10-10 123839736 INFO [org.springframework.beans.factory.support.DefaultListableBeanFactory] -
2011-10-10 123839753 INFO [com.farmerdev.spring.logs.services.SimpleServiceImpl] - &lt ----- Log trace example ---&gt
2011-10-10 123839758 INFO [org.springframework.context.support.GenericApplicationContext] -
El asunto de esta configuración es que estamos formando una instancia de Log para cada una de las clases de la aplicación. Al ser una propiedad static solo tendremos una instancia por clase pero si nuestra aplicación está compuesta por N clases nos encontraremos con N instancias Log asociadas. Esto puede repercutir en un mayor consumo de memoria al tener muchas instancias Log. Además nos encontramos con código duplicado para conseguir la instancia del log en todas nuestras clases.

¿Cómo podemos mejorar nuestro sistema de logs?

Logs con Spring
Pues aprovechamos que usamos Spring para hacer una serie de cambios en nuestras clases

Paso 1 Modificación del servicio

package com.farmerdev.spring.logs.services.impl

import javax.inject.Inject

import org.apache.commons.logging.Log
import org.apache.commons.logging.LogFactory
import org.springframework.stereotype.Service

import com.farmerdev.spring.logs.services.SimpleService

@Service
public class SimpleServiceImpl implements SimpleService {
@Inject
private Log logger
public void businessMethod() {
logger.info( ----- Log trace example ---)

}
}
Como podemos observar estamos utilizando toda la potencia de Spring 3 y su anotación @Inject para hacer la inyección de dependencias de la clase de Log. Delegamos por tanto toda la gestión y creación de la clase de log a Spring.

Paso 2 Debemos agregar la configuración del logger en Spring

?xml version=1.0 encoding=UTF-8?
beans xmlns=//.springframework.org/schema/beans
xmlnsxsi=//.w3.org/2001/XMLSchema-instance
xmlnscontext=//.springframework.org/schema/context
xmlnsp=//.springframework.org/schema/p
xsischemaLocation=//.springframework.org/schema/beans //.springframework.org/schema/beans/spring-beans.xsd
//.springframework.org/schema/context //.springframework.org/schema/context/spring-context-3.0.xsd

contextcomponent-scan base-package=com.farmerdev.spring.logs


bean id=mylog class=org.springframework.beans.factory.config.CommonsLogFactoryBean
plogName=myLog

beans
La traza que obtenemos es la próximo

2011-10-10 125312351 INFO [myLog] - &lt ----- Log trace example ---&gt
2011-10-10 125312351 INFO [myLog] -
Hemos conseguido que aparezcan nuestras trazas pero podemos observar que no surgen el nombre de las clases donde se han producido esas trazas. Lo único que surge es myLog que es el nombre con que anteriormente configuramos el log en Spring.

Lo único que debemos cambiar para solucionar este asunto es el fichero de configuración de log4j (log4j.properties). Donde antes aparecía

log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - %n
Ahora lo sustituimos por

log4j.appender.stdout.layout.ConversionPattern=%d %p [%C] - %n
Vemos que el cambio consiste en cambiar c por C. Tan fácil como esto así conseguiremos que lo que aparezca sea el nombre donde se genera la traza. Algo como esto

2011-10-10 125706020 INFO [com.farmerdev.spring.logs.services.impl.SimpleServiceImpl] - &lt ----- Log trace example ---&gt
2011-10-10 125706023 INFO [com.farmerdev.spring.logs.services.impl.JustAnotherServiceImpl] -