În contextul unuia dintre proiectele noastre, ne-am confruntat cu multe cazuri de depanare. Simplele mesaje din jurnale nu sunt întotdeauna suficiente. De exemplu, poate fi necesar să avem mai multe informații despre utilizatorul care a executat acțiunea (IP-ul său, permisiunile sale, identificatorul său, …). În cazul nostru, aplicația noastră a fost construită din mai multe microservicii și am dorit să identificăm cu precizie fluxul urmat de o cerere care trece de la un microserviciu la altul. În acest scop, a fost generat un identificator unic care a fost afișat în jurnalele fiecărei aplicații web. Acest lucru ne-a ajutat foarte mult să rezolvăm problemele cu aplicațiile terțe pe care le foloseam.
- Cum să înregistrăm mesajele în jurnal?
- Ce informații de context să adăugăm în mesajele noastre?
- Pot fi adăugate aceste informații atunci când se execută fire de execuție asincrone?
Acest articol vă va ajuta să construiți o aplicație Spring Boot Java pentru a înregistra mesaje cu Log4j și să folosiți MDC-ul acestei biblioteci (Mapping Diagnostic Context) pentru a adăuga date de contextualizare în plus față de mesaje, în special pentru sarcinile asincrone.
Să începem prin a genera o aplicație Spring Boot clasică cu biblioteca Log4j încorporată. Această bibliotecă ne permite să folosim un logger care generează mesaje de jurnal de diferite tipuri (info, error, warning, …)
- Pe Spring Initializr (https://start.spring.io/), construiți un proiect Spring Boot de bază fără dependențe.
- Editați fișierul pom.xml pentru a adăuga dependențele necesare pentru a utiliza biblioteca Log4j
- Crearea fișierului src/main/resources/log4j2.xml care definește formatul pentru viitoarele mesaje de jurnal
pom.xml
src/main/resources/log4j2.xml
Afișează primul nostru mesaj de jurnal
În starea actuală, dacă lansăm aplicația (prin intermediul unui IDE, de exemplu, sau folosind Maven), nu apare niciun mesaj de jurnal. Vom crea o componentă care utilizează biblioteca Log4j pentru a înregistra un mesaj de informare.
Creăm un fișier src/main/java/com/sipios/example/mdc/Execute.java cu codul de mai jos. Numele pachetului este aici com.sipios.example.mdc, bineînțeles, trebuie înlocuit cu al dumneavoastră 🙂
src/main/java/com/sipios/example/mdc/Execute.java
Dacă rulați aplicația, acum este afișat un prim mesaj de jurnal care respectă formatul definit. De asemenea, este posibil să se utilizeze metodele error
și warning
. Vor fi afișate mesajele de jurnal care corespund acestor tipuri.
Utilizați MDC (Mapping Diagnostic Context) în jurnalul dumneavoastră
Acum că știm cum să folosim biblioteca Log4j, vom putea utiliza Mapping Diagnostic Context (MDC) care ne permite să asociem o hartă de date cu mesajul nostru. Câteva exemple de date pe care vă recomandăm să le puneți în MDC:
- Date ale sesiunii curente (utilizator, interogare, cerere …)
- Metrice despre execuția procesului (timpul inițial și timpul de execuție, …)
- Piețe de informații despre versiunea aplicației
- …
Această hartă este afișată în jurnale dacă se utilizează masca %X
în definirea formatului mesajului Log4j. Acesta este cazul aici, în fișierul nostru src/main/resources/log4j2.xml. În execuția anterioară, vedem {}
, ceea ce indică o Mapă goală.
Utilizarea MDC este foarte simplă și se utilizează ca o Mapă clasică prin metodele put
, get
, remove
, clear
… Să adăugăm două intrări în MDC și să executăm aplicația.
src/main/java/com/sipios/exemple/mdc/Execute.java
MDC este global și se păstrează atâta timp cât nu este modificat. Dacă doriți să îl goliți, părăsind o componentă, de exemplu, este suficient să utilizați metoda clear
. Aceasta ar da atunci următorul rezultat.
Cum funcționează cu componente asincrone?
Să încercăm MDC cu componente asincrone (o componentă executată pe un fir paralel cu firul principal)! Mai întâi de toate, trebuie să ne configurăm aplicația pentru a executa astfel de boabe. Creăm un serviciu cu două metode, una este sincronă și cealaltă asincronă.
- Adaugăm adnotarea @EnableAsync la clasa Application
- Creăm un serviciu cu o metodă normală și una @Async
- Modificăm componenta pentru a injecta și utiliza metodele serviciului
- Lansăm aplicația
src/main/java/com/sipios/exemple/mdc/Application.java
src/main/java/com/sipios/example/mdc/service/Example.java
src/main/java/com/sipios/example/mdc/Execute.java
Adaugați taskExecutor în clasa Application
src/main/java/com/sipios/example/mdc/Application.java
Reexecutați din nou :
După cum putem vedea, MDC în firul asincron este gol. Într-adevăr, fiecare sarcină asincronă este inițiată într-un fir nou. Fiecare fir este legat de un Map MDC inițiat de către executorul sarcinii. Este posibil să se joace pe acest executor pentru a obține același MDC ca și pe firul principal. Să adăugăm un decorator în asyncExecutor pentru a duplica MDC!
src/main/java/com/sipios/example/mdc/ExampleTaskDecorator.java
Setați acest decorator în configurația asincronă
src/main/java/com/sipios/example/mdc/Application.java
Relaunch application
Iată! Putem transmite în jurnale tot contextul pe care îl dorim în sarcini sincrone sau asincrone.
Grație acestui fapt, depanarea unei aplicații este simplificată și mai ușor de înțeles. În cadrul proiectului nostru, acest lucru ne-a economisit mult timp în schimburile cu colaboratorii noștri. Depinde de voi acum 🙂
Bibliografie
- 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/
.