LOG4J es una forma de liberarse de println(). Es una herramienta para centralizar y administrar los mensajes de debugging y avisos de la aplicación
La primera y una de las mayores ventajas de cualquier API de logging sobre el tradicional System.out.println es la capacidad de habilitar y deshabilitar ciertos logs, mientras otros no sufren ninguna alteración. Esto se realiza categorizando los mensajes de logs de acuerdo al criterio del programador.
Para empezar hay que decir que estamos hablando de la versión 1.2.8, es decir, el archivo log4j-1.2.8.jar.
El archivo log4j.jar se ha puesto en el jre\lib\ext. Si usas Eclipse hay que asegurarse de que sea este el JRE que usa Eclipse (no es raro tener varios JREs cargados, ver "Archivos de programa-Java"). El JRE que usamos en Eclipse se comprueba en Windows-Preferences-Java-Installed JREs. El Java Runtime Edition carga de forma automática las librerias que están en jre\lib\ext. En Eclipse se puede comprobar las librerias cargadas en Windows-Preferences-Java-Installed JREs, selecciona el JRE usado y pulsa "Edit". Al cargarse la librería, nuestro entorno de programación reconocerá las clases org.apache.log4j.Logger, org.apache.log4j.PropertyConfigurator, etc. No aparecerá el mensaje de error "The import org.apache.log4j.Logger cannot be resolves". Si se coloca en 'ext' no es necesario ponerlo en el CLASSPATH.
Otra opción es ponerlo en CLASSPATH.
Página donde puedes obtener jar, documentación, etc.
Ya hemos dicho que el archivo log4j.jar se ha puesto en el jre\lib\ext que utiliza nuestro IDE (Eclipse en nuestro caso).
La configuración del tipo de logging se realiza en el archivo log4j.properties. Un sencillo ejemplo, que lanza mensajes al archivo /doc/Java_eclipse/geniu/log/ser_geniu.log; más adelante veremos los detalles:
log4j.rootCategory=ALL, Default log4j.appender.Default=org.apache.log4j.FileAppender log4j.appender.Default.Threshold=INFO log4j.appender.Default.ImmediateFlush=true log4j.appender.Default.file=/doc/Java_eclipse/geniu/log/ser_geniu.log log4j.appender.Default.layout=org.apache.log4j.PatternLayout log4j.appender.Default.layout.ConversionPattern=%d %-5p %C.%M(%L)===> %m%n log4j.appender.Default.append=false
¿Dónde colocar el archivo log4j.properties?. En el directorio que quieras. Pero ojo, no lo pongas derivando de WEB-INF/classes, ya que si haces "Clean" del proyecto, el IDE que utilices (Eclipse, JBUilder, etc.) lo eliminará. En este ejemplo lo hemos colocado en el directorio raíz del contexto de aplicación.
El archivo properties define el archivo log donde se volcarán los mensajes. Este archivo puedes ponerlo en el directorio LOG del contexto.Un ejemplo de archivo de propiedades:
| Propiedad | Observaciones |
|---|---|
| log4j.rootCategory=ALL, Default | Indica alias para las salidas de log, podemos tener varias |
| log4j.appender.Default=org.apache.log4j.FileAppender | Para Default su appender (lugar donde se envian los mensajes) es un archivo |
| log4j.appender.Default.Threshold=INFO | No mostrará mensajes por debajo del nivel INFO |
| log4j.appender.Default.ImmediateFlush=true | Se vuelca el mensaje inmediatamente en el appender |
| log4j.appender.Default.file=/doc/Java_eclipse/geniu/log/ser_geniu.log | Archivo (appender) |
| log4j.appender.Default.layout=org.apache.log4j.PatternLayout | Tipo de diseño de los mensajes |
| log4j.appender.Default.layout.ConversionPattern=%d %-5p %C.%M(%L)===> %m%n | Diseño del mensaje |
| log4j.appender.Default.append=false | No añade, borra el contenido anterior |
Los niveles de mensajes de menor a mayor prioridad:
Página donde explica el patrón de diseño de mensajes.
En este servlet:
package geniu.controlador;
import javax.servlet.http.HttpServlet;
import java.io.IOException;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.log4j.Logger;
import org.apache.log4j.PropertyConfigurator;
import geniu.persistencia.LectorPropiedades;
/****************************************************************************************
* @see Controlador
****************************************************************************************/
public class Controlador extends HttpServlet {
private LectorPropiedades propAcciones;
private ServletContext sc;
Logger log = Logger.getLogger( this.getClass() );
/*********************************************************************************
* INIT: se obtiene el contexto y se cargan las propiedades que representan
* las acciones
**********************************************************************************/
public void init(ServletConfig config) throws ServletException {
propAcciones = new LectorPropiedades( "acciones.properties");
sc = config.getServletContext();
PropertyConfigurator.configure( sc.getRealPath("/") + "log4j.properties");
System.out.println("===> ARCHIVO LOGJ4.PROPERTIES: "+sc.getRealPath("/") + "log4j.properties");
log.info( "INICIO DE CONTROLADOR" );
}
/*********************************************************************************
* DOPOST: Lee de las propiedades el componente al que se debe redirigir la llamada
**********************************************************************************/
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String nomServlet = null;
try {
//// Leo de request la entrada: página y operación (evento) que desencadena la llamada
StringBuffer propiedad = new StringBuffer();
propiedad.append( request.getParameter("pagina") );
propiedad.append( "." + request.getParameter("operacion" ));
// Leo de las propiedades la salida (servlet/jsp al que se redirige la llamada)
nomServlet = propAcciones.getParametro( propiedad.toString(), "");
log.info( "CONTROLADOR. PROPIEDAD IN:" + propiedad + " SERVLET OUT:" + nomServlet);
//// Si no se ha podido leer la salida, página de error
if ( nomServlet == null || nomServlet.length() == 0) {
response.sendError( response.SC_NOT_FOUND, "Error en la redirección a " + nomServlet);
return;
}
//// Redirijo la llamada
RequestDispatcher dispatcher = sc.getRequestDispatcher( nomServlet);
dispatcher.forward( request, response );
}
catch (Exception e) {
log.error( "ERROR. MENSAJE: " + e.toString());
e.printStackTrace();
response.sendError( response.SC_NOT_FOUND, "Error en la redirección a " + nomServlet);
}
}
Como resultado de este ejemplo obtendremos lo siguiente en ser_geniu.log:
13:59:49,312 INFO geniu.controlador.Controlador.init(36)===> INICIO DE CONTROLADOR ... 2006-11-01 13:59:49,312 INFO geniu.controlador.Controlador.doPost(53)===> CONTROLADOR. PROPIEDAD IN:index.Cargar rangos SERVLET OUT:/LectorTablasRangos.jsp 2006-11-01 13:59:49,359 DEBUG org.apache.catalina.core.ApplicationDispatcher.(143)===> servletPath=/LectorTablasRangos.jsp, pathInfo=null, queryString=null, name=null ....
En el siguiente ejemplo tenemos algo más complejo: dos dispositivos de log, uno el típico archivo y otro es la consola (en este caso la consola de Eclipse)
log4j.rootCategory=ALL, Default, MiConsola
log4j.appender.Default=org.apache.log4j.FileAppender
log4j.appender.Default.Threshold=DEBUG
log4j.appender.Default.ImmediateFlush=true
log4j.appender.Default.file=/doc/Java_eclipse/geniu/log/ser_geniu.log
log4j.appender.Default.layout=org.apache.log4j.PatternLayout
log4j.appender.Default.layout.ConversionPattern=%d %-5p %C.%M(%L)===> %m%n
log4j.appender.Default.append=false
log4j.appender.MiConsola=org.apache.log4j.ConsoleAppender
log4j.appender.MiConsola.Threshold=INFO
log4j.appender.MiConsola.ImmediateFlush=true
log4j.appender.MiConsola.layout=org.apache.log4j.PatternLayout
log4j.appender.MiConsola.layout.ConversionPattern=%d{ABSOLUTE} %-5p %C.%M(%L):%n==> %m%n
Además, en las últimas versiones de log4j viene Chainsaw, un monitor gráfico de mensajes, en la forma de una aplicación Java. Antes de utilizarlo hay que ejecutarlo:
java org.apache.log4j.chainsaw.Main
Evidentemente esta ejecución es imposible si log4j.jar no está en jre/lib/ext o en el CLASSPATH (además java debe estar en el path del sistema).
Un vistazo a lo que ofrece Chainsaw:
Ahora vamos a poner un ejemplo de archivo properties que usa como appenders (salidas) tanto un archivo como Chainsaw:
log4j.rootCategory=ALL, Default, Chainsaw
log4j.appender.Default=org.apache.log4j.FileAppender
log4j.appender.Default.Threshold=INFO
log4j.appender.Default.ImmediateFlush=true
log4j.appender.Default.file=/doc/Java_eclipse/geniu/log/ser_geniu.log
log4j.appender.Default.layout=org.apache.log4j.PatternLayout
log4j.appender.Default.layout.ConversionPattern=%d %-5p %C.%M(%L)===> %m%n
log4j.appender.Default.append=false
log4j.appender.Chainsaw=org.apache.log4j.net.SocketAppender
log4j.appender.Chainsaw.Threshold=INFO
log4j.appender.Chainsaw.remoteHost=localhost
log4j.appender.Chainsaw.port=4445 --> Ojo con el puerto para que no haya conflictos por coincidencia de puertos con otras aplicaciones
log4j.appender.Chainsaw.locationInfo=true
Si hemos referenciado Chainsaw en el archivo log4j.properties y nos hemos olvidado de ejecutar Chainsaw antes de lanzar la aplicación nos aparecerá un aviso como este:
log4j:ERROR Could not connect to remote log4j server at [localhost]. We will try again later. java.net.ConnectException: Connection refused: connect
Indica que no ha podido encontrar Chainsaw y conectarse a él. Normalmente no hay que preocuparse, no impedirá que se despliegue el contexto de aplicación.
En algunas versiones de Tomcat el uso de log4j.jar puede dar lugar al siguiente mensaje cuando se arranca Tomcat:
log4j:WARN No appenders could be found for logger log4j:WARN Please initialize the log4j system properly.
Explicación: Tomcat reconoce que log4j ha sido cargado, pero advierte que no hay archivo properties. No lo hay para Tomcat, sólo lo hemos puesto en nuestro contexto de aplicación. El mensaje es inofensivo, lo que se dice a continuación puede evitar esta advertencia.
Si queremos usar log4j en el propio Tomcat conviene poner este jar en common/lib. Junto con commons-logging.jar (este jar esta también sometido a múltiples versiones). El archivo log4j.properties se pondrá en common/classes. Por ejemplo:
log4j.rootLogger=info, R
log4j.appender.R=org.apache.log4j.ConsoleAppender
log4j.appender.R.layout=org.apache.log4j.PatternLayout
log4j.appender.R.layout.ConversionPattern=%-5p %-30.30c{1} %x - %m%n
En este ejemplo lo que hacemos es volcar de manera formateada los mensajes en la consola de Tomcat. Se podría usar un FileAppender.