CodeGym /Blog Java /Aleatoriu /Înregistrare: ce, cum, unde și cu ce?
John Squirrels
Nivel
San Francisco

Înregistrare: ce, cum, unde și cu ce?

Publicat în grup
Salutare tuturor din comunitatea CodeGym! Înregistrare: ce, cum, unde și cu ce?  - 1 Astăzi să vorbim despre logare:
  1. Ce este, de ce există, când ar trebui să-l folosești, când ar trebui să-l eviți.
  2. Ce implementări de înregistrare sunt disponibile în Java și ce ar trebui să faceți cu toate aceste opțiuni de înregistrare.
  3. Și niveluri de jurnal. Vom discuta ce este apendicele și cum să-l configurați corect.
  4. Nodurile de logare și cum să le configurați corect, astfel încât totul să funcționeze așa cum ne dorim.
Acest material este destinat unui public larg. Va fi clar pentru oricine care tocmai cunoaște Java, precum și pentru cei care lucrează deja, dar care au explorat doar logger.info("log something"); Să mergem!

De ce ai nevoie de logare?

Să ne uităm la câteva cazuri reale în care înregistrarea poate rezolva o problemă. Iată un exemplu din munca mea. Există puncte în care o aplicație se integrează cu alte servicii. Folosesc înregistrarea în aceste puncte pentru a stabili un fel de „alibi” : dacă integrarea nu funcționează, atunci devine ușor să ne dăm seama de ce parte are problema. De asemenea, este de dorit să înregistrați informații importante stocate într-o bază de date. De exemplu, crearea unui utilizator admin. Acesta este exact genul de lucru pe care ar fi bine să îl logați.

Instrumente pentru autentificare în Java

Dintre soluțiile de logare binecunoscute în Java, putem evidenția următoarele:
  • Log4j
  • IUL — java.util.logging
  • JCL — Jakarta Commons Logging
  • Logback
  • SLF4J — Fațadă de logare simplă pentru Java
Vom oferi o privire de ansamblu asupra fiecăruia dintre ele. Apoi vom lua o legare slf4j - log4j ca bază a unei discuții practice. Acest lucru poate părea ciudat acum, dar nu vă faceți griji: până la sfârșitul articolului, totul va fi clar.

System.err.println

La început, a existat System.err.println (afișează intrările de jurnal pe consolă). Chiar și astăzi, această tehnică este folosită pentru a realiza rapid un jurnal la depanare. Desigur, nu există setări de discutat aici, așa că amintiți-vă de această metodă și vom merge mai departe.

Log4j

Aceasta este o soluție completă pe care dezvoltatorii au creat-o din necesitate. Rezultatul este un instrument cu adevărat interesant pe care îl puteți folosi. Din diverse circumstanțe, această soluție nu a ajuns în JDK, fapt care a supărat foarte mult întreaga comunitate. Log4j are opțiuni de configurare care vă permit să activați conectarea în com.example.typepachet și să o dezactivați în com.example.type.genericsubpachet. Acest lucru face posibilă excluderea rapidă a codului care nu trebuie înregistrat. Este important de reținut aici că există două versiuni de Log4j: 1.2.x și 2.xx și sunt incompatibile între ele . Log4j a adăugat conceptele de appender(un instrument folosit pentru a scrie jurnalele) și aspectul (formatarea jurnalelor). Acest lucru vă permite să vă înregistrați doar ceea ce aveți nevoie și să îl înregistrați exact așa cum aveți nevoie. Vom vorbi mai multe despre apendice puțin mai târziu.

IUL — java.util.logging

Unul dintre avantajele cheie ale acestei soluții este că JUL este inclus în JDK (Java Development Kit). Din păcate, atunci când a fost dezvoltat, creatorii săi nu l-au bazat pe popularul utilitar Log4j, ci mai degrabă pe o soluție de la IBM. Decizia a avut consecințe. Realitatea este că nimeni nu folosește JUL acum. Nivelurile de jurnal din JUL diferă de ceea ce au Logback, Log4j și Slf4j. Acest lucru le face mai greu să se înțeleagă între ei. Crearea unui logger este mai mult sau mai puțin similară. Pentru a face acest lucru, trebuie să faceți un import:

java.util.logging.Logger log = java.util.logging.Logger.getLogger(LoggingJul.class.getName());
Numele clasei este transmis, așa că știm de unde va veni înregistrarea noastră. Începând cu Java 8, puteți trece Supplier<String>. Acest lucru ne ajută să citim și să creăm o linie doar atunci când avem cu adevărat nevoie de ea, mai degrabă decât de fiecare dată, așa cum a fost cazul anterior. Numai odată cu lansarea Java 8, dezvoltatorii au rezolvat în sfârșit probleme importante și au făcut JUL cu adevărat utilizabil. Și anume, metode cu un Supplier<String> msgSupplierparametru, după cum se arată mai jos:

public void info(Supplier<String> msgSupplier) {
   log(Level.INFO, msgSupplier);
}

JCL — Jakarta Commons Logging

Deoarece nu a existat un standard industrial în ceea ce privește înregistrarea de mult timp și mulți oameni și-au creat propriile lor loggere personalizate, a fost luată decizia de a lansa JCL, un wrapper general care ar putea fi folosit peste altele. De ce? Uneori, dependențele adăugate la proiect au folosit un logger diferit de cel din proiect. Din această cauză, au fost adăugate la proiect ca dependențe tranzitive, iar acest lucru a creat probleme reale atunci când încercam să le adunăm pe toate. Din păcate, ambalajul nu a fost foarte funcțional și nu a adăugat nimic. Probabil că ar fi convenabil dacă toată lumea ar folosi JCL. Dar nu asta sa întâmplat, așa că folosirea JCL nu este cea mai bună idee în acest moment.

Logback

Calea open-source este spinoasă... Același dezvoltator care a scris Log4j a scris și Logback ca un cadru de logare succesor. S-a bazat pe aceeași idee ca Log4j. Diferențele în Logback sunt:
  • performanta imbunatatita
  • a adăugat suport nativ pentru Slf4j
  • opțiuni de filtrare extinse
În mod implicit, Logback nu necesită nicio configurație și înregistrează toate evenimentele la nivel DEBUG și mai sus. Dacă aveți nevoie de personalizare, o puteți obține printr-o configurație XML:

<configuration> 
    <appender name="FILE" class="ch.qos.logback.core.FileAppender"> 
        <file>app.log</file> 
        <encoder> 
            <pattern>%d{HH:mm:ss,SSS} %-5p [%c] - %m%n</pattern> 
        </encoder> 
    </appender> 
    <logger name="org.hibernate.SQL" level="DEBUG" /> 
    <logger name="org.hibernate.type.descriptor.sql" level="TRACE" /> 
    <root level="info"> 
        <appender-ref ref="FILE" /> 
    </root> 
</configuration>

SLF4J — Fațadă de logare simplă pentru Java

Cândva în 2006, unul dintre părinții fondatori ai Log4j a părăsit proiectul și a creat Slf4j (Simple Logging Facade for Java), un wrapper pentru Log4j, JUL, common-logging și Logback. După cum puteți vedea, am avansat până la punctul de a crea un wrapper peste un wrapper... În acest caz, acesta este împărțit în două părți: un API care este utilizat în aplicație și o implementare care este adăugată separat dependențe pentru fiecare tip de înregistrare. De exemplu, slf4j-log4j12.jarși slf4j-jdk14.jar. Trebuie să conectați implementarea corectă și gata: întregul dvs. proiect o va folosi. Slf4j acceptă toate cele mai recente caracteristici, cum ar fi formatarea șirurilor pentru înregistrare. Anterior, a existat o astfel de problemă. Să presupunem că creăm o intrare de jurnal ca aceasta:

log.debug("User " + user + " connected from " + request.getRemoteAddr());
Datorită operatorului de concatenare, userobiectul devine în tăcere un șir datorită user.toString(). Acest lucru necesită timp și încetinește sistemul. Și asta poate fi OK dacă depanăm aplicația. Începem să întâlnim probleme dacă nivelul de jurnal pentru această clasă este INFO sau mai mare. Cu alte cuvinte, nu ar trebui să scriem această intrare de jurnal (pentru INFO sau mai mult) și nu ar trebui să folosim concatenarea șirurilor. În teorie, biblioteca de jurnal în sine ar trebui să abordeze acest lucru. După cum se întâmplă, aceasta sa dovedit a fi cea mai mare problemă în prima versiune a Log4j. Nu a oferit o soluție decentă, ci a sugerat să faceți ceva de genul acesta:

if (log.isDebugEnabled()) {
    log.debug("User " + user + " connected from " + request.getRemoteAddr());
}
Adică, în loc de o linie de cod pentru înregistrare, ei au sugerat să scrie 3! Înregistrarea ar trebui să minimizeze modificările codului, iar cele trei linii încalcă în mod clar această abordare generală. Slf4j nu a avut probleme de compatibilitate cu JDK și API, așa că a apărut imediat o soluție frumoasă:

log.debug("User {} connected from {}", user, request.getRemoteAddr());
unde {}denotă substituenți pentru argumentele transmise metodei. Adică primul {}corespunde user, iar al doilea {}corespunde request.getRemoteAddr(). Făcând acest lucru, vom efectua concatenarea șirurilor doar dacă nivelul jurnalului ne cere să scriem intrarea în jurnal. După aceea, Sjf4j a început să crească rapid în popularitate. În prezent, este cea mai bună soluție. În consecință, să aruncăm o privire la înregistrarea utilizând o slf4j-log4j12legare.

Ce trebuie înregistrat

Desigur, nu ar trebui să înregistrați totul. Acest lucru nu este adesea necesar și uneori chiar periculos. De exemplu, dacă înregistrezi datele personale ale cuiva și se scurg cumva, vor fi probleme reale, mai ales în proiectele axate pe piețele occidentale. Dar există și lucruri pe care cu siguranță ar trebui să le înregistrezi :
  1. Începutul/sfârșitul aplicației. Trebuie să știm dacă aplicația a început și s-a încheiat într-adevăr conform așteptărilor.
  2. Probleme de securitate. Aici ar fi bine să înregistrați încercările de a ghici parola cuiva, cazurile în care administratorii se conectează etc.
  3. Anumite stări de aplicare . De exemplu, trecerea de la o stare la alta într-un proces de afaceri.
  4. Anumite informații de depanare împreună cu nivelul de jurnal corespunzător.
  5. Anumite scripturi SQL. Există cazuri din lumea reală când acest lucru este necesar. Dar din nou, prin ajustarea cu pricepere a nivelurilor de jurnal, puteți obține rezultate excelente.
  6. Firele care rulează pot fi înregistrate atunci când se verifică dacă lucrurile funcționează corect.

Erori populare în înregistrare

Există multe nuanțe aici, dar vom menționa în mod special câteva greșeli comune:
  1. Înregistrare excesivă. Nu ar trebui să înregistrați fiecare pas care teoretic ar putea fi important. Aici o regulă de bază bună: Buștenii nu trebuie să depășească 10% din încărcătură. În caz contrar, vor apărea probleme de performanță.
  2. Înregistrarea tuturor datelor într-un singur fișier. La un moment dat, acest lucru va face foarte dificilă citirea/scrierea jurnalului, ca să nu mai vorbim de faptul că anumite sisteme au limite în ceea ce privește dimensiunea fișierului.
  3. Utilizarea nivelurilor de jurnal incorecte. Fiecare nivel de jurnal are limite clare și trebuie respectate. Dacă o limită este neclară, puteți ajunge la un acord cu privire la ce nivel să utilizați.

Niveluri de jurnal

x: Vizibil
FATAL EROARE A AVERTIZA INFO DEBUG URMĂ TOATE
OFF
FATAL X
EROARE X X
A AVERTIZA X X X
INFO X X X X
DEBUG X X X X X
URMĂ X X X X X X
TOATE X X X X X X X
Ce sunt nivelurile de jurnal? Pentru a crea cumva o ierarhie a intrărilor de jurnal, sunt necesare anumite convenții și delimitări. Acesta este motivul pentru care au fost introduse nivelurile de jurnal. Nivelul este setat în aplicație. Dacă o intrare este sub un nivel specificat, atunci nu este înregistrată. De exemplu, avem jurnalele pe care le folosim la depanarea aplicației. În timpul funcționării normale (când aplicația este utilizată în scopul propus), astfel de jurnale nu sunt necesare. Prin urmare, nivelul jurnalului este mai mare decât pentru depanare. Să ne uităm la nivelurile de jurnal folosind Log4j. În afară de JUL, alte soluții folosesc aceleași niveluri de jurnal. Iată-le în ordine descrescătoare:
  • OFF: Nu sunt înregistrate intrări în jurnal; totul este ignorat.
  • FATAL: O eroare care împiedică aplicația să continue să ruleze. De exemplu, „Eroare JVM fără memorie”.
  • EROARE: Erorile la acest nivel indică probleme care trebuie rezolvate. Eroarea nu oprește aplicația în ansamblu. Alte solicitări pot funcționa corect.
  • AVERTISMENT: intrări de jurnal care reprezintă un avertisment. S-a întâmplat ceva neașteptat, dar sistemul a fost capabil să facă față și a îndeplinit cererea
  • INFO: intrări de jurnal care indică acțiuni importante în aplicație. Acestea nu sunt erori sau avertismente. Sunt evenimente de sistem așteptate.
  • DEBUG: intrările de jurnal trebuie să depaneze aplicația. Pentru a se asigura că aplicația face exact ceea ce se așteaptă sau pentru a descrie acțiunile întreprinse de aplicație, adică „Metoda introdusă1”.
  • TRACE: intrări de jurnal cu prioritate inferioară pentru depanare. Cel mai scăzut nivel de jurnal.
  • ALL: Un nivel de jurnal pentru scrierea tuturor intrărilor de jurnal ale aplicației.
În jurnalul INFO nivelul este activat undeva în aplicație, apoi intrările pentru fiecare nivel vor fi înregistrate, de la INFO la FATAL. Dacă este setat nivelul de jurnal FATAL, vor fi scrise numai intrările de jurnal cu acel nivel.

Înregistrarea și trimiterea jurnalelor: Appender

Să luăm în considerare cum funcționează toate acestea atunci când folosim Log4j, care oferă oportunități ample de scriere/trimitere a jurnalelor:
  • a scrie într-un fișier -DailyRollingFileAppender
  • pentru a scrie informații pe consolă -ConsoleAppender
  • pentru a scrie jurnalele într-o bază de date -JDBCAppender
  • pentru a gestiona trimiterea jurnalelor prin TCP/IP —TelnetAppender
  • pentru a vă asigura că înregistrarea nu are un impact negativ asupra performanței —AsyncAppender
Mai sunt câteva implementări: o listă completă este disponibilă aici . Apropo, dacă aplicația de care aveți nevoie nu există, nu este nicio problemă. Puteți scrie propriul dvs. appender prin implementarea interfeței Appender , pe care Log4j o acceptă.

Noduri de logare

În scop demonstrativ, vom folosi o interfață Slf4j, cu o implementare de la Log4j. Crearea unui logger este foarte simplă: într-o clasă numită MainDemo, care va face unele înregistrări, trebuie să adăugăm următoarele:

org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(MainDemo.class);
Acest lucru va crea un logger pentru noi. Pentru a face o intrare în jurnal, există mai multe metode disponibile ale căror nume reflectă nivelul de jurnal care va fi utilizat. De exemplu:

logger.trace("Method 1 started with argument={}", argument);
logger.debug("Database updated with script = {}", script);
logger.info("Application has started on port = {}", port);
logger.warn("Log4j didn't find the log4j.properties file. Please fix this.");
logger.error("Connection refused to host = {}", host);
Deși trecem clasa, numele final este numele complet al clasei, inclusiv pachetele. Acest lucru se face astfel încât ulterior să puteți împărți înregistrarea în noduri și să configurați nivelul de înregistrare și anexa pentru fiecare nod. De exemplu, loggerul a fost creat în com.github.romankh3.logginglecture.MainDemoclasă. Numele oferă baza pentru crearea unei ierarhii de noduri de înregistrare. Nodul principal este RootLogger de nivel superior . Acesta este nodul care primește toate intrările de jurnal pentru întreaga aplicație. Nodurile rămase pot fi descrise după cum se arată mai jos: Înregistrare: ce, cum, unde și cu ce?  - 3Apendicele sunt configurate pentru anumite noduri de înregistrare. Acum ne vom uita la fișierul log4j.properties pentru a vedea un exemplu de configurare a acestora.

Un ghid pas cu pas pentru fișierul log4j.properties

Vom configura totul pe rând și vom vedea ce este posibil:

log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
Această linie spune că înregistrăm aplicația CONSOLE, care utilizează implementarea org.apache.log4j.ConsoleAppender. Acest anexat scrie informații în consolă. În continuare, înregistrăm un alt apendice. Acesta va scrie într-un fișier:

log4j.appender.FILE=org.apache.log4j.RollingFileAppender
Este important de reținut că anexele în sine trebuie încă configurate. Odată ce ne-am înregistrat anexele, putem determina ce niveluri de jurnal și ce anexe vor fi folosite la noduri.

log4j.rootLogger=DEBUG, CONSOLĂ, FIȘIER

  • log4j.rootLogger înseamnă că configurăm nodul rădăcină, care conține toate intrările de jurnal
  • Primul cuvânt după semnul egal indică nivelul minim de jurnal de scris (în cazul nostru, este DEBUG)
  • După virgulă, indicăm toți anexele care vor fi folosite.
Pentru a configura un nod de înregistrare mai specific, ați folosi o intrare ca aceasta:

log4j.logger.com.github.romankh3.logginglecture=TRACE, OWN, CONSOLE
unde log4j.logger.este folosit pentru a face referire la un anumit nod. În cazul nostru, com.github.romankh3.logginglecture. acum să vorbim despre configurarea aplicației CONSOLE:

# CONSOLE appender customization
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.CONSOLE.threshold=DEBUG
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
log4j.appender.CONSOLE.layout.ConversionPattern=[%-5p] : %c:%L : %m%n
Aici vedem că este posibil să setăm nivelul specific la care va începe să lucreze aplicatorul. Iată un exemplu de ceea ce se întâmplă de fapt: să presupunem că un mesaj cu nivelul INFO este primit de nodul de logare și transmis aplicatorului care îi este atribuit. Dacă pragul aplicatorului este setat la WARN, atunci acesta primește intrarea în jurnal, dar nu face nimic cu ea. Apoi, trebuie să decidem ce aspect va folosi mesajul. Folosesc PatternLayout în exemplu, dar există multe alte opțiuni. Nu le vom acoperi în acest articol. Exemplu de configurare a anexei FILE:

# File appender customization
log4j.appender.FILE=org.apache.log4j.RollingFileAppender
log4j.appender.FILE.File=./target/logging/logging.log
log4j.appender.FILE.MaxFileSize=1MB
log4j.appender.FILE.threshold=DEBUG
log4j.appender.FILE.MaxBackupIndex=2
log4j.appender.FILE.layout=org.apache.log4j.PatternLayout
log4j.appender.FILE.layout.ConversionPattern=[ %-5p] - %c:%L - %m%n
Puteți configura fișierul specific în care vor fi scrise intrările de jurnal, așa cum se poate vedea din această linie:

log4j.appender.FILE.File=./target/logging/logging.log
Intrarea este scrisă în logging.logfișier. Pentru a evita probleme cu dimensiunea fișierului, puteți configura maximul, care în acest caz este de 1MB. MaxBackupIndexindică câte astfel de fișiere jurnal vor exista. Dacă trebuie să creăm mai multe fișiere decât acestea, atunci primul fișier va fi șters. Pentru a vedea un exemplu real în care este configurată înregistrarea în jurnal, puteți merge la depozitul public de pe GitHub.

Întăriți ceea ce am discutat

Încercați pe cont propriu să faceți tot ce am descris:
  • Creați-vă propriul proiect similar cu exemplul nostru de mai sus.
  • Dacă știi să folosești Maven, folosește-l. Dacă nu, atunci citiți acest tutorial, care descrie cum să conectați biblioteca.

În concluzie

  1. Am vorbit despre soluțiile de logare care există în Java.
  2. Aproape toate bibliotecile de logare binecunoscute au fost scrise de o singură persoană :D
  3. Am învățat ce ar trebui și ce nu trebuie înregistrat.
  4. Ne-am dat seama de nivelurile de jurnal.
  5. Am fost introduși în nodurile de logare.
  6. Ne-am uitat la ce este un apendice și pentru ce este.
  7. Am creat un fișier log4j.proterties pas cu pas.

Materiale suplimentare

  1. CodeGym: lecție de logger
  2. Weekly Geekly: Înregistrare Java. Salut Lume
  3. Codificare Horror: Problema cu înregistrarea
  4. YouTube: Înțelegerea iadului Java Logging - Elementele de bază. Java Logging Hell și cum să rămâi departe de el
  5. Log4j: Anexă
  6. Log4j: Aspect
Vezi și celălalt articol al meu:
Comentarii
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION