Home| New Wiki | | Login | User registry | Home Tree PDF
Log4J en EJBs
Owner:, Version: 8, Date:Wed 23, July 2008,

¿Donde esta el problema?

1- Configuración

log4j requiere leer un archivo de configuración antes de poder realizar log de comandos. El archivo de configuración se lee normalmente a través de un servlet cargado al inicio del application server "ConfigureLog4j" que ejecuta: PropertyConfigurator.configure(String propertyFileName). Cuando esta función no es llamada, sucede que en STDERR se genera el siguiente mensaje:

[8/3/04 12:17:18:812 VET] 5e2a1f7a SystemErr R log4j:WARN No appenders could be found for logger (web).
[8/3/04 12:17:18:822 VET] 5e2a1f7a SystemErr R log4j:WARN Please initialize the log4j system properly.

En el caso de EJBs, cada módulo EJB tiene su propio ClassLoader, esto significa que aunque dos módulos puedan "ver" las clases (.jar) de log4j, estas no serán compartidas, de manera que cuando se inicialice un modulo Web, las clases de log4j del modulo EJB no serán inicializadas. Junto con esto, no existen "Iniciadores" para módulos EJB (aun).

¿Cómo pedirle al módulo EJB que se inicialice con un archivo de propiedades?

  • Usar un Servlet o ContextListener en el área Web, que ejecute una llamada a un EJB que inicialice log4j.
  • Usar la carga dinámica de log4j del archivo log4j.properties que debe encontrarse en el classpath. Esto sucederá automáticamente cuando se use una función de Log y log4j no este previamente inicializado.
  • Inicializar log4j desde el método ejbCreate de CADA EJB que use log4j usando un patrón singleton.

La opción a) es interesante pero requiere de mucho más codigo (1 listener y 1 ejb) y requiere incluir un modulo Web que talvez este casi vacio (salvo por el iniciador).

La opción b) es mas atractiva pero implica decidir donde debe quedar el archivo log4j.properties. Y es posible qyue haya un conflicto porque algunas librerias incluyen su propio archivo de configuracion

  • Si queda en el classpath del modulo, el archivo no podrá modificarse en produccion porque los archivos EJB JAR no son expandidos en el sistema operativo.
  • Si queda en un classpath mas global ($WAS_ROOT/$properties), entonces solo habra 1 archivo de configuracion de log4j para TODAS las aplicaciones dentro del servidor. Y la pregunta es: Que pasa si se requieren versiones distintas de log4j funcionando?

La opcion c) parece un compromiso adecuado aun cuando se requiere algo de código para iniciar log4j y para configurar cada EJB.

Implementación de la opción C

Listado 1: clase de apoyo para cargar log4j dinamicamente usando la opción c.

package demo.util;
import java.net.URL;
import org.apache.log4j.PropertyConfigurator;
public class Log4jInit {
  public static final String propertiesFile = "DemoLog4j.properties";  
  // Es importante que el nombre del archivo 
  // referencie de alguna manera al proyecto. 
  static final boolean watch = true;
  static boolean initialized = false;
  public static void init() {
    if (initialized)
      return;
    initialized = true;
    URL propsURL =  Log4jInit.class.getClassLoader().getResource(propertiesFile); 
   if (propsURL == null) {
     System.err.println(propertiesFile
       + ": No se encontró archivo de propiedades de log4j en el classpath.");
     return;
    }
    if (watch)
      PropertyConfigurator.configureAndWatch(propsURL.getFile());
    else
      PropertyConfigurator.configure(propsURL.getFile());
  }
}

Listado 2: EJB que usa log4j (opcion c)

package demo.log4j;
import org.apache.log4j.Logger;
import demo.util.Log4jInit;
public class DemoLog4JBean implements javax.ejb.SessionBean {
   static Logger log = Logger.getLogger(DemoLog4JBean.class); // Declaracion del log
   private javax.ejb.SessionContext mySessionCtx;
   public javax.ejb.SessionContext getSessionContext() {
       return mySessionCtx;
  }
   public void setSessionContext(javax.ejb.SessionContext ctx) {
      mySessionCtx = ctx;
  }
   public void ejbCreate() throws javax.ejb.CreateException {
      Log4jInit.init(); // Inicializacion de log4j
  }
   public void ejbActivate() {
  }
   public void ejbPassivate() {
  }
   public void ejbRemove() {
  }
   public void run(){
       log.debug("> run");
       System.out.println("hola a todos!!!");
       log.info(" inside run()");
       System.out.println("hola a todos!!!");
       log.debug("< run");
  }  
}

Finalmente, el archivo de propiedades:DemoLog4j.properties debe quedar en la carpeta $WAS_HOME$/properties o en alguna carpeta en el classpath del Application Server.

NOTA RELEVANTE: En ambientes de desarrollo, el archivo de properties puede quedar copiado en las clases del EJB (hay que quitar el archivo cuando se hace deploy) o en una carpeta externa al proyecto (opcion preferida: c:javaclasses ). Para que el archivo se incluya en el CVS, tambien puede quedar una copia en el proyecto EAR. La idea general es que tanto el modulo WEB como el módulo EJB usen el mismo archivo.

2- Restricciones de J2EE

J2EE prohibe el uso de funciones dentro del paquete java.io, es decir, todo lo que tiene que ver con manejo de archivos. Dada esta restricción, log4j solo podría utilizar el Appender de Consola (ConsoleAppender) que es el único objeto de podría utilizarse.

Esto significa que al agregar mas appenders, estos no debieran escribir en archivos, esto debido a las capacidades de los servidores de aplicaciones para controlar procesos paralelos y crear clones de EJBs.

Para dar soporte a esta característica en forma global se puede considerar que cualquier otro appender distinto de ConsoleAppender debe ser uno de los siguientes:

  • JDBC Appender : Coloca el mensaje de log en una tabla de la base de datos.
  • JMS Appender : Coloca un mensaje dentro de una cola de JMS, luego un MessageDrivenBean (EJB) puede tomar el mensaje y escribirlo en otra parte.
  • Socket Appender: Es un appender que se conecta con un servlet en un contenedor Web, dejando que el servlet sea el que realiza el proceso de logging, no el EJB.


Edit - History - Extract PDF - Extract Tree as PDF

Last Modified

Sat, Oct 3 Fri, Sep 25 Mon, Aug 3 Mon, Apr 27 Sat, Mar 28 Mon, Jan 19 Tue, Jan 6 Sat, Jan 3 Fri, Dec 12

Home| New Wiki