Log4j2 implementazione

di il
33 risposte

Log4j2 implementazione

Ciao a tutti,
ho implementato Log4j2 nel mio sito web ed ho notato che tutto procede bene, non senza difficoltà di configurazione, fino a che la variabile globale LOGGER non viene richiamata da una classe dotata di metodo main. Il lancio di una classe con il metodo main da parte di un cron, che soddisfa delle operazioni schedulate, sovrascrive tutto il registro.
Qualcuno che abbia esperienza con Log4j2, mi potrebbe suggerire un modo per evitare questo inconveniente?
Ciao e Grazie in anticipo
Fulvio

33 Risposte

  • Re: Log4j2 implementazione

    fulviot66 ha scritto:


    ho implementato Log4j2 nel mio sito web ed ho notato che tutto procede bene, non senza difficoltà di configurazione, fino a che la variabile globale LOGGER non viene richiamata da una classe dotata di metodo main. Il lancio di una classe con il metodo main da parte di un cron, che soddisfa delle operazioni schedulate, sovrascrive tutto il registro.
    Detto così purtroppo è un po' vago, nel senso che bisognerebbe vedere come è configurato log4j, come è fatta la applicazione, quali dipendenze ci sono, su quale server è deployata, ecc... Così "a naso" non sarebbe facile rispondere.

    Comunque quando dici "sovrascrive tutto il registro" cosa intendi?? Che RIscrive da zero il file di log? Allora bisogna vedere come è configurato log4j2 e soprattutto QUANDO viene inizializzato e se per qualche motivo c'è un momento in cui viene REinizializzato (quando succedono queste cose generalmente la causa è il class-loading).
  • Re: Log4j2 implementazione

    Ciao @andbin,
    hai perfettamente ragione, non sono stato sufficientemente chiaro, proverò a rimediare:
    Il file di configurazione log4j.xml è il seguente:
    <?xml version="1.0" encoding="UTF-8"?>
    <configuration status="warn">
    	
    	<appenders>
    		<Console name="Console" target="SYSTEM_OUT">
    			<PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} %-5plevel [%t]: %logger{50}:%L - %msg%n" />
    		</Console>
    
    		<RollingFile name="rollingFile" fileName="/tomcat-logs/rolling.log"
    			filePattern="/tomcat-logs/%d{yyyy-MM-dd}-rolling.log" ignoreExceptions="false">
    			<PatternLayout>
    					<Pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %-5plevel [%t]: %logger{50}:%L - %msg%n</Pattern>
    			</PatternLayout>
    			<Policies>
    				<OnStartupTriggeringPolicy />
    				<SizeBasedTriggeringPolicy size="10MB" />
    				<TimeBasedTriggeringPolicy />
    			</Policies>
    			<DefaultRolloverStrategy max="20" />
    		</RollingFile>
    	</appenders>
    
    	<loggers>
    		<root level="all"> <!-- Livelli possibili: all,trace,debug,info,warn,error,fatal,off -->
    			<appender-ref ref="Console" />
    			<appender-ref ref="rollingFile" />
    		</root>
    	</loggers>
    	
    </configuration>
    
    L'istanza viene richiamata all'interno della classe con:
    // Ottengo l'istanza Singleton Log4j2
    	private static final Logger LOGGER = LogManager.getLogger(Log4j2Test.class);
    
    Sostituendo di volta in volta il nome della classe con quella interessata.

    Il mio obbiettivo è di scrivere un file al giorno, max di 10MB, conservandone 20 e poi iniziare a sovrascriverli.

    Sicuramente sbaglio qualcosa, ma cosa?
  • Re: Log4j2 implementazione

    fulviot66 ha scritto:


    Il file di configurazione log4j.xml è il seguente:
    <?xml version="1.0" encoding="UTF-8"?>
    <configuration status="warn">
    	
    	<appenders>
       ......
    
    Scusa la domanda MA: stai usando log4j 1.x .... oppure log4j 2.x ??

    Quello che hai usato (nome file log4j.xml e la struttura del xml) è per la 1.x. Per la 2.x è un po' diverso.


    EDIT: sì in effetti hai usato Log4j 2 perché vedo LogManager.getLogger (per la 1.x si usava Logger.getLogger).

    Quindi devi cambiare:

    a) il file deve chiamarsi log4j2.xml
    b) la struttura è differente, trovi informazioni qui:
    https://logging.apache.org/log4j/2.x/manual/configuration.html
    e
    https://logging.apache.org/log4j/2.x/manual/migration.html
  • Re: Log4j2 implementazione

    Grazie @andbin,
    per le precisazioni.
    In effetti il file di configurazione si chiama log4j2.xml e la struttura che ho ricavato è frutto di una ricerca sia sulla documentazione Apache, che su vari tutorial e sembrerebbe funzionare.
    Credo anche di aver risolto il problema che avevo lanciando classi che contengono un main come Java Application, rimuovendo la riga:
    <OnStartupTriggeringPolicy />
    Sembra che fosse questa a riavviare il Log ad ogni avvio di JVM, quindi posso considerare chiusa la mia richiesta.
  • Re: Log4j2 implementazione

    fulviot66 ha scritto:


    In effetti il file di configurazione si chiama log4j2.xml e la struttura che ho ricavato è frutto di una ricerca sia sulla documentazione Apache, che su vari tutorial e sembrerebbe funzionare.
    Ok però mi pare strano che ti "funzioni" perché ad esempio nel pezzo che hai postato prima si vede es.

    <appender-ref ref="Console" />

    ma questo è di log4j 1.x mentre per log4j 2.x è

    <AppenderRef ref="Console"/>

    Cioè .. i nomi/struttura sono differenti. Quel documento di migration che ho linkato è utile in questo senso.
  • Re: Log4j2 implementazione

    Fantastico, GRAZIE!
    btw: esiste un parametro specifico per fargli loggare anche l'IP remoto del chiamante?
  • Re: Log4j2 implementazione

    fulviot66 ha scritto:


    btw: esiste un parametro specifico per fargli loggare anche l'IP remoto del chiamante?
    Se intendi dire che il IP compaia su TUTTE le righe di log (in un punto che scegli tu della riga) generate lungo tutto un stack di chiamate nella gestione di una request, allora devi vedere il concetto di MDC (Mapped Diagnostic Context) di log4j.

    Vedi https://logging.apache.org/log4j/2.x/manual/thread-context.html
    Se hai dubbi, chiedi. Non uso normalmente il MDC ma se ti serve vediamo ...
  • Re: Log4j2 implementazione

    Ciao @andbin,
    ho provato ad implementare MDC, che nella mia versione 2.4 di Log4j2 si chiama ThreadContextStack, ma senza successo.
    Ho provato anche a cercare un tutorial, oltre agli esempi del manuale, ma ho trovato soltanto cose che riguardano Log4j o Slf4j e niente su Log4j2. Di seguito gli snippet:
    Log4j2.xml
    <?xml version="1.0" encoding="UTF-8"?>
    <Configuration status="warn">
    	<Appenders>
    		<Console name="Console" target="SYSTEM_OUT">
    			<PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} %X{logData} %-5p [%t]: %logger{50}:%L - %msg%n" />
    		</Console>
    		<RollingFile name="RollingFile" fileName="/tomcat-logs/afi-rolling.log"
    			filePattern="/tomcat-logs/%d{yyyy-MM-dd}-afi-rolling.log" ignoreExceptions="false">
    			<PatternLayout>
    					<Pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %X{logData} %-5p [%t]: %logger{50}:%L - %msg%n</Pattern>
    			</PatternLayout>
    			<Policies>
    				<!-- <OnStartupTriggeringPolicy /> Rilancia il log all'avvio della JVM -->				
    				<SizeBasedTriggeringPolicy size="10MB" />
    				<TimeBasedTriggeringPolicy />
    			</Policies>
    			<DefaultRolloverStrategy max="20" />
    		</RollingFile>
    	</Appenders>
    	<Loggers>
    		<Root level="all"> <!-- Livelli possibili: all,trace,debug,info,warn,error,fatal,off -->
    			<AppenderRef ref="Console" />
    			<AppenderRef ref="RollingFile" />
    		</Root>
    	</Loggers>
    </Configuration>
    LogEnricherFilter.java
    /**
     * @author fulvio
     * @since 2020/07/16
     * 
     * Tiene traccia degli IP remoti, assegnando una REQ_ID univoca ad IP
     */
    @WebFilter(urlPatterns = "/*")
    public class LogEnricherFilter implements Filter {
    	@Override
    	public void doFilter(ServletRequest request, ServletResponse response,
    			FilterChain filterChain) throws IOException, ServletException {
    		try {
    			String logData = String.format("[IP: %s | REQ_ID: %s]",
    					request.getRemoteAddr(), UUID.randomUUID().toString());
    			ThreadContext.push("logData", logData);
    			filterChain.doFilter(request, response);
    		} finally {
    			ThreadContext.pop();
    		}
    	}
    
    	@Override
    	public void destroy() {
    	}
    
    	@Override
    	public void init(FilterConfig arg0) throws ServletException {
    		
    	}
    }
    web.xml
    
    <filter>
        <filter-name>LogEnricherFilter</filter-name>
        <filter-class>miopackage.util.LogEnricherFilter</filter-class>
    </filter>
    	...
    <filter-mapping>
        <filter-name>LogEnricherFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
        
  • Re: Log4j2 implementazione

    fulviot66 ha scritto:


    ho provato ad implementare MDC, che nella mia versione 2.4 di Log4j2 si chiama ThreadContextStack, ma senza successo.
    Sì, nel "vecchio" log4j (intendo la 1.x) c'erano i due concetti MDC e NDC (N sta per Nested) che erano distinti e gestiti separatamente. In log4j 2.x questi due concetti ci sono ancora ma sono stati tecnicamente "fusi" insieme in quel ThreadContext.

    La cosa comunque è abbastanza semplice:

    a) Nel pattern del log devi mettere nel punto che vuoi lo specificatore per espandere il valore di una chiave gestita con il MDC, ad esempio:

    <PatternLayout pattern="%d %-5p [%t] IP=%X{remIP} %C{2} (%F:%L) - %m%n"/>

    Nota che il valore della chiave è assolutamente a scelta. Ho messo remIP ma puoi mettere IP, remoteIP, remInd, ecc... come ti pare.

    b) Poi la questione è dove mettere il put del valore. MA dipende dalla API che usi a livello web. Cosa hai usato? Spring? JavaEE? Altro?
    Dal momento che il set di quel valore deve fare da "contesto" per tutta la gestione di una request in un certo thread, allora dovrebbe essere settato possibilmente in un punto più "alto" possibile della richiesta.
    Con Spring l'ideale sarebbe farlo in un interceptor che intercetta tutte le chiamate. Per il momento, per provare, se usi Spring puoi farlo all'inizio di un metodo di un controller.
    ThreadContext.put("remIP", request.getRemoteAddr());
    Dove request è il classico HttpServletRequest di Java (lo puoi avere come parametro in un controller Spring)

    Se usi Spring e non sai come fare un interceptor, ti faccio un esempio.


    EDIT: ho visto il codice che hai aggiunto. Con push/pop stai usando il NDC. A te basta usare il MDC, quindi put. E nota che %X è per il MDC ... non NDC.

    EDIT2: ho notato che hai usato un filter mettendo @WebFilter(urlPatterns = "/*") ma poi l'hai dichiarato anche nel web.xml
    Non è corretto. O usi @WebFilter O dichiari nel web.xml. Non vanno usati entrambi.
  • Re: Log4j2 implementazione

    Grazie @andbin,
    probabilmente mentre elaboravi la risposta, siamo giunti entrambi alle stesse conclusioni.
    Continuo però a non capire perché mi lavora a singhiozzo su localhost.
    Domani deployo sul server di test e ti saprò dire meglio.
  • Re: Log4j2 implementazione

    Ciao @andbin,
    qui ogni giorno se ne scopre una nuova!
    Mi riferisci al fatto che sono andato a vedere i log di 2 giorni fa sul server Tomcat di test e con mia grande sorpresa, i log sono limitati dalle ultime 100 linee registrate da mezzanotte alle 08:15.
    Mentre su quello di prod trovo addirittura soltanto le ultime 5 regitsrate dalle 00:00!
    Inoltre tutto ciò che sto scrivendo attraverso il LOGGER.info sul mio registro di rolling, viene riportato specularmente su /tomcat/instance/appname/logs/catalina.out.
    Questa ridondanza potrebbe dipendere dalle logging properties impostate di default su Tomcat?
    Se uso Log4J, devo eliminarle?
  • Re: Log4j2 implementazione

    fulviot66 ha scritto:


    Inoltre tutto ciò che sto scrivendo attraverso il LOGGER.info sul mio registro di rolling, viene riportato specularmente su /tomcat/instance/appname/logs/catalina.out.
    Questa ridondanza potrebbe dipendere dalle logging properties impostate di default su Tomcat?
    La tua configurazione di log4j 2 "butta" il logging ANCHE su System.out. E con Tomcat l'output su System.out per default va nel catalina.out (sugli Unix-like perlomeno).
    Lo dice la documentazione: https://tomcat.apache.org/tomcat-8.5-doc/logging.html#Console

    When running Tomcat on unixes, the console output is usually redirected to the file named catalina.out.
    [...] When running as a service on Windows, the console output is also caught and redirected, but the file names are different.
  • Re: Log4j2 implementazione

    Ok, e questo è comprensibile. Mio errore.
    Ecco cosa intendo per funzionamento a singhiozzo.
    
    2020-07-17 16:06:17.674 [IP: 0:0:0:0:0:0:0:1] INFO  [http-bio-8080-exec-9]:
    2020-07-17 16:13:07.921 [IP: 0:0:0:0:0:0:0:1] INFO  [http-bio-8080-exec-8]:
    2020-07-17 16:41:06.120  INFO  [http-bio-8080-exec-7]:
    2020-07-17 16:41:15.452  INFO  [http-bio-8080-exec-9]:
    2020-07-17 16:41:21.467  INFO  [http-bio-8080-exec-8]:
    2020-07-17 16:42:53.941  INFO  [http-bio-8080-exec-9]:
    
    Senza nessuna modifica nè al codice, nè alla configurazione.
    Questo invece come lo spieghi?
  • Re: Log4j2 implementazione

    Un'altra questione riguarda l'implementazione di log4j2 nelle pagine .jsp, ma anche qui é il buio totale.
    Dalla documentazione ufficiale mi sembra di aver capito che sia sufficiente specificare nel tag xmlns: il path http://logging.apache.org/log4j/tld/lo, ovvero
    
    <html xmlns:"http://www.w3.org/1999/xhtml" log:http://logging.apache.org/log4j/tld/
    per permettere a log4j2 di poter gestire adeguatamente i tag <log: >.
    Perciò mi sarei aspettato che il tag
    <log:log level="info" message="Sono nella homepage" />
    
    Scritto appunto nella homepage.jsp producesse una riga di log, invece nulla.
    Potrei avere gentilmente un aiutino?
    Grazie in anticipo
    Fulvio
Devi accedere o registrarti per scrivere nel forum
33 risposte