O logger deve ser estático privado ou não

103

O logger deve ser declarado estático ou não? Normalmente, tenho visto dois tipos de declaração para um logger:

    log de log protegido = novo Log4JLogger (aClass.class);

ou

    log de log estático privado = new Log4JLogger (aClass.class);

Qual deve ser usado? quais são os prós e contras de ambos?

Drahakar
fonte
1
A extração de madeira é uma preocupação transversal. Use Aspectos e a questão é discutível.
Dave Jarvis,
4
staticé uma referência por classe. não estático é uma referência por instância (+ inicialização). Portanto, em alguns casos, o último vem com um impacto significativo na memória se você tiver toneladas de instâncias. Nunca use o não estático em um objeto frequente . Eu sempre uso a versão estática. (que deve ser maiúsculo LOG )
QUIT - Anony-Mousse
2
conforme já sugerido, use AOP e anotações, por exemplo: jcabi.com/jcabi-aspects/annotation-loggable.html
yegor256
1
RobertHume, a versão estática, está usando uma constante. É exatamente por isso que deve ser maiúsculo.
QUIT - Anony-Mousse
2
Não, deve ser em private static final Log logminúsculas. O logger não é uma constante, o logger é um objeto final estático (que pode sofrer mutação). Pessoalmente, sempre uso logger.
osundblad

Respostas:

99

A vantagem da forma não estática é que você pode declará-la em uma classe base (abstrata) como a seguir, sem se preocupar se o nome de classe correto será usado:

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

No entanto, sua desvantagem é obviamente que uma nova instância do logger será criada para cada instância da classe. Isso pode não ser caro por si só, mas adiciona uma sobrecarga significativa. Se quiser evitar isso, use o staticformulário. Mas sua desvantagem é que você precisa declará-lo em cada classe individual e tomar cuidado em cada classe para que o nome de classe correto seja usado durante a construção do logger porque getClass()não pode ser usado em contexto estático. No entanto, no IDE comum, você pode criar um modelo de preenchimento automático para isso. Por exemplo, logger+ ctrl+space.

Por outro lado, se você obtiver o logger de uma fábrica que, por sua vez, pode armazenar em cache os loggers já instanciados, o uso da forma não estática não adicionará muito overhead. Log4j, por exemplo, tem um LogManagerpara esse propósito.

protected Log log = LogManager.getLogger(getClass());
BalusC
fonte
6
Declare abstract Log getLogger();na classe abstrata. Implemente este método, retornando o logger estático para a instância particular. Adicione private final static Log LOG = LogManager.getLogger(Clazz.class);ao seu modelo de classe IDE.
QUIT - Anony-Mousse
2
Para slf4j:protected Logger log = LoggerFactory.getLogger(getClass());
Markus Pscheidt
3
@BalusC o problema de passar getClass () para o método getLogger é que ele retorna a classe da instância atual. Normalmente é mais desejável que o log seja associado à classe onde está o código. Por exemplo, se o código de registro estiver na classe Pai, então queremos que o registro seja associado ao Pai, mesmo que a instância de execução seja uma instância da classe Criança, que é uma subclasse de Pai. Com getClass (), ele será associado ao filho, incorretamente
ou em
@inor: "incorretamente"? Se você não quiser abstrair a classe, então não deve usar getClass () herdado em primeiro lugar. Há desenvolvedores que o consideram correto e útil, pois revela informações em qual subclasse exatamente a lógica foi executada.
BalusC
2
@ BalusC getLogger (getClass ()) resulta em sempre registrar o nome da subclasse incorretamente. As classes de log devem sempre fazer getLogger (Clazz.class) para associar o log feito pelo código na classe Clazz. Os desenvolvedores que desejam saber qual das subclasses está executando (por exemplo, SubClazz extends Clazz) devem fazer no SubClazz: getLogger (SubClazz.class) e algo como: log.info ("chamando <algo em minha classe base>");
INOR
44

Eu costumava pensar que todos os loggers deveriam ser estáticos; no entanto, este artigo em wiki.apache.org traz à tona algumas questões importantes sobre memória, relacionadas a vazamentos de carregador de classe. Declarar um logger como estático evita que a classe declarante (e carregadores de classe associados) seja coletado como lixo em contêineres J2EE que usam um carregador de classe compartilhado. Isso resultará em erros de PermGen se você reimplantar seu aplicativo vezes suficientes.

Eu realmente não vejo nenhuma maneira de contornar esse problema de vazamento do carregador de classe, além de declarar os registradores como não estáticos.

piepera
fonte
4
Suspeitei que o campo estático também teria problemas de vazamento de memória. Não estático pode ter problemas de desempenho como outros disseram. Qual é a forma ideal então?
liang,
@piepera, o principal problema descrito no artigo que você referiu é a capacidade de controlar o nível de registro em cada aplicativo quando "considerar o caso quando uma classe usando" private static Log log = "é implantada por meio de um ClassLoader que é ancestral de vários supostamente "aplicações" independentes. Não vejo isso como um problema porque nesta situação particular os aplicativos têm um "terreno comum" e nesse "terreno comum" o nível de registro para a classe é decidido e sim, isso vale para todos os aplicativos ... mas mantenha lembre-se de que essa classe é [carregada] fora desses aplicativos
ou em
17

A diferença mais importante é como isso afeta seus arquivos de log: em qual categoria vão os logs?

  • Em sua primeira escolha, os logs de uma subclasse terminam na categoria da superclasse. Isso parece muito contra-intuitivo para mim.
  • Existe uma variante do seu primeiro caso:

    log de log protegido = new Log4JLogger (getClass ());

    Nesse caso, sua categoria de log diz em qual objeto o código que fez o log estava trabalhando.

  • Em sua segunda opção (private static), a categoria de log é a classe que contém o código de log. Normalmente a classe que está fazendo a coisa que está sendo registrada.

Eu recomendaria fortemente essa última opção. Ele tem essas vantagens, em comparação com as outras soluções:

  • Existe uma relação direta entre o log e o código. É fácil descobrir de onde veio uma mensagem de log.
  • Se alguém tiver que ajustar os níveis de registro (o que é feito por categoria), geralmente é porque está interessado (ou não) em algumas mensagens específicas, escritas por uma classe específica. Se a categoria não for a classe que está escrevendo as mensagens, será mais difícil ajustar os níveis.
  • Você pode fazer login em métodos estáticos
  • Os registradores precisam ser inicializados (ou pesquisados) apenas uma vez por classe, portanto, na inicialização, em vez de para cada instância criada.

Também tem desvantagens:

  • Ele precisa ser declarado em cada classe onde você registra mensagens (sem reutilização de registradores de superclasse).
  • Você precisa ter o cuidado de colocar o nome de classe correto ao inicializar o logger. (Mas bons IDEs cuidam disso para você).
Wouter Coekaerts
fonte
4

Use a inversão de controle e passe o logger para o construtor. Se você criar o logger dentro da classe, terá muito trabalho com seus testes de unidade. Você está escrevendo testes de unidade, não é?

Wayne Allen
fonte
5
Os testes de unidade que examinam o registro que você está produzindo parecem inúteis e incrivelmente frágeis.
Michael
1
Utilidade dependendo do sistema em teste. Às vezes, o registro é tudo a que você tem acesso.
Wayne Allen de
@Wayne Allen quando você está fazendo testes de unidade, por definição, você também tem resultados de teste. Você está sugerindo uma situação em que alguém faz o teste de unidade, mas não tem os resultados dos testes? só tem os logs?
inor
criar o logger dentro da classe não cria problemas. Você pode mostrar um exemplo simples que é difícil de usar porque a classe cria seu próprio logger?
inor
1
Certo. Que tal um logger que envia e-mails. Não queira fazer isso toda vez que executar seus testes. Além disso, como você afirma o efeito colateral?
Wayne Allen