Um guia para desenvolvedores Java

Uma das partes mais cruciais do processo de desenvolvimento de software é ter um registro adequado. Com muitas estruturas de criação de log Java disponíveis, é importante escolher uma que seja fácil de usar. Ao mesmo tempo, sua estrutura de escolha deve ter alto desempenho, recursos extensíveis e permitir personalização. Log4j2 é uma biblioteca de registro Java gratuita que marca todas as caixas.

A integração do Log4j2 com qualquer aplicativo desbloqueia opções como filtragem avançada, suporte a Java 8 lambda, pesquisas de propriedade e níveis de log personalizados. Vamos dar uma olhada em como você pode adicionar o Log4j2 aos seus projetos e quais recursos podem ajudá-lo a ficar no topo do seu jogo.

O que é Log4j2?

Logging é o método de capturar informações úteis, conhecidas como logs, que podem ser referenciadas e analisadas posteriormente. Você pode usar os logs para depurar rapidamente o código do aplicativo. Os logs do aplicativo ajudam a entender o fluxo do código e a lidar com problemas e erros de produção.

Além dos casos de uso de diagnóstico, os logs também são usados ​​para fins de auditoria, por exemplo, rastrear se uma mensagem de notificação foi ou não enviada com êxito ao usuário.

Log4j2 é uma das bibliotecas de log Java mais populares. É um sucessor da muito influente biblioteca Log4j. Desenvolvido pela Apache Software Foundation e parte dos Apache Logging Services, o Log4j2 é um Software Livre e de Código Aberto (FOSS) distribuído sob a Licença Apache, versão 2.0.

Log4j2 é construído sobre a base sólida do Log4j original. Há vantagens em usar um Logger em vez de instruções de impressão simples de System.out.println(). Isso inclui ter controle sobre quais mensagens serão exibidas, evitando outras mensagens de log. Ter logs adequados é crucial em um ambiente de produção onde os depuradores não estão disponíveis.

Como adicionar o Log4j2 ao seu projeto?

Existem várias maneiras de adicionar Log4j2 ao seu projeto Java. É aconselhável estar no Java 8 ou superior para usar todos os recursos do Log4j2.

Vamos discutir os vários métodos pelos quais você pode adicionar Log4j2, dependendo dos requisitos que você possa ter.

Adicionando Log4j2 a projetos usando Apache Maven

Se seu projeto usa Apache Maven como o sistema de construção, as dependências do Log4j2 precisam 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 facilitar a manutenção da mesma versão em diferentes artefatos, o Log4j2 possui um arquivo pom.xml da lista de materiais (BOM). Se você adicioná-lo em seu gerenciamento de dependência, não precisará adicionar as versões individualmente.

<!-- Add the BOM to the 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>

<!-- Once the BOM is added, the versions are not required -->
<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ê use o Apache Gradle como sua ferramenta de construção, você pode adicionar as dependências do Log4j2 ao seu 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 no Gradle versão 5.0 ou superior, terá a opção de importar a lista de materiais Log4j2 Maven (BOM) para manter as versões de dependência consistentes. 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 Gradle 2.8-4.10, não há opção para importar diretamente o Maven BOM. Você precisa adicionar um plug-in adicional para 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 o seu projeto não tiver uma ferramenta de compilação, você poderá baixar a versão do artefato necessária do Log4j2 na página oficial de download do Log4j2.

Depois de baixá-los, você precisa garantir que o caminho de classe do seu aplicativo inclua os jars a seguir.

  • 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 utilizar seus recursos ao máximo, é importante entender como o Log4j2 funciona. Sob a superfície, vários blocos de construção compõem o Log4j2. Vamos falar sobre eles um por um.

#1. LoggerContext

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

#2. Configuração

A Configuração contém todas as informações exigidas pelo sistema de registro. Isso inclui os Loggers, Appenders, Filters e muito mais. No Log4j2, você pode definir a configuração usando vários formatos de arquivo, como XML, JSON e YAML, e também programaticamente por meio da API Log4j2.

Um recarregamento automático ocorre sempre que qualquer propriedade é alterada na Configuração. Portanto, não há necessidade de reiniciar o aplicativo.

#3. registrador

O principal componente do sistema Log4j2 é o Logger. Os loggers são obtidos dentro do 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 determinado Logger. Ele define o comportamento e as configurações para registrar eventos gerados por esse registrador específico. Permite a configuração de diferentes níveis de log, configuração de appenders e aplicação de filtros.

#5. Filtro

Você pode processar eventos de log seletivamente em Log4j2 usando filtros. Os filtros são aplicados com base em critérios específicos. Você pode aplicar esses filtros a registradores ou anexadores. Os filtros controlam quais eventos de log têm permissão para passar pelo pipeline de log para processamento posterior. Com a ajuda de filtros, o comportamento de log pode ser ajustado, garantindo que apenas os logs relevantes sejam processados.

#6. Anexador

O destino de qualquer mensagem de log é determinado pelo Appender. Um único Logger pode ter vários Appenders. Um evento de log será enviado a todos os Appenders para o Logger fornecido. O Log4j2 tem muitos anexadores pré-configurados. Por exemplo, ConsoleAppender é usado para registrar mensagens no console e FileAppender é usado para enviar mensagens para um arquivo. Cada Appender precisa de seu próprio Layout que determina como será a aparência da mensagem de log final.

#7. Disposição

No Log4j2, o Layout é usado para definir como será a aparência da mensagem de log final. Um Layout está associado a um Appender. Enquanto um Appender determina o destino de saída, o Layout descreve como a mensagem será enviada.

Os 5 principais recursos do Log4j2

O Log4j2 é rico em recursos e é isso que o diferencia de outras estruturas de log Java disponíveis. Desde ter registradores assíncronos até o suporte a Java 8 lambdas, o Log4j2 tem uma vantagem sobre os outros. Vamos discutir alguns dos recursos notáveis ​​dessa estrutura.

#1. Estendendo as funcionalidades usando Plugins

No Log4j 1.x, para criar extensões, muitas modificações de código foram necessárias. O Log4j2 resolve o problema de extensibilidade introduzindo o sistema Plugin.

Você pode declarar um novo Plugin usando a anotação @Plugin em sua classe. Usando o poder dos plug-ins, você pode criar seus próprios componentes, como filtros e anexos. Componentes de terceiros também podem ser facilmente adicionados à biblioteca.

#2. Suporte a Java 8 Lambda

Com o lançamento do Log4j2 versão 2.4, foi introduzido o suporte para expressões lambda Java 8. Com expressões lambda, você pode definir sua lógica de registro em linha. Isso reduz a necessidade de verificações de várias linhas ou classes internas anônimas. Isso também garante que métodos caros não sejam executados desnecessariamente. Assim, o código não é apenas mais limpo e fácil de ler, mas também reduz a sobrecarga do sistema.

Vamos considerar um exemplo em que você registra o resultado de uma operação cara, mas apenas se o nível de depuração estiver ativado. Antes do suporte para lambdas, isso seria executado usando o código abaixo mencionado:

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

Ter vários desses casos de uso introduziria desnecessariamente verificações condicionais. No entanto, com o Log42, a mesma ação pode ser executada da seguinte maneira:

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

O método exprensiveOperation() só é avaliado se o nível de depuração estiver habilitado. Não há necessidade de verificações explícitas.

#3. Registradores assíncronos

Cada evento de log é uma operação de E/S, o que aumenta a sobrecarga do sistema. Para mitigar isso, o Log4j2 introduz registradores assíncronos que são executados em um encadeamento separado do encadeamento do aplicativo. Ao usar registradores assíncronos, o encadeamento do chamador recupera imediatamente o controle após invocar o método logger.log().

Isso permite que ele continue com a lógica do aplicativo em vez de esperar que o evento de log seja concluído. Aproveitar esse comportamento assíncrono atinge uma taxa de transferência de registro maior. Você pode optar por tornar todos os loggers assíncronos por padrão ou ter uma mistura de comportamento síncrono e assíncrono.

#4. Log sem lixo

Em Java, a coleta de lixo é o processo pelo qual os objetos não utilizados no aplicativo são automaticamente limpos. Embora você não precise cuidar manualmente dessa operação, a coleta de lixo tem sua própria sobrecarga.

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

Desde a versão 2.6, o Log4j2 é executado no modo “livre de lixo”. Este é o comportamento padrão. Assim, os objetos são reutilizados e a criação de objetos temporários é fortemente reduzida.

As imagens a seguir mostram como o Log4j2 versão 2.6 atenua 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, não há objetos temporários criados durante o processo de registro; Fonte: apache.org

#5. Pesquisas

No log4j2, você pode adicionar informações contextuais aos seus logs usando Lookups. Ao utilizá-los, você pode adicionar dados de várias fontes, como propriedades do sistema, variáveis ​​de ambiente ou valores personalizados. Assim, você pode incluir informações relevantes que são buscadas dinamicamente, tornando os logs mais úteis.

Vamos considerar um exemplo em que você deseja registrar o ID da sessão do usuário com todas as linhas de log. Isso permitiria que você procurasse todos os logs correspondentes a um ID de sessão.

A maneira de força bruta de fazer isso seria adicionar explicitamente o ID da sessão individualmente, o que se torna difícil de manter. Em breve você pode esquecer de adicioná-lo, perdendo assim 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 maneira melhor de fazer isso seria usar a pesquisa de mapa de contexto. O ID da sessão pode ser adicionado ao Contexto do encadeamento no código do aplicativo. O valor pode então ser usado dentro da configuração do Log4j2. Assim, a necessidade de mencioná-lo explicitamente nas mensagens de log é eliminada.

ThreadContext.put("sessionId", sessionId);

Uma vez adicionado o valor, o mesmo pode ser utilizado no Lookup utilizando 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 usados ​​para categorizar eventos de log com base em sua gravidade ou importância. Você pode controlar o nível de log ao registrar uma mensagem no código do aplicativo.

Por exemplo, logger.debug() adiciona o nível DEBUG. Correspondentemente, logger.error() adiciona o nível ERROR. Isso determina quais mensagens finalmente aparecem na saída. Você pode configurar o nível de log no arquivo de configuração.

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

OFF0FATAL100ERROR200WARN300INFO400DEBUG500TRACE600ALLMAX VALUE

Se o nível de log for definido para um determinado nível, todas as linhas de log para esse valor correspondente e as acima dele (com valor menor) serão geradas. Os outros são ignorados.

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

Talvez você queira definir o nível de log como INFO ou DEBUG ao executar o código em seu ambiente de desenvolvimento. Isso permitirá que você veja mais logs e ajude no processo de desenvolvimento. No entanto, ao executar em um ambiente de produção, convém defini-lo como ERROR. Assim, você poderá se concentrar em encontrar o problema caso ocorra alguma anomalia e não terá que passar por linhas de log desnecessárias.

Pode acontecer que você queira adicionar seu próprio nível de log personalizado além dos pré-configurados. Log4j2 permite que você faça isso facilmente. Vamos ver como você pode adicionar seus próprios níveis de log e usá-los em seu aplicativo.

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

Você pode adicionar níveis de log personalizados declarando-os no arquivo de configuração.

No exemplo abaixo, um nível de log personalizado chamado NOTICE foi definido com um valor de 450. Isso o coloca entre INFO (com um valor de 400) e DEBUG (com um valor de 500). Isso significa que, se o nível for 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ê pode definir seus próprios níveis de log personalizados em seu código.

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

Isso criará um novo nível de log chamado VERBOSE. Este nível de log ficará entre DEBUG (com um valor de 500) e TRACE (com um valor de 600). Se o logger estiver definido para o nível VERBOSE, todas as mensagens de log de VERBOSE e acima serão registradas, incluindo DEBUG. No entanto, as mensagens TRACE serão ignoradas.

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

Os níveis de log personalizados primeiro precisam ser declarados antes de serem usados. Você pode declará-los no arquivo de configuração ou no seu código. Uma vez declarados, você está livre para usá-los.

Este exemplo de código mostra como você pode declarar um nível personalizado chamado NOTICE e, em seguida, usar o mesmo.

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 sempre passar o nível explicitamente. Felizmente, você pode gerar o código-fonte para obter métodos auxiliares para registrar seus níveis personalizados. Usando o mesmo, você poderá usar seu próprio método de logger.notice() semelhante a como usaria logger.debug() ou logger.error().

O Log4j2 vem com um utilitário que ajuda você a criar seus próprios loggers estendidos. O comando a seguir cria um arquivo Java chamado CustomLogger.java. Este arquivo contém os métodos de log existentes, juntamente com os 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 usar a classe em seu código para criar novos loggers. Esses loggers conterão métodos adicionais para seu nível de log personalizado. Assim, você pode estender a funcionalidade de seus registradores.

final Logger logger = CustomLogger.create(ValueFirstSmsSender.class);

//this new method is similar to using logger.debug()
logger.notice("a notice level message");

Conclusão

Log4j2 é uma estrutura de log Java muito poderosa, que oferece uma ampla gama de recursos, configurações, melhorias de desempenho e muito mais. Como os logs são uma parte muito importante do processo de desenvolvimento de software, ter uma estrutura robusta como Log4j2 aprimora as habilidades do aplicativo.

A flexibilidade e extensibilidade do Log4j2 permitem a captura adequada de eventos que ocorrem em seu aplicativo. Posteriormente, ele permite que você pense nos logs como uma ferramenta poderosa para depuração e auditoria. Com todos os seus recursos e melhorias, o Log4j2 se destaca e se torna a escolha preferida em uma ampla gama de projetos de software.

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