2014-12-03 19 views
5

Usiamo log4j 1.2.x per accedere al nostro prodotto e stiamo cercando di migrare a log4j 2.x nel prossimo futuro. Una delle funzionalità che abbiamo implementato è quella di registrare le informazioni di sistema e altri parametri importanti su ogni nuovo file di registro di rollover generato. Il modo in cui abbiamo implementato in log4j 1.2.x è che abbiamo esteso RollingFileAppender classe di log4j e hanno ignorato il metodo rollOver(), qui di seguito è il frammento di parte dell'implementazioneCome personalizzare log4j2 RollingFileAppender?

@Override 
public void rollOver() { 

    super.rollOver(); //We are not modifying it's default functionality but as soon as rollOver happens we apply our logic 

    // 
    // Logic to log required system properties and important parameters. 
    // 

} 

Ora, come vogliamo migrare verso log4j2 siamo guardando una nuova soluzione per ottenere la stessa funzionalità. Ma come vedo il codice sorgente per log4j2 è molto diverso dal vecchio codice sorgente. La classe RollingFileAppender non contiene il metodo rollover() poiché è stato spostato su RollingManagerhelper ed è stato impostato su private come bene.

Sviluppare un nuovo pacchetto completo ed estendere/implementare alcune classi astratte/helper da log4j2 è una delle possibili soluzioni per noi ma richiederebbe molto codice/copia poiché non modifichiamo ciò che RollingFileAppender preferisce, ma abbiamo solo bisogno piccola estensione ad esso. C'è una soluzione semplice ad esso?

UPDATE

ho creato una ricerca personalizzata secondo il suggerimento nelle risposte e al di sotto è come ho creato;

@Plugin(name = "property", category = StrLookup.CATEGORY) 
public class CustomLookup extends AbstractLookup { 

private static AtomicLong aLong = new AtomicLong(0); 

@Override 
public String lookup(LogEvent event, String key) { 

    if (aLong.getAndIncrement() == 0) { 
     return "this was first call"; 
    } 
    if (key.equalsIgnoreCase("customKey")) { 
     return getCustomHeader(); 
    } else { 
     return "non existing key"; 
    } 
} 

private static String getCustomHeader() { 

    // Implementation of custom header 
    return "custom header string"; 

}} 

Ma questo non ha funzionato come menzionato; questo stampa sempre this was first call nell'intestazione. Ho anche provato a mettere il breakoint nella prima condizione di if e quello che ho notato è che viene chiamato solo una volta. Quindi quello che temo è che la classe customLookup venga inizializzata solo all'avvio quando log4j2 sta inizializzando le sue proprietà da xml config. Non so in quale altro modo avrei potuto implementare questa classe di ricerca personalizzata.

UPDATE 2

Dopo la suddetta implementazione ho provato in modo diverso bit che è come sotto;

private static AtomicLong aLong = new AtomicLong(0); 

@Override 
public String lookup(LogEvent event, String key) { 
    return getCustomHeader(key); 
} 

private static String getCustomHeader(final String key) { 

    if (aLong.getAndIncrement() == 0) { 
     return "this was first call"; 
    } 
    if (key.equalsIgnoreCase("customKey")) { 
     // Implementation for customKey 
     return "This is custom header"; 
    } else { 
     return "non existing key"; 
    } 
} 

Ma la stessa cosa è la stessa cosa. log4j2 crea le intestazioni durante l'inizializzazione dal suo file di configurazione xml e quindi usa le intestazioni dalla memoria. Il valore return del metodo lookup() sottoposto a override non può essere modificato dinamicamente in quanto viene richiamato solo durante l'inizializzazione. Qualsiasi altro aiuto sarebbe molto apprezzato.

+0

Se si desidera utilizzare la sostituzione della variabile di runtime, utilizzare double $$ quando si specifica la chiave variabile nel file delle proprietà log4j2. – TrueCurry

risposta

5

Un'alternativa all'utilizzo delle ricerche integrate consiste nel creare una ricerca personalizzata. Questo può essere realizzato in poche righe di codice con un plugin log4j2. La tua ricerca personalizzata fornisce quindi il valore esatto che desideri mostrare nell'intestazione del file ad ogni passaggio.

Il codice del plugin dovrebbe essere simile a questa:

package com.mycompany; 
import org.apache.logging.log4j.core.LogEvent; 
import org.apache.logging.log4j.core.config.plugins.Plugin; 
import org.apache.logging.log4j.core.lookup.AbstractLookup; 
import org.apache.logging.log4j.core.lookup.StrLookup; 

/** 
* Looks up keys from a class SomeClass which has access to all 
* information you want to provide in the log file header at rollover. 
*/ 
@Plugin(name = "setu", category = StrLookup.CATEGORY) 
public class SetuLookup extends AbstractLookup { 

    /** 
    * Looks up the value of the specified key by invoking a 
    * static method on SomeClass. 
    * 
    * @param event The current LogEvent (ignored by this StrLookup). 
    * @param key the key to be looked up, may be null 
    * @return The value of the specified key. 
    */ 
    @Override 
    public String lookup(final LogEvent event, final String key) { 
     return com.mycompany.SomeClass.getValue(key); 
    } 
} 

Poi, nella configurazione è possibile utilizzare l'intestazione del modello di layout per produrre questo ad ogni rollover:

<RollingFile name="RollingFile" fileName="logs/app.log" 
      filePattern="logs/$${date:yyyy-MM}/app-%d{MM-dd-yyyy}.log.gz"> 

    <!-- use custom lookups to access arbitrary internal system info --> 
    <PatternLayout header="${setu:key1} ${setu:key2}"> 
    <Pattern>%d %m%n</Pattern> 
    </PatternLayout> 
    <Policies> 
    <TimeBasedTriggeringPolicy /> 
    </Policies> 
</RollingFile> 

Il log4j2 manuale ha dettagli sulla costruzione/distribuzione custom plugins. Breve riassunto:

Il modo più semplice è quello di costruire il vaso con Maven; ciò farà sì che il processore di annotazione log4j2 produca un file di indice binario nel jar in modo che il tuo plugin possa essere trovato rapidamente da log4j2.

L'alternativa è quella di specificare il nome del pacchetto della classe plug-in l'attributo di configurazione del log4j2.xml packages:

<Configuration status="warn" packages="com.mycompany"> 
    ... 

UPDATE: Si noti che nell'implementazione di ricerca è possibile ottenere creativo come necessario . Ad esempio:

package com.mycompany; 

public class SomeClass { 
    private static AtomicLong count = new AtomicLong(0); 

    public static String getValue(final String key) { 
     if (count.getAndIncrement() == 0) { // is this the first call? 
      return ""; // don't output a value at system startup 
     } 
     if ("FULL".equals(key)) { 
      // returns info to shown on rollover, nicely formatted 
      return fullyFormattedHeader(); 
     } 
     return singleValue(key); 
    } 
    .... 
} 
+0

Ovviamente sentiti libero di usare un nome di plugin diverso da "setu". :-) –

+0

sembra promettente. provatelo – Setu

+0

Ho provato, ma due problemi uno è che la chiave nell'attributo header deve essere completamente formattata come richiesto per essere loggato, ad es. se si utilizza più di una chiave nell'intestazione non c'è modo di separare in linee diverse. Il secondo è il problema principale, l'esigenza è di stampare le proprietà del sistema all'inizio del nuovo file di rollover non all'inizio dell'esecuzione del programma. l'intestazione viene registrata all'inizio di ogni esecuzione e ad ogni rollover che semplicemente non ha senso. – Setu

2

Ciò può essere eseguito con la configurazione. È possibile utilizzare l'intestazione del layout del modello per fornire informazioni. Questo sarà incluso in ogni rollover.

<RollingFile name="RollingFile" fileName="logs/app.log" 
      filePattern="logs/$${date:yyyy-MM}/app-%d{MM-dd-yyyy}.log.gz"> 

    <!-- use built-in lookups for system info or system properties --> 
    <PatternLayout header="${java:runtime} - ${java:vm} - ${java:os}"> 
    <Pattern>%d %m%n</Pattern> 
    </PatternLayout> 
    <Policies> 
    <TimeBasedTriggeringPolicy /> 
    </Policies> 
</RollingFile> 
+0

Grazie per la risposta @Remko ma una domanda. Il prodotto che abbiamo è enorme e installiamo più proprietà di sistema in fase di esecuzione, fondamentalmente si tratta di un motore che gira continuamente sul server e interagisce con webservice, strumenti gui e db back-end. utilizzando questo approccio abbiamo bisogno di scrivere tutti i nomi delle proprietà con precisione nell'intestazione. C'è un modo di utilizzare un qualche tipo di loop per includere tutte le proprietà di sistema nell'intestazione. – Setu

+0

È possibile configurare una singola proprietà di sistema nell'intestazione e aggiornare il valore di tale proprietà aggiornando i valori di tutte le altre proprietà di sistema a cui si è interessati, funzionerebbe? –

+0

Grazie per il suggerimento; Dovrò fare un tentativo ma questo significa che ogni volta che nel mio codice imposto le proprietà di sistema dovrò aggiungerlo a questa proprietà di sistema. Sembra come se funzionasse, ma penso che questo sarebbe considerato come soluzione temporanea dal management e non una soluzione reale. grazie per il tuo suggerimento comunque – Setu