¿Es Logger.getLogger (MyClass.class) la mejor manera de inicializar log4j loggers?

13

Este tutorial de Mkyong sugiere iniciar loggers de esta manera:

@Controller
public class WelcomeController {

    private static final Logger logger = Logger.getLogger(WelcomeController.class);

   // etc

}

Ahora, presumiblemente, cualquier otra clase que use, que tenga un registrador, inicializará sus registradores de la misma manera.

Mi pregunta es: ¿es esta la mejor manera de hacerlo? Parece ... repetitivo.

dwjohnston
fuente
2
¿Qué le parece detallado sobre esto (dejando de lado la sintaxis de Java)? Debe crear una variable para contener el registrador y debe indicar getLogger()el nombre del registrador para obtener.
kdgregory
2
@kdgregory el hecho de que estoy haciendo lo mismo en todas las clases.
dwjohnston
1
Eche un vistazo a esta pregunta en Stack Overflow
toniedzwiedz
3
Demasiado viejo para migrar, pero esta pregunta está fuera de tema aquí y es más adecuada para StackOverflow.
Andres F.
1
@AndresF. Creo que lo puse aquí porque es más una cuestión sobre el estilo de código / patrones de diseño, que un problema técnico.
dwjohnston

Respuestas:

16

Su comentario dice que "detallado" se refiere a la necesidad de repetir esta línea de código en cada clase. Mi primera respuesta es que, en general, agregar dos líneas de código (definición variable más declaración de importación) a cada clase no es tan importante. Especialmente porque solo necesita agregarlos a las clases que tienen comportamiento y, por lo tanto, necesita hacer un registro. Dicho esto, la línea específica de código que está utilizando es propensa a copiar y pegar errores (más sobre eso más adelante).

Pero, dado que desea alternativas, aquí hay algunas, con razones por las que puede querer o no usarlas.

Use un solo registrador para toda la aplicación

Si no le importa qué clase está informando, o está dispuesto a poner todo el contexto necesario en el mensaje, un simple registrador de singleton hará el trabajo:

LoggerSingleton.getInstance().debug("MyController is running")

En mi opinión, uno de los grandes beneficios de un marco de registro es tener el contexto proporcionado por instancias de registrador separadas, aunque solo sea para dirigir los mensajes de registro a diferentes destinos. No renunciaría a eso solo para guardar una línea de código (aún necesita la importación).

Además, esto aumenta la verbosidad en el punto de uso, lo que terminará en muchas más pulsaciones de teclas.

Crea tus registradores en el punto de uso

Estoy descartando esto solo porque elimina la variable. No creo que necesite comentarlo. Aunque muestra mi técnica preferida para obtener una instancia de registrador.

Logger.getLogger(getClass()).debug("blah blah blah");

Use un postprocesador de frijoles para inyectar el registrador

Su ejemplo usa Spring, y Spring le permite conectar el código de inicialización del bean. Podría crear un postprocesador que inspeccione el bean en busca de una loggervariable miembro y cree una Loggerinstancia cuando encuentre una.

Si bien dicho postprocesador es solo unas pocas docenas de líneas de código, es otra parte móvil de su aplicación y, por lo tanto, otra fuente potencial de errores. Prefiero tener la menor cantidad posible.

Usa un mixin

Scala y Groovy proporcionan rasgos que le permiten encapsular el comportamiento. Un patrón típico de Scala es crear un Loggingrasgo y luego agregarlo a la clase que necesita registro:

class MyController with Logging

Desafortunadamente, esto significa que tienes que cambiar de idioma. A menos que esté utilizando Java 8, en cuyo caso puede crear una Logginginterfaz con un "método predeterminado":

public interface Logging {
    default Logger getLogger() {
        return Logger.getLogger(getClass());
    } 
}

Ahora, dentro de su código de clase, simplemente puede usar

getLogger().debug("blah blah blah");

Si bien es fácil, esto tiene un par de desventajas. Por un lado, contamina la interfaz de cada clase que lo usa, porque todos los métodos de interfaz son públicos. Tal vez no sea tan malo si lo usa solo para clases que Spring instancia e inyecta, especialmente si sigue la separación de interfaz / implementación.

El mayor problema es que tiene que buscar la instancia de registrador real en cada llamada. Lo cual es rápido, pero innecesario.

Y aún necesita una declaración de importación.

Mover el registrador a una superclase

Repetiré: no encuentro definiciones de registrador repetidas detalladas, pero si lo hace, creo que este es el mejor enfoque para eliminarlas.

public abstract class AbstractController {
    protected Logger logger = Logger.getLogger(getClass());
}

Ahora sus clases de controlador heredan AbstractControllery tienen acceso a la loggervariable. Recuerda que tienes que poner la @Controlleranotación en la clase concreta.

Algunas personas encontrarán esto como una perversión de la herencia. He tratado de aplacarlos nombrando la clase en AbstractControllerlugar de AbstractProjectClass. Puedes decidir por ti mismo si existe o no una relación es-es .

Otras personas se opondrán al uso de una variable de instancia en lugar de una variable estática. Los registradores estáticos de IMO son propensos a copiar y pegar errores, ya que debe hacer referencia explícita al nombre de clase; getClass()asegura que su registrador sea siempre correcto.

kdgregory
fuente
Creo que debe tener cuidado con getClass () en la clase de herencia, MethodHandles.lookup (). LookupClass () es mejor después de jdk 7
yuxh
@luxh: ¿tienes una explicación para eso?
kdgregory
@yuxh: la única mención de MethodHandles en esa pregunta (que no estaba en la respuesta que vinculó), es una afirmación similar no respaldada con un comentario que solicita una aclaración. ¿Tiene apoyo autorizado para la afirmación que MethodHandles.lookup().lookupClass()es mejor que Object.getClass()?
kdgregory
MethodHandles.lookup().lookupClass()se puede usar para variables estáticas, no es propenso a copiar y pegar errores y es rápido: stackoverflow.com/a/47112323/898747 Significa un ingreso adicional, pero me gusta que mis registradores sean estáticos, por lo que al menos vale la pena mención :)
FableBlaze
0

Para ampliar la respuesta proporcionada por @kdgregory, Groovy proporciona @Slf4j( groovy.util.logging.Slf4j) fuera de la caja como una anotación que realiza una transformación AST en la clase para agregarla con un registrador que asume el nombre de la variable logpor defecto si no se especifica.

Daniel Paul Anzaldo
fuente