Benvenuti al Tutorial di Esempio di Apache Log4j2. Se chiedi a uno sviluppatore esperto quale sia la cosa più fastidiosa di un’applicazione, la risposta potrebbe essere legata al logging. Se non c’è un logging adeguato in un’applicazione, la manutenzione diventerà un incubo. La maggior parte delle applicazioni passa attraverso test di sviluppo, test di unità, test di integrazione. Ma quando si tratta di produzione, ti troverai sempre di fronte a scenari e eccezioni uniche. Quindi l’unico modo per capire cosa è successo in un caso specifico è fare il debug attraverso i log. Molti framework forniscono in qualche modo un logging predefinito, ma è sempre meglio seguire il meccanismo di logging standard del settore. Apache Log4j è uno dei framework di logging più ampiamente utilizzati. Apache Log4j 2 è la versione successiva, che è molto migliore di Log4j.
Esempio di Log4j
In questo Tutorial di Esempio di Log4j2, imparerai come iniziare con Apache Log4j2. Esploreremo anche l’architettura di Log4j2, la configurazione di log4j2, i livelli di logging di log4j2, gli appenders, i filtri e molto altro ancora.
- Panoramica di Log4j2
- Architettura di Log4j2
- Configurazione di Log4j2
- Livelli di Log4j2
- Ricerche di Log4j2
- Appenders di Log4j2
- Filtri di Log4j2
- Layout di Log4j2
- Quale livello di Log4j2 dovresti usare
- Sommario del Tutorial di Log4j2
Panoramica di Log4j2
Utilizzare l’API di logging nell’applicazione non è un lusso, ma è un must. Log4j è una libreria open source pubblicata e concessa in licenza sotto Apache Software. È possibile eseguire il debug di un’applicazione utilizzando il debug di Eclipse o altri strumenti, ma ciò non è sufficiente e fattibile in un ambiente di produzione. Il meccanismo di logging fornirà diversi vantaggi che non troverai nel normale debugging.
Category / Operation (Debugging, Logging) | Debugging | Logging |
---|---|---|
Human Intervention | There’s a need for human intervention | No need for human intervention |
Persistent Medium | Can’t be integrated with persistent storage | Can be integrated with persistent storage (Files, Database, NoSQL database, etc.) |
May used for Auditing | Can’t be used for achieving auditing | Can be used for achieving auditing if it’s used efficiently |
Sufficient for complicated structure and flow | Not sufficient; you may get lost with flow. | Sufficient |
Productivity | Less productive | More productive |
Come puoi vedere sopra, l’utilizzo del meccanismo di logging sarà più efficiente con minor costo di manutenzione. Apache Log4j è lo strumento leader per il logging nelle applicazioni Java, quindi dovresti utilizzarlo.
Architettura di Log4j2
Prima di procedere con il tutorial sull’esempio di Log4j, è utile dare un’occhiata all’architettura di Log4j2. Nell’immagine sottostante sono mostrate le classi importanti nell’API di Log4j2. Ecco la spiegazione dettagliata dell’architettura mostrata sopra:
-
Le applicazioni chiederanno a
LogManager
unLogger
con un nome specifico. -
LogManager
troverà ilLoggerContext
appropriato e quindi otterrà ilLogger
da esso. -
Se il Logger non è ancora stato creato, verrà creato e associato a LoggerConfig secondo tre scelte di seguito:
- L’istanza del Logger verrà creata e associata al LoggerConfig che ha lo stesso nome. Ad esempio, App.class in
getLogger(App.class)
sarà valutato come una stringacom.journaldev.App
. Il nome di LoggerConfig è identico al nome della classe completamente qualificato (componente software). - L’istanza del Logger verrà creata e associata al LoggerConfig che ha lo stesso pacchetto genitore dei Loggers. Ad esempio,
com.journaldev
ingetLogger("com.journaldev")
- L’istanza del Logger verrà creata e associata al Root LoggerConfig. Il Root LoggerConfig verrà utilizzato quando non c’è un file di configurazione o quando si sta ottenendo un logger con un nome non definito nelle dichiarazioni dei logger.
- L’istanza del Logger verrà creata e associata al LoggerConfig che ha lo stesso nome. Ad esempio, App.class in
-
Gli oggetti
LoggerConfig
vengono creati dalla dichiarazione del Logger nel file di configurazione. LoggerConfig viene anche utilizzato per gestire gliLogEvents
e delegarli ai loro Log4j2 Appenders definiti. -
Il logger Root è un caso eccezionale, per quanto riguarda la sua esistenza. Esiste sempre e si trova in cima a qualsiasi gerarchia di logger.
-
Puoi ottenere il logger root utilizzando le seguenti istruzioni:
Logger logger = LogManager.getLogger(LogManager.ROOT_LOGGER_NAME); Logger logger = LogManager.getRootLogger();
-
Il nome dei logger di log4j2 è sensibile alle maiuscole e minuscole.
-
Eccetto il logger root, tutti i logger possono essere ottenuti passando il loro nome a
LogManager.getLogger()
. -
LoggerContext è un punto focale per il sistema di logging poiché è possibile avere più LoggerContext all’interno dell’applicazione. Per ciascun LoggerContext deve essere impostata una configurazione attiva.
-
La configurazione di Log4j2 contiene tutti gli asset del sistema di logging; LoggerConfig(s), Appender(s), Filter(s) e molti altri.
-
Chiamare LogManager.getLogger() passando lo stesso nome restituirà sempre il riferimento per la stessa istanza di logger esatta.
-
La configurazione del sistema di logging viene tipicamente eseguita durante l’inizializzazione dell’applicazione. Questo può avvenire in forme diverse; in modo programmato o leggendo un file di configurazione log4j2.
Ogni logger è associato a un oggetto LoggerConfig, insieme di oggetti LoggerConfig costituiscono una gerarchia di logger. Questo concetto è noto come Gerarchia dei Logger. Gerarchia dei Logger è costituita da un insieme di oggetti LoggerConfig con una relazione genitore-figlio. L’elemento più alto in ogni Gerarchia dei Logger è il Logger Radice. Se Log4j2 non trova il file di configurazione, verrà utilizzato solo il Logger Radice per la registrazione con il livello di registrazione impostato su ERROR. Nell’immagine seguente viene mostrato il messaggio di avviso che verrà visualizzato in questo caso. Errore StatusLogger Nessun file di configurazione log4j2 trovato. Utilizzo della configurazione predefinita: registrazione solo degli errori sulla console. La tabella seguente mostra la relazione genitore-figlio nella Gerarchia dei Logger.
LoggerConfig (Is A) | Root | com | com.journaldev | com.journaldev.logging |
---|---|---|---|---|
Root | X | Child | descendant | descendant |
com | Parent | X | Child | descendant |
com.journaldev | Ancestor | Parent | X | Child |
com.journaldev.logging | Ancestor | Ancestor | Parent | X |
Per chiarire la relazione genitore-figlio, la tabella sopra va letta nel seguente modo:
- Root è un genitore per com.
- Root è un antenato per com.journaldev.
- Root è un antenato per com.journaldev.logging.
- com è un figlio per Root.
- com è un genitore per com.journaldev.
- com è un antenato per com.journaldev.logging.
- com.journaldev.logging è un figlio per com.journaldev e così via.
Un’istanza di LoggerConfig è considerata un antenato di un altro LoggerConfig; se il suo nome seguito da un punto è un prefisso del nome del discendente. Un’istanza di LoggerConfig è considerata un genitore di un altro LoggerConfig; se non ci sono nomi interposti tra entrambi.
Configurazione Log4j2
Ci sono molti modi per utilizzare la configurazione di Log4j2 nella tua applicazione.
- Utilizzando un file di configurazione scritto in XML, JSON, YAML o file di proprietà.
- Programmaticamente, creando una fabbrica di configurazione e implementazione della configurazione.
- Programmaticamente, chiamando le API esposte nell’interfaccia di configurazione.
- Programmaticamente, chiamando metodi sulla classe logger interna.
Ci concentreremo principalmente sul file di configurazione. Tuttavia, è bene conoscere anche l’approccio di programmazione, nel caso in cui si desideri configurare una strategia di registrazione specifica per alcuni Logger specifici. Prima di tutto, consideriamo il caso in cui non hai fornito un file di configurazione. L’implementazione di Log4j2 assume che ci sia una variabile di sistema chiamata log4j.configurationFile per indicare la posizione del file di configurazione di log4j2.
package com.journaldev;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class App
{
public static void main( String[] args ) {
Logger logger = LogManager.getRootLogger();
logger.trace("Configuration File Defined To Be :: "+System.getProperty("log4j.configurationFile"));
}
}
A simple log4j2 configuration file will look like below. configuration.xml:
<?xml version="1.0" encoding="UTF-8"?>
<Configuration>
<Appenders>
<Console name="Console">
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
</Console>
</Appenders>
<Loggers>
<Root level="trace">
<AppenderRef ref="Console"/>
</Root>
</Loggers>
</Configuration>
Ecco la spiegazione dettagliata per il codice elencato sopra:
- L’applicazione ha referenziato il logger radice chiamando il metodo
getRootLogger
di LogManager. - Il riferimento al logger da parte di LogManager ha avviato il sistema Log4j.
- Log4j controllerà ispezionare la proprietà di sistema log4j.configurationFile per determinare il file di configurazione di log4j2. La configurazione di Log4j può essere scritta in JSON, YAML e XML.
- È possibile impostare la proprietà di sistema log4j.configurationFile tramite
System.setProperties("log4j.configurationFile","PERCORSO_FILE")
o passandola come parametro JVM come si vede nella figura qui sotto. Notare anche il prefisso del protocollo File.
- Nel caso in cui non sia definita nessuna proprietà di sistema, l’ordine di configurazione segue la seguente precedenza:
- La ConfigurationFactory delle proprietà cercherà
log4j2-test.properties
nel classpath. - La ConfigurationFactory YAML cercherà log4j2-test.yaml o log4j2-test.yml nel classpath.
- La ConfigurationFactory JSON cercherà log4j2-test.jsn o log4j2-test.json nel classpath.
- La ConfigurationFactory XML cercherà log4j2-test.xml nel classpath.
- La ConfigurationFactory delle proprietà cercherà
log4j2.properties
nel classpath. - La ConfigurationFactory YAML cercherà log4j2.yml o log4j2.yaml nel classpath.
- La ConfigurationFactory JSON cercherà log4j2.jsn o log4j2.json nel classpath.
- La ConfigurationFactory XML cercherà log4j2.xml nel classpath.
- Se non è stato fornito alcun file di configurazione, verrà utilizzata la DefaultConfiguration e ciò comporterà un insieme di comportamenti predefiniti:
- Verrà utilizzato il logger radice.
- Il livello del logger radice sarà impostato su ERROR.
- Il logger radice propagherà i messaggi di log sulla console.
- PatternLayout sarà impostato su
%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n
- La ConfigurationFactory delle proprietà cercherà
L’utilizzo del file di configurazione log4j2 rende la configurazione log4j2 così semplice, ma vediamo come è possibile configurarla programmatically. Questo riguarda tutto l’uso di ConfigurationFactory.
package com.journaldev;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.core.Logger;
import org.apache.logging.log4j.core.LoggerContext;
import org.apache.logging.log4j.core.appender.ConsoleAppender;
import org.apache.logging.log4j.core.config.Configuration;
import org.apache.logging.log4j.core.config.ConfigurationFactory;
import org.apache.logging.log4j.core.config.ConfigurationSource;
import org.apache.logging.log4j.core.config.LoggerConfig;
import org.apache.logging.log4j.core.config.xml.XmlConfigurationFactory;
import org.apache.logging.log4j.core.layout.PatternLayout;
public class App
{
public static void main( String[] args ) throws FileNotFoundException, IOException {
// Ottieni un'istanza della factory di configurazione; le opzioni disponibili sono ConfigurationFactory predefinita, XMLConfigurationFactory,
// YamlConfigurationFactory & JsonConfigurationFactory
ConfigurationFactory factory = XmlConfigurationFactory.getInstance();
// Individua la fonte di questa configurazione, questo file individuato è un file fittizio contenente solo un tag di configurazione vuoto
ConfigurationSource configurationSource = new ConfigurationSource(new FileInputStream(new File("C:/dummyConfiguration.xml")));
// Ottieni un riferimento dalla configurazione
Configuration configuration = factory.getConfiguration(configurationSource);
// Crea un'appender console predefinito
ConsoleAppender appender = ConsoleAppender.createDefaultAppenderForLayout(PatternLayout.createDefaultLayout());
// Aggiungi l'appender console alla configurazione
configuration.addAppender(appender);
// Crea LoggerConfig
LoggerConfig loggerConfig = new LoggerConfig("com",Level.FATAL,false);
// Aggiungi appender
loggerConfig.addAppender(appender,null,null);
// Aggiungi il logger e associarlo all'istanza di LoggerConfig
configuration.addLogger("com", loggerConfig);
// Ottieni un'istanza del contesto
LoggerContext context = new LoggerContext("JournalDevLoggerContext");
// Avvia il sistema di logging
context.start(configuration);
// Ottieni un riferimento per il logger
Logger logger = context.getLogger("com");
// Log dell'evento di messaggio DEBUG
logger.log(Level.FATAL, "Logger Name :: "+logger.getName()+" :: Passed Message ::");
// Log dell'evento di messaggio di errore per il logger configurato come FATAL
logger.log(Level.ERROR, "Logger Name :: "+logger.getName()+" :: Not Passed Message ::");
// Log dell'evento di messaggio di ERRORE che sarà gestito da Root
logger.getParent().log(Level.ERROR, "Root Logger :: Passed Message As Root Is Configured For ERROR Level messages");
}
}
- Puoi utilizzare qualsiasi ConfigurationFactory fornita da Log4j2 o utilizzare quella predefinita. Abbiamo utilizzato
XMLConfigurationFactory
per ottenere un’istanza di ConfigurationFactory. - La factory ti fornirà un’istanza del riferimento di configurazione richiesta passando il file di configurazione corrispondente.
- Un’istanza di configurazione verrà utilizzata insieme a LoggerContext per avviare il sistema di logging.
- A console Appender has been configured and added into configuration instance with default layout. This Appender would print out messages into your console.
- È stata creata un’istanza di LoggerConfig con il nome, il LIVELLO fornito e senza filtro utilizzato. L’appender creato verrà assegnato a questa istanza di LoggerConfig.
- L’istanza di LoggerConfig è stata aggiunta all’istanza di configurazione.
- A new instance of LoggerContext is created with defined name.
- L’istanza di configurazione è stata passata per l’istanza LoggerContext e invocata start su quest’ultima.
- A logger instance has been acquired from LoggerContext. This logger instance will be used to fire set of Log events.
- L’istanza Logger ha generato tre eventi che verranno spiegati nella sezione Livelli di Log4j2.
- Il logger com è configurato per stampare i messaggi il cui livello è FATAL.
- Per default, il logger Root è configurato per stampare i messaggi il cui livello è ERROR.
- I messaggi di ERROR non verranno registrati dal logger ‘com’ perché il suo livello è FATAL.
La stessa configurazione può essere fatta utilizzando YAML, JSON o un file di proprietà. Tuttavia la configurazione del file di proprietà di log4j2 è diversa da quella del file di proprietà di log4j, quindi assicurati di non provare a utilizzare la configurazione del file di proprietà di log4j con log4j2. Verrà generato l’errore seguente;
ERROR StatusLogger No log4j2 configuration file found. Using default configuration: logging only errors to the console.
Mentre si elabora il codice sopra, verrà generata l’output seguente:
Logger Name :: com :: Passed Message ::
00:01:27.705 [main] ERROR - Root Logger:: Passed Message As Root Is Configured For ERROR Level messages
La prima riga dei log è dal logger com e la seconda è dal Root Logger. Il messaggio di errore del logger com non viene stampato perché il suo livello è FATAL.
Livelli di Log4j2
È possibile vedere negli esempi di codice sopra che ogni volta che definiamo un LoggerConfig, forniamo anche il livello di registrazione. Per impostazione predefinita, la registrazione di log4j2 è additiva. Ciò significa che tutti i log superiori verranno utilizzati anche quando viene utilizzato un logger specifico. L’immagine seguente chiarisce questa situazione. E qui punti di chiarimento per questo:
- Come abbiamo già detto, ogni logger è associato a un’istanza di LoggerConfig. Questo LoggerConfig è stato definito a livello di configurazione.
- Il livello di registrazione può essere determinato a livello di LoggerConfig.
- Puoi ottenere il logger dal suo nome, dal package genitore o puntando direttamente al Root Logger stesso.
- Il Root Logger è il nodo di livello superiore per ogni gerarchia di LoggerConfig.
- Una volta ottenuto il logger
com.journaldev
e iniziato un logEvent per la registrazione, il loggerConfig (net.journaldev) registrerà il messaggio e il messaggio sarà propagato anche nella gerarchia senza alcun rispetto per i livelli di registrazione dei genitori. Quindi l’evento di log verrà propagato ai logger com e Root e registreranno anche il messaggio rispettivamente secondo i livelli definiti. - Una volta ottenuto il logger com e iniziato un logEvent per la registrazione, il loggerConfig(com) registrerà il messaggio e il messaggio sarà propagato anche nella gerarchia senza alcun rispetto per i livelli di registrazione dei genitori. Cioè, il Root logger verrà propagato l’evento di log e registrerà anche il messaggio.
- Lo stesso vale per la gerarchia net.journaldev.
- Le prossime sezioni aggiungeranno ulteriori chiarimenti per il concetto additivo.
- C’è la possibilità che il genitore ignori il messaggio utilizzando il concetto di filtro o impostando l’indicatore additivo su falso, quindi gli eventi di log non verranno propagati ai genitori.
- C’è la possibilità che il logger ignori il messaggio se il livello della rispettiva configurazione del logger è MAGGIORE DEL livello degli eventi di log.
Ora, vediamo l’esempio associato al concetto di additività spiegato sopra:
import net.NetApp;
import net.journaldev.NetJournalDevApp;
import com.ComApp;
import com.journaldev.ComJournalDevApp;
public class Main {
public static void main(String [] args){
new ComApp();
new ComJournalDevApp();
new NetApp();
new NetJournalDevApp();
}
}
package com.journaldev;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class ComJournalDevApp {
public ComJournalDevApp(){
Logger logger = LogManager.getLogger(ComJournalDevApp.class);
logger.trace("COM :: JournalDev :: LEVEL :: ComJournalDevApp TRACE Message ::");
}
}
package net;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class NetApp {
public NetApp(){
Logger logger = LogManager.getLogger(NetApp.class);
logger.error("NET :: LEVEL :: NetApp ERROR Message ::");
}
}
package net.journaldev;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class NetJournalDevApp {
public NetJournalDevApp(){
Logger logger = LogManager.getLogger(NetJournalDevApp.class);
logger.error("NET :: JournalDev :: LEVEL :: NetJournalDevApp ERROR Message ::");
}
}
Mentre il file di configurazione di log4j2 appare come segue:
<?xml version="1.0" encoding="UTF-8"?>
<Configuration>
<Appenders>
<Console name="Console">
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
</Console>
</Appenders>
<Loggers>
<Root level="ERROR">
<AppenderRef ref="Console"/>
</Root>
<logger name="com" level="TRACE">
<AppenderRef ref="Console"/>
</logger>
<logger name="com.journaldev" level="TRACE">
<AppenderRef ref="Console"/>
</logger>
<logger name="net" level="ERROR">
<AppenderRef ref="Console"/>
</logger>
<logger name="net.journaldev" level="ERROR">
<AppenderRef ref="Console"/>
</logger>
</Loggers>
</Configuration>
Se viene eseguita la classe Main, troveresti i seguenti risultati:
10:34:47.168 [main] TRACE com.ComApp - COM :: LEVEL :: ComApp TRACE Message ::
10:34:47.168 [main] TRACE com.ComApp - COM :: LEVEL :: ComApp TRACE Message ::
10:34:47.170 [main] TRACE com.journaldev.ComJournalDevApp - COM :: JournalDev :: LEVEL :: ComJournalDevApp TRACE Message ::
10:34:47.170 [main] TRACE com.journaldev.ComJournalDevApp - COM :: JournalDev :: LEVEL :: ComJournalDevApp TRACE Message ::
10:34:47.170 [main] TRACE com.journaldev.ComJournalDevApp - COM :: JournalDev :: LEVEL :: ComJournalDevApp TRACE Message ::
10:34:47.171 [main] ERROR net.NetApp - NET :: LEVEL :: NetApp ERROR Message ::
10:34:47.171 [main] ERROR net.NetApp - NET :: LEVEL :: NetApp ERROR Message ::
10:34:47.171 [main] ERROR net.journaldev.NetJournalDevApp - NET :: JournalDev :: LEVEL :: NetJournalDevApp ERROR Message ::
10:34:47.171 [main] ERROR net.journaldev.NetJournalDevApp - NET :: JournalDev :: LEVEL :: NetJournalDevApp ERROR Message ::
10:34:47.171 [main] ERROR net.journaldev.NetJournalDevApp - NET :: JournalDev :: LEVEL :: NetJournalDevApp ERROR Message ::
Ecco una spiegazione dettagliata del codice elencato sopra:
- Il file di configurazione contiene cinque istanze di loggerConfig definite e sono Root, com, com.journaldev, net & net.journaldev. Proprio come la gerarchia dei logger mostrata sopra.
- Il livello di Root è configurato per essere ERRORE e questo è effettivamente il valore predefinito.
- I livelli di com & com.journaldev sono configurati per essere TRACCIA.
- I livelli di net & net.journaldev sono configurati per essere ERRORE.
- Potresti aver notato che i messaggi dei logger ComAPP e ComJournalDevApp sono stati mostrati due e tre volte rispettivamente. Questi messaggi vengono mostrati in base alla gerarchia dei logger per ComApp & ComJournalDevApp dove si trovano rispettivamente nei pacchetti com & com.journalDev. Abbiamo un caso simile con le classi NetApp & NetJournalDevApp.
- I genitori vengono propagati poiché l’indicatore additivo è impostato su vero per impostazione predefinita.
Lo spazio di registrazione tiene conto dei livelli degli eventi di log e del livello di loggerConfig oltre alla gerarchia del logger.
Quindi, cosa succederebbe se cambiamo il LoggerConfig per com in INFO e lasciamo il programma così com’è:
<?xml version="1.0" encoding="UTF-8"?>
<Configuration>
<Appenders>
<Console name="Console">
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
</Console>
</Appenders>
<Loggers>
<Root level="ERROR">
<AppenderRef ref="Console"/>
</Root>
<logger name="com" level="INFO">
<AppenderRef ref="Console"/>
</logger>
<logger name="com.journaldev" level="TRACE">
<AppenderRef ref="Console"/>
</logger>
<logger name="net" level="ERROR">
<AppenderRef ref="Console"/>
</logger>
<logger name="net.journaldev" level="ERROR">
<AppenderRef ref="Console"/>
</logger>
</Loggers>
</Configuration>
Il risultato sarebbe come segue:
11:08:10.305 [main] TRACE com.journaldev.ComJournalDevApp - COM :: JournalDev :: LEVEL :: ComJournalDevApp TRACE Message ::
11:08:10.305 [main] TRACE com.journaldev.ComJournalDevApp - COM :: JournalDev :: LEVEL :: ComJournalDevApp TRACE Message ::
11:08:10.305 [main] TRACE com.journaldev.ComJournalDevApp - COM :: JournalDev :: LEVEL :: ComJournalDevApp TRACE Message ::
11:08:10.307 [main] ERROR net.NetApp - NET :: LEVEL :: NetApp ERROR Message ::
11:08:10.307 [main] ERROR net.NetApp - NET :: LEVEL :: NetApp ERROR Message ::
11:08:10.308 [main] ERROR net.journaldev.NetJournalDevApp - NET :: JournalDev :: LEVEL :: NetJournalDevApp ERROR Message ::
11:08:10.308 [main] ERROR net.journaldev.NetJournalDevApp - NET :: JournalDev :: LEVEL :: NetJournalDevApp ERROR Message ::
11:08:10.308 [main] ERROR net.journaldev.NetJournalDevApp - NET :: JournalDev :: LEVEL :: NetJournalDevApp ERROR Message ::
- Sicuramente noterai che l’evento di log di ComAPP viene ignorato e ciò è dovuto al livello definito di loggerConfig per il pacchetto com. Il livello INFO (400) è inferiore al livello dell’evento di log che qui è TRACE (600). Quindi, il messaggio di ComApp non verrà più mostrato e per farlo apparire, è necessario modificare il livello di LoggerConfig per com in TRACE (600) o ALL (Integer.MAX_VALUE).
Per garantire che gli eventi di log siano stati visualizzati, il livello di LoggerConfig dovrebbe essere maggiore o uguale al livello dell’evento di log.
La tabella qui sotto ti mostra i livelli di log4j2 e il peso per ciascuno di essi:
LEVEL | Weight |
---|---|
OFF | 0 |
FATAL | 100 |
ERROR | 200 |
WARN | 300 |
INFO | 400 |
DEBUG | 500 |
TRACE | 600 |
ALL | Integer.MAX_VALUE |
Certo, la tabella sopra chiarisce molto più delle parole e ti fornisce la causa principale per cui l’evento di log TRACE non viene visualizzato mentre il livello di LoggerConfig è INFO.
Nota che la propagazione degli eventi di log nella gerarchia del logger va oltre questo calcolo e ignora i livelli.
Ma cosa succede se rimuoviamo il LoggerConfig di com.journaldev dalla configurazione e ne aggiungiamo uno nuovo per com.journaldev.logging per fare in modo che il file di configurazione appaia come segue:
<?xml version="1.0" encoding="UTF-8"?>
<Configuration>
<Appenders>
<Console name="Console">
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
</Console>
</Appenders>
<Loggers>
<Root level="ERROR">
<AppenderRef ref="Console"/>
</Root>
<logger name="com" level="TRACE">
<AppenderRef ref="Console"/>
</logger>
<logger name="com.journaldev.logging" level="TRACE">
<AppenderRef ref="Console"/>
</logger>
<logger name="net" level="ERROR">
<AppenderRef ref="Console"/>
</logger>
<logger name="net.journaldev" level="ERROR">
<AppenderRef ref="Console"/>
</logger>
</Loggers>
</Configuration>
Potresti trovare più conveniente la figura qui sotto per capire cosa è successo nella configurazione log4j2 sopra. Ecco alcune delucidazioni per la figura mostrata sopra e come potrebbe influenzare il comportamento degli eventi di logging:
- Quando gli eventi di log sono stati generati da un Logger chiamato com.journaldev.logging, il LoggerConfig associato a quel nome (cioè com.journaldev.logging) è stato usato per gestirlo e stampare il messaggio.
- Essendo l’attributo additivo di LoggerConfig com.journaldev.logging impostato per impostazione predefinita su true, l’evento di log è stato propagato al genitore che in questo caso si riferisce a com.journaldev.
- Essendo com.journaldev LoggerConfig non definito nella configurazione, non succede nulla e l’evento di log verrà propagato fino a com e quindi alle istanze Root LoggerConfig.
- Com & Root riceveranno l’evento di log e lo stamperanno indipendentemente dal livello con cui è stato inviato.
Come risultato dei punti menzionati, vedresti le seguenti uscite:
14:08:37.634 [main] TRACE com.ComApp - COM :: LEVEL :: ComApp TRACE Message ::
14:08:37.634 [main] TRACE com.ComApp - COM :: LEVEL :: ComApp TRACE Message ::
14:08:37.636 [main] TRACE com.journaldev.ComJournalDevApp - COM :: JournalDev :: LEVEL :: ComJournalDevApp TRACE Message ::
14:08:37.636 [main] TRACE com.journaldev.ComJournalDevApp - COM :: JournalDev :: LEVEL :: ComJournalDevApp TRACE Message ::
14:08:37.637 [main] TRACE com.journaldev.logging.ComJounralDevLoggingApp - COM :: JournalDev :: LOGGING :: LEVEL :: ComJounralDevLoggingApp TRACE Message ::
14:08:37.637 [main] TRACE com.journaldev.logging.ComJounralDevLoggingApp - COM :: JournalDev :: LOGGING :: LEVEL :: ComJounralDevLoggingApp TRACE Message ::
14:08:37.637 [main] TRACE com.journaldev.logging.ComJounralDevLoggingApp - COM :: JournalDev :: LOGGING :: LEVEL :: ComJounralDevLoggingApp TRACE Message ::
14:08:37.638 [main] ERROR net.NetApp - NET :: LEVEL :: NetApp ERROR Message ::
14:08:37.638 [main] ERROR net.NetApp - NET :: LEVEL :: NetApp ERROR Message ::
14:08:37.640 [main] ERROR net.journaldev.NetJournalDevApp - NET :: JournalDev :: LEVEL :: NetJournalDevApp ERROR Message ::
14:08:37.640 [main] ERROR net.journaldev.NetJournalDevApp - NET :: JournalDev :: LEVEL :: NetJournalDevApp ERROR Message ::
14:08:37.640 [main] ERROR net.journaldev.NetJournalDevApp - NET :: JournalDev :: LEVEL :: NetJournalDevApp ERROR Message ::
E potresti notare quanto segue:
- L’evento di log nel pacchetto com viene mostrato due volte. Una per com mentre la seconda per Root.
- Evento di registro presso com.journaldev è stato mostrato due volte. Uno per com e il secondo per Root. Anche se era tre volte prima, ma per ora il LoggerConfig di com.journaldev è assente e quindi potrebbe non esserci stato alcun registro di logging nel pacchetto com.journaldev e l’evento sarebbe stato propagato per com e Root.
- L’evento di registro presso com.journaldev.logging è stato mostrato tre volte, una per il pacchetto com.journaldev.logging e la seconda per com e la terza per Root. Secondo la propagazione della gerarchia dei logger, dovrebbe essere visualizzato quattro volte, ma a causa dell’assenza di LoggerConfig di com.journaldev, viene visualizzato tre volte.
Nel caso in cui tu abbia definito un’istanza LoggerConfig di com.journaldev senza specificare il livello, erediterà il livello dal suo genitore.
Ma cosa succede se hai definito un LoggerConfig di com.journaldev nel tuo file di configurazione e hai tralasciato di specificare il livello del LoggerConfig. Fortunatamente, il concetto di gerarchia dei logger ti salverà qui e com.journaldev erediterà il valore del livello dal suo genitore. Di seguito è riportato un file di configurazione di esempio seguito dalla tabella per il livello di registrazione di ciascuna configurazione del logger.
<?xml version="1.0" encoding="UTF-8"?>
<Configuration>
<Appenders>
<Console name="Console">
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
</Console>
</Appenders>
<Loggers>
<Root level="ERROR">
<AppenderRef ref="Console"/>
</Root>
<logger name="com" level="TRACE">
<AppenderRef ref="Console"/>
</logger>
<logger name="com.journaldev">
<AppenderRef ref="Console"/>
</logger>
<logger name="com.journaldev.logging" level="TRACE">
<AppenderRef ref="Console"/>
</logger>
<logger name="net" level="ERROR">
<AppenderRef ref="Console"/>
</logger>
<logger name="net.journaldev" level="ERROR">
<AppenderRef ref="Console"/>
</logger>
</Loggers>
</Configuration>
Logger Name | Assigned LoggerConfig | LoggerConfig Level | Logger Level |
---|---|---|---|
Root | Root | ERROR | ERROR |
com | com | TRACE | TRACE |
com.journaldev | com | TRACE | TRACE |
com.journaldev.logging | com.journaldev.logging | TRACE | TRACE |
- Il pacchetto com.journaldev.logging è già associato a un LoggerConfig con livello di registrazione TRACE.
- Il pacchetto com.journaldev è già associato a un LoggerConfig senza livello di registrazione specificato, quindi erediterà il livello di registrazione del genitore e sicuramente il valore sarà TRACE per il pacchetto com.
- Il pacchetto com è già associato a un LoggerConfig con livello di registrazione TRACE.
- Per default, Root ha ERROR come livello di registrazione.
- Nel caso in cui il pacchetto com non sia dichiarato, LoggerConfig di com.journaldev erediterà il livello di registrazione di Root.
Di seguito è il risultato dell’esecuzione mentre com.journaldev eredita il livello di log di com:
14:41:37.419 [main] TRACE com.ComApp - COM :: LEVEL :: ComApp TRACE Message ::
14:41:37.419 [main] TRACE com.ComApp - COM :: LEVEL :: ComApp TRACE Message ::
14:41:37.421 [main] TRACE com.journaldev.ComJournalDevApp - COM :: JournalDev :: LEVEL :: ComJournalDevApp TRACE Message ::
14:41:37.421 [main] TRACE com.journaldev.ComJournalDevApp - COM :: JournalDev :: LEVEL :: ComJournalDevApp TRACE Message ::
14:41:37.421 [main] TRACE com.journaldev.ComJournalDevApp - COM :: JournalDev :: LEVEL :: ComJournalDevApp TRACE Message ::
14:41:37.422 [main] TRACE com.journaldev.logging.ComJounralDevLoggingApp - COM :: JournalDev :: LOGGING :: LEVEL :: ComJounralDevLoggingApp TRACE Message ::
14:41:37.422 [main] TRACE com.journaldev.logging.ComJounralDevLoggingApp - COM :: JournalDev :: LOGGING :: LEVEL :: ComJounralDevLoggingApp TRACE Message ::
14:41:37.422 [main] TRACE com.journaldev.logging.ComJounralDevLoggingApp - COM :: JournalDev :: LOGGING :: LEVEL :: ComJounralDevLoggingApp TRACE Message ::
14:41:37.422 [main] TRACE com.journaldev.logging.ComJounralDevLoggingApp - COM :: JournalDev :: LOGGING :: LEVEL :: ComJounralDevLoggingApp TRACE Message ::
14:41:37.423 [main] ERROR net.NetApp - NET :: LEVEL :: NetApp ERROR Message ::
14:41:37.423 [main] ERROR net.NetApp - NET :: LEVEL :: NetApp ERROR Message ::
14:41:37.423 [main] ERROR net.journaldev.NetJournalDevApp - NET :: JournalDev :: LEVEL :: NetJournalDevApp ERROR Message ::
14:41:37.423 [main] ERROR net.journaldev.NetJournalDevApp - NET :: JournalDev :: LEVEL :: NetJournalDevApp ERROR Message ::
14:41:37.423 [main] ERROR net.journaldev.NetJournalDevApp - NET :: JournalDev :: LEVEL :: NetJournalDevApp ERROR Message ::
E di seguito il risultato se rimuovi la dichiarazione di LoggerConfig per il pacchetto com:
14:43:28.809 [main] TRACE com.journaldev.logging.ComJounralDevLoggingApp - COM :: JournalDev :: LOGGING :: LEVEL :: ComJounralDevLoggingApp TRACE Message ::
14:43:28.809 [main] TRACE com.journaldev.logging.ComJounralDevLoggingApp - COM :: JournalDev :: LOGGING :: LEVEL :: ComJounralDevLoggingApp TRACE Message ::
14:43:28.809 [main] TRACE com.journaldev.logging.ComJounralDevLoggingApp - COM :: JournalDev :: LOGGING :: LEVEL :: ComJounralDevLoggingApp TRACE Message ::
14:43:28.811 [main] ERROR net.NetApp - NET :: LEVEL :: NetApp ERROR Message ::
14:43:28.811 [main] ERROR net.NetApp - NET :: LEVEL :: NetApp ERROR Message ::
14:43:28.812 [main] ERROR net.journaldev.NetJournalDevApp - NET :: JournalDev :: LEVEL :: NetJournalDevApp ERROR Message ::
14:43:28.812 [main] ERROR net.journaldev.NetJournalDevApp - NET :: JournalDev :: LEVEL :: NetJournalDevApp ERROR Message ::
14:43:28.812 [main] ERROR net.journaldev.NetJournalDevApp - NET :: JournalDev :: LEVEL :: NetJournalDevApp ERROR Message ::
Potresti notare che non sono stati registrati messaggi per com e com.journaldev, di seguito le ragioni.
- La cancellazione di LoggerConfig associato al pacchetto com farà sì che tutti gli eventi di log menzionati in quel pacchetto vengano ignorati.
- Dato che non è definito LoggerConfig per il pacchetto com nella configurazione, LoggerConfig associato a com.journaldev erediterà il livello di log dal suo genitore. Com non è definito e la gerarchia dei logger raggiunge il Top e si riferisce ora a Root. Il livello di log radice è ERRORE (200) e il livello di log dell’evento in com.journaldev è TRACCIA (600) – Vedi ComJournalDevApp – e secondo l’equazione definita in precedenza, il livello di LoggerConfig dovrebbe essere maggiore o uguale al Livello di Evento di Log e questo non è vero, quindi non verranno visualizzati messaggi qui per com.journaldev.
Ultimo ma non meno importante, seguendo la Tabella qui sotto ti mostra tutti gli scenari di logging possibili che potresti affrontare quando utilizzi il sistema di Logging:
X (N/A) | LoggerConfig Level | OFF(0) | FATAL(100) | ERROR(200) | WARN(300) | INFO(400) | DEBUG(500) | TRACE(600) | ALL(MAX) |
---|---|---|---|---|---|---|---|---|---|
Event Level | X | X | X | X | X | X | X | X | X |
OFF(0) | X | YES | NO | NO | NO | NO | NO | NO | NO |
FATAL(100) | X | NO | YES | YES | YES | YES | YES | YES | YES |
ERROR(200) | X | NO | NO | YES | YES | YES | YES | YES | YES |
WARN(300) | X | NO | NO | NO | YES | YES | YES | YES | YES |
INFO(400) | X | NO | NO | NO | NO | YES | YES | YES | YES |
DEBUG(500) | X | NO | NO | NO | NO | NO | YES | YES | YES |
TRACE(600) | X | NO | NO | NO | NO | NO | NO | YES | YES |
ALL(MAX) | X | NO | NO | NO | NO | NO | NO | NO | YES |
- Non c’è un metodo diretto che può essere utilizzato per lanciare eventi di log OFF/ALL.
- Principalmente, per lanciare eventi di log OFF/ALL potresti utilizzare logger.log(Level.OFF, “Msg”) o logger.log(LEVEL.ALL, “Msg”), rispettivamente.
- Il metodo di log è responsabile di gestire l’evento di log in base all’equazione menzionata.
Il metodo di gestione dice: se il Livello di LoggerConfig è maggiore o uguale al livello dell’evento di log, l’evento verrà accettato per ulteriore elaborazione.
L’evento di registro sarebbe accettato per ulteriore elaborazione – questo è così importante perché hai la possibilità di impedire che un evento venga gestito anche se è accettato utilizzando i Filtri Log4j2. Puoi impostare la proprietà additive su false per evitare la propagazione degli eventi di registro ai log parent. Segue di seguito lo stesso esempio che hai visto prima ma questa volta con un attributo di additività, quindi potresti notare la differenza.
<?xml version="1.0" encoding="UTF-8"?>
<Configuration>
<Appenders>
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
</Console>
</Appenders>
<Loggers>
<Root level="ERROR">
<AppenderRef ref="Console"/>
</Root>
<logger name="com" level="TRACE" additivity="false">
<AppenderRef ref="Console"/>
</logger>
<logger name="com.journaldev" additivity="false">
<AppenderRef ref="Console"/>
</logger>
<logger name="com.journaldev.logging" level="TRACE" additivity="false">
<AppenderRef ref="Console"/>
</logger>
<logger name="net" level="ERROR" additivity="false">
<AppenderRef ref="Console"/>
</logger>
<logger name="net.journaldev" level="ERROR" additivity="false">
<AppenderRef ref="Console"/>
</logger>
</Loggers>
</Configuration>
E il risultato dell’esecuzione sarebbe come segue:
17:55:30.558 [main] TRACE com.ComApp - COM :: LEVEL :: ComApp TRACE Message ::
17:55:30.560 [main] TRACE com.journaldev.ComJournalDevApp - COM :: JournalDev :: LEVEL :: ComJournalDevApp TRACE Message ::
17:55:30.561 [main] TRACE com.journaldev.logging.ComJounralDevLoggingApp - COM :: JournalDev :: LOGGING :: LEVEL :: ComJounralDevLoggingApp TRACE Message ::
17:55:30.561 [main] ERROR net.NetApp - NET :: LEVEL :: NetApp ERROR Message ::
17:55:30.562 [main] ERROR net.journaldev.NetJournalDevApp - NET :: JournalDev :: LEVEL :: NetJournalDevApp ERROR Message ::
E potresti notare che non c’è propagazione degli eventi di registro ai log parent.
Log4j2 Ricerche
Idealemente, è possibile definire le ricerche come un modo in cui è possibile passare valori per il file di configurazione del Registro. Log4j2 fornisce un diverso insieme di Ricerche che possono essere utilizzate indipendentemente per impostare valori da contesti diversi:
- Ricerca della mappa di contesto
- Ricerca della data
- Ricerca dell’ambiente
- Ricerca Java
- Ricerca JNDI
- Ricerca degli argomenti di input JVM (JMX)
- Ricerca degli argomenti principali
- Ricerca della mappa
- Ricerca dei dati strutturati
- Ricerca delle proprietà di sistema
- Ricerca web
Puoi fare riferimento alla documentazione di Log4j2 per ulteriori dettagli su ogni tipo di lookup, ma vediamo alcuni esempi qui per coprire i fondamenti del Lookup di Log4j2. Lookup dell’Ambiente rappresenta il modo in cui puoi passare un valore dell’ambiente (sia tramite Linux etc/profile, variabili di ambiente di sistema Windows o script di avvio per l’applicazione). Come molti di noi sanno, abbiamo la possibilità di definire un insieme di valori ambientali per l’uso dell’applicazione. Vediamo i modi più famosi per definire le variabili ambientali.
- Definisci la variabile d’ambiente utilizzando la funzionalità dell’ambiente Windows:
- Fai clic destro sull’icona del tuo computer e seleziona proprietà. Dovrebbe essere visualizzata la home del pannello di controllo.
- Fai clic su Impostazioni di sistema avanzate e poi apri la finestra delle Variabili d’ambiente.
- Nella sezione Variabili di sistema, definisci la variabile JournalDevVar con valore JournalDev.
- Aggiorna il PatternLayout all’interno del tuo log4j2.xml per includere la variabile appena aggiunta.
- Definisci le tue variabili ambientali utilizzando la struttura di avvio dello script.
- Invece di utilizzare lo script predefinito normale, puoi utilizzare la struttura di esecuzione dello script di Eclipse IDE, fare clic sul menu Esegui e scegliere Configurazione esecuzione.
- Naviga nella scheda Ambiente & definisci la tua variabile lì.
Osserva ora il file log4j2.xml modificato e nota l’uso delle variabili ambientali.
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="%d{HH:mm:ss.SSS} $${env:JournalDevVar} $${env:JournalDevSecondVar} [%t] %-5level %logger{36} - %msg%n"/>
</Console>
E il risultato dell’esecuzione assomiglierebbe a quanto segue:
23:57:02.511 JournalDev www.journaldev.com [main] TRACE com.ComApp - COM :: LEVEL :: ComApp TRACE Message ::
23:57:02.517 JournalDev www.journaldev.com [main] TRACE com.journaldev.ComJournalDevApp - COM :: JournalDev :: LEVEL :: ComJournalDevApp TRACE Message ::
23:57:02.520 JournalDev www.journaldev.com [main] TRACE com.journaldev.logging.ComJounralDevLoggingApp - COM :: JournalDev :: LOGGING :: LEVEL :: ComJounralDevLoggingApp TRACE Message ::
23:57:02.523 JournalDev www.journaldev.com [main] ERROR net.NetApp - NET :: LEVEL :: NetApp ERROR Message ::
23:57:02.527 JournalDev www.journaldev.com [main] ERROR net.journaldev.NetJournalDevApp - NET :: JournalDev :: LEVEL :: NetJournalDevApp ERROR Message ::
Ma potresti incontrare un piccolo problema qui e soprattutto quando stai definendo le variabili d’ambiente di un sistema operativo e si tratta della Cache di Eclipse. Idealmente, Eclipse ha memorizzato tutte le variabili di sistema quando è stato avviato e puoi trovarle tutte sotto Esegui – Configurazione di esecuzione – Scheda Ambiente – Clicca sul pulsante Seleziona. Quindi, potresti confonderti quando le hai definite ma l’Applicazione non le riconosce. Anche se riavvii il tuo Eclipse, non otterrai la soluzione e per risolverlo devi eseguire eclipse.exe -clean sulla tua installazione di Eclipse. Per assicurarti che le tue variabili d’ambiente siano definite correttamente e che il tuo sistema le trovi volentieri, puoi usare il tipo di plugin corrispondente fornito dall’API di Log4j2. Crea un’istanza di EnvironmentLookup e chiedigli di cercare una certa variabile e, se è definita, potrai trovarla facilmente.
EnvironmentLookup lookup = new EnvironmentLookup();
LogManager.getRootLogger().error(lookup.lookup("JournalDevSecondVar"));
Log4j2 Appenders
Hai visto in precedenza come utilizzare i Lookups per inserire variabili nel tuo file di configurazione. Tuttavia, potresti voler modificare il mezzo attraverso il quale passano i tuoi messaggi. Invece di utilizzare direttamente la console, potresti voler utilizzare un file o un repository di database per assicurarti che i tuoi messaggi siano conservati permanentemente. Log4j2 ha fornito molti Appenders, e potresti fare riferimento alla documentazione di Log4j2 per ottenere ulteriori dettagli sugli Appender. In modo breve, di seguito è riportato l’elenco di tutti gli Appender di Log4j2.
- ConsoleAppender
- AsyncAppender
- FailoverAppender
- FileAppender
- FlumeAppender
- JDBCAppender
- JMSAppender
- JPAAppender
- MemoryMappedFileAppender
- NoSQLAppender
- OutputStreamAppender
- RandomAccessFileAppender
- RewriteAppender
- RollingFileAppender
- RollingRandomAccessFileAppender
- RoutingAppender
- SMTPAppender
- SocketAppender
- SyslogAppender
I mezzi più famosi utilizzati per registrare gli eventi sono la console, il file e il database. Poiché il file salvarebbe i tuoi messaggi, il database potrebbe essere utilizzato per auditarli. A questo scopo, questa sezione si concentrerà su come JDBCAppender possa essere utilizzato efficientemente.
JDBCAppender
Il principale obiettivo di JDBCAppender è scrivere eventi di log in una tabella relazionale tramite connessioni JDBC. Non ci dilungheremo molto nell’illustrare come ottimizzare i pool di connessioni poiché questo tutorial non è destinato a tale scopo. Tuttavia, otterrai sicuramente un esempio completamente funzionale che aiuta a scrivere i tuoi eventi di log nel database. Prima di procedere, vediamo tutti i parametri necessari e una descrizione di ciascuno per configurare correttamente JDBCAppender.
Parameter Name | Type | Description |
---|---|---|
Name | String | Required, The name of the Appender |
ignoreExceptions | boolean | Default value is set to true, making exceptions thrown to be logged also and then ignored. False value means the exception will be propagated for the caller. |
filter | Filter | The filter that should be used to make a decision whether the log events are going to be handled by this Appender or not. |
bufferSize | int | Default value is zero, indicating there’s no buffering have been done upon log events. Value greater than 0 would lead the Appender to buffer log events and then flush them once the buffer reaches the limit specified. |
connectionSource | ConnectionSource | Required, the connections source from which the database connections should be retrieved. |
tableName | String | Required, the name of the Table on which your log events should be persisted. |
columnConfigs | ColumnConfig[] | Required, additional information may be set upon those used columns and how the data should be persisted on each of them. This can be handled with multiple <Column> elements. |
Parameter Name | Type | Description |
---|---|---|
jndiName | String | Required, full prefixed JNDI name that the javax.sql.Datasource is bound to. |
Parameter Name | Type | Description |
---|---|---|
class | String | Requird, The fully qualified name for a class containg a static factory method for obtaining JDBC connections. |
method | boolean | Required, The name of the static factory method for obtaining JDBC connections. |
Parameter Name | Type | Description |
---|---|---|
name | String | Required, the name of the database column |
pattern | String | Ability to specify any legal pattern that Log event would be formatted with |
literal | String | Ability to specify literal value in this column (i.e. SEQ.NEXTVAL) |
isEventTimestamp | boolean | Indicating whether the event would consider Timestamp |
isUnicode | boolean | For unicode purpose as you may refer for Log4j2 documentation for further details |
isClob | boolean | For storing character large object, you may refer for Log4j2 documentation for further details. |
Dato che sei obbligato a utilizzare JNDI, il nostro esempio configurerebbe una sorgente dati di connessione per il database Oracle e Apache Tomcat 7.
- Se non hai installato il database Oracle nel tuo ambiente, è apprezzato se puoi farlo. Se non sei molto familiare con Oracle, ti consiglio di installare la sua Express Edition.
- Installa Apache Tomcat 7 nel tuo ambiente.
- Crea un progetto Maven WebApp nel tuo Eclipse.
- Assicurati che il tuo progetto sia stato creato con successo e se noti eventuali errori nel file pom, assicurati di correggerli.
- Aggiungi le dipendenze di Log4j2.
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.2</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.2</version>
</dependency>
- Configura il tuo contesto per includere una dichiarazione della sorgente dati MySQL. Secondo la documentazione di Apache, questo file dovrebbe trovarsi all’interno della cartella META-INF della tua applicazione Web.
<Context path="/JournalDevWebLogging"
privileged="true" antiResourceLocking="false" antiJARLocking="false">
<Resource name="jdbc/JournalDevDB" auth="Container"
factory="org.apache.tomcat.jdbc.pool.DataSourceFactory"
type="javax.sql.DataSource" maxActive="100" maxIdle="30" maxWait="10000"
username="root" password="root" driverClassName="com.mysql.jdbc.Driver"
url="jdbc:mysql://localhost:3306/journaldev" />
</Context>
- Configura il tuo database e crea la tua tabella di registrazione.
CREATE TABLE `logging` (
`EVENT_ID` int(11) NOT NULL AUTO_INCREMENT,
`EVENT_DATE` datetime DEFAULT NULL,
`LEVEL` varchar(45) DEFAULT NULL,
`LOGGER` varchar(45) DEFAULT NULL,
`MSG` varchar(45) DEFAULT NULL,
`THROWABLE` varchar(45) DEFAULT NULL,
PRIMARY KEY (`EVENT_ID`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
- Configurare il tuo file log4j2.xml in modo che assomigli al seguente:
<?xml version="1.0" encoding="UTF-8"?>
<Configuration>
<Appenders>
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout
pattern="%d{HH:mm:ss.SSS} $${env:JournalDevVar} $${env:JournalDevSecondVar} [%t] %-5level %logger{36} - %msg%n" />
</Console>
<JDBC name="databaseAppender" tableName="journaldev.logging">
<DataSource jndiName="java:/comp/env/jdbc/JournalDevDB" />
<Column name="EVENT_DATE" isEventTimestamp="true" />
<Column name="LEVEL" pattern="%level" />
<Column name="LOGGER" pattern="%logger" />
<Column name="MSG" pattern="%message" />
<Column name="THROWABLE" pattern="%ex{full}" />
</JDBC>
</Appenders>
<Loggers>
<Root level="ERROR">
<AppenderRef ref="Console" />
</Root>
<logger name="com" level="TRACE" additivity="false">
<AppenderRef ref="databaseAppender" />
</logger>
<logger name="com.journaldev" additivity="false">
<AppenderRef ref="databaseAppender" />
</logger>
</Loggers>
</Configuration>
- Crea una risorsa Web che ti consenta di ottenere un riferimento a un logger e quindi di registrare un evento.
package com.journaldev;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class JournalDevServlet extends HttpServlet{
private static final long serialVersionUID = 1L;
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{
Logger logger = LogManager.getLogger(JournalDevServlet.class);
logger.trace("JournalDev Database Logging Message !");
}
}
- Puoi opzionalmente configurare un ServletContextListener che garantisca che l’inizializzazione del data source venga eseguita correttamente.
package com.journaldev;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import org.apache.logging.log4j.LogManager;
public class JournalDevServletContextListener implements ServletContextListener{
private InitialContext context = null;
public void contextDestroyed(ServletContextEvent event) {
}
public void contextInitialized(ServletContextEvent event) {
try {
// Ottenere il contesto iniziale
context = new InitialContext();
// Ottenere un riferimento al sotto-contesto env
Context envContext = (Context)context.lookup("java:comp/env");
// Ottenere un riferimento al sotto-contesto jdbc e quindi individuare il data source definito
LogManager.getRootLogger().error(((Context)envContext.lookup("jdbc")).lookup("JournalDevDB"));
} catch (NamingException e) {
LogManager.getRootLogger().error(e);
}
}
}
- Definisci il tuo Servlet all’interno del file web.xml.
- Esegui l’applicazione e accedi al Servlet definito sopra. Vedrai i seguenti log.
Mar 15, 2015 2:31:41 PM org.apache.catalina.core.AprLifecycleListener init
INFO: The APR based Apache Tomcat Native library which allows optimal performance in production environments was not found on the java.library.path: C:\Program Files\Java\jdk1.6.0_26\bin;C:\Windows\Sun\Java\bin;C:\Windows\system32;C:\Windows;C:\Program Files\Java\jdk1.6.0_26\jre\bin;C:/Program Files/Java/jdk1.6.0_26/bin/../jre/bin/server;C:/Program Files/Java/jdk1.6.0_26/bin/../jre/bin;C:/Program Files/Java/jdk1.6.0_26/bin/../jre/lib/amd64;D:\OracleWebCenter\OracleWC\Oracle11g\app\oracle\product\11.2.0\server\bin;;C:\Program Files\Common Files\Microsoft Shared\Windows Live;C:\Program Files (x86)\Common Files\Microsoft Shared\Windows Live;D:\OracleDB\app\product\11.2.0\dbhome_1\bin;org.C:\Program Files (x86)\Common Files\NetSarang;C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Windows\System32\WindowsPowerShell\v1.0\;C:\Program Files (x86)\Intel\OpenCL SDK\2.0\bin\x86;C:\Program Files (x86)\Intel\OpenCL SDK\2.0\bin\x64;D:\SpringRoo\spring-roo-1.2.5.RELEASE\bin;D:\Ant\apache-ant-1.9.2\bin;C:\Python27;C:\Program Files\Java\jdk1.6.0_26\bin;D:\Maven\apache-maven-3.2.1/bin;D:\bower-master\bin;C:\Program Files (x86)\Git\cmd;C:\Program Files\nodejs\;C:\Program Files\Microsoft Windows Performance Toolkit\;D:\Grails\grails-2.4.0\bin;D:\Gradle\gradle-2.0\bin;C:\Program Files (x86)\Windows Live\Shared;C:\Program Files\TortoiseSVN\bin;D:\Strawberry\perl\bin;D:\Strawberry\perl\site\bin;D:\Strawberry\c\bin;C:\Users\mohammad.amr\AppData\Roaming\npm;D:\JournalDev\eclipse;;.
Mar 15, 2015 2:31:41 PM org.apache.tomcat.util.digester.SetPropertiesRule begin
WARNING: [SetPropertiesRule]{Server/Service/Engine/Host/Context} Setting property 'source' to 'org.eclipse.jst.j2ee.server:JournalDevWebLogging' did not find a matching property.
Mar 15, 2015 2:31:41 PM org.apache.coyote.AbstractProtocol init
INFO: Initializing ProtocolHandler ["http-bio-8080"]
Mar 15, 2015 2:31:41 PM org.apache.coyote.AbstractProtocol init
INFO: Initializing ProtocolHandler ["ajp-bio-8009"]
Mar 15, 2015 2:31:41 PM org.apache.catalina.startup.Catalina load
INFO: Initialization processed in 1020 ms
Mar 15, 2015 2:31:41 PM org.apache.catalina.core.StandardService startInternal
INFO: Starting service Catalina
Mar 15, 2015 2:31:41 PM org.apache.catalina.core.StandardEngine startInternal
INFO: Starting Servlet Engine: Apache Tomcat/7.0.35
14:31:43.847 [localhost-startStop-1] ERROR - org.apache.tomcat.jdbc.pool.DataSource@10fd0a62{ConnectionPool[defaultAutoCommit=null; defaultReadOnly=null; defaultTransactionIsolation=-1; defaultCatalog=null; driverClassName=com.mysql.jdbc.Driver; maxActive=100; maxIdle=30; minIdle=10; initialSize=10; maxWait=10000; testOnBorrow=false; testOnReturn=false; timeBetweenEvictionRunsMillis=5000; numTestsPerEvictionRun=0; minEvictableIdleTimeMillis=60000; testWhileIdle=false; testOnConnect=false; password=root; url=jdbc:mysql://localhost:3306/journaldev; username=root; validationQuery=null; validatorClassName=null; validationInterval=30000; accessToUnderlyingConnectionAllowed=true; removeAbandoned=false; removeAbandonedTimeout=60; logAbandoned=false; connectionProperties=null; initSQL=null; jdbcInterceptors=null; jmxEnabled=true; fairQueue=true; useEquals=true; abandonWhenPercentageFull=0; maxAge=0; useLock=false; dataSource=null; dataSourceJNDI=null; suspectTimeout=0; alternateUsernameAllowed=false; commitOnReturn=false; rollbackOnReturn=false; useDisposableConnectionFacade=true; logValidationErrors=false; propagateInterruptState=false; }
Mar 15, 2015 2:31:43 PM org.apache.coyote.AbstractProtocol start
INFO: Starting ProtocolHandler ["http-bio-8080"]
Mar 15, 2015 2:31:43 PM org.apache.coyote.AbstractProtocol start
INFO: Starting ProtocolHandler ["ajp-bio-8009"]
Mar 15, 2015 2:31:43 PM org.apache.catalina.startup.Catalina start
INFO: Server startup in 1909 ms
Filtri Log4j2
Anche se c’è un candidato LoggerConfig per gestire l’evento Log, puoi configurarlo per negare il passaggio degli eventi Log nei back end Appenders. Ciò può essere fatto tramite un filtro log4j2. Questa sezione non è intesa a fornirti un tutorial invasivo, massiccio e enorme sull’uso dei filtri in Log4j2, poiché ne servirebbero molti per coprirne ognuno. Ma qui vedrai come utilizzare il filtro più semplice per apprendere il concetto. Uno dei filtri più semplici che puoi usare è BurstFilter che ti fornisce un meccanismo per controllare il tasso a cui vengono elaborati gli eventi Log, eliminando silenziosamente gli eventi dopo che è stato raggiunto il limite massimo. Per ora, puoi vedere qui sotto tutti i dettagli necessari per utilizzare BurstFilter.
Parameter Name | Type | Description |
---|---|---|
level | String | Level of messages to be filtered |
rate | float | The average number of events per second to allow |
maxBurst | integer | The maximum number of events that can occur before events are filtered for exceeding the average rate. The default is 10 times the rate. |
onMatch | String | Action to take when filter matches. May be Accept, DENY or NEUTRAL. The default is NEUTRAL |
onMismatch | String | Action to tale when filter doesn’t match. May be Accept, DENY or NEUTRAL. The default is NEUTRAL |
Ora, guarda la posizione di BurstFilter all’interno del tuo Appender del database.
<?xml version="1.0" encoding="UTF-8"?>
<Configuration>
<Appenders>
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout
pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n" />
</Console>
<JDBC name="databaseAppender" tableName="journaldev.logging">
<DataSource jndiName="java:/comp/env/jdbc/JournalDevDB" />
<BurstFilter level="TRACE" rate="20" maxBurst="2"/>
<Column name="EVENT_DATE" isEventTimestamp="true" />
<Column name="LEVEL" pattern="%level" />
<Column name="LOGGER" pattern="%logger" />
<Column name="MSG" pattern="%message" />
<Column name="THROWABLE" pattern="%ex{full}" />
</JDBC>
</Appenders>
<Loggers>
<Root level="ERROR">
<AppenderRef ref="Console" />
</Root>
<logger name="com" level="TRACE" additivity="false">
<AppenderRef ref="databaseAppender" />
</logger>
<logger name="com.journaldev" additivity="false">
<AppenderRef ref="databaseAppender" />
</logger>
</Loggers>
</Configuration>
- L’Appender del database considera il BurstFilter mentre l’Appender della console no.
- Utilizzando il logger della console, tutti gli eventi di log verrebbero registrati, mentre l’Appender del database non lo farebbe poiché il BurstFilter ne negherebbe la registrazione.
- Questo rifiuto degli eventi di log è ottenuto anche quando i logger utilizzati sono idonei per gestirli. Questo è molto ragionevole, come mostrato nel JournalDevServlet di seguito.
package com.journaldev;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class JournalDevServlet extends HttpServlet{
private static final long serialVersionUID = 1L;
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{
Logger logger = LogManager.getLogger(JournalDevServlet.class);
for(int i = 0 ; i < 1000 ; i++){
logger.trace("Index :: "+i+" :: JournalDev Database Logging Message !");
LogManager.getRootLogger().error("Index :: "+i+" :: JournalDev Database Logging Message !");
}
}
}
Anche se queste LoggerConfigs sono idonee per gestire gli eventi di log inviati, il filtro ha impedito che alcuni di essi venissero gestiti e quindi registrati. Puoi aggiungere questo per il concetto di Spazio di Registro per comprendere appieno il concetto di registrazione.
Layout di Log4j2
A causa dei diversi Appenders che consumano gli eventi di Log e della natura di ciascun appender, i layout sono realizzati per formare il LogEvent nel formato che soddisfa le esigenze di chiunque consumi l’evento di log. Nelle API Log4j 1.x e Logback, la trasformazione del layout degli eventi di log avveniva in una stringa, mentre i layout Log4j2 hanno considerato un modo diverso di trasformazione; e cioè trasformando il LogEvent in un array di byte. Questo nuovo tipo di trasformazione richiederebbe di configurare il Charset per garantire che l’array di byte contenga i valori corretti. È altamente consigliabile tornare al sito ufficiale di Apache Log4j2 e approfondire i Layout e i diversi tipi che Log4j2 fornisce. In questa sezione, considereremo il layout più famoso che viene sempre utilizzato dalla maggior parte dei nostri sviluppatori e di cui probabilmente avrai sentito parlare; è il PatternLayout.
Log4j2 PatternLayout
Il layout del modello è un modello stringa configurabile e flessibile mirato a formattare il LogEvent. Questo tipo di formattazione dipende dal concetto di modello di conversione. Questa sezione ti illustrerà le caratteristiche più importanti che il layout del modello fornisce. Il modello di conversione è correlato al modello di conversione che printf nel linguaggio C fornisce. Generalmente, il modello di conversione è composto da testo letterale ed espressioni di controllo del formato chiamate specificatori di conversione. La figura qui sotto ti mostra di quali parti è composto il modello di conversione: Questa figura sopra è un tentativo di semplificare il Modello di Conversione, ma è sicuramente meglio fare riferimento alla documentazione di Apache Log4j2 per ulteriori dettagli su Layout e Layout del Modello in particolare. Inoltre, puoi fare riferimento sopra agli eventi di log e vedere ogni volta quale Modello di Conversione viene utilizzato per formattare i messaggi.
Quale livello Log4j2 dovresti usare
La domanda più grande che potresti farti è quando dovrebbe essere utilizzato uno specifico livello di evento di log. Nel campo dello sviluppo, è normale utilizzare l’evento di log DEBUG mentre in produzione dovremmo utilizzare il livello INFO o WARN. La tabella qui sotto dovrebbe guidarti su quale livello di log4j2 dovrebbe essere utilizzato in quale caso.
Log Event Level | When It Should Be Used |
---|---|
OFF | When no events will be logged |
FATAL | When a severe error will prevent the application from continuing |
ERROR | When an error in the application, possibly recoverable |
WARN | When an event that might possible lead to an error |
INFO | When an event for informational purposes |
DEBUG | When a general debugging event required |
TRACE | When a fine grained debug message, typically capturing the flow through the application |
ALL | When all events should be logged |
Riepilogo del Tutorial su Log4j2
Log4j2 è la versione rinnovata del framework di registrazione di Apache. Log4j2 ha fornito un insieme di nuove funzionalità e miglioramenti delle prestazioni rispetto a Log4j1.x. Questo tutorial su log4j2 è destinato ad aiutarti a ottenere tutto in un’unica posizione. Poiché alcuni di questi concetti non sono così facili da coprire tutti in una volta, abbiamo deciso di concentrare i nostri sforzi nell’esplicare il concetto e nell’utilizzare alcuni esempi per una maggiore chiarezza. Gli appender, i filtri, i layout e le ricerche sono soggetti a questa regola. Alcuni punti importanti Per assicurarti di essere in grado di far funzionare l’applicazione sottostante e di evitare eventuali ostacoli, verifica quanto segue:
- Il tuo IDE Eclipse è abilitato a Maven.
- Il tuo Apache Tomcat ha un file JAR mysql-connector all’interno della cartella lib della tua Home di Apache.
- Sei a conoscenza di come utilizzare Maven.
Scarica il Progetto di Esempio di Apache Log4j 2
Questo è tutto per il tutorial su log4j2, spero che la maggior parte dei punti importanti siano stati coperti per farti iniziare ad utilizzare Log4j2 nella tua applicazione.