• Showing Page History #80361

ログ出力時の処理の流れ(概念的なもの)

 ここでは、log4cpp::Categoryクラスのlogメソッド(あるいは優先度ごとに用意されたメソッド、すなわち、debuginfonoticewarnerrorcritalertemergfatal)を呼んだときにどのような流れで実際にログ出力が行われるかを説明します。

 図1はログ出力時の処理の流れをUML2.0のシーケンス図で表記したものです。
細かい部分は省いています。実際には、Category::log()が呼ばれると、

Category::log()
→Categoryが引数で渡された優先度のログを出力対象としているかチェック(図1の1.1の部分。Category::isPriorityEnabled()
Category::_logUnconditionally()を呼び出し
 →Category::_logUnconditionally2()を呼び出し
  →LoggingEventを生成(図1の1.2の部分)
  →Category::callAppenders()を呼び出し
   →Category::_appenderSetMutexをロック(図1のcritical複合フラグメントの部分)
   →Categoryが持つ全appenderに対してdoAppend()を呼び出し
   →additivityフラグがtrue(かつ、親カテゴリがある(今のカテゴリがルートカテゴリではない))の場合、親のCategoryに対しcallAppenders()を呼び出し

という感じです。さらに図1の1.3.1~1.3.3の部分(Appender::doAppend()の内部)は実際にはAppenderSkelton(Appenderのサブクラス)での処理なのですが、図1ではさすがにここまで記述していません。
 以下に、ポイントとなる部分を説明します。

LoggingEventとは

 APIドキュメントを見れば分かると思いますが、ログ出力するデータを保持するクラスです。
 これの存在理由ですが、たとえば、ログ出力時の現在時刻というのは、ログを出力するたびに取得していたら、Categoryが複数のAppenderを持っていたときに、最初の出力(最初のAppender)と最後出力(最後のAppender)で時刻が変わってしまいますが、まあ、そういう風にならないようにという理由が1つあると思います。あとは、Appenderを継承して自作のAppenderを作成するときやフォーマットを考えるときに、出力するのって何?とならないように定義しているという意味もあると思います。

Category::log()はスレッドセーフなの?

 はっきり言うが、スレッドセーフでないとログ出力ライブラリとして使えない。シーケンス図を見るとcriticalと記載があり、排他をかけている。
 ・・・と、言うことは、スレッドセーフなのだろうか・・・?

 しかし、Log for C++ の本家サイトのFAQの「3.2. Is log4cpp thread-safe?」を見ると、なんと質問に対する答えが書かれていないです。

※注意:ここから先、間違っていたら申し訳ないです。

 log4cppのソースを読んだ限りでは、排他をかけているので、もちろん、スレッドセーフなのですが、条件があります。

  1. 複数のCategoryで1つのAppenderを共有するのはやめよう
    どうも、