Nel contesto di uno dei nostri progetti, ci siamo trovati di fronte a molti casi di debug. I semplici messaggi nei log non sono sempre sufficienti. Per esempio, può essere necessario avere più informazioni sull’utente che ha eseguito l’azione (il suo IP, i suoi permessi, il suo identificatore, …). Nel nostro caso, la nostra applicazione è stata costruita da diversi microservizi e volevamo identificare con precisione il flusso seguito da una richiesta che passa da un microservizio all’altro. A questo scopo, un identificatore unico è stato generato e visualizzato nei log di ogni applicazione web. Questo ci ha aiutato molto a risolvere i problemi con le applicazioni di terze parti che stavamo usando.
- Come registrare i messaggi?
- Quali informazioni di contesto aggiungere ai nostri messaggi?
- È possibile aggiungere queste informazioni quando si eseguono thread asincroni?
Questo articolo vi aiuterà a costruire un’applicazione Spring Boot Java per registrare i messaggi con Log4j e utilizzare l’MDC di questa libreria (Mapping Diagnostic Context) per aggiungere dati di contestualizzazione oltre ai messaggi, in particolare per le attività asincrone.
Iniziamo a generare una classica applicazione Spring Boot con la libreria Log4j integrata. Questa libreria ci permette di utilizzare un logger che genera messaggi di log di diverso tipo (info, error, warning, …)
- Su Spring Initializr (https://start.spring.io/), costruiamo un progetto Spring Boot di base senza alcuna dipendenza.
- Modifichiamo il file pom.xml per aggiungere le dipendenze necessarie per utilizzare la libreria Log4j
- Creiamo il file src/main/resources/log4j2.xml che definisce il formato per i futuri messaggi di log
pom.xml
src/main/resources/log4j2.xml
Visualizza il nostro primo messaggio di log
Nello stato attuale, se lanciamo l’applicazione (tramite un IDE per esempio o usando Maven), non appare alcun messaggio di log. Creeremo un componente che usa la libreria Log4j per registrare un messaggio informativo.
Creiamo un file src/main/java/com/sipios/example/mdc/Execute.java con il codice seguente. Il nome del pacchetto è qui com.sipios.example.mdc, naturalmente, dovrebbe essere sostituito dal vostro 🙂
src/main/java/com/sipios/example/mdc/Execute.java
Se si esegue l’applicazione, viene ora visualizzato un primo messaggio di log che rispetta il formato definito. È anche possibile utilizzare i metodi error
e warning
. Verranno visualizzati i messaggi di log corrispondenti a questi tipi.
Utilizzare MDC (Mapping Diagnostic Context) nel vostro log
Ora che sappiamo come usare la libreria Log4j, potremo utilizzare il Mapping Diagnostic Context (MDC) che ci permette di associare un dato Map al nostro messaggio. Alcuni esempi di dati che vi consigliamo di inserire nell’MDC:
- Dati della sessione corrente (utente, query, richiesta …)
- Metriche sull’esecuzione del processo (tempo iniziale e tempo di esecuzione, …)
- Pezzi di informazione sulla versione dell’applicazione
- …
Questa mappa viene visualizzata nei log se la maschera %X
è usata nella definizione del formato del messaggio Log4j. Questo è il caso qui nel nostro file src/main/resources/log4j2.xml. Nell’esecuzione precedente, vediamo {}
, che indica una Map vuota.
L’utilizzo di MDC è molto semplice e si usa come una Map classica tramite i metodi put
, get
, remove
, clear
… Aggiungiamo due voci all’MDC ed eseguiamo l’applicazione.
src/main/java/com/sipios/example/mdc/Execute.java
MDC è globale e si conserva finché non viene modificato. Se volete svuotarlo lasciando un componente per esempio, basta usare il metodo clear
. Questo darebbe quindi il seguente risultato.
Come funziona con i componenti asincroni?
Proviamo MDC con i componenti asincroni (un componente eseguito su un thread parallelo al thread principale)! Prima di tutto, dobbiamo configurare la nostra applicazione per eseguire tali fagioli. Creiamo un servizio con due metodi, uno sincrono e l’altro asincrono.
- Aggiungi l’annotazione @EnableAsync alla classe Application
- Crea un servizio con un metodo normale e uno @Async
- Modifica il componente per iniettare e usare i metodi del servizio
- Lancia l’applicazione
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
Aggiungi taskExecutor nella classe Application
src/main/java/com/sipios/example/mdc/Application.java
Re-execute :
Come possiamo vedere, MDC in thread async è vuoto. Infatti ogni compito asincrono è avviato in un nuovo thread. Ogni thread è collegato a una Map MDC avviata dall’esecutore del compito. È possibile giocare su questo esecutore per ottenere lo stesso MDC del thread principale. Aggiungiamo un decoratore in asyncExecutor per duplicare l’MDC!
src/main/java/com/sipios/example/mdc/ExampleTaskDecorator.java
Imposta questo decoratore nella configurazione asincrona
src/main/java/com/sipios/example/mdc/Application.java
Rilancia l’applicazione
Ecco fatto! Possiamo trasmettere nei log tutto il contesto che vogliamo nei compiti sincroni o asincroni.
Grazie a questo, il debug di un’applicazione è semplificato e più comprensibile. Nell’ambito del nostro progetto questo ci ha fatto risparmiare molto tempo negli scambi con i nostri collaboratori. Ora tocca a voi 🙂
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/