webentwicklung-frage-antwort-db.com.de

Anzeige der Thread-ID statt des Thread-Namens im Protokoll

Ich habe eine Struts-Anwendung mit log4j, um Informationen zur Anwendung anzuzeigen.

Das Muster zum Formatieren der Protokollausgabe lautet wie folgt:

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

Ich muss die Thread-ID anstelle des Thread-Namens im Protokoll anzeigen. Das Konvertierungszeichen, das den Threadnamen anzeigt, lautet% t. Ich sehe in der log4j-Dokumentation nicht den Weg, um es zu bekommen.

Kann mir jemand helfen??

18
Alex Cuervo

Es ist möglich, aber nicht so einfach wie das Verwenden einiger vorkonfigurierter Muster.

Log4j 1.X und Log4j 2.x haben keine vorkonfigurierten Muster zum Drucken der Thread-ID, Sie können jedoch immer einen "Zaubertrick" verwenden.

PatternLayout verwendet eine PatternParser-Klasse, die als final-Klasse markiert ist und eine statische Zuordnung von "Mustern" als Schlüssel und Converters-Klassen als Werte enthält. Jedes Mal, wenn Parses ein Muster zur Protokollierung des Musterformats findet, das mit % beginnt, wird ein mit diesem Musterschlüssel in der Map übereinstimmender Konverter verwendet.

Sie können der Map keine eigene Regel hinzufügen, aber Sie können trotzdem Ihr eigenes MyOwnPatternLayout schreiben:

public class MyOwnPatternLayout extends PatternLayout

welche in ihrer format-Methode einen solchen Trick ausführen wird:

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

Das einzige Problem ist, dass Sie diese Thread-ID auf irgendeine Weise erhalten müssen. Manchmal müssen Sie nur den Thread-Namen analysieren, den Sie leicht sammeln können:

String threadName = event.getThreadName();

Zum Beispiel hat Apache-Tomcat die Thread-ID am Ende des Thread-Namens eingefügt http-nio-/127.0.0.1-8084 "-exec-41.

Um sicher zu sein, dass die Thread-ID korrekt ist, können Sie auch Ihre eigene Unterklasse für LogginEvent und Logger (MyLoggingEvent und MyLogger) erstellen. In MyLogger erstellen Sie MyLoggingEvent, das auch den Thread-ID und nicht nur den Thread-Namen verwendet. Dann können Sie es einfach im obigen Code sammeln.

Tut mir leid für eine lange Antwort und ich hoffe, das wird Ihnen wenigstens helfen.

8
emka86

Sie können dies unter Verwendung von log4j MDC selbst hinzufügen. Wir verwenden es, um den Benutzernamen für Webanfragen hinzuzufügen. Wir tun dies in einem Filter zu Beginn jeder Anfrage. Z.B.

import org.Apache.log4j.MDC;

...

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

Fügen Sie dann [%X{user}] zu Ihrem Konvertierungsmuster hinzu. 

7
TedTrippin

Ich habe Thread-ID und Thread-Priorität für das kommende 2.6 implementiert. Tracking hier: https://issues.Apache.org/jira/browse/LOG4J2-1299

Sie können einen 2.6-SNAPSHOT-Build aus dem Apache-Snapshots-Repository abrufen: https://repository.Apache.org/content/repositories/snapshots/

6
Gary Gregory

Sie können die ThreadContext Map verwenden, um log4j2 Metadaten bereitzustellen. Dies ist eine String Map von Werten, die Sie [~ # ~] durch normale Formatierung hinzufügen können [~ # ~].

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

Und ein viel vernünftigeres Muster:

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

Vollständige Log4j2-Dokumentation zu "Fish Tagging"

5
Terry

Erweitern Sie PatternLayout wie folgt und geben Sie dann MyPatternLayout mit $X{threadId} in der Formatzeichenfolge an.

Diese Implementierung verwendet ThreadLocal, um die Auswirkungen auf die Leistung bei der Berechnung der Thread-ID zu minimieren:

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

Ich denke, es ist nicht möglich, die Thread-ID mit der Standard-Formatierung von log4j anzuzeigen. Ich habe auch durch den Code von PatterParser class gesucht und nichts gefunden, was nützlich sein könnte. Ich habe einige kundenspezifische Lösungen gefunden, aber nur für IBM-Server, die die Option %i haben:

% i: Fügt die Thread-ID ein. Im Gegensatz zum Thread-Namen (angegeben durch% t) ist dies die numerische ID des Threads . Beachten Sie, dass dieser Parameter für Initiate spezifisch ist, während die anderen hier aufgeführten Parameter standardmäßig mit log4j verwendet werden.

Siehe diesen Link

2
partlov

Ich weiß nicht, wann es eingeführt wird, aber in log4j2 haben wir % tid für

Gibt die ID des Threads aus, der das Protokollierungsereignis generiert hat.

https://logging.Apache.org/log4j/2.x/manual/layouts.html

1
abbas

Ich erstelle meinen eigenen Appender und setze Thread.currentThread (). GetId () auf die MDC-Eigenschaft. % X {threadId} sollte mir die Thread-ID geben. Diese Lösung funktioniert seit dem 1.2.15. Sie können dann AsyncAppender an dieses anschließen. 

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

Eine weitere elegante Lösung mit log4j2 ist die Verwendung von org.Apache.logging.log4j.core.pattern.LogEventPatternConverter.

Sie können eine Klasse so schreiben

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

Auf diese Weise erstellen Sie ein neues Muster tid und können es verwenden, wenn Sie das Layout Ihres Appenders definieren

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

Das Letzte, was Sie zu beachten haben, ist die Aktivierung Ihres log4j2-Plugins. Dazu müssen Sie das Paket, das Ihre Plugins enthält, der log4j2-Konfigurationsdatei hinzufügen, indem Sie das Attribut package im Knoten Configuration verwenden

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

Eine mögliche Lösung besteht darin, eine eigene Klasse zu erstellen, die zwischen Ihrem Code und Log4J liegt und die Thread-ID an jede Protokollnachricht anfügt:

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

Nachteile:

  • Performance? Dies fügt jeder Protokollanweisung einige zusätzliche Schritte hinzu.
  • Stabilität? Dies fügt einen potenziellen Fehlerpunkt hinzu (den Aufruf Class.forName).
  • Sie müssen alle vorhandenen Protokollanweisungen durch Aufrufe der neuen Klasse ersetzen.
  • Die Thread-ID wird erst nach der regulären Log4J-Formatierung angezeigt. 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
Alex Johnson