web-dev-qa-db-fra.com

Afficher l'identifiant du fil au lieu du nom du fil dans le journal

J'ai une application Struts avec log4j pour afficher des informations sur l'application.

Le modèle pour formater la sortie du journal est le suivant:

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

Je dois montrer le thread id à la place du nom du thread in log. Le caractère de conversion qui affiche le nom du fil est% t. Je ne vois pas dans la documentation de log4j le moyen de l'obtenir.

Quelqu'un peut-il m'aider??

18
Alex Cuervo

C'est possible mais pas si facile que d'utiliser quelques modèles préconfigurés.

Log4j 1.X et Log4j 2.x n'ont pas de modèles préconfigurés pour l'impression d'ID de thread, mais vous pouvez toujours utiliser un "tour de magie".

PatternLayout utilise la classe PatternParser qui est marquée comme classe final et possède une carte statique de "modèles" en tant que clés et les classes Converters en tant que valeurs. À chaque fois que Parses trouve un modèle utilisé pour consigner le format de modèle commençant par %, il utilise un convertisseur correspondant à cette clé de modèle dans la carte.

Vous ne pouvez pas ajouter votre propre règle à cette carte, mais vous pouvez toujours écrire votre propre MyOwnPatternLayout:

public class MyOwnPatternLayout extends PatternLayout

qui va dans sa méthode format faire une telle astuce:

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

Le seul problème est que vous devez obtenir cet ID de fil d'une manière ou d'une autre. Parfois, tout ce que vous avez à faire est d’analyser le nom du fil qui peut facilement être collecté:

String threadName = event.getThreadName();

Par exemple, Apache-Tomcat a mis l'ID de thread à la fin du nom du thread. http-nio-/127.0.0.1-8084 "-exec-41.

Pour vous assurer que l'ID de thread est correct, vous pouvez également créer votre propre sous-classe de LogginEvent et Logger (MyLoggingEvent et MyLogger) et créer dans MyLogger MyLoggingEvent qui prendra également en argument ID de thread et non seulement le nom de thread. Ensuite, vous pouvez facilement le collecter dans le code ci-dessus.

Désolé pour la longue réponse et j'espère que cela vous aidera au moins.

8
emka86

Une façon de le faire est de l’ajouter vous-même à l’aide de log4j MDC. Nous l'utilisons pour ajouter le nom d'utilisateur pour les requêtes Web. Nous faisons cela dans un filtre au début de chaque demande. Par exemple.

import org.Apache.log4j.MDC;

...

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

Ajoutez ensuite [%X{user}] à votre modèle de conversion. 

7
TedTrippin

J'ai mis en œuvre un ID de fil et une priorité de fil pour la prochaine version 2.6. Suivi ici: https://issues.Apache.org/jira/browse/LOG4J2-1299

Vous pouvez choisir une version 2.6-SNAPSHOT dans le référentiel des instantanés Apache: https://repository.Apache.org/content/repositories/snapshots/

6
Gary Gregory

Vous pouvez utiliser la carte ThreadContext pour fournir des métadonnées à log4j2. Ceci est une chaîne de valeurs contenant les valeurs que vous POUVEZ ajouter par le biais d'un formatage normal.

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

Et un modèle beaucoup plus raisonnable:

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

Documentation complète de Log4j2 sur le "marquage des poissons"

5
Terry

Étendez PatternLayout comme ci-dessous, puis spécifiez MyPatternLayout avec $X{threadId} dans la chaîne de format.

Cette implémentation utilise ThreadLocal pour minimiser l'impact sur les performances du calcul de l'ID de 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);
        }
    }
2
robert

Je pense qu'il n'est pas possible d'afficher un identifiant de fil avec le formatage standard de log4j. J'ai aussi étudié par le code de la classe PatterParser et rien trouvé qui puisse être utile. J'ai trouvé des solutions personnalisées, mais uniquement pour le serveur IBM doté de l'option %i:

% i: insère l'ID de thread. Contrairement au nom du thread (indiqué par% t), il s'agit de l'ID numérique du thread . Notez que ce paramètre est spécifique à Initiate, alors que les autres paramètres listés ici sont standard avec log4j.

Voir ce lien

2
partlov

Je crée mon propre appender et définit Thread.currentThread (). GetId () sur la propriété MDC. % X {threadId} devrait me donner l'identifiant du thread. Cette solution fonctionne depuis 1.2.15. Vous pouvez ensuite attacher AsyncAppender à cela. 

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

Une autre solution élégante avec log4j2 consiste à utiliser org.Apache.logging.log4j.core.pattern.LogEventPatternConverter.

Vous pouvez écrire un cours comme celui-ci

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

De cette façon, vous créez un nouveau modèle tid et vous pouvez l’utiliser lorsque vous définissez la présentation de votre éditeur.

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

La dernière chose importante à retenir est de savoir comment activer votre plugin log4j2. Pour ce faire, vous devez ajouter le paquet contenant vos plugins dans le fichier de configuration log4j2 en utilisant l'attribut package sur le noeud Configuration.

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

Une solution possible est de créer votre propre classe qui se situe entre votre code et Log4J et ajoute l’ID de thread à chaque message du journal:

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

Inconvénients:

  • Performance? Cela ajoute quelques étapes supplémentaires à chaque instruction du journal.
  • Stabilité? Cela ajoute un point d'échec potentiel (l'appel Class.forName).
  • Vous devez remplacer toutes vos instructions de journal existantes par des appels à la nouvelle classe.
  • L'ID de fil n'apparaîtra qu'après le formatage Log4J habituel. C'EST À DIRE:

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