¿Debe el registrador ser estático privado o no?

103

¿Debería declararse el registrador estático o no? Por lo general, he visto dos tipos de declaración para un registrador:

    registro de registro protegido = nuevo Log4JLogger (aClass.class);

o

    Registro de registro estático privado = nuevo Log4JLogger (aClass.class);

¿Cuál debería usarse? ¿Cuales son las ventajas y desventajas de ambos?

Drahakar
fuente
1
La tala es una preocupación transversal. Utilice Aspectos y la pregunta es discutible.
Dave Jarvis
4
statices una referencia por clase. no estático es una referencia por instancia (+ inicialización). Entonces, en algunos casos, este último tiene un impacto significativo en la memoria si tiene toneladas de instancias. Nunca use lo no estático en un objeto frecuente . Siempre uso la versión estática. (que debe estar en mayúsculas LOG )
Ha SALIR - Anony-Mousse
2
como ya se sugirió, use AOP y anotaciones, por ejemplo: jcabi.com/jcabi-aspects/annotation-loggable.html
yegor256
1
RobertHume la versión estática se utiliza una constante. Esa es exactamente la razón por la que debería estar en mayúsculas.
Ha QUIT - Anony-Mousse
2
No, debería ser private static final Log logque esté en minúsculas. El registrador no es una constante, el registrador es un objeto final estático (que puede ser mutado). Personalmente siempre uso logger.
osundblad

Respuestas:

99

La ventaja de la forma no estática es que puede declararla en una clase base (abstracta) como sigue sin preocuparse de que se utilice el nombre de clase correcto:

protected Log log = new Log4JLogger(getClass());

Sin embargo, su desventaja es, obviamente, que se creará una instancia de registrador completamente nueva para cada instancia de la clase. Esto puede no ser caro en sí mismo, pero agrega una sobrecarga significativa. Si desea evitar esto, le gustaría utilizar el staticformulario en su lugar. Pero su desventaja es, a su vez, que debe declararlo en cada clase individual y tener cuidado en cada clase de que se use el nombre de clase correcto durante la construcción del registrador porque getClass()no se puede usar en un contexto estático. Sin embargo, en el IDE promedio puede crear una plantilla de autocompletar para esto. Por ejemplo, logger+ ctrl+space.

Por otro lado, si obtiene el registrador de una fábrica que a su vez puede almacenar en caché los registradores ya instanciados, entonces el uso de la forma no estática no agregará demasiada sobrecarga. Log4j, por ejemplo, tiene un LogManagerpara este propósito.

protected Log log = LogManager.getLogger(getClass());
BalusC
fuente
6
Declare abstract Log getLogger();en la clase abstracta. Implemente este método, devolviendo el registrador estático para la instancia en particular. Agregue private final static Log LOG = LogManager.getLogger(Clazz.class);a su plantilla de clase IDE.
Ha SALIR - Anony-Mousse
2
Para slf4j:protected Logger log = LoggerFactory.getLogger(getClass());
Markus Pscheidt
3
@BalusC el problema de pasar getClass () al método getLogger es que devuelve la clase de la instancia actual. Normalmente es más deseable que el registro esté asociado con la clase donde está el código. Por ejemplo, si el código de registro está en la clase Parent, entonces queremos que el registro esté asociado con Parent, aunque la instancia en ejecución sea una instancia de la clase Child, que es una subclase de Parent. Con getClass () que se asocia con el Niño, de forma incorrecta
enor
@inor: "incorrectamente"? Si no desea abstraer la clase, entonces simplemente no debe usar el getClass () heredado en primer lugar. Hay desarrolladores que lo encuentran correcto y útil, ya que revela información en qué subclase se ha realizado exactamente la lógica.
BalusC
2
@ BalusC getLogger (getClass ()) da como resultado que siempre se registre el nombre de la subclase incorrectamente. Las clases de registro siempre deben hacer getLogger (Clazz.class) para asociar el registro realizado por el código en la clase Clazz. Los desarrolladores que quieran saber cuál de las subclases se está ejecutando (por ejemplo, SubClazz extiende Clazz) deben hacer en SubClazz: getLogger (SubClazz.class) y algo como: log.info ("llamando a <algo en mi clase base>");
inor
44

Solía ​​pensar que todos los registradores deberían ser estáticos; sin embargo, este artículo en wiki.apache.org plantea algunas preocupaciones importantes sobre la memoria, con respecto a las fugas del cargador de clases. Declarar un registrador como estático evita que la clase declarante (y los cargadores de clases asociados) sean recolectados como basura en contenedores J2EE que usan un cargador de clases compartido. Esto resultará en errores de PermGen si vuelve a implementar su aplicación suficientes veces.

Realmente no veo ninguna forma de evitar este problema de fuga del cargador de clases, aparte de declarar los registradores como no estáticos.

piepera
fuente
4
Sospeché que el campo estático también tendría un problema de pérdida de memoria. No estático puede tener problemas de rendimiento como dijeron otros. ¿Cuál es la forma ideal entonces?
liang
@piepera, el principal problema descrito en el artículo al que hizo referencia es la capacidad de controlar el nivel de registro en cada aplicación cuando "considere el caso cuando una clase que usa" private static Log log = "se implementa a través de un ClassLoader que se encuentra en la ascendencia de múltiples supuestamente "aplicaciones" independientes ". No veo eso como un problema porque en esta situación particular las aplicaciones tienen un "terreno común" y en ese "terreno común" se decide el nivel de registro para la clase y sí, se aplica a todas las aplicaciones ... pero siga
Tenga
17

La diferencia más importante es cómo afecta a sus archivos de registro: ¿en qué categoría van los registros?

  • En su primera elección, los registros de una subclase terminan en la categoría de la superclase. Eso me parece muy contrario a la intuición.
  • Hay una variante de su primer caso:

    registro de registro protegido = nuevo Log4JLogger (getClass ());

    En ese caso, su categoría de registro dice en qué objeto estaba trabajando el código que registró.

  • En su segunda opción (estática privada), la categoría de registro es la clase que contiene el código de registro. Entonces, normalmente, la clase que está haciendo lo que se está registrando.

Recomendaría encarecidamente esa última opción. Tiene estas ventajas, en comparación con las otras soluciones:

  • Existe una relación directa entre el registro y el código. Es fácil encontrar el origen de un mensaje de registro.
  • Si alguien tiene que ajustar los niveles de registro (que se hace por categoría), generalmente es porque está interesado (o no) en algunos mensajes en particular, escritos por una clase en particular. Si la categoría no es la clase que está escribiendo los mensajes, es más difícil ajustar los niveles.
  • Puede iniciar sesión en métodos estáticos
  • Los registradores solo necesitan inicializarse (o buscarse) una vez por clase, por lo tanto, al inicio, en lugar de para cada instancia creada.

También tiene desventajas:

  • Debe declararse en cada clase en la que registre mensajes (sin reutilización de registradores de superclase).
  • Debe tener cuidado de poner el nombre de clase correcto al inicializar el registrador. (Pero los buenos IDE se encargan de eso por ti).
Wouter Coekaerts
fuente
4

Utilice la inversión de control y pase el registrador al constructor. Si crea el registrador dentro de la clase, se lo va a pasar muy mal con sus pruebas unitarias. Estás escribiendo pruebas unitarias, ¿no?

Wayne Allen
fuente
5
Las pruebas unitarias que examinan el registro que está produciendo suenan inútiles e increíblemente frágiles.
Michael
1
Utilidad en función del sistema sometido a prueba. A veces, el registro es todo a lo que tiene acceso.
Wayne Allen
@Wayne Allen cuando está haciendo pruebas unitarias, por definición, también tiene resultados de pruebas. ¿Está sugiriendo una situación en la que uno hace una prueba unitaria pero no tiene resultados de pruebas? solo tienes los registros?
2018
crear el registrador dentro de la clase no crea problemas. ¿Puede mostrar un ejemplo simple que sea difícil de UT porque la clase crea su propio registrador?
2018
1
Por supuesto. ¿Qué tal un registrador que envía correos electrónicos? No quiero hacer eso cada vez que ejecute sus pruebas. Además, ¿cómo se afirma el efecto secundario?
Wayne Allen