Domine o Log4j2: Guia Completo para Desenvolvedores Java

No intrincado mundo do desenvolvimento de software, a manutenção de um registro detalhado é crucial. Em meio a uma variedade de frameworks de logging Java, a escolha de uma ferramenta intuitiva torna-se essencial. Simultaneamente, essa ferramenta deve apresentar alto desempenho, recursos expansíveis e capacidade de personalização. O Log4j2, uma biblioteca de logging Java gratuita, atende a todos esses critérios.

Ao integrar o Log4j2 em qualquer aplicação, você desbloqueia uma série de possibilidades, como filtragem avançada, suporte a lambdas Java 8, pesquisas de propriedades e níveis de log personalizados. Vamos explorar como incorporar o Log4j2 em seus projetos e como seus recursos podem otimizar seu trabalho.

O que é Log4j2?

O logging é um método para capturar informações valiosas, conhecidas como logs, que podem ser referenciadas e analisadas posteriormente. Esses logs são vitais para a depuração eficiente do código do aplicativo, auxiliando na compreensão do fluxo do código e na resolução de problemas de produção.

Além do diagnóstico, os logs também são cruciais para auditorias, como o rastreamento do envio bem-sucedido de mensagens de notificação ao usuário.

O Log4j2 se destaca como uma das bibliotecas de logging Java mais populares, sendo o sucessor do influente Log4j. Desenvolvido pela Apache Software Foundation e parte do Apache Logging Services, o Log4j2 é um software de código aberto (FOSS) distribuído sob a Licença Apache, versão 2.0.

O Log4j2 foi construído sobre a base sólida do Log4j original. A utilização de um Logger em vez de simples instruções System.out.println() oferece vantagens significativas. Isso inclui o controle sobre as mensagens exibidas e a prevenção de outras mensagens de log. Em um ambiente de produção, onde os depuradores não estão disponíveis, logs adequados são essenciais.

Como adicionar o Log4j2 ao seu projeto?

Existem várias abordagens para incorporar o Log4j2 em seu projeto Java. Recomenda-se estar no Java 8 ou superior para usufruir de todos os recursos do Log4j2.

A seguir, discutiremos os diferentes métodos pelos quais você pode adicionar o Log4j2, de acordo com suas necessidades específicas.

Adicionando Log4j2 a projetos usando Apache Maven

Se seu projeto utiliza o Apache Maven como sistema de construção, as dependências do Log4j2 devem ser adicionadas ao arquivo pom.xml.

    <dependencies>
    <dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-api</artifactId>
    <version>2.20.0</version>
    </dependency>
    <dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-core</artifactId>
    <version>2.20.0</version>
    </dependency>
    </dependencies>
    

Para simplificar a manutenção da mesma versão em diferentes artefatos, o Log4j2 oferece um arquivo pom.xml da lista de materiais (BOM). Ao adicioná-lo ao gerenciamento de dependência, não há necessidade de inserir as versões individualmente.

    <!-- Adicione o BOM ao dependencyManagement -->
    <dependencyManagement>
    <dependencies>
    <dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-bom</artifactId>
    <version>2.20.0</version>
    <scope>import</scope>
    <type>pom</type>
    </dependency>
    </dependencies>
    </dependencyManagement>
    
    <!-- Uma vez que o BOM é adicionado, as versões não são mais necessárias -->
    <dependencies>
    <dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-api</artifactId>
    </dependency>
    <dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-core</artifactId>
    </dependency>
    </dependencies>
    

Adicionando Log4j2 a projetos usando Apache Gradle

Caso você utilize o Apache Gradle como ferramenta de construção, é possível adicionar as dependências do Log4j2 ao arquivo build.gradle.

    dependencies {
    implementation 'org.apache.logging.log4j:log4j-api:2.20.0'
    implementation 'org.apache.logging.log4j:log4j-core:2.20.0'
    }
    

Se você estiver utilizando o Gradle versão 5.0 ou superior, há a opção de importar a lista de materiais (BOM) do Log4j2 Maven para garantir a consistência das versões de dependência. Isso pode ser feito adicionando o seguinte ao seu arquivo build.gradle.

    dependencies {
    implementation platform('org.apache.logging.log4j:log4j-bom:2.20.0')
    
    implementation 'org.apache.logging.log4j:log4j-api'
    runtimeOnly 'org.apache.logging.log4j:log4j-core'
    }
    

Para as versões do Gradle de 2.8 a 4.10, não há opção para importar diretamente o Maven BOM. É necessário adicionar um plugin adicional para obter a funcionalidade de gerenciamento de dependência.

    plugins {
    id 'io.spring.dependency-management' version '1.0.15.RELEASE'
    }
    
    dependencyManagement {
    imports {
    mavenBom 'org.apache.logging.log4j:log4j-bom:2.20.0'
    }
    }
    
    dependencies {
    implementation 'org.apache.logging.log4j:log4j-api'
    runtimeOnly 'org.apache.logging.log4j:log4j-core'
    }
    

Adicionando Log4j2 a aplicativos independentes sem uma ferramenta de compilação

Se seu projeto não utiliza uma ferramenta de compilação, é possível baixar a versão necessária do artefato do Log4j2 na página oficial de download do Log4j2.

Após o download, certifique-se de que o caminho de classe do seu aplicativo inclua os seguintes jars:

  • log4j-api-2.20.0.jar
  • log4j-core-2.20.0.jar

Quais são os componentes do Log4j2?

Para entender os recursos do Log4j2 e utilizá-los ao máximo, é importante conhecer seu funcionamento. Vários blocos de construção compõem o Log4j2. Vamos explorá-los um a um.

#1. LoggerContext

O LoggerContext é a unidade central do sistema de logging. Ele contém todos os Loggers solicitados na aplicação e também mantém uma referência ao arquivo de Configuração.

#2. Configuração

A Configuração engloba todas as informações exigidas pelo sistema de logging, como Loggers, Appenders, Filters e outros. No Log4j2, a configuração pode ser definida por meio de arquivos em formatos como XML, JSON e YAML, ou programaticamente via API Log4j2.

Ocorre um recarregamento automático sempre que uma propriedade é alterada na Configuração, eliminando a necessidade de reinicializar o aplicativo.

#3. Logger

O Logger é o principal componente do sistema Log4j2. Os loggers são obtidos no código do aplicativo usando a instrução LogManager.getLogger() e são usados para gerar logs. As mensagens de log podem ser geradas em vários níveis de gravidade, como depuração, informações, aviso, erro e fatal.

#4. LoggerConfig

O LoggerConfig é responsável pelo comportamento de um Logger específico. Ele define as configurações para registrar eventos gerados por esse logger, permitindo a configuração de níveis de log, appenders e filtros.

#5. Filtro

Os filtros permitem o processamento seletivo de eventos de log no Log4j2, sendo aplicados com base em critérios específicos a loggers ou appenders. Os filtros controlam quais eventos de log passam pelo pipeline para processamento posterior, possibilitando o ajuste do comportamento de logging e garantindo que apenas logs relevantes sejam processados.

#6. Appender

O Appender determina o destino de uma mensagem de log. Um único Logger pode ter vários Appenders, e cada evento de log é enviado a todos os Appenders do Logger correspondente. O Log4j2 oferece diversos appenders pré-configurados, como ConsoleAppender, usado para registrar mensagens no console, e FileAppender, utilizado para enviar mensagens a um arquivo. Cada Appender requer seu próprio Layout, que define a aparência da mensagem de log final.

#7. Layout

O Layout é responsável por definir a aparência da mensagem de log final. Um Layout está associado a um Appender. Enquanto o Appender define o destino da saída, o Layout descreve como a mensagem será enviada.

Os 5 principais recursos do Log4j2

O Log4j2 se destaca por sua riqueza de recursos que o diferenciam de outros frameworks de logging Java. Desde loggers assíncronos até suporte a lambdas Java 8, o Log4j2 possui uma vantagem sobre seus concorrentes. Vamos explorar alguns dos recursos notáveis ​​deste framework.

#1. Estendendo as funcionalidades usando Plugins

No Log4j 1.x, a criação de extensões exigia diversas modificações no código. O Log4j2 resolve esse problema de extensibilidade com a introdução do sistema Plugin.

Você pode declarar um novo Plugin usando a anotação @Plugin em sua classe, o que permite a criação de seus próprios componentes, como filtros e anexos, além da adição facilitada de componentes de terceiros à biblioteca.

#2. Suporte a Java 8 Lambda

Com o lançamento da versão 2.4 do Log4j2, foi introduzido o suporte a expressões lambda Java 8. As expressões lambda possibilitam a definição da lógica de logging em linha, reduzindo a necessidade de verificações de várias linhas ou classes internas anônimas. Isso garante que métodos complexos não sejam executados desnecessariamente, resultando em código mais limpo e fácil de ler, além de reduzir a sobrecarga do sistema.

Por exemplo, considere um cenário onde você registra o resultado de uma operação custosa apenas se o nível de depuração estiver ativo. Antes do suporte a lambdas, isso seria feito da seguinte forma:

    if (logger.isDebugEnabled()) {
    logger.debug("The output of the given operation is: {}", expensiveOperation());
    }
    

A necessidade de várias verificações condicionais seria introduzida em vários desses cenários. No entanto, com o Log4j2, a mesma ação pode ser realizada de forma mais eficiente:

    logger.debug("The output of the given operation is: {}", () -> expensiveOperation()
    

O método exprensiveOperation() só é avaliado se o nível de depuração estiver ativo, eliminando a necessidade de verificações explícitas.

#3. Loggers Assíncronos

Cada evento de log representa uma operação de E/S, o que aumenta a sobrecarga do sistema. Para mitigar esse problema, o Log4j2 introduz loggers assíncronos que operam em um thread separado do thread da aplicação. Ao usar loggers assíncronos, o thread que chama recupera o controle imediatamente após invocar o método logger.log().

Isso permite que o thread continue com a lógica da aplicação em vez de esperar que o evento de log seja concluído. Essa abordagem assíncrona aumenta a taxa de transferência de logging. Você tem a opção de definir todos os loggers como assíncronos por padrão ou de ter uma combinação de comportamento síncrono e assíncrono.

#4. Log sem Lixo

Em Java, a coleta de lixo é um processo no qual os objetos não utilizados no aplicativo são automaticamente limpos. Embora você não precise gerenciar essa operação manualmente, a coleta de lixo impõe uma sobrecarga.

Se seu aplicativo cria muitos objetos em um curto período de tempo, o processo de coleta de lixo pode consumir mais recursos do sistema do que o necessário. Várias bibliotecas de logging, incluindo versões anteriores do Log4j, criam muitos objetos temporários durante o processo de logging. Isso resulta em um aumento da pressão no coletor de lixo, que impacta o desempenho do sistema.

Desde a versão 2.6, o Log4j2 opera no modo “sem lixo”, o que se tornou o comportamento padrão. Isso garante que os objetos sejam reutilizados e que a criação de objetos temporários seja reduzida significativamente.

As imagens a seguir ilustram como o Log4j2 versão 2.6 resolve o problema de objetos desnecessários em comparação com o Log4j2 versão 2.5.

No Log4j2 versão 2.5, muitos objetos temporários são criados durante o processo de registro; Fonte: apache.org

No Log4j2.6, nenhum objeto temporário é criado durante o processo de logging; Fonte: apache.org

#5. Pesquisas

No Log4j2, as Pesquisas (Lookups) permitem adicionar informações contextuais aos seus logs, incluindo dados de fontes diversas, como propriedades do sistema, variáveis de ambiente ou valores personalizados. Isso permite incorporar informações relevantes, que são recuperadas dinamicamente, tornando os logs mais úteis.

Por exemplo, considere o cenário onde você deseja registrar o ID da sessão do usuário com cada linha de log. Isso facilitaria a pesquisa de todos os logs correspondentes a um determinado ID de sessão.

A abordagem tradicional seria adicionar explicitamente o ID da sessão individualmente, o que seria difícil de manter, podendo levar ao esquecimento da adição e à perda de informações valiosas.

    logger.info("The user data has been fetched for session id {}", sessionId);
    ...
    logger.info("The transaction has been processed for session id {}", sessionId);
    ...
    logger.info("Request has been successfully processed for session id {}", sessionId);
    

Uma abordagem mais eficiente seria usar a pesquisa de mapa de contexto. O ID da sessão pode ser adicionado ao Contexto do thread no código do aplicativo, e o valor pode ser utilizado dentro da configuração do Log4j2, eliminando a necessidade de mencioná-lo explicitamente nas mensagens de log.

    ThreadContext.put("sessionId", sessionId);
    

Uma vez adicionado o valor, o mesmo pode ser utilizado na Lookup usando a palavra-chave ctx.

    <File name="Application" fileName="application.log">
    <PatternLayout>
    <pattern>%d %p %c{1.} [%t] $${ctx:sessionId} %m%n</pattern>
    </PatternLayout>
    </File>
    

Como fazer níveis de log personalizados no Log4j2?

Os níveis de log no Log4j2 são utilizados para categorizar eventos de log com base em sua gravidade ou importância. É possível controlar o nível de log ao registrar uma mensagem no código da aplicação.

Por exemplo, logger.debug() adiciona o nível DEBUG, enquanto logger.error() adiciona o nível ERROR. Isso determina quais mensagens são exibidas na saída, e o nível de log pode ser configurado no arquivo de configuração.

Os níveis de log pré-configurados no Log4j2 e seus valores correspondentes são mencionados abaixo.

OFF0FATAL100ERROR200WARN300INFO400DEBUG500TRACE600ALLMAX VALUE

Se o nível de log estiver definido em um determinado valor, todas as linhas de log desse valor e de valores inferiores (com valores menores) serão exibidas. As outras serão ignoradas.

Por exemplo, se você definir o nível de log como WARN, as mensagens WARN, ERROR e FATAL serão exibidas. Qualquer linha de log com nível diferente será ignorada. Isso é útil quando você está executando o mesmo código em ambientes diferentes.

Você pode desejar definir o nível de log como INFO ou DEBUG ao executar o código em seu ambiente de desenvolvimento para ver mais logs e auxiliar no processo de desenvolvimento. No entanto, ao executar em um ambiente de produção, pode ser preferível defini-lo como ERROR para focar na solução de problemas em caso de anomalias e evitar linhas de log desnecessárias.

Você pode desejar adicionar seu próprio nível de log personalizado além dos predefinidos, e o Log4j2 permite que isso seja feito facilmente. Vamos explorar como adicionar e utilizar seus próprios níveis de log em sua aplicação.

#1. Adicionando nível de log personalizado usando o arquivo de configuração

Você pode adicionar níveis de log personalizados ao declará-los no arquivo de configuração.

No exemplo abaixo, um nível de log personalizado chamado NOTICE foi definido com o valor 450, posicionando-o entre INFO (valor 400) e DEBUG (valor 500). Isso significa que, se o nível estiver definido como NOTICE, as mensagens INFO serão registradas, mas as mensagens DEBUG serão ignoradas.

    <?xml version="1.0" encoding="UTF-8"?>
    <Configuration>
    <CustomLevels>
    <CustomLevel name="NOTICE" intLevel="450" />
    </CustomLevels>
    
    <Appenders>
    <File name="MyFile" fileName="logs/app.log">
    <PatternLayout pattern="%d %-7level %logger{36} - %msg%n"/>
    </File>
    </Appenders>
    <Loggers>
    <Root level="trace">
    <AppenderRef ref="MyFile" level="NOTICE" />
    </Root>
    </Loggers>
    </Configuration>
    

#2. Adicionando nível de log personalizado no código

Além de declará-los no arquivo de configuração, você também pode definir seus próprios níveis de log personalizados no seu código.

    final Level VERBOSE = Level.forName("VERBOSE", 550);
    

Isso criará um novo nível de log chamado VERBOSE, que ficará entre DEBUG (valor 500) e TRACE (valor 600). Se o logger estiver definido no nível VERBOSE, todas as mensagens de log VERBOSE e superiores serão registradas, incluindo DEBUG, mas as mensagens TRACE serão ignoradas.

#3. Usando o nível de log personalizado no código

Os níveis de log personalizados precisam ser declarados antes de serem usados, seja no arquivo de configuração ou no código. Depois de declarados, você pode utilizá-los.

Este exemplo de código mostra como declarar um nível personalizado chamado NOTICE e como utilizá-lo.

    final Level NOTICE = Level.forName("NOTICE", 550);
    
    final Logger logger = LogManager.getLogger();
    logger.log(NOTICE, "a notice level message");
    

Embora isso gere a mensagem necessária com o nível recém-criado, pode ser complicado ter que passar o nível explicitamente. Felizmente, é possível gerar o código-fonte para obter métodos auxiliares para registrar seus níveis personalizados. Assim, você poderá usar seu próprio método logger.notice() da mesma forma que usaria logger.debug() ou logger.error().

O Log4j2 inclui um utilitário que facilita a criação de seus próprios loggers estendidos. O comando abaixo cria um arquivo Java chamado CustomLogger.java, contendo os métodos de log existentes, além de métodos recém-gerados para o nível NOTICE.

    java -cp log4j-core-2.20.0.jar org.apache.logging.log4j.core.tools.ExtendedLoggerGenerator
    com.example.CustomLogger NOTICE=450 > com/example/CustomLogger.java
    

Depois que o arquivo é gerado, você pode utilizar a classe no seu código para criar novos loggers, que incluirão métodos adicionais para seu nível de log personalizado. Dessa forma, você pode estender a funcionalidade de seus loggers.

    final Logger logger = CustomLogger.create(ValueFirstSmsSender.class);
    
    //este novo método é semelhante ao uso de logger.debug()
    logger.notice("a notice level message");
    

Conclusão

O Log4j2 é um framework de logging Java muito poderoso, que oferece uma ampla gama de recursos, configurações, melhorias de desempenho e outros benefícios. Como os logs desempenham um papel crucial no processo de desenvolvimento de software, um framework robusto como o Log4j2 aprimora as capacidades da aplicação.

A flexibilidade e a extensibilidade do Log4j2 permitem a captura adequada de eventos que ocorrem no seu aplicativo, possibilitando a utilização dos logs como uma ferramenta poderosa para depuração e auditoria. Com seus recursos e melhorias, o Log4j2 se destaca como a escolha preferida em diversos projetos de software.

Você também pode estar interessado nestes IDEs Java e compiladores online.