2013-01-16 13 views
16

Ho un'applicazione Struts con log4j per visualizzare le informazioni sull'applicazione.ID thread di visualizzazione invece nome thread nel registro

Il modello di output di log formato è il seguente:

log4j.appender.RALL.layout.ConversionPattern=[%p] %d{dd/MM/yyyy HH:mm:ss} [THREAD ID=%t] [CLASS=(%C{1}:%L)] %m%n 

ho bisogno di mostrare l'id filo invece il nome filo nel registro. Il carattere di conversione che mostra il nome del thread è% t. Non vedo nella documentazione di log4j il modo per ottenerlo.

Qualcuno può aiutarmi ??

+2

Avete provato con '% I'? – partlov

+0

Sì, l'ho provato, ma come dici sotto funziona solo per IBM Server –

+0

@partlov Che non funziona per me, '% i' non può essere riconosciuto, sto usando log4j' 1.2.16'. –

risposta

7

È possibile ma non è così facile come utilizzare solo alcuni modelli preconfigurati.

Log4j 1.X e Log4j 2.x non hanno schemi preconfigurati per la stampa ID filettatura ma è sempre possibile utilizzare alcuni "trucchi magici".

PatternLayout sta usando PatternParser classe che è marchio come final classe e ha mappa statica di "pattern" come chiavi e Converters classi come valori. Ogni volta che Parses trova il pattern utilizzando per il formato del modello di registrazione a partire da %, utilizza il convertitore che corrisponde a questa chiave del modello nella mappa.

Non puoi aggiungere la tua regola per quella mappa, ma è ancora possibile scrivere il proprio MyOwnPatternLayout:

public class MyOwnPatternLayout extends PatternLayout 

che nella sua format metodo fare tale trucco:

public String format(LoggingEvent event) { 
    String log = super.format(event); 
    /* 
    Now you just have to replace with regex all occurences of %i or 
    any mark you would like to use as mark to represent Thread ID 
    with Thread ID value. 
    Only thing you have to be sure to not use any mark as your Thread ID 
    that already is defined by PatterParser class 
    */ 
    return log.replaceAll("%i", someThreadID); 
} 

L'unico problema è che devi ottenere quell'ID thread in qualche modo. A volte tutto ciò che dovete fare è quello di analizzare il nome Discussione strega può facilmente si raccolgono:

String threadName = event.getThreadName(); 

Per esempio Apache-Tomcat messo ID filo alla fine del nome del thread http-nio-/127.0.0.1-8084" -exec-41.

per essere sicuri che ID thread è corretta si può anche fare la propria sottoclasse di LogginEvent e Logger (MyLoggingEvent e myLogger) e dentro myLogger creare strega MyLoggingEvent terrà anche come argomento ID thread non unico filo Nome. Quindi puoi facilmente raccoglierlo nel codice sopra.

Siamo spiacenti per la risposta lunga e spero che questo almeno ti darà un po 'di aiuto.

+0

Utilizzo di Thread.currentThread(). GetId() in MyOwnPatternLayout? –

+0

Nop, perché quando si utilizza 'logger.info()' o qualche altro metodo da 'org.apache.log4j.Logger', viene creata una nuova istanza di 'LoggingEvent' e sa che Thread lo ha creato ma quando il registro viene aggiunto a file o console o qualsiasi altra cosa con il tuo 'MyPatternLayout' succede in altro Thread e ci sono informazioni su Thread usando' logger.info() 'esiste solo all'interno dell'istanza di LoggingEvent, in altre parole non è disponibile con' Thread.currentThread(). getId () '. Come ho scritto, devi sottoclassi una piccola classe 'Logger' e una classe' LoggingEvent'. – emka86

+0

Grazie, la soluzione penso sia quella di creare una sottoclasse di LoggingEvent e mantenere la classe predefinita di Logger perché non riesco a cambiare la classe di Logger nell'applicazione (più di 500 classi che lo utilizzano). Come posso assegnare la classe MyLoggingEvent alla classe predefinita di Logger? –

2

Penso che non sia possibile mostrare ID thread con la formattazione standard di log4j. Ho anche studiato attraverso il codice della classe PatterParser e non ho trovato nulla che possa essere utile. Ho trovato alcune soluzioni personalizzate, ma solo per server IBM che ha l'opzione %i:

% i: inserisce l'ID filo. A differenza del nome del thread (indicato da% t), questo è l'ID numerico del thread. Si noti che questo parametro è specifico per Iniziare, mentre gli altri parametri elencati qui sono standard con log4j.

Vedi this link

+4

Colpisce (dolorosamente) chiunque altro tranne me che nonostante numerose revisioni e una versione 2 di log4j che ha modificato/inutilmente il formato del file di configurazione da proprietà a xml, che non ci sia un modo semplice per aggiungere l'id di thread, qualcosa che essere effettivamente utile all'USO del prodotto? È possibile che ci stiano tutti perdendo qualcosa? – stu

6

Un modo per farlo è quello di aggiungere da soli, usando log4j MDC. Lo usiamo per aggiungere il nome utente per le richieste web. Facciamo questo in un filtro all'inizio di ogni richiesta. Per esempio.

import org.apache.log4j.MDC; 

... 

    // Add username to MDC 
    String username = ...; 
    MDC.put("user", username); 

Quindi aggiungere [%X{user}] al vostro modello di conversione.

3

È possibile utilizzare la mappa ThreadContext per la fornitura di meta-dati per log4j2. Questa è una mappa di valori stringa che è possibile aggiungere tramite la normale formattazione.

String threadId = String.valueOf(Thread.currentThread().getId()); 
ThreadContext.put("TId", threadId); 

e un motivo molto più ragionevole:

<PatternLayout pattern="%d{yyyyMMdd}T%d{HHmmss.SSS} %-5level [%t] [%5X{TId}] %15c{1} - %msg%n"/> 

Full Log4j2 documentation on "Fish Tagging"

0

Una possibile soluzione è quella di creare la propria classe che si trova tra il codice e Log4J e aggiunge l'ID del thread per ogni registro messaggio:

public class ThreadLogger 
{ 
    // Constructor declared private to prevent instantiation. Use static methods instead. 
    private ThreadLogger() {} 

    private static enum LogLevel 
    { 
     TRACE, 
     DEBUG, 
     INFO, 
     WARN, 
     ERROR 
    } 

    public static void trace(String message) 
    { 
     logMessage(message, LogLevel.ERROR); 
    } 

    public static void debug(String message) 
    { 
     logMessage(message, LogLevel.ERROR); 
    } 

    public static void info(String message) 
    { 
     logMessage(message, LogLevel.ERROR); 
    } 

    public static void warn(String message) 
    { 
     logMessage(message, LogLevel.WARN); 
    } 

    public static void error(String message) 
    { 
     logMessage(message, LogLevel.ERROR); 
    } 

    private static void logMessage(String message, LogLevel logLevel) 
    { 
     // Get the Log4J logger for the class that originally wanted to log the message 
     String callingClassName = Thread.currentThread().getStackTrace()[3].getClassName(); 
     Class callingClass; 
     try 
     { 
      callingClass = Class.forName(callingClassName); 
     } 
     catch(ClassNotFoundException e) 
     { 
      String errorMessage = String.format("Could not reference class [%s]. Unable to log call!", callingClassName); 
      throw new RuntimeException(errorMessage); 
     } 
     Logger logger = Logger.getLogger(callingClass); 

     // Get the thread ID and place it in front of the logged message 
     long threadId = Thread.currentThread().getId(); 
     String formattedMessage = String.format("[%s] %s", threadId, message); 

     // Log the message 
     switch(logLevel) 
     { 
      case TRACE: 
       logger.trace(formattedMessage); 
       break; 
      case DEBUG: 
       logger.debug(formattedMessage); 
       break; 
      case INFO: 
       logger.info(formattedMessage); 
       break; 
      case WARN: 
       logger.warn(formattedMessage); 
       break; 
      case ERROR: 
       logger.error(formattedMessage); 
       break; 
     } 
    } 
} 

Aspetti negativi:

  • prestazioni? Ciò aggiunge alcuni passaggi aggiuntivi a ogni istruzione del registro.
  • Stabilità? Ciò aggiunge un potenziale punto di errore (la chiamata Class.forName).
  • È necessario sostituire tutte le istruzioni di registro esistenti con le chiamate alla nuova classe.
  • L'ID del thread non verrà visualizzato fino a dopo la normale formattazione di Log4J. IE:

1234 [main] INFO com.foo.bar.Baz - [1] Hello world on thread #1! 
 
1234 [main] INFO com.foo.bar.Baz - [2] Hello world on thread #2!

0

creo la mia appender e impostare Thread.currentThread() getId() per la proprietà MDC.. % X {threadId} dovrebbe darmi l'id del thread. Questa soluzione funziona dal 1.2.15. È quindi possibile collegare AsyncAppender a questo.

public class CurrentThreadIdAppender extends AppenderSkeleton implements AppenderAttachable { 

    private final AppenderAttachableImpl appenders = new AppenderAttachableImpl(); 

... 

    @Override 
    protected void append(LoggingEvent event) { 
     synchronized (appenders) { 
      event.setProperty("threadId", String.valueOf(Thread.currentThread().getId())); 
      appenders.appendLoopOnAppenders(event); 
     } 
    } 

... 

} 
0

Un'altra soluzione elegante con log4j2 è quello di utilizzare org.apache.logging.log4j.core.pattern.LogEventPatternConverter.

È possibile scrivere una classe come questa

@Plugin(name = "ThreadIdConverter", category = "Converter") 
@ConverterKeys({ "tid" }) 
public class ThreadIdConverter extends LogEventPatternConverter { 

    protected ThreadIdConverter(String name, String style) { 
     super(name, style); 
    } 

    @Override 
    public void format(LogEvent event, StringBuilder toAppendTo) { 
     toAppendTo.append(getThreadId()); 
    } 

    protected String getThreadId() { 
     long id = Thread.currentThread().getId(); 
     return Long.toHexString(id); 
    } 

    public static ThreadIdConverter newInstance(String[] options) { 
     return new ThreadIdConverter("tid", "tid"); 
    } 
} 

In questo modo si sta creando un nuovo modello tid e si può utilizzare quando si definisce il layout del tuo appender

<Appenders> 
    <Console name="console" target="SYSTEM_OUT"> 
     <PatternLayout> 
      <Pattern>%d{dd-MMM HH:mm:ss.SSS} %-7level [%5tid] %logger - %message%n</Pattern> 
     </PatternLayout> 
    </Console> 
</Appenders> 

L'ultima cosa importante ricordare è come attivare il plugin log4j2.Per farlo è necessario aggiungere il pacchetto che contiene i plugin nel file di configurazione log4j2 utilizzando l'attributo package su Configuration nodo

<?xml version="1.0" encoding="UTF-8" ?> 
<!DOCTYPE Configuration> 
<Configuration status="warn" 
    packages="my.package.logging.plugins"> 
    <Appenders> 
     <Console name="console" target="SYSTEM_OUT"> 
      <PatternLayout> 
       <Pattern>%d{dd-MMM HH:mm:ss.SSS} %-7level [%5tid] %logger - %message%n</Pattern> 
      </PatternLayout> 
     </Console> 
    </Appenders> 
    <Loggers> 
     <Root level="warn"> 
      <AppenderRef ref="console" /> 
     </Root> 
     <Logger name="my.package" level="trace" /> 
    </Loggers> 
</Configuration> 
+1

Questo funzionerà solo se non si registra in modo asincrono (ad es. Con AsyncLogger)! – Ph3n1x

1

Estendere PatternLayout come qui di seguito, e quindi specificare MyPatternLayout con $X{threadId} nella stringa di formato.

Questa implementazione utilizza ThreadLocal per ridurre al minimo l'impatto sulle prestazioni di calcolo del ID thread:

MyPatternLayout extends PatternLayout { 

     private final ThreadLocal<String> threadId = new ThreadLocal<String>() { 

      @Override 
      protected String initialValue() { 
       String t = Long.toString(Thread.currentThread().getId()); 
       MDC.put("threadId", t); 
       return t; 
      } 
     }; 

     @Override 
     public String format(LoggingEvent event) { 

      this.threadId.get(); 
      return super.format(event); 
     } 
    } 
Problemi correlati