2011-10-02 20 views
18

Actualmente estoy escribiendo un gran proyecto en Java, con numerosas clases, algunas clases son pequeñas y silenciosas, que simplemente representan objetos con solo unos pocos métodos. Tengo un registrador en mi clase principal, y funciona bien. Quiero ser capaz de usar solo un registrador (con un appender de consola) con todas las clases. Intenté pasar una referencia al registrador a las diferentes clases, pero no se ve bien. además, a veces estoy ejecutando pruebas en las clases sin ejecutar main, y por lo tanto el registrador no se inicializa para las otras clases.cómo usar log4j con múltiples clases?

¿Cuál es el mejor enfoque para lograr eso, es decir, cómo iniciar sesión de diferentes clases en un solo registro, sin una dependencia fuerte entre las clases y con la capacidad de usar el registro de forma independiente con cada clase?

Respuesta

16

Si he entendido bien, lo que tiene en el momento es:

public class Main { 
    public static final Logger LOGGER = Logger.getLogger(Main.class); 
} 

public class AnotherClass { 
    public void doSomething() { 
     Main.LOGGER.debug("value=" + value); 
    } 
} 

o, se pasa a un registrador de referencias a los constructores de la clase.

En primer lugar, se puede utilizar uno registrador mundial por el simple uso del mismo valor pasado a Logger.getLogger, como:

public class Main { 
    private static final Logger LOGGER = Logger.getLogger("GLOBAL"); 
} 

public class AnotherClass { 
    private final Logger LOGGER = Logger.getLogger("GLOBAL"); 

    public void doSomething() { 
     LOGGER.debug("value=" + value); 
    } 
} 

Esto utiliza exactamente el mismo registrador, Logger.getLogger devuelve el mismo objeto en las dos llamadas . Ya no tienes una dependencia entre las clases, y esto funcionará.

La otra cosa que deduzco de sus comentarios es que está configurando a mano (usando BasicConfigurator.configure. La mayoría de las veces esto no es necesario, y debe hacer su configuración simplemente agregando un log4j.properties o log4j. xml a su classpath. En Eclipse esto se hace agregándolo a src/(o src/main/resources si está usando maven). Si usa junit, agréguelo al directorio de prueba/fuente (o src/test/resources con maven). Esta es una manera mucho mejor a largo plazo de configurar log4j, porque no es necesario pasar información entre clases.

Además, la forma recomendada de utilizar los registradores es pasar la clase a Logger.getLogger(). De esta manera se puede filtrar la salida basándose en el nombre de la clase, que suele ser mucho más útil que acaba de tener un registrador mundial:

public class Main { 
    private static final Logger LOGGER = Logger.getLogger(Main.class); 
    public static final main(String[] args) { 
     LOGGER.debug("started"); 
    } 
} 

public class AnotherClass { 
    private final Logger LOGGER = Logger.getLogger(this.getClass()); 

    public void doSomething() { 
     LOGGER.debug("value=" + value); 
    } 
} 

Luego, en los log4j.properties, se puede configurar un único appender a un archivo .

# Set root logger level to DEBUG and its only appender to A1. 
log4j.rootLogger=DEBUG, A1 

# A1 is set to be a ConsoleAppender. 
log4j.appender.A1=org.apache.log4j.ConsoleAppender 

# A1 uses PatternLayout. 
log4j.appender.A1.layout=org.apache.log4j.PatternLayout 
log4j.appender.A1.layout.ConversionPattern=%-4r [%t] %-5p %c %x - %m%n 

Finalmente, no es necesario declarar todos sus registradores como estáticos. Esto solo hace una diferencia notable si está haciendo lotes [*] de creación de objeto. Declarar sus registradores como campos no estáticos le permite usar Logger.getLogger(this.getClass()); en cuyo caso agregar un registrador a una clase se convierte en un corte y pegado de una sola línea. Ver Should I declare Log references static or not? (desafortunadamente el enlace a la página wiki está roto), pero el slf4j page contiene una buena explicación también. Así que use campos no estáticos a menos que tenga una muy buena razón para no hacerlo.

Cameron tiene razón cuando dice que debe probar y usar slf4j si es posible, tiene una característica asesina, puede usar múltiples marcos de registro con ella.

[*] y me refiero a muchos.

+0

Hice exactamente lo que mencionaste, pero solo se imprimen los registros de la clase principal, no se imprimen los registros de otros métodos. Como arreglarlo –

5

Your logger instances should typically be private, static and final. Al hacerlo, cada clase tendrá su propia instancia de registrador (que se crea una vez que se cargue la clase), para que pueda identificar la clase donde se creó el registro, y también ya no necesita pasar instancias de registrador entre clases.

+0

Dónde coloco el "BasicConfigurator.configure();" en una clase sin un método principal? – stdcall

+0

@Mellowcandle, no necesita invocar 'BasicConfigurator.configure()' en cada clase. Hazlo, solo en tu método principal de la clase que inicializa tu aplicación, y solo si es necesario. Editar: ['BasicConfigurator.configure'] (http://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/BasicConfigurator.html#configure%28%29) se usa para crear una configuración de log4j. cuando falta un archivo funcional log4j.properties configurado con appenders y diseños. –

+0

¿Qué quieres decir con tener que? Necesito ejecutar pruebas unitarias en varias clases. cuando tengo que ejecutarlo? – stdcall

4

La mejor manera de hacerlo es hacer que cada clase tenga su propio registrador (nombrado después de la clase), luego configure su configuración para que todos se agreguen al mismo appender.

Por ejemplo:

class A { 
    private static final Logger log = Logger.getLogger(A.class); 
} 

class B { 
    private static final Logger log = Logger.getLogger(B.class); 
} 

A continuación, sus log4j.properties pueden verse como el ejemplo en la documentación log4j:

# Set root logger level to DEBUG and its only appender to A1. 
log4j.rootLogger=DEBUG, A1 

# A1 is set to be a ConsoleAppender. 
log4j.appender.A1=org.apache.log4j.ConsoleAppender 

# A1 uses PatternLayout. 
log4j.appender.A1.layout=org.apache.log4j.PatternLayout 
log4j.appender.A1.layout.ConversionPattern=%-4r [%t] %-5p %c %x - %m%n 

Tanto A y B registrará al registrador de la raíz y por lo tanto a la mismo appender (en este caso, la consola).

Esto le dará lo que desea: cada clase es independiente pero todas escriben en el mismo registro. También obtienes la característica de bonificación de que puedes cambiar el nivel de registro para cada clase en la configuración de log4j.

Como un aparte, es posible que desee considerar mudarse a slf4j si el proyecto todavía está en desarrollo temprano. slf4j tiene algunas mejoras sobre log4j que hacen que sea un poco más fácil trabajar con él.

+0

¿Dónde pongo el "BasicConfigurator.configure();" En una clase sin un método principal? – stdcall

+2

@Mellowcandle: no. Use un archivo 'log4j.properties'.Solo necesita ponerlo en classpath y log4j lo usará. –

+0

@CameronSkinner Hice exactamente lo que mencionaste, pero solo los registros en la clase principal se imprimen en la consola, los registros en otros métodos no se imprimen, ¿cómo solucionarlo? –

1

La razón por la que tiene varias instancias de registrador es porque desea que se comporten de manera diferente al iniciar sesión (normalmente imprimiendo el nombre de clase con el que están configuradas). Si no te importa eso, puedes simplemente crear una sola instancia de registrador estático en una clase y usar eso en cualquier lugar.

Para crear un solo registrador, puede simplemente crear una clase de registro de servicios estáticos para ser registrador de punto único, por lo que si necesitamos cambiar el paquete de registrador, solo actualizará esta clase.

final public class Logger { 
    private static final org.apache.log4j.Logger logger = org.apache.log4j.Logger.getLogger("Log"); 

    enum Level {Error, Warn, Fatal, Info, Debug} 

    private Logger() {/* do nothing */}; 

    public static void logError(Class clazz, String msg) { 
     log(Level.Error, clazz, msg, null); 
    } 

    public static void logWarn(Class clazz, String msg) { 
     log(Level.Warn, clazz, msg, null); 
    } 

    public static void logFatal(Class clazz, String msg) { 
     log(Level.Fatal, clazz, msg, null); 
    } 

    public static void logInfo(Class clazz, String msg) { 
     log(Level.Info, clazz, msg, null); 
    } 

    public static void logDebug(Class clazz, String msg) { 
     log(Level.Debug, clazz, msg, null); 
    } 


    public static void logError(Class clazz, String msg, Throwable throwable) { 
     log(Level.Error, clazz, msg, throwable); 
    } 


    public static void logWarn(Class clazz, String msg, Throwable throwable) { 
     log(Level.Warn, clazz, msg, throwable); 
    } 

    public static void logFatal(Class clazz, String msg, Throwable throwable) { 
     log(Level.Fatal, clazz, msg, throwable); 
    } 

    public static void logInfo(Class clazz, String msg, Throwable throwable) { 
     log(Level.Info, clazz, msg, throwable); 
    } 

    public static void logDebug(Class clazz, String msg, Throwable throwable) { 
     log(Level.Debug, clazz, msg, throwable); 
    } 

    private static void log(Level level, Class clazz, String msg, Throwable throwable) { 
     String message = String.format("[%s] : %s", clazz, msg); 
     switch (level) { 
      case Info: 
       logger.info(message, throwable); 
       break; 
      case Warn: 
       logger.warn(message, throwable); 
       break; 
      case Error: 
       logger.error(message, throwable); 
       break; 
      case Fatal: 
       logger.fatal(message, throwable); 
       break; 
      default: 
      case Debug: 
       logger.debug(message, throwable); 
     } 
    } 

} 
Cuestiones relacionadas