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??
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.
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.
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/
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"
É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);
}
}
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
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);
}
}
...
}
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>
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:
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!