web-dev-qa-db-ja.com

Java複数のスレッドからのロギングのベストプラクティス?

データを管理するいくつかのタスクによって生成される診断ログが必要です。これらのタスクは複数のスレッドにある場合があります。各タスクは、要素(場合によってはサブ要素も含む)をログに書き込む必要があります。すぐに出入りします。これが単一タスクの状況である場合は、膨らみのあるXMLドキュメントをメモリに保持する必要がなく、単純性/機能性に最適と思われるため、 XMLStreamWriter を使用します。

しかし、これは単一のタスクの状況ではなく、これが「スレッドセーフ」であることを確認する最善の方法がわかりません。このアプリケーションの「スレッドセーフ」とは、各ログ要素がログに正しくシリアルに書き込まれる必要があることを意味しますもう1つは、インターリーブされていません)。

助言がありますか?行く方法はログ要素のキューを使用することであるという漠然とした直感があります(各要素は迅速に作成できます:アプリケーションはパフォーマンスに敏感な実際の作業でビジーです)、ログを処理する別のスレッドがあります要素を作成してファイルに送信するため、ロギングによってプロデューサーが中断されることはありません。

ログは必ずしもXMLである必要はありませんが、構造化され、機械可読である必要があります。

編集:「スレッドセーフ」を引用符で囲みます。 Log4jは明らかな選択のようです(私にとっては新しいが、コミュニティにとっては古い)。なぜホイールを再発明するのか...

25
Jason S

Log4j などのロギングフレームワークを使用します。

21
Avi

あなたは間違った道を進んでいると思います。 「スレッドセーフ」と言いますが、実際には「シリアル化」を意味します。スレッドセーフとは、あるスレッドが他のスレッドからのデータに干渉しないことを意味します。ほとんどの場合、スレッドの問題は事前に解決されており、ロギングのためだけに心配する必要はありません。たとえば、次のように記述したとします。

myVariableSum = 0 + myVariable;
//here comes other thread - Not very likely!
logger.info("Log some INFO; myVariable has value" + myVariable.toString());

MyVariableが、計算(最初の行)が実行された瞬間から、loggingメソッドが呼び出される前に、他のスレッドによって変更されていないことを確認する必要があります。これが発生した場合、操作の実行に使用されなかったダーティな値をログに記録しますが、他のスレッドによって割り当てられた値をログに記録します。これは一般に処理されます。たとえば、ローカル(メソッドレベル)変数は他のスレッドでは変更できません。とにかく、ロギング時にこれについて心配する必要がある場合、99%以上がプログラムにすでに深刻なスレッドの問題があることを示しています。
すべての主要なロギングフレームワークは、それ自体が「スレッドセーフ」であり、マルチスレッド環境に展開でき、上記で内部的に説明したものと同様の問題を表示しません。
トレースを発生順にログに表示することは、実際には通常、コールの「シリアル化」と呼ばれます。ログ書き込みのシリアル化は、マルチスレッドアプリでのパフォーマンスの主要なボトルネックになります。 log4jのようなロギングフレームワークを使用する場合、すべてのスレッドからのトレースは、発生する順番に1か所に表示されます。ただし、1つの列は通常スレッド名であるため、ログデータをスレッドで簡単にフィルタリングできます。各スレッドは時系列でデータを記録します。このリンクをチェックしてください: http://logging.Apache.org/log4j/1.2/faq.html#1.7
最後に、ログ書き込みのシリアル化が本当に必要な場合、Java.util.concurrent.BlockingQueueのようなある種の構造を使用してメッセージをルーティングできます。

22
Dan

Logback-classicを使用します。これは、log4jのより新しくより優れた実装です。

9
Ceki

私は SLF4J をLog4Jの上で使用する傾向があります。 parameterized logging 機能は、実稼働環境でオフに切り替えられる可能性のある多くのロギングステートメントがある場合に特に魅力的です。

また、Java.util.loggingの上で実行することも、独自の単純な出力を使用することもできます。

5
Evan

同期メカニズム(モニターやセマフォーなど)を使用して、次のログリクエストを受け入れる前に1つのログリクエストが処理されることを確認できます。これはすべて、ロギングルーチンを呼び出すコードから隠すことができます。

5
jpfollenius

Log4などのロギングフレームワークを使用します。

また、出力に満足できない場合は、独自のアペンダー、フィルターを作成できます。したがって、エントリを再配置するためにキャッシュを実行することもできますが、これは良いアイデアだとは言いません。

4
Jens Schauder

Log4J のような、何らかの形式の NDCパターン を実装するロギングフレームワークを使用します。

4
eljenso

log4jは、Javaロギングの何年もの間の標準です。しかし、外部依存関係を気に入らない場合は、 Java.util.logging パッケージが提供する許容できるソリューション。

3
Gareth Davis

私は同様の問題と特別なログのみの実装要求がありました。私の解決策は:

  1. アプリのトラフィックの*2のサイズでblockinglinkedqueueを取得しました/分。

  2. すべてのスレッドがオブジェクトをキューに入れ、ジョブを終了します。

  3. 別のLog-Writerスレッドがキューからヘッドオブジェクトを取得し、別のアペンダーを使用してlog4jファイルに書き込みます。このアペンダーはシステムログには使用されませんでした。

これにより、ログがシリアルに書き込まれ、常に正しい順序で書き込まれます。

ログの書き込みは完全に別のプロセスであり、ボトルネックを作成しないため、これはアプリケーションのパフォーマンスに影響しません。

log4jaysncappenderを使用することもできます。

2
JDev

必要に応じて、シングルライター/シングルリーダーFIFOまたはキューを使用して、独自のロールを行うことができます。

1
JustJeff

これは古い質問ですが、ここにプログラムでLog4Jを使用する私の解決策があります。

LogFactoryクラス

import org.Apache.log4j.Logger;
import org.Apache.log4j.PropertyConfigurator;

import Java.util.Properties;

public class LogFactory {

    private final static ThreadLocal<Logger> logFactory = new ThreadLocal<>();

    public static void createNewLogger(String className) {

        Logger log = Logger.getLogger("Thread" + className);

        Properties props = new Properties();
        props.setProperty("log4j.appender.file", "org.Apache.log4j.RollingFileAppender");

        props.setProperty("log4j.appender.file.maxFileSize", "100MB");
        props.setProperty("log4j.appender.file.Append", "false");
        props.setProperty("log4j.", "100MB");
        props.setProperty("log4j.appender.file.maxBackupIndex", "100");
        props.setProperty("log4j.appender.file.File", "logs/" + className + ".log");
        props.setProperty("log4j.appender.file.threshold", "info");
        props.setProperty("log4j.appender.file.layout", "org.Apache.log4j.PatternLayout");
        props.setProperty("log4j.appender.file.layout.ConversionPattern", "%d{yyyy-MM-dd HH-mm-ss} | %-5p | %C{1}:%L | %m%n");
        props.setProperty("log4j.appender.stdout", "org.Apache.log4j.ConsoleAppender");
        props.setProperty("log4j.appender.stdout.Target", "System.out");
        props.setProperty("log4j.logger." + "Thread" + className, "INFO, file");
        PropertyConfigurator.configure(props);
        logFactory.set(log);
    }

    public static Logger getLogger() {
        return logFactory.get();
    }

}

次に、ロガーを初期化するには、次のアプローチを使用します

logFactory.createNewLogger(String.valueOf(Thread.currentThread().getId()));
logFactory.getLogger().info(" TEST . Thread id is: " + id);
0
KahunaDub

これをスレッドセーフな方法で自分で開発することは簡単ではないので、スレッドセーフな既存のロギングフレームワークを実際に使用する必要があります。最も一般的に使用されるのは Log4J であり、これはスレッドセーフです( [〜#〜] faq [〜#〜] を参照)。

0
Christian Berg