2013-01-16 39 views
16

私はアプリケーションに関する情報を表示するためにlog4jを持つStrutsアプリケーションを持っています。次のようにスレッド名をログに表示する代わりにスレッド名を

形式のログの出力にパターンがある:

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

私はログに代わりスレッド名スレッドIDを表示する必要があります。スレッド名を表示する変換文字は%tです。私はlog4jのドキュメントでそれを得る方法を見ていない。

誰でも私を助けることができますか?

+2

てみましたか? – partlov

+0

はい、試しましたが、以下のようにIBMサーバの場合にのみ有効です –

+0

@partlovそれは私にとってはうまくいかず、 '%i'は認識できません。私はlog4j' 1.2.16'を使用しています。 –

答えて

7

いくつかの事前設定されたパターンを使用するだけでは簡単ではない可能性があります。

Log4j 1.XとLog4j 2.xには、スレッドIDを印刷するための事前設定済みのパターンはありませんが、いつでも「マジックトリック」を使用できます。

PatternLayoutfinalクラスとしてマークされ、値としてキーとConvertersクラスとして「パターン」の静的な地図を有するPatternParserクラスを使用しています。 Parsesが%で始まるロギングパターンフォーマットを使用してパターンを検出するたびに、マップ内のこのパターンキーに一致するコンバーターを使用します。

あなたはそのマップに独自のルールを追加することはできませんが、あなたはまだあなた自身のMyOwnPatternLayoutを書き込むことができます。それにformat方法だだろう

public class MyOwnPatternLayout extends PatternLayout 

は、このようなトリックを行います

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

唯一の問題何らかの方法でそのスレッドIDを取得しなければならないということです。時には、あなたがしなければならないすべてはあなたがイースリー収集できるスレッド名の魔女を解析することです:

例えば
String threadName = event.getThreadName(); 

のApache-Tomcatはスレッド名の末尾にスレッドIDを入れのhttp-nio-/127.0.0.1-8084" -exec-41

スレッドIDが正しいことを確認するには、LogginEventとLogger(MyLoggingEventとMyLogger)の独自のサブクラスを作成することもできます。MyLogger内でMyLoggingEventを作成すると、スレッドIDだけでなくスレッド名も取得します。そして、あなたはeasly上記のコードでそれを集めることができます。

申し訳ありませんが長い回答と私はこれが少なくともあなたにいくつかの助けを願っています。

+0

MyOwnPatternLayoutでThread.currentThread()。getId()を使用していますか? –

+0

Nop、 'logger.info()'や 'org.apache.log4j.Logger'の他のメソッドを使用すると、新しい' LoggingEvent'インスタンスが作成され、スレッド作成者がそれを作成したことがわかりますが、ログが追加されたときファイルやコンソール、またはあなたの 'MyPatternLayout'によって他のスレッドで発生しているものは、' logger.info() 'を使っているスレッドに関する情報はLoggingEventインスタンス内にのみ存在します。つまり' Thread.currentThread()。getId() ) '。私が書いたように、少しの 'Logger'クラスと' LoggingEvent'クラスをサブクラス化しなければなりません。 – emka86

+0

ありがとう、私が考えるソリューションは、LoggerEventサブクラスを作成し、Loggerクラスをアプリケーション(500以上のクラスを使用)で変更できないため、Loggerのデフォルトクラスを維持することです。 MyLoggingEventクラスをLoggerのデフォルトクラスに割り当てる方法は? –

2

標準のlog4j書式でスレッドIDを表示することはできないと思います。私はまた、PatterParserクラスのコードを調べて、何も役に立たないことを見つけました。私はいくつかのカスタムソリューションを見つけましたが、%iオプションを持つIBMサーバーの場合のみです。

%i:スレッドIDを挿入します。スレッド名(%tで示される)とは異なり、スレッドの数値IDです。 このパラメータはInitiateに特有であり、ここにリストされている他のパラメータはlog4jに標準であることに注意してください。

は、あなたがそれを行うことができますthis link

+4

多くのリビジョンとlog4jのバージョン2があっても、設定ファイルフォーマットをプロパティからXMLに変更したので、スレッドIDを追加する簡単な方法はありません。実際に製品の使用に役立つでしょうか?私たちは何かが欠けている可能性はありますか? – stu

6

一つの方法は、log4jのMDCを使用して、それを自分で追加することです参照してください。我々はそれを使用してWebリクエストのユーザ名を追加します。これは、各リクエストの開始時にフィルタで行います。例えば。

import org.apache.log4j.MDC; 

... 

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

次に、変換パターンに[%X{user}]を追加します。

3

ThreadContext Mapを使用して、メタデータをlog4j2に供給できます。これは、通常の書式設定で追加する値の文字列マップです。CAN

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

そして、はるかに合理的なパターン:

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

Full Log4j2 documentation on "Fish Tagging"

0

一つの可能​​な解決策は、あなたのコードとLog4Jの間に位置し、すべてのログにスレッドIDを追加し、独自のクラスを作成することですメッセージ:

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

ダウンサイド:

  • パフォーマンス?これにより、すべてのログステートメントにいくつかの追加ステップが追加されます。
  • 安定性?これにより、潜在的な障害点(Class.forName呼び出し)が追加されます。
  • 既存のすべてのログステートメントを新しいクラスへの呼び出しで置き換える必要があります。
  • スレッドIDは、通常の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

私は私自身のアペンダを作成し、(にThread.currentThreadを設定)のgetId()MDCプロパティに。 %X {threadId}は私にスレッドIDを与える必要があります。この解決策は1.2.15以降に行われています。これに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

log4j2と別のエレガントな解決策は、org.apache.logging.log4j.core.pattern.LogEventPatternConverterを使用することです。

あなたのアペンダのレイアウトを定義

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

に最後に重要なことをあなたは新しいパターンtidを作成している。このように、この

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

のようなクラスを作成することができますし、それを使用することができますあなたのlog4j2プラグインを有効にする方法が覚えておいてください。これを行うには、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> 
+1

を使用します。これは非同期でログに記録しないと動作しません(AsyncLoggerなど)。 – Ph3n1x

1

以下のようにPatternLayoutを拡張して、フォーマット文字列で$X{threadId}MyPatternLayoutを指定するにpackage属性を使用してlog4j2のコンフィギュレーションファイルにプラグインが含まれているパッケージを追加する必要があります。

この実装では、スレッドIDを計算するのパフォーマンスへの影響を最小限に抑えるためにThreadLocalを使用して:あなたは `%のi`で

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); 
     } 
    } 
関連する問題