CodeGym /Java Blog /Random-IT /Logging: cosa, come, dove e con cosa?
John Squirrels
Livello 41
San Francisco

Logging: cosa, come, dove e con cosa?

Pubblicato nel gruppo Random-IT
Ciao a tutti nella community di CodeGym! Logging: cosa, come, dove e con cosa?  - 1 Oggi parliamo di registrazione:
  1. Cos'è, perché esiste, quando dovresti usarlo, quando dovresti evitarlo.
  2. Quali implementazioni di registrazione sono disponibili in Java e cosa dovresti fare con tutte queste opzioni di registrazione.
  3. E registra i livelli. Discuteremo cos'è l'appender e come configurarlo correttamente.
  4. Registrazione dei nodi e come configurarli correttamente in modo che tutto funzioni come vogliamo.
Questo materiale è destinato a un vasto pubblico. Sarà chiaro a chiunque abbia appena iniziato a conoscere Java, così come alle persone che stanno già lavorando ma hanno solo esplorato logger.info("log something"); Let's go!

Perché hai bisogno di accedere?

Diamo un'occhiata ad alcuni casi reali in cui la registrazione può risolvere un problema. Ecco un esempio dal mio lavoro. Ci sono punti in cui un'applicazione si integra con altri servizi. Uso il logging in questi punti per stabilire una sorta di "alibi" : se l'integrazione non funziona, diventa facile capire da che parte sta il problema. È anche auspicabile registrare le informazioni importanti memorizzate in un database. Ad esempio, la creazione di un utente amministratore. Questo è precisamente il genere di cose che sarebbe bene registrare.

Strumenti per l'accesso a Java

Tra le ben note soluzioni di registrazione in Java, possiamo evidenziare quanto segue:
  • Log4j
  • LUGLIO — java.util.logging
  • JCL — Jakarta Commons Logging
  • Logback
  • SLF4J — Facciata di registrazione semplice per Java
Forniremo una panoramica di ciascuno di essi. Quindi prenderemo un'associazione slf4j - log4j come base di una discussione pratica. Questo può sembrare strano ora, ma non preoccuparti: entro la fine dell'articolo sarà tutto chiaro.

System.err.println

All'inizio c'era System.err.println (che visualizzava le voci del registro sulla console). Ancora oggi, questa tecnica viene utilizzata per eseguire rapidamente un registro durante il debug. Naturalmente, non ci sono impostazioni da discutere qui, quindi ricorda solo questo metodo e andremo avanti.

Log4j

Questa è una soluzione completa che gli sviluppatori hanno creato per necessità. Il risultato è uno strumento davvero interessante che puoi usare. A causa di varie circostanze, questa soluzione non è finita nel JDK, un fatto che ha sconvolto molto l'intera comunità. Log4j ha opzioni di configurazione che ti consentono di abilitare la registrazione nel com.example.typepacchetto e disattivarla nel com.example.type.genericsottopacchetto. Ciò consente di escludere rapidamente il codice che non deve essere registrato. È importante notare qui che ci sono due versioni di Log4j: 1.2.xe 2.xx, e sono incompatibili tra loro . Log4j ha aggiunto i concetti di appender(uno strumento utilizzato per scrivere i log) e il layout (formattazione dei log). Ciò ti consente di registrare solo ciò di cui hai bisogno e di registrarlo proprio come ti serve. Parleremo di più di appender un po' più tardi.

LUGLIO — java.util.logging

Uno dei principali vantaggi di questa soluzione è che JUL è incluso nel JDK (Java Development Kit). Sfortunatamente, quando è stato sviluppato, i suoi creatori non si sono basati sulla popolare utility Log4j, ma piuttosto su una soluzione di IBM. Quella decisione ha avuto delle conseguenze. La realtà è che nessuno usa JUL ora. I livelli di log in JUL differiscono da quelli di Logback, Log4j e Slf4j. Questo rende più difficile per loro capirsi. La creazione di un logger è più o meno simile. Per fare ciò, è necessario eseguire un'importazione:

java.util.logging.Logger log = java.util.logging.Logger.getLogger(LoggingJul.class.getName());
Il nome della classe viene passato, quindi sappiamo da dove proverrà la nostra registrazione. A partire da Java 8, puoi passare Supplier<String>. Questo ci aiuta a leggere e creare una linea solo quando ne abbiamo veramente bisogno, piuttosto che ogni volta, come avveniva in precedenza. Solo con il rilascio di Java 8 gli sviluppatori hanno finalmente risolto problemi importanti e reso JUL veramente utilizzabile. Vale a dire, metodi con un Supplier<String> msgSupplierparametro, come mostrato di seguito:

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

JCL — Jakarta Commons Logging

Poiché per molto tempo non esisteva uno standard di settore per quanto riguarda la registrazione e molte persone hanno creato i propri logger personalizzati, è stata presa la decisione di rilasciare JCL, un wrapper generale che potrebbe essere utilizzato sopra altri. Perché? A volte le dipendenze aggiunte al progetto utilizzavano un logger diverso da quello nel progetto. Per questo motivo, sono state aggiunte al progetto come dipendenze transitive e questo ha creato seri problemi quando si cercava di mettere tutto insieme. Sfortunatamente, l'involucro non era molto funzionale e non aggiungeva nulla. Probabilmente sarebbe conveniente se tutti usassero JCL. Ma non è quello che è successo, quindi usare JCL non è la migliore idea al momento.

Logback

Il percorso open source è spinoso ... Lo stesso sviluppatore che ha scritto Log4j ha anche scritto Logback come framework di registrazione successore. Era basato sulla stessa idea di Log4j. Le differenze in Logback sono:
  • prestazione migliorata
  • aggiunto il supporto nativo per Slf4j
  • opzioni di filtraggio estese
Per impostazione predefinita, Logback non richiede alcuna configurazione e registra tutti gli eventi a livello DEBUG e superiore. Se hai bisogno di personalizzazione, puoi ottenerla tramite una configurazione 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 — Facciata di registrazione semplice per Java

Nel 2006, uno dei padri fondatori di Log4j ha lasciato il progetto e ha creato Slf4j (Simple Logging Facade for Java), un wrapper per Log4j, JUL, common-logging e Logback. Come puoi vedere, siamo arrivati ​​al punto di creare un wrapper sopra un wrapper... In questo caso, è diviso in due parti: un'API che viene utilizzata nell'applicazione e un'implementazione che viene aggiunta con dipendenze per ogni tipo di registrazione. Ad esempio, slf4j-log4j12.jare slf4j-jdk14.jar. Devi collegare l'implementazione corretta e il gioco è fatto: l'intero progetto lo utilizzerà. Slf4j supporta tutte le funzionalità più recenti, come la formattazione delle stringhe per la registrazione. In precedenza, c'era un tale problema. Supponiamo di creare una voce di log come questa:

log.debug("User " + user + " connected from " + request.getRemoteAddr());
A causa dell'operatore di concatenazione, l' useroggetto diventa silenziosamente una stringa grazie a user.toString(). Questo richiede tempo e rallenta il sistema. E potrebbe andare bene se stiamo eseguendo il debug dell'applicazione. Iniziamo a riscontrare problemi se il livello di registro per questa classe è INFO o superiore. In altre parole, non dovremmo scrivere questa voce di log (per INFO o superiore) e non dovremmo usare la concatenazione di stringhe. In teoria, la libreria di registrazione stessa dovrebbe risolvere questo problema. Si dà il caso che questo si sia rivelato il problema più grande nella prima versione di Log4j. Non ha fornito una soluzione decente, ma invece ha suggerito di fare qualcosa del genere:

if (log.isDebugEnabled()) {
    log.debug("User " + user + " connected from " + request.getRemoteAddr());
}
Cioè, invece di una riga di codice per la registrazione, hanno suggerito di scrivere 3! La registrazione dovrebbe ridurre al minimo le modifiche al codice e le tre righe violano chiaramente tale approccio generale. Slf4j non ha avuto problemi di compatibilità con JDK e API, quindi è emersa immediatamente una bella soluzione:

log.debug("User {} connected from {}", user, request.getRemoteAddr());
dove {}denota i segnaposto per gli argomenti passati al metodo. Cioè, il primo {}corrisponde a usere il secondo {}corrisponde a request.getRemoteAddr(). In questo modo, eseguiremo la concatenazione di stringhe solo se il livello di log ci richiede di scrivere la voce di log. Successivamente, Sjf4j ha iniziato a crescere rapidamente in popolarità. Al momento, è la soluzione migliore. Di conseguenza, diamo un'occhiata alla registrazione utilizzando slf4j-log4j12un'associazione.

Cosa deve essere registrato

Naturalmente, non dovresti registrare tutto. Questo spesso non è necessario e talvolta anche pericoloso. Ad esempio, se registri i dati personali di qualcuno e in qualche modo vengono trapelati, ci saranno problemi reali, specialmente nei progetti focalizzati sui mercati occidentali. Ma ci sono anche cose che dovresti assolutamente registrare :
  1. Inizio/fine dell'applicazione. Abbiamo bisogno di sapere se l'applicazione è veramente iniziata e terminata come previsto.
  2. Problemi di sicurezza. Qui sarebbe utile registrare i tentativi di indovinare la password di qualcuno, le istanze in cui gli amministratori accedono, ecc.
  3. Determinati stati dell'applicazione . Ad esempio, la transizione da uno stato all'altro in un processo aziendale.
  4. Determinate informazioni di debug insieme al livello di registro corrispondente.
  5. Alcuni script SQL. Ci sono casi reali in cui ciò è necessario. Ma ancora una volta, regolando abilmente i livelli del registro, puoi ottenere risultati eccellenti.
  6. I thread in esecuzione possono essere registrati quando si verifica che le cose funzionino correttamente.

Errori comuni nella registrazione

Ci sono molte sfumature qui, ma faremo menzione speciale di alcuni errori comuni:
  1. Registrazione eccessiva. Non dovresti registrare ogni passaggio che potrebbe teoricamente essere importante. Ecco una buona regola empirica: i log non devono superare il 10% del carico. Altrimenti, ci saranno problemi di prestazioni.
  2. Registrazione di tutti i dati in un unico file. Ad un certo punto, questo renderà molto difficile leggere/scrivere il registro, per non parlare del fatto che alcuni sistemi hanno limiti sulla dimensione del file.
  3. Utilizzo di livelli di registro errati. Ogni livello di log ha confini chiari e dovrebbero essere rispettati. Se un confine non è chiaro, puoi raggiungere un accordo su quale livello utilizzare.

Livelli di registro

x: Visibile
FATALE ERRORE AVVISARE INFORMAZIONI DEBUG TRACCIA TUTTO
SPENTO
FATALE X
ERRORE X X
AVVISARE X X X
INFORMAZIONI X X X X
DEBUG X X X X X
TRACCIA X X X X X X
TUTTO X X X X X X X
Cosa sono i livelli di registro? Per creare in qualche modo una gerarchia di voci di registro, sono necessarie determinate convenzioni e delimitazioni. Questo è il motivo per cui sono stati introdotti i livelli di registro. Il livello è impostato nell'applicazione. Se una voce è al di sotto di un livello specificato, non viene registrata. Ad esempio, abbiamo registri che utilizziamo durante il debug dell'applicazione. Durante il normale funzionamento (quando l'applicazione viene utilizzata per lo scopo previsto), tali registri non sono necessari. Pertanto, il livello di registro è superiore a quello per il debug. Diamo un'occhiata ai livelli di registro utilizzando Log4j. Oltre a JUL, altre soluzioni utilizzano gli stessi livelli di log. Eccoli in ordine decrescente:
  • OFF: non vengono registrate voci di registro; tutto viene ignorato.
  • IRREVERSIBILE: un errore che impedisce all'applicazione di continuare a funzionare. Ad esempio, "errore JVM di memoria insufficiente".
  • ERRORE: gli errori a questo livello indicano problemi che devono essere risolti. L'errore non interrompe l'intera applicazione. Altre richieste potrebbero funzionare correttamente.
  • AVVISO: voci di registro che rappresentano un avviso. È successo qualcosa di inaspettato, ma il sistema è stato in grado di far fronte e soddisfare la richiesta
  • INFORMAZIONI: voci di registro che indicano azioni importanti nell'applicazione. Questi non sono errori o avvisi. Sono eventi di sistema previsti.
  • DEBUG: le voci dei log devono eseguire il debug dell'applicazione. Per garantire che l'applicazione faccia esattamente ciò che ci si aspetta, o per descrivere le azioni intraprese dall'applicazione, ad es. "Metodo immesso1".
  • TRACE: voci di registro con priorità inferiore per il debug. Il livello di log più basso.
  • ALL: un livello di registro per la scrittura di tutte le voci di registro dell'applicazione.
Nel registro INFO il livello è abilitato da qualche parte nell'applicazione, quindi verranno registrate le voci per ogni livello, da INFO a FATAL. Se è impostato il livello di log FATAL, verranno scritte solo le voci di log con quel livello.

Registrazione e invio di registri: Appender

Consideriamo come funziona tutto questo quando utilizziamo Log4j, che offre ampie opportunità per la scrittura/l'invio di log:
  • scrivere su un file —DailyRollingFileAppender
  • per scrivere informazioni sulla console —ConsoleAppender
  • per scrivere i log in un database —JDBCAppender
  • per gestire l'invio di log su TCP/IP —TelnetAppender
  • per garantire che la registrazione non influisca negativamente sulle prestazioni —AsyncAppender
Ci sono alcune altre implementazioni: un elenco completo è disponibile qui . A proposito, se l'appender di cui hai bisogno non esiste, non c'è problema. Puoi scrivere il tuo appender implementando l' interfaccia Appender , supportata da Log4j.

Nodi di registrazione

A scopo dimostrativo, utilizzeremo un'interfaccia Slf4j, con un'implementazione da Log4j. La creazione di un logger è molto semplice: in una classe denominata MainDemo, che eseguirà alcuni logging, dobbiamo aggiungere quanto segue:

org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(MainDemo.class);
Questo creerà un logger per noi. Per creare una voce di registro, sono disponibili diversi metodi i cui nomi riflettono il livello di registro che verrà utilizzato. Per esempio:

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);
Anche se stiamo passando la classe, il nome finale è il nome completo della classe, compresi i pacchetti. Questo viene fatto in modo che in seguito sia possibile dividere la registrazione in nodi e configurare il livello di registrazione e l'appender per ciascun nodo. Ad esempio, il logger è stato creato nella com.github.romankh3.logginglecture.MainDemoclasse. Il nome fornisce la base per la creazione di una gerarchia di nodi di registrazione. Il nodo principale è RootLogger di primo livello . Questo è il nodo che riceve tutte le voci di registro per l'intera applicazione. I nodi rimanenti possono essere rappresentati come mostrato di seguito: Logging: cosa, come, dove e con cosa?  - 3Gli appender sono configurati per nodi di registrazione specifici. Ora esamineremo il file log4j.properties per vedere un esempio di come configurarli.

Una guida passo passo al file log4j.properties

Imposteremo tutto un passo alla volta e vedremo cosa è possibile:

log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
Questa riga indica che stiamo registrando l'appender CONSOLE, che utilizza l'implementazione org.apache.log4j.ConsoleAppender. Questo appender scrive le informazioni nella console. Successivamente, registriamo un altro appender. Questo scriverà in un file:

log4j.appender.FILE=org.apache.log4j.RollingFileAppender
È importante notare che gli appender stessi devono ancora essere configurati. Dopo aver registrato i nostri appender, possiamo determinare quali livelli di log e quali appender verranno utilizzati nei nodi.

log4j.rootLogger=DEBUG, CONSOLE, FILE

  • log4j.rootLogger significa che stiamo configurando il nodo radice, che contiene tutte le voci di registro
  • La prima parola dopo il segno di uguale indica il livello minimo di log da scrivere (nel nostro caso è DEBUG)
  • Dopo la virgola indichiamo tutti gli appendici da utilizzare.
Per configurare un nodo di registrazione più specifico, dovresti utilizzare una voce come questa:

log4j.logger.com.github.romankh3.logginglecture=TRACE, OWN, CONSOLE
dove log4j.logger.è usato per fare riferimento a un nodo specifico. Nel nostro caso, com.github.romankh3.logginglecture. ora parliamo della configurazione dell'appender 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
Qui vediamo che è possibile impostare il livello specifico al quale l'appender inizierà a funzionare. Ecco un esempio di ciò che effettivamente accade: supponiamo che un messaggio con il livello INFO venga ricevuto dal nodo di registrazione e passato all'appender ad esso assegnato. Se la soglia dell'appender è impostata su WARN, riceve la voce di registro ma non ne fa nulla. Successivamente, dobbiamo decidere quale layout utilizzerà il messaggio. Uso PatternLayout nell'esempio, ma ci sono molte altre opzioni. Non li tratteremo in questo articolo. Esempio di configurazione dell'appender 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
È possibile configurare il file specifico in cui verranno scritte le voci di registro, come si può vedere da questa riga:

log4j.appender.FILE.File=./target/logging/logging.log
La voce viene scritta nel logging.logfile. Per evitare problemi con la dimensione del file, puoi configurare il massimo, che in questo caso è 1 MB. MaxBackupIndexindica quanti di questi file di log ci saranno. Se dobbiamo creare più file di questo, il primo file verrà eliminato. Per vedere un esempio reale in cui è configurata la registrazione, puoi andare al repository pubblico su GitHub.

Rafforza ciò di cui abbiamo discusso

Prova da solo a fare tutto ciò che abbiamo descritto:
  • Crea il tuo progetto simile al nostro esempio sopra.
  • Se sai come usare Maven, usalo. In caso contrario, leggi questo tutorial, che descrive come connettere la libreria.

In sintesi

  1. Abbiamo parlato delle soluzioni di registrazione che esistono in Java.
  2. Quasi tutte le note librerie di logging sono state scritte da una sola persona :D
  3. Abbiamo imparato cosa dovrebbe e cosa non dovrebbe essere registrato.
  4. Abbiamo calcolato i livelli di log.
  5. Siamo stati introdotti ai nodi di registrazione.
  6. Abbiamo esaminato cos'è un appender e a cosa serve.
  7. Abbiamo creato un file log4j.proterties passo dopo passo.

Materiali aggiuntivi

  1. CodeGym: lezione sui logger
  2. Geekly settimanale: registrazione Java. Ciao mondo
  3. Coding Horror: il problema con la registrazione
  4. YouTube: Capire l'inferno di registrazione Java - Le basi. Java Logging Hell e come starne fuori
  5. Log4j: Appender
  6. Log4j: Disposizione
Vedi anche il mio altro articolo:
Commenti
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION