マルチスレッドアプリケーションでのロガーの同時実行パターン


8

コンテキスト:パイプラインモデルに従うマルチスレッド(Linux-C)アプリケーションに取り組んでいます。

各モジュールには、データの処理を行うプライベートスレッドとカプセル化されたオブジェクトがあります。各ステージには、次のユニットとデータを交換する標準形式があります。

アプリケーションはメモリリークがなく、データを交換するポイントでロックを使用してスレッドセーフです。スレッドの総数は約15で、各スレッドには1〜4個のオブジェクトを含めることができます。約25〜30個の奇妙なオブジェクトを作成します。これらすべてに重要なロギングが必要です。

私が見たほとんどの議論は、Log4Jのようなさまざまなレベルと、それ以外の翻訳です。本当に大きな問題は、全体的なロギングが実際にどのように行われるべきかについてです。

1つのアプローチは、すべてのローカルロギングが行うfprintfことstderrです。stderrはいくつかのファイルにリダイレクトされます。ログが大きくなりすぎると、このアプローチは非常に悪くなります。

すべてのオブジェクトが個々のロガーをインスタンス化する場合(そのうちの約30〜40)、ファイルが多すぎます。そして、上記とは異なり、イベントの真の順序についての考えはありません。タイムスタンプは1つの可能性ですが、照合するのはまだ面倒です。

グローバルロガー(シングルトン)のパターンが1つある場合、ログの作成でビジー状態になっている間、非常に多くのスレッドが間接的にブロックされます。スレッドの処理が重い場合、これは受け入れられません。

では、ロギングオブジェクトを構造化するための理想的な方法は何でしょうか。実際の大規模アプリケーションでのベストプラクティスにはどのようなものがありますか?

また、インスピレーションを得るために、大規模アプリケーションの実際のデザインのいくつかから学びたいと思っています。

======

編集:

ここでの両方の答えに基づいて、私は今残っている質問です:

ロガー(ロギングキュー)をオブジェクトに割り当てる場合のベストプラクティスは何ですか?それらがglobal_api()を呼び出すか、ロガーがコンストラクターでロガーに割り当てられるかどうかです。オブジェクトが深い階層にある場合、この後のアプローチは面倒になります。それらがいくつかのglobal_api()を呼び出している場合、それはアプリケーションとの一種のカップリングであるため、他のアプリケーションでこのオブジェクトを使用しようとすると、この依存関係がスローされます。これのためのよりきれいな方法はありますか?


1
こんにちは、おかえりなさい!ここと職場の両方であなたがいなくて寂しかったです。
yannis 2012年

回答:


10

実際のロギングを独自のスレッドに委任するシングルトンロガーを使用する許容可能な方法

次に、任意の効率的なプロデューサー/コンシューマーソリューション(アトミックCaSに基づく非ブロッキングリンクリストなど)を使用して、暗黙的なグローバルロックであることを心配せずにログメッセージを収集できます。

次に、ログ呼び出しは最初にログメッセージをフィルタリングして作成し、それをコンシューマーに渡します。コンシューマーはそれを取得してそれを書き出します(そして個々のメッセージのリソースを解放します)。


答えてくれてありがとう。内部的には同じ方向に考えていました。私の唯一の質問は-オブジェクトを一般的な目的にしたい場合-それはアプリケーションから期待される外部グローバルへのアクセスを制限するのではないですか?モジュールごとの割り当て方法とグローバルロガーについて詳しく説明できますか?
Dipan Mehta

6

ラチェット・フリークの答えは、私も最初に考えたものです。

別の方法として、各モジュールに独自のプロデューサー/コンシューマーキューを割り当て、ロガーメカニズムにこれらのキューを独自のスレッドでスキャンさせる方法があります。

使用するロガーの数を切り替えることができるため、これはより柔軟になる可能性があります-すべてに1つ、各モジュールに1つ、またはモジュールをグループに分割して、各グループに1つ持つことができます。

編集:エラボレーション

(私のCを気にしないでください-それはあなたがコーディングしているとあなたが言ったことですよね?)

したがって、このアイデアは、モジュールごとにプロデューサー/コンシューマーキュー/リストを作成することです。このようなキューは、おそらく次のようになります。

enum LogItemType {INFO, ERROR};

struct LogItem
{
    LogItemType type;
    char *msg;
};

struct LogQueue {...}; // implementation details -- holds an array/list of LogItem

bool queueLogItem(log_queue *, log_item *);
bool queueHasItems(log_queue *);
bool queueIsFull(log_queue *);
LogItem *dequeueLogItem(log_queue *);

各モジュールは、そのような独自のキューを初期化するか、スレッドなどを設定する初期化コードによって渡される必要があります。初期化コードは、おそらくすべてのキューへの参照を保持する必要があります。

LogQueue *module_queues = {module_1_queue_ptr, module_2_queue_ptr, ... , module_N_queue_ptr};

setModuleLoggingQueue(module1, module_1_queue_ptr);
// .
// .
// .

モジュール内では、LogItemを作成して、メッセージごとにキューに入れます。

LogItem *item = malloc(sizeof(LogItem));
item->type = INFO;
item->msg = malloc(MSG_SIZE)
memcpy("MSG", item->msg);
queueLogItem(module_queue, item);

次に、メッセージを受け取り、実際にメインループでログを書き込むキューの1つ以上のコンシューマーが存在します。

void loggingThreadBody()
{
    while (true)
    {
        for (i = 0; i < N; i++)
        {
            if (queueHasItems(module_queues[i]))
                writeLogItem(dequeueLogItem(module_queues[i]));
        }

        threadSleep(200);
    }
}

またはそのようなもの。

これにより、キューのさまざまなコンシューマを使用できるようになります。たとえば、次のようになります。

// For one logger:

LogQueue *module_queues = {module_1_queue_ptr, module_2_queue_ptr, ... , module_N_queue_ptr};

initLoggerThread(module_queues);


// -OR-
// For multiple loggers:

LogQueue *group1_queues = {module_1_queue_ptr, ..., module_4_queue_ptr};
LogQueue *group2_queues = {module_5_queue_ptr, ... , module_Nmin7_queue_ptr};
LogQueue *group3_queues = {module_Nmin7_queue_ptr, ... , module_N_queue_ptr};

initLoggerThread(group1_queues);
initLoggerThread(group2_queues);
initLoggerThread(group3_queues);

注:キュー時にログメッセージ用のメモリを割り当て、消費時にメモリの割り当てを解除する必要があると思います(メッセージに動的コンテンツが含まれている場合)。

別の編集:

言及を忘れた:モジュールスレッドで多くのアクティビティを期待している場合は、ログ書き込みを非同期で実行して、ブロックされないようにできるかどうかを確認できます。

さらに別の編集:

また、LogItemの一部としてタイムスタンプを配置することもできます。ロガースレッドがキューを順番に通過するため、ログステートメントがモジュールで発生順に発生したときから、ログステートメントの順序が乱れる場合があります。

そしてさらに別の編集:

この設定では、すべてのモジュールに同じキューを渡し、消費者にその1つのキューを表示させるだけでかなり簡単になり、ラチェットフリークの答えに戻ることができます。

Geeze、編集をやめてくれないか!?:

また、モジュールのグループごとに1つのキューを用意し、各キューにロガーを用意することもできます。

よし、やめよう。


これは面白い。このデザインについて詳しく説明していただけますか?
Dipan Mehta

@DipanMehtaもちろんですが、現時点ではできません。私は今夜​​それを更新しようとします-明日の夜までに間違いなく。
ポール・

@DipanMehta [OK]を、私は私の答え:)更新しました
ポール・

@DipanMehta別の編集を追加しました...
ポール

ありがとう、ポール。これが最良のようで、本当に私の質問に答えます。質問にもう少しコメントを追加しました-これに光を当てられる人がいるかどうか見てみましょう。
Dipan Mehta 2012
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.