No contexto de um dos nossos projectos, fomos confrontados com muitos casos de depuração. As mensagens simples nos logs nem sempre são suficientes. Por exemplo, pode ser necessário ter mais informações sobre o usuário que executou a ação (seu IP, suas permissões, seu identificador, …). No nosso caso, a nossa aplicação foi construída a partir de vários microserviços e quisemos identificar com precisão o fluxo seguido por uma solicitação que passa de um microserviço para outro. Para este fim, um identificador único foi gerado e exibido nos logs de cada aplicação web. Isso nos ajudou muito a resolver problemas com aplicações de terceiros que estávamos usando.
- Como registrar mensagens?
- Que informação de contexto adicionar às nossas mensagens?
- Pode esta informação ser adicionada ao executar threads assíncronos?
Este artigo irá ajudá-lo a construir uma aplicação Spring Boot Java para registar mensagens com o Log4j e usar o MDC desta biblioteca (Mapping Diagnostic Context) para adicionar dados de contextualização para além das mensagens, especialmente para tarefas assíncronas.
Deixe começar por gerar uma aplicação Spring Boot clássica com a biblioteca Log4j incorporada. Esta biblioteca permite-nos usar um logger que gera mensagens de log de diferentes tipos (info, error, warning, …)
- On Spring Initializr (https://start.spring.io/), construir um projecto básico de Spring Boot sem quaisquer dependências.
- Editar o ficheiro pom.xml para adicionar as dependências necessárias para usar a biblioteca Log4j
- Criar o src/main/resources/log4j2.Arquivo xml que define o formato para futuras mensagens de log
pom.xml
src/main/resources/log4j2.xml
Exibir nossa primeira mensagem de log
No estado atual, se iniciarmos a aplicação (via IDE por exemplo ou usando Maven), nenhuma mensagem de log aparecerá. Vamos criar um componente que usa a biblioteca Log4j para registrar uma mensagem de informação.
Criar um arquivo src/main/java/com/sipios/example/mdc/Execute.java com o código abaixo. O nome do pacote está aqui com.sipios.example.mdc, claro, ele deve ser substituído pelo seu 🙂
src/main/java/com/sipios/example/mdc/Execute.java
Se você executar a aplicação, uma primeira mensagem de log respeitando o formato definido é agora exibida. Também é possível usar os métodos error
e warning
. As mensagens de log correspondentes a estes tipos serão exibidas.
Utilizar o MDC (Mapping Diagnostic Context) no seu log
Agora sabemos como usar a biblioteca Log4j, poderemos usar o Mapping Diagnostic Context (MDC) que nos permite associar um mapa de dados à nossa mensagem. Alguns exemplos de dados que recomendamos que você coloque no MDC:
- Dados da sessão atual (usuário, consulta, solicitação …)
- Métricas sobre a execução do processo (tempo inicial e tempo de execução, …)
- Pieces of information about the version of the application
- …
Este Mapa é exibido nos logs se a máscara %X
for usada na definição do formato da mensagem Log4j. Este é o caso aqui no nosso arquivo src/main/resources/log4j2.xml. Na execução anterior, vemos {}
, que indica um Mapa vazio.
Usar o MDC é muito simples e é usado como um Mapa clássico via put
, get
, remove
, clear
métodos… Vamos adicionar duas entradas ao MDC e executar o aplicativo.
src/main/java/com/sipios/example/mdc/Execute.java
MDC é global e é preservado desde que não seja modificado. Se você quiser esvaziá-lo deixando um componente, por exemplo, basta usar o método clear
. Isto dará então o seguinte resultado.
Como funciona com componentes assíncronos?
Tentemos o MDC com componentes assíncronos (um componente executado em uma rosca paralela da rosca principal)! Antes de mais nada, temos de configurar a nossa aplicação para executar tais feijões. Nós criamos um serviço com dois métodos, um é síncrono e o outro assíncrono.
- Adicionar a anotação @EnableAsync à classe Application
- Criar um serviço com um método normal e um @Async
- Modificar o componente para injetar e usar os métodos do serviço
- Lançar a aplicação
src/main/java/com/sipios/example/mdc/Application.java
src/main/java/com/sipios/example/mdc/service/Example.java
src/main/java/com/sipios/example/mdc/Execute.java
Adicionar tarefaExecutor na classe de aplicação
src/main/java/com/sipios/example/mdc/Application.java
Reexecutar :
Como podemos ver, o MDC na linha async está vazio. Na verdade, cada tarefa assíncrona é iniciada em uma nova thread. Cada thread é ligada a um MDC de mapa iniciado pelo executor da tarefa. É possível tocar neste executor para obter o mesmo MDC que na thread principal. Vamos adicionar um decorador no asyncExecutor para duplicar o MDC!
src/main/java/com/sipios/example/mdc/ExampleTaskDecorator.java
Configurar este decorador em async config
src/main/java/com/sipios/example/mdc/Application.java
Relaunch application
There you go! Podemos transmitir nos logs todo o contexto que queremos em tarefas síncronas ou assíncronas.
Alguns agradecimentos, a depuração de uma aplicação é simplificada e mais compreensível. Como parte do nosso projecto isto poupou-nos muito tempo nas trocas com os nossos colaboradores. Agora é com você 🙂
Bibliografia
- http://www.baeldung.com/mdc-in-log4j-2-logback
- https://spring.io/guides/gs/async-method/
- https://moelholm.com/2017/07/24/spring-4-3-using-a-taskdecorator-to-copy-mdc-data-to-async-threads/