2011-12-02 29 views
6

ユニークなIDを持つさまざまなスレッドを起動するJAVAクラスがあります。 各スレッドは、ID.logの名前に基づいて一意のログファイルにログインする必要があります。複数のスレッドを異なるログファイルに記録する方法は?

私は、実行時にのみ一意のIDを取得するので、私はプログラム的にLog4Jを設定する必要があります。私は順次ジョブを開始した場合

// Get the jobID 
myJobID = aJobID; 
// Initialize the logger 
myLogger = Logger.getLogger(myJobID); 
FileAppender myFileAppender; 
try 
{ 
    myFileAppender = new FileAppender(new SimpleLayout(), myJobID + ".log", false); 
    BasicConfigurator.resetConfiguration(); 
    BasicConfigurator.configure(myFileAppender); 
} catch (IOException e1) { 
// TODO Auto-generated catch block 
    e1.printStackTrace(); 
} 

は今、これは正常に動作します - しかし、私は(同じクラスの)2つのスレッドを起動したとき同時に両方のログが作成されますが、ログは混在します。第2のスレッドは、第1のログと第2のログにログインします。

各インスタンスが一意であることを確認するにはどうすればよいですか? 各ロガーインスタンスに一意の名前を付けようとしましたが、何も変更されませんでした。

+0

Javaコードでこの区別を行う理由はありますか?一般的なプラクティスは、1つのファイルにログインしてから、いくつかの後処理を実行して抽出/集約することです。また、http://logging.apache.org/log4j/1.2/faq.html#a3.1 – havexz

+1

を参照してください。[各スレッドが独自のログファイルに出力するようにlog4jプロパティを設定するにはどうすればいいですか?](http: /stackoverflow.com/q/1172113/127035) – sudocode

答えて

11

Logbackは、あなたが記述問題の種類に非常に素晴らしいソリューションを提供しSiftingAppenderと呼ばれる特別なアペンダを持っています。 SiftingAppenderを使用して、スレッドIDを含む実行時属性に基づいてロギングを分離(または選別)できます。

+0

これと同じことを行うJava実装はありますか?https://www.mkyong.com/logging/logback-different-log-file-for-each-thread/ つまり、そのブログに記載されています。 – prime

+0

あなたが表示するリンクは、完全にログバックのSiftingAppenderに基づいています。 – Ceki

+0

Hmm。とった。私たちは複数のsiftingAppendersを持つことができますか?基本的には、2つの異なる場所に書き込むには2つのロガーが必要です。どちらも別々のスレッドで使用されるので、私は思ったふるいを使わなければならない。しかし、私は2つ持っていると、それはとにかく動作しません。私は2つのMDC値も持っています。 – prime

0

クラスに静的インスタンスカウンタ変数を追加するとどうなりますか?次に、作成された各オブジェクトのカウンタを増やし、その値からログファイル名を作成する同期メソッドが必要になります。このようなもの:

class yourClass { 

    private static int cnt = 0; 

    public yourClass(){ 
    ... 
    initLogger(); 
    } 

    private synchronized initLogger(){ 
    yourClass.cnt++; 
    myJobid = yourClass.cnt; 

    //include your logging code here 
    } 
} 
+0

私のJobIDは単純なUUIDです。問題はログファイルの名前ではなく、混乱するアペンダーです。 – Tim

+0

私はこれを見つけました:http://www.manniwood.com/log4j_stuff/index.htmlしかし、それは私がいくつのジョブ(スレッド)を開始するのか知る必要があることを意味します。 – Tim

5

@havexzのアプローチはかなり良いです:writing everything to the same log file and using nested diagnostic contexts

あなたの懸念が同じFileAppenderへの書き込みいくつかのJVM程度であれば、私は二つのことをお勧めしたい:、ロギング実装としてin prudent mode

logbackを使用して

  • using SLF4J as a logging facade
  • を慎重なモードでは、他のFileAppenderインスタンスが存在する場合でも、FileAppenderは指定されたファイル に安全に書き込みます潜在的に異なるホストで動作している異なるJVMを で実行しています。

0

私が言うことができる限り、ThreadLocal APIはあなたのことを行うように設計されています。以下のような

コードは、独自の(スレッド毎)FileAppenderを使用してスレッドごとのロガーそれぞれを確立する:

/** 
* usage: threadLocalLogger.get().info("hello thread local logger") 
*/ 
static ThreadLocal<Logger> threadLocalLogger = newThreadLocalLogger("myJobId"); 

private static ThreadLocal<Logger> newThreadLocalLogger(final String myJobID) { 
    return new ThreadLocal<Logger>() { 
     @Override 
     protected Logger initialValue() { 
      return logger(myJobID, Thread.currentThread().getId()); 
     } 
    }; 
} 

private static Logger logger(String myJobID, long threadId) { 
    // Initialize the logger 
    String loggerId = myJobID + "-" + threadId; 
    Logger myLogger = Logger.getLogger(loggerId); 
    FileAppender myFileAppender; 
    try 
    { 
     myFileAppender = new FileAppender(new SimpleLayout(), 
       loggerId + ".log", false); 
     BasicConfigurator.resetConfiguration(); 
     BasicConfigurator.configure(myFileAppender); 
    } catch (IOException e1) { 
    // TODO Auto-generated catch block 
     e1.printStackTrace(); 
    } 
    return myLogger; 
} 
8

log4j v2では、RoutingAppenderを使用してメッセージを動的にルーティングできます。 ThreadContextマップにキー 'threadId'の値を設定し、このIDをファイル名の一部として使用できます。あなたと同じ目的で簡単に申し込んだ例があります。 http://logging.apache.org/log4j/2.x/faq.html#separate_log_files

ThradContextマップに値を設定するときに注意してください。「子スレッドは、親のマップされた診断コンテキストのコピーを自動的に継承します。したがって、親スレッドにキー 'threadId'の値を入れて、最終的に複数のスレッドを作成した場合、すべての子スレッドは 'threadId'値の値を継承します。もう一度put()を使ってこの値をオーバーライドすることはできませんでした。スレッドコンテキストマップから値を明示的に削除()するか、ThreadContext.clear()を使用する必要があります。ここで

は私の作業のlog4j.xmlである:ここで

<?xml version="1.0" encoding="UTF-8"?> 
<configuration status="WARN"> 
    <properties> 
     <property name="logMsgPattern">%d{HH:mm:ss} %-5level - %msg%n</property> 
     <property name="logDir">test logs</property><!-- ${sys:testLogDir} --> 
    </properties> 
    <appenders> 
     <Console name="Console" target="SYSTEM_OUT">   
      <PatternLayout pattern="${logMsgPattern}"/> 
     </Console> 

     <Routing name="Routing"> 
        <Routes pattern="$${ctx:threadId}">    
         <Route> 
          <RollingFile name="RollingFile-${ctx:threadId}" fileName="${logDir}/last-${ctx:threadId}.log" filePattern="${logDir}/%d{yyyy-MM-dd}/archived_%d{HH-mm}-${ctx:threadId}.log"> 
            <PatternLayout pattern="${logMsgPattern}"/> 
            <Policies> 
           <OnStartupTriggeringPolicy /> 
          </Policies> 
        </RollingFile> 
         </Route> 
        </Routes> 
      </Routing> 
    </appenders> 

    <loggers>    
     <root level="debug"> 
      <appender-ref ref="Console" level="debug" /> 
      <appender-ref ref="Routing" level="debug"/> 
     </root>      
    </loggers> 
</configuration> 
+0

このthreadId変数はどこで定義しますか? –

2

は作業log4j.xmlファイルからルーティングコードのスニペットです。

<Appenders> 

     <Console name="ConsoleAppender" target="SYSTEM_OUT"> 
      <PatternLayout> 
       <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %-50logger{4}: %msg%n</pattern> 
      </PatternLayout> 
     </Console> 
    <Routing name="RoutingAppender"> 
       <Routes pattern="${ctx:logFileName}"> 


        <!-- This route is chosen if ThreadContext has a value for logFileName. 
         The value dynamically determines the name of the log file. --> 

        <Route> 
         <RollingFile name="Rolling-${ctx:logFileName}" 
          fileName="${sys:log.path}/${ctx:logFileName}.javalog" 
          filePattern="./logs/${date:yyyy-MM}/${ctx:logFileName}_%d{yyyy-MM-dd}-%i.log.gz"> 
          <PatternLayout> 
           <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %-50logger{4}: %msg%n</pattern> 
          </PatternLayout> 
          <Policies> 
           <TimeBasedTriggeringPolicy interval="6" 
            modulate="true" /> 
           <SizeBasedTriggeringPolicy size="10 MB" /> 
          </Policies> 
         </RollingFile> 
        </Route> 

        <!-- This route is chosen if ThreadContext has no value for key logFileName. --> 
        <Route key="${ctx:logFileName}" ref="ConsoleAppender" /> 

       </Routes> 
      </Routing> 
</Appenders> 
    <loggers> 
     <root level="debug"> 
      <appender-ref ref="RoutingAppender" level="debug" /> 
     </root> 
    </loggers> 

キー「LOGFILENAME」次のようにRunnableをクラスのrun()メソッド内のスレッド・コンテキスト・マップに追加することができ、また

public class SomeClass implements Runnable{ 

private int threadID; 

public SomeClass(int threadID){ 
    this.threadID=threadID; 
    } 
@Override 
public void run() { 
    String logFileName = "thread_log_"+ threadID; 
    ThreadContext.put("logFileName", logFileName); 
    //Some code 
    ThreadContext.remove("threadId"); 
    } 
} 

、正しいlog4jのパッケージをインポートする必要があります以下のようになります。

import org.apache.logging.log4j.LogManager; 
import org.apache.logging.log4j.Logger; 
import org.apache.logging.log4j.ThreadContext; 

次のインポートは機能しませんのでご注意ください。 LogManagerとLoggerはorg.apache.logging.log4jからも来なければなりません。

import org.apache.log4j.LogManager; 
import org.apache.log4j.Logger; 
import org.apache.logging.log4j.ThreadContext; 
関連する問題