Show page source of OutputLog_Interaction #80363

[[PageNavi(NavigationList)]]

=== ログ出力時の処理の流れ(概念的なもの) ===
 ここでは、[http://log4cpp-jp.sourceforge.jp/api/classlog4cpp_1_1Category.html log4cpp::Category]クラスの[http://log4cpp-jp.sourceforge.jp/api/classlog4cpp_1_1Category.html#a763caceab3981c2e06811c29655b361b log]メソッド(あるいは優先度ごとに用意されたメソッド、すなわち、[http://log4cpp-jp.sourceforge.jp/api/classlog4cpp_1_1Category.html#aafa1ca0c9d57f9f87eae8628e89c5950 debug]、[http://log4cpp-jp.sourceforge.jp/api/classlog4cpp_1_1Category.html#ad218a5e279fc9b15a076bf62503f37b4 info]、[http://log4cpp-jp.sourceforge.jp/api/classlog4cpp_1_1Category.html#a5a4ba8063d108e275affa4aca5aec4ef notice]、[http://log4cpp-jp.sourceforge.jp/api/classlog4cpp_1_1Category.html#a9c372f41858234fe5ab0cfe94cfdb94a warn]、[http://log4cpp-jp.sourceforge.jp/api/classlog4cpp_1_1Category.html#a90d7771a3113219532878f1fea554afe error]、[http://log4cpp-jp.sourceforge.jp/api/classlog4cpp_1_1Category.html#acf854aca1676ede517914bf5185fc36d crit]、[http://log4cpp-jp.sourceforge.jp/api/classlog4cpp_1_1Category.html#aca8a704d6397ac3a4300966a6142460c alert]、[http://log4cpp-jp.sourceforge.jp/api/classlog4cpp_1_1Category.html#a3663faf2ad6873c3da8cdfef5780277a emerg]、[http://log4cpp-jp.sourceforge.jp/api/classlog4cpp_1_1Category.html#a249d46ce876a4af55a25226255e626fa fatal])を呼んだときにどのような流れで実際にログ出力が行われるかを説明します。

[[Thumb(outputLog_interaction.jpg, size=large, caption=図1 ログ出力時の処理の流れ)]]

 図1はログ出力時の処理の流れをUML2.0のシーケンス図で表記したものです。[[BR]]
細かい部分は省いています。実際には、Category::log()が呼ばれると、
 [http://log4cpp-jp.sourceforge.jp/api/classlog4cpp_1_1Category.html#a763caceab3981c2e06811c29655b361b Category::log()][[BR]]
 →Categoryが引数で渡された優先度のログを出力対象としているかチェック(図1の1.1の部分。[http://log4cpp-jp.sourceforge.jp/api/classlog4cpp_1_1Category.html#a257d630620c054314daad09bba2f0a02 Category::isPriorityEnabled()])[[BR]]
 →[http://log4cpp-jp.sourceforge.jp/api/classlog4cpp_1_1Category.html#a2020a52bc0abfa16f6c2f879791ca4ef Category::_logUnconditionally()]を呼び出し[[BR]]
  →[http://log4cpp-jp.sourceforge.jp/api/classlog4cpp_1_1Category.html#a77265549183a4f2fc9ecea8da5aec8b4 Category::_logUnconditionally2()]を呼び出し[[BR]]
   →[http://log4cpp-jp.sourceforge.jp/api/structlog4cpp_1_1LoggingEvent.html LoggingEvent]を生成(図1の1.2の部分)[[BR]]
   →[http://log4cpp-jp.sourceforge.jp/api/classlog4cpp_1_1Category.html#ac07af8f9e1f8342dce0a437c6ad9764f Category::callAppenders()]を呼び出し[[BR]]
    →Category::_appenderSetMutexをロック(図1のcritical複合フラグメントの部分)[[BR]]
    →Categoryが持つ全appenderに対して[http://log4cpp-jp.sourceforge.jp/api/classlog4cpp_1_1Appender.html#ad867da8a98bfee7e7aa9ade86044a1d6 doAppend()]を呼び出し[[BR]]
    →additivityフラグがtrue(かつ、親カテゴリがある(今のカテゴリがルートカテゴリではない))の場合、親のCategoryに対しcallAppenders()を呼び出し

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

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

==== 1つのCategoryにたくさんのAppenderがあると、処理が遅くなる? ====
 遅くなります。[[BR]]
 log4cppのコアの部分ではログ出力を別スレッドで行うということはしていません。ですので、Appenderが多いと、Category::log()が帰ってくるまでにかかる時間が長くなります。

==== [http://log4cpp-jp.sourceforge.jp/api/classlog4cpp_1_1Category.html#a763caceab3981c2e06811c29655b361b Category::log()]はスレッドセーフなの? ====
 はっきり言うが、スレッドセーフでないとログ出力ライブラリとして使えない。シーケンス図を見るとcriticalと記載があり、排他をかけている。[[BR]]
 ・・・と、言うことは、スレッドセーフなのだろうか・・・?

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

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

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

 まず、排他といってもlog()を呼んでいる最中にCategoryオブジェクトが持つAppdenderが変更されないようにするための排他なのです。これはメンバ名(_appenderSetMutex)からも分かります。[[BR]]
 恐らく、スレッドセーフかどうかで最も大事なのはあるCategoryオブジェクトのlogメソッドを呼んでいる最中に[[BR]]
(1)同じCategoryオブジェクトのlogメソッドを別スレッドで呼んで良いか?[[BR]]
(2)別のCategoryオブジェクトのlogメソッドを別スレッドで呼んで良いか?[[BR]]
でしょう。ですから、これについて考えます。


 1. 複数のCategoryで1つのAppenderを共有するのはやめよう(FilterやLayoutの共有もやめよう)[[BR]]
 Categoryのログ出力部分では排他をかけています()が、Appenderはスレッドセーフではないです。[[BR]]
 ですので、
 

[[PageNavi(NavigationList)]]