O Log4j é um dos frameworks de log mais utilizados, se não for o mais utilizado – no mundo Java.
No post de hoje eu vou colocar um resumão com as configurações do Log4j2 e alguns detalhes que devem ser observados para que tudo funcione sem dor de cabeça.
Novidades do Log4j2
- Agora é possível utilizar JSON ou YAML no arquivo de configuração além de XML
- A configuração através de arquivo properties foi removida
- O arquivo XML de configuração utilizado na versão 1.2 não é compatível com a versão 2
- Muitas melhorias de performance foram implementadas na versão 2, principalmente para cenários de log assíncronos
- Possibilidade de aplicar filtros nos logs, ou seja, antes de passar o log para Loggers ou Appenders o log propriamente dito pode ser encaminhado para um classe de filtro
- Suporte para substituição de propriedades, algo parecido com o funcionamento do Maven onde é possível declarar propriedades e depois fazer referência as mesmas
- Suporte a reconfiguração automática quando o arquivo de configuração é modificado
- Agora o log4j está dividido entre vários jar e não em somente em um como na versão 1.2
- Requer no mínimo Java 6
São muitas novidades legais, mas a que menos me agradou foi a retirada do arquivo de properties para fazer a configuração de log. Gostava do arquivo de propriedades por achar mais fácil de ler e entender. Destaco duas novidades que achei bem legal, o “reload” automático do arquivo de configuração e o suporte a substituição de propriedades, além da tradicional melhoria de performance.
Configuração
Para configurar o Log4j2 eu optei por utilizar o arquivo XML. Uma importante mudança é o nome do arquivo que o Log4j2 procura no classpath da aplicação, no caso de xml o nome deve ser log4j2.xml. A extensão poderia ser json ou jsn para JSON ou ainda yaml ou yml para YAML. Também vale lembrar que é possível adicionar um arquivo de configuração para ambientes de testes, neste caso é necessário adicionar “-test” ao nome do arquivo e manter a extensão, algo como lo4j2-test.xml. Os arquivos de configuração do tipo teste tem preferência sobre os arquivos de produção. Mais informações sobre a ordem de leitura dos arquivos de configuração podem ser obtidas aqui.
Exemplo de XML:
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="debug">
<Properties>
<Property name="pattern">%d{dd/MM/yyyy HH:mm:ss,SSS EEEE} %p [%-20c{1}] %m%n</Property>
<Property name="filePath">${sys:user.home}</Property>
</Properties>
<Appenders>
<RollingFile name="RollingFile"
fileName="${filePath}/logs/meuLog.log"
filePattern="${filePath}/logs/meuLog-%d{dd-MM-yyyy-HH:mm:ss}-%i.log">
<PatternLayout pattern="${pattern}"/>
<Policies>
<SizeBasedTriggeringPolicy size="10 MB" />
</Policies>
<DefaultRolloverStrategy max="20"/>
</RollingFile>
<Console name="Console">
<PatternLayout pattern="${pattern}"/>
</Console>
</Appenders>
<Loggers>
<Root level="debug">
<AppenderRef ref="RollingFile"/>
<AppenderRef ref="Console"/>
</Root>
<Logger name="org.hibernate" level="debug" additivity="false">
<AppenderRef ref="Console"/>
<AppenderRef ref="RollingFile"/>
</Logger>
</Loggers>
</Configuration>
Troubleshooting
Acho importante destacar alguns pontos do arquivo de configuração.
- Não existe ainda nenhum XSD ou DTD (pelo menos no momento em que escrevo esse post) para validar o arquivo de configuração. O que na minha opinião é um grande erro, dai a importância do atributo status=”debug” na tag Configuration, pelo menos em ambiente de desenvolvimento e testes, dessa forma o Log4j2 ao parsear o arquivo mostra no log os erros caso existam. Como não existe nenhum autocomplete das IDE’s um exemplo comum de erro é errar o nome da tag, é preciso ficar atento pois esse parser vai ser executado somente na primeira execução do Log4j2.
- A documentação do Log4j2 diz que é possível declarar um property usando a seguinte sintaxe:
<Property name="pattern" value="%d{dd/MM/yyyy HH:mm:ss,SSS EEEE} %p [%-20c{1}] %m%n"/>
Mas não funcionou, pelo menos comigo, o Log4j2 mostra o seguinte erro ao realizar o parser: Property contains an invalid element or attribute “value”. Não sei se é um bug, dei uma olhada rápida na internet e não encontrei nada relacionado
- A property com o nome filePath possui o valor ${sys:user.home}. É dessa forma que o Log4j2 busca por variáveis de sistema, não é possível utilizar diretamente ${user.home} pois o Log4j2 vai procurar por uma propriedade com esse nome. Existe outras configurações que permitem ao Log4j2 ler as variáveis a partir de outras fontes, mais detalhes aqui
- Por fim, mas não menos importante, reparem no atributo additivity=”false” da tag Logger. Essa tag evita que o log seja duplicado, primeiro o Log4j passa o log para o Appender e depois para o Root que contém a referência para Appender e bingo! log duplicado
Dependências
Como falei no começo do post, agora é preciso utilizar mais de uma arquivo jar. Como utilizo Maven, a minha configuração dessa maneira:
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.2</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.2</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-web</artifactId>
<version>2.2</version>
</dependency>
Além da API e core adicionei o módulo web, dessa forma ao realizar o deploy o Log4j2 já procura o arquivo de configuração e faz o parser. Se algo de errado acontecer, ele já informa na hora. Sem esse módulo o parser é realizado na primeira chamada ao Log4j2, o que pode demorar um pouco para acontecer e ainda custar mais tempo na realização de outro deploy.
Como no meu caso eu utilizo o Slf4j que é uma fachada para outras implementações de log tal como java.util.logging, logback, log4j, etc é necessário adicionar mais duas dependências:
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
<version>2.2</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.10</version>
</dependency>
Usando o Log4j2
Para usar basta declarar o seguinte atributo de classe:
private static final Logger logger = LogManager.getLogger(NomeDaClasse.class);
E usar da seguinte forma:
logger.debug("Teste");
Os imports para esse esse exemplo são:
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;
O Log4j2 possui os seguinte níveis de log: trace, debug, info, warn, error e fatal. Sendo trace o nível mais baixo e fatal o nível mais alto, esse um conceito importante, pois ao definir no arquivo de configuração o level de debug no Logger todo o log que estiver abaixo desse nível será ignorado. Exemplo level=”warn” faz com que os logs com logger.debug não sejam logados.
Para quem utiliza Slf4j o código é quase o mesmo e não muda nada em função de ser o Log4j2.
Até a próxima.