2013-01-16 11 views
16

Mam aplikację Struts z log4j, aby wyświetlić informacje o aplikacji.Wyświetl nazwę wątku zamiast nazwy wątku w logu

Wzór na wyjściu format dziennika jest następująca:

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

muszę pokazać identyfikator wątek zamiast nazwa wątek w dzienniku. Znak konwersji wyświetlający nazwę wątku to% t. Nie widzę w dokumentacji log4j sposobu, aby to uzyskać.

Czy ktoś może mi pomóc?

+2

Próbowałeś z '% I'? – partlov

+0

Tak, próbowałem, ale jak pan mówi poniżej działa tylko dla serwera IBM –

+0

@partlov To nie działa dla mnie, '% i' nie może zostać rozpoznany, używam log4j' 1.2.16'. –

Odpowiedz

7

Jest to możliwe, ale nie tak proste, jak po prostu użycie niektórych wstępnie skonfigurowanych wzorów.

Log4j 1.X i Log4j 2.x nie mają wstępnie skonfigurowanych wzorców do drukowania Identyfikatora nici, ale zawsze możesz użyć "magicznej sztuczki".

PatternLayout korzysta PatternParser klasę, która jest znak jako final klasy i ma mapę statyczne „wzorców” jako klucze i Converters klasach wartości. Za każdym razem, gdy Parses odnajduje wzorzec przy użyciu formatu wzoru rejestrowania zaczynającego się od %, używa konwertera dopasowanego do tego wzoru na mapie.

Nie można dodać własne reguły do ​​tej mapie, ale nadal można napisać własny MyOwnPatternLayout:

public class MyOwnPatternLayout extends PatternLayout 

które będą w jego format sposób zrobić taką sztuczkę:

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); 
} 

Jedyny problem jest to, że musisz w jakiś sposób uzyskać ten identyfikator wątku. Czasem wszystko co musisz zrobić, to do analizowania nazwa wątku czarownicę można w łatwy zbierać:

String threadName = event.getThreadName(); 

Na przykład Apache Tomcat umieścić identyfikator wątku na końcu nazwy wątku http-nio-/127.0.0.1-8084" -exec-41.

Aby upewnić się, że identyfikator wątku jest poprawny, możesz także utworzyć własną podklasę LogginEvent i Logger (MyLoggingEvent i MyLogger), a wewnątrz MyLogger utworzyć MyLoggingEvent również jako argument ID wątku nie tylko Nazwa wątku. Wtedy możesz łatwo pobrać go w powyższym kodzie.

Przepraszamy za długą odpowiedź i mam nadzieję, że to przynajmniej pomoże.

+0

Używanie Thread.currentThread(). GetId() w MyOwnPatternLayout? –

+0

Nop, ponieważ gdy użyjesz 'logger.info()' lub innych metod z 'org.apache.log4j.Logger', wtedy zostanie utworzona nowa instancja' LoggingEvent' i wie ona o wątku, który ją utworzył, ale po dołączeniu dziennika plik lub konsola lub cokolwiek innego przez twój "MyPatternLayout" zdarza się w innym wątku i tam informacje o wątku za pomocą 'logger.info()' istnieją tylko wewnątrz instancji LoggingEvent, innymi słowy nie jest dostępne w 'Thread.currentThread(). getId () '. Jak napisałem, musisz podklasować trochę klasy 'Logger' i' LoggingEvent'. – emka86

+0

Dzięki, Myślę, że rozwiązaniem jest stworzenie podklasy LoggingEvent i utrzymanie domyślnej klasy Logger, ponieważ nie mogę zmienić klasy Logger w aplikacji (ponad 500 klas używających go). Jak przypisać klasę MyLoggingEvent do domyślnej klasy Logger? –

2

Myślę, że nie można wyświetlić identyfikatora wątku ze standardowym formatowaniem log4j. Sprawdziłem również za pomocą kodu klasy PatterParser nic nie znaleziono, co może być przydatne. Znalazłem kilka niestandardowych rozwiązań, ale tylko dla serwera IBM z opcją %i:

% i: wstawia identyfikator wątku. W przeciwieństwie do nazwy wątku (wskazywanej przez% t), jest to numeryczny identyfikator wątku. Należy zauważyć, że ten parametr dotyczy Inicjowania, podczas gdy pozostałe wymienione tutaj parametry są standardem w log4j.

Zobacz this link

+4

Czy uderza (boleśnie) ktokolwiek inny oprócz mnie, że pomimo licznych poprawek i wersji 2 log4j, która tak wygodnie/bezużytecznie zmieniła format pliku konfiguracyjnego z właściwości na xml, że nie ma prostego sposobu na dodanie identyfikatora wątku, coś co mogłoby faktycznie przydaje się do UŻYTKOWANIA produktu? Czy to możliwe, że WSZYSTKO czegoś nie ma? – stu

6

Jednym ze sposobów można to zrobić jest dodanie go samodzielnie za pomocą log4j MDC. Używamy go do dodawania nazwy użytkownika do żądań internetowych. Robimy to w filtrze na początku każdego żądania. Na przykład.

import org.apache.log4j.MDC; 

... 

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

Następnie dodaj [%X{user}] do wzoru konwersji.

3

Możesz użyć mapy ThreadContext Map do dostarczenia metadanych do log4j2. Jest to ciągowa mapa wartości, które można dodać za pomocą normalnego formatowania.

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

i dużo bardziej rozsądne Wzór:

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

Full Log4j2 documentation on "Fish Tagging"

0

Jednym z możliwych rozwiązań jest stworzenie własnej klasy, która znajduje się pomiędzy kodzie i log4j i dołącza identyfikator wątku do każdego dziennika wiadomość:

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; 
     } 
    } 
} 

Wady:

  • Wydajność? To dodaje kilka dodatkowych kroków do każdego wyciągu dziennika.
  • Stabilność? To dodaje potencjalny punkt awarii (wywołanie Class.forName).
  • Musisz zastąpić wszystkie swoje istniejące wyciągi w dzienniku telefonami do nowej klasy.
  • Identyfikator wątku pojawi się dopiero po zwykłym formatowaniu 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

tworzę własną appender i ustaw Thread.currentThread()(), aby getId nieruchomości. MDC. % X {threadId} powinien dać mi identyfikator wątku. To rozwiązanie działa od 1.2.15. Następnie możesz dołączyć do tego AsyncAppender.

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

Innym eleganckie rozwiązanie z log4j2 jest użycie org.apache.logging.log4j.core.pattern.LogEventPatternConverter.

Można napisać klasy jak ten

@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"); 
    } 
} 

W ten sposób tworzysz nowy wzór tid i można go używać podczas definiowania ukĹ,adu appender za

<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> 

Ostatnią ważną rzeczą aby zapamiętać, jak aktywować plugin log4j2.Aby to zrobić trzeba dodać pakiet, który zawiera wtyczek w pliku konfiguracyjnym log4j2 pomocą atrybutu package na Configuration węzła

<?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

To zadziała, jeśli nie zalogujesz się asynchronicznie (np. Z AsyncLogger)! – Ph3n1x

1

Extend PatternLayout jak poniżej, a następnie określ MyPatternLayout z $X{threadId} w ciągu formatu.

Ta implementacja wykorzystuje ThreadLocal aby zminimalizować wpływ na wydajność obliczania identyfikator wątku:

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); 
     } 
    } 
Powiązane problemy