.. Sphinx standard indentations # with overline, for parts * with overline, for chapters =, for sections -, for subsections ^, for subsubsections ", for paragraphs 「Short introduction to Apache log4cxx」訳 ########################################################################## https://logging.apache.org/log4cxx/ トップページの訳。 * 翻訳の価値があまりない部分は原文のままとする。(苦労せずに読めるとか、英文が難しい割に大して重要でなかったりとか。) * 必要に応じ、(過激な)意訳をする。 Introduction ************************************************************************** Apache log4cxx is a logging framework for C++ patterned after `Apache log4j `_. Apache log4cxx uses `Apache Portable Runtime `_ for most platform-specific code and should be usable on any platform supported by APR. Apache log4cxx is licensed under the `Apache License `_, an open source license certified by the `Open Source Initiative `_. Almost every large application includes its own logging or tracing API. Inserting log statements into code is a low-tech method for debugging it. It may also be the only way because debuggers are not always available or applicable. This is usually the case for multithreaded applications and distributed applications at large. Experience indicates that logging is an important component of the development cycle. It offeres several advantages. It provides precise context about a run of the application. Once inserted into the code, the generation of logging output requires no human intervention. Moreover, log output can be saved in persistent medium to be studied at a later time. In addition to its use in the development cycle, a sufficiently rich logging package can also be viewed as an auditing tool. Logging does have its drawbacks. It can slow down an application. If too verbose, it can cause scrolling blindness. To alleviate these concerns, log4cxx is designed to be reliable, fast and extensible. Since logging is rarely the main focus of an application, the log4cxx API strives to be simple to understand and to use. Loggers, Appenders and Layouts ************************************************************************** Log4cxx は3つの主要コンポーネントから成る: ``loggers``, ``appenders`` そして ``layouts`` である。 これらは、開発者にメッセージ種類(type)・レベルに基づくメッセージを記録する能力を与え、かつまた、実行時に これらメッセージをどのように書式化するのか・あるいはどこに出力するのかの制御を可能とするために協調動作する。 Logger hierarchy ========================================================================== ただの ``std::cout`` 以上のことをするどのような logging API であれ、それを利用することの 最も重大な利点は、出力したいものの出力を妨げることなく特定のログを無効にすることが出来る(「ゼロかイチかの出力制御をしなくて済む」) ことにある。この能力が仮定するのは、ロギング空間、すなわちあらゆるロギング文というものは ある種の開発者選択基準に基づく分類を持っているものだ、ということである。 ロガーとは、名前付けされた実体である。 ロガーの名前は case-sensitive であり、また、階層的命名規則に従う: **Named Hierarchy** 命名が子孫の名前の「ドットで接続されたプレフィクス」であるならば、そのロガー名は「先祖(ancestor)」 と呼ばれる。自身と子孫ロガーの「先祖(ancestor)」がなければ、「親(parent)」と呼ばれる。 例えばロガー名が "com.foo" であるならば、これは "com.foo.Bar" の親であるし、 "java" は "java.util" の親であるとともに "java.util.Vector" の先祖である。 この命名体系はほとんどの開発者にとって馴染みのものであるはずである。 ルートロガー、が、ロガー階層のトップレベルに存在する。これは2つの意味で例外的である: 1. 必ず存在する 2. 名前で選べない ルートロガーは、static メソッドである `log4cxx::Logger::getRootLogger `_ を介して利用する。他の全てのロガーは、インスタンス化し、static メソッドの `log4cxx::Logger::getLogger `_ を介して利用する。このメソッドはロガー名を意図した名前をパラメータとして受け取る。 ``Logger`` クラスが持ついくつかの基礎的なメソッドを以下にリストする。 .. code-block:: c++ namespace log4cxx { class Logger { public: // 生成 & 抽出メソッド: static LoggerPtr getRootLogger(); static LoggerPtr getLogger(const std::string& name); static LoggerPtr getLogger(const std::wstring& name); } } // // ロガーメソッドを直接呼び出さずに以下マクロを使うこと。 // これらマクロは char であるとか wchar_t であるとかあるいは // std::string、もしくは std::basic_string::operator<< の // 右辺となるようなあらゆるものを適切に扱うであろう。 // #define LOG4CXX_TRACE(logger, expression) ... #define LOG4CXX_DEBUG(logger, expression) ... #define LOG4CXX_INFO(logger, expression) ... #define LOG4CXX_WARN(logger, expression) ... #define LOG4CXX_ERROR(logger, expression) ... #define LOG4CXX_FATAL(logger, expression) ... ロガーはレベルに対応付く *かもしれない* 。定義済みのレベル: TRACE, DEBUG, INFO, WARN, ERROR と FATAL、 これらは `log4cxx::Level `_ クラス内にアクセッサ関数とともに定義されている。 与えられたロガーがレベルに対応付かない場合には、レベルに対応付けられた最も近い先祖のレベルが継承される。 より正確には: **Level Inheritance** あるロガー「C」についての「継承レベル」とは、ロガー階層を「C」から上向きにルートロガーに向かって 辿った最初の非 null レベルである。 結果的にあらゆるロガーにレベルが継承されるよう、ルートロガーは常にレベルが付けられている。 Below are four tables with various assigned level values and the resulting inherited levels according to the above rule. .. csv-table:: Example 1 :header: "Logger name", "Assigned level", "Inherited level" root,Proot,Proot X,none,Proot X.Y,none,Proot X.Y.Z,none,Proot In example 1 above, only the root logger is assigned a level. This level value, Proot, is inherited by the other loggers X, X.Y and X.Y.Z. .. csv-table:: Example 2 :header: "Logger name", "Assigned level", "Inherited level" root,Proot,Proot X,Px,Px X.Y,Pxy,Pxy X.Y.Z,Pxyz,Pxyz In example 2, all loggers have an assigned level value. There is no need for level inheritence. .. csv-table:: Example 3 :header: "Logger name", "Assigned level", "Inherited level" root,Proot,Proot X,Px,Px X.Y,none,Px X.Y.Z,Pxyz,Pxyz In example 3, the loggers root, X and X.Y.Z are assigned the levels Proot, Px and Pxyz respectively. The logger X.Y inherits its level value from its parent X. .. csv-table:: Example 4 :header: "Logger name", "Assigned level", "Inherited level" root,Proot,Proot X,Px,Px X.Y,none,Px X.Y.Z,none,Px In example 4, the loggers root and X and are assigned the levels Proot and Px respectively. The loggers X.Y and X.Y.Z inherits their level value from their nearest parent X having an assigned level. ロギング要求はロガーインスタンスのメソッドを呼び出すことによって生成されるが、可能な限り *LOG4CXX_INFO* あるいは類似のマクロを利用すること。これは(レベルによる出力)閾値未満 ではメソッドを呼び出さないし、また、メッセージパラメータ内で出力オペレータ ( *<<* )を 使用することも出来る。 .. code-block:: c++ log4cxx::LoggerPtr logger(log4cxx::Logger::getLogger("com.foo")); const char* region = "World"; LOG4CXX_INFO(logger, "Simple message text.") LOG4CXX_INFO(logger, "Hello, " << region) LOG4CXX_DEBUG(logger, L"Iteration " << i) LOG4CXX_DEBUG(logger, "e^10 = " << std::scientific << exp(10.0)) // // wchar_t 版を確実に使いたければ、 wchar_t 型を最初のオペランドにする: // LOG4CXX_WARN(logger, L"" << i << L" is the number of the iteration.") ロギング要求は、そのレベルがロガーのものより高いか等しい場合に有効となる、と言うことが 出来る。そうでないなら「無効となる」。レベルに対応付かないロガーは階層に基づき継承される。 このルールを下記に要約する。 **Basic Selection Rule** A log request of level p in a logger with (either assigned or inherited, whichever is appropriate) level q, is enabled if p >= q. This rule is at the heart of log4cxx. It assumes that levels are ordered. For the standard levels, we have TRACE < DEBUG < INFO < WARN < ERROR < FATAL. Here is an example of this rule. .. code-block:: c++ // "com.foo" 名のロガーインスタンスを得る log4cxx::LoggerPtr logger(log4cxx::Logger::getLogger("com.foo")); // さあ、レベルをセットしよう。普通はこんなふうにプログラム内で明示的に // セットするのではなく設定ファイルに書く。 logger->setLevel(log4cxx::Level::getInfo()); log4cxx::LoggerPtr barlogger(log4cxx::Logger::getLogger("com.foo.Bar"); // これは有効だ。 WARN >= INFO だから。 LOG4CXX_WARN(logger, "Low fuel level.") // これは無効である。DEBUG < INFO なので。 LOG4CXX_DEBUG(logger, "Starting search for nearest gas station.") // ロガーインスタンスは barlogger 、名前としては "com.foo.Bar" // なのであるから "com.foo" から継承されるわけで、であるからして // 以下は「INFO >= INFOなので有効」である。 LOG4CXX_INFO(barlogger. "Located nearest gas station.") // これは無効である。 DEBUG < INFO なので。 LOG4CXX_DEBUG(barlogger, "Exiting gas station search") ``getLogger`` メソッドに同名を渡せば、常に完全に同一ロガーオブジェクトへの参照を返す。 For example, in .. code-block:: c++ log4cxx::LoggerPtr x = log4cxx::Logger::getLogger("wombat"); log4cxx::LoggerPtr y = log4cxx::Logger::getLogger("wombat"); x and y refer to exactly the same logger object. ゆえ、ロガーの設定と利用において、あなたは独力で頑張って参照を引き回したりしなくて良い。 「生物学的に親であること」と根本的に異なっているのは、親たちはいつだって子に先立つ(親が 子より後から産まれるわけがない)のに、log4cxx のロガーはどういう順序で作ってもいいし 設定してもいい、ということだ。とりわけ「親」ロガーはたとえそれが後から作られたもの だとしても子孫を見つけるしリンクする。 log4cxx 環境の設定は、典型的にはアプリケーションの初期化時に行う。推奨されるのは、 設定ファイルを読むことである。このアプローチについては手短に議論する。 Log4cxx はロガーの命名を、 *ソフトウェアコンポーネント* によって行うことを 簡単にしてくれる。これには(あなたの)個々の class 内に、(あなたの)class のフルネーム をロガー名としてスタティックにロガーをインスタンス化すれば良いだろう。これはロガーの 定義方法としては有用だし直截的だ。そのログの出力はその生成されたロガー名を運ぶので、 この命名戦略はメッセージの識別を容易にするわけだ。ただ…、一般的だからといって これが唯一無二の方法というわけではない。Log4cxx はロガーの可能なセットを制限したりは しない。開発者は望むようにロガーを命名してもいい。 とはいえ、ロガー名をそれが位置する場所の class で修飾するのが、知られている限りは 最善の戦略であるようだ。 Appenders and Layouts ************************************************************************** 有効に出来るとか無効に出来る、という能力は全体像の一部に過ぎない。 Log4cxx はその出力先を多重化出来る。 log4cxx 式言い方をするならば、 出力先、は、「 appender 」と呼びならわす。現在のところ、「 appender 」には `コンソール向け `_ 、 `ファイル向け `_ 、 GUI コンポーネント向け、 `リモートのソケットサーバ向け `_ 、 (Windows の) `NT Event ロガー `_ 、 それにリモート UNIX `Syslog `_ デーモン向け、がある( [#f1]_ )。 また、これらの `非同期 `_ ログも 可能である([#f2]_)。 .. [#f1] log4cxx をソースからビルドする場合は、デフォルトでは全てが使えるわけではない。 必要に応じて configure で制御すること。 .. [#f2] 非同期ログの信頼性についてよくわからないので、喜んでいいものかわからない。どうしても それをしたいならば、かなりの検証がいるだろう。 appender はロガーに対して複数貼り付けられる。 `addAppender `_ メソッドが appender を ロガーに追加する。あるロガーに対する個々の有効なロギング要求は、自身が持つ appender 全てと、ロガー階層のより高い位置にある appender 全てに転送されることになる。換言すれば、appender はロガーの階層より相加的に継承される。例えば、もし console appender が ルートロガーに追加されるならば、全ての有効なロギング要求は最低でもコンソールには出力される。もし追加として file appender をロガーに追加、いまこれを「C」とするとして、これは「C」と「Cの子」に対する有効なロギング要求 をファイルとコンソールに出力する。このデフォルトの振る舞いを変更し、相加的に appender が積み上がるのをやめることは 可能である: `additivity flag `_ を false にする。 appender の相加を支配するルールを以下に要約する: **Appender Additivity** The output of a log statement of logger C will go to all the appenders in C and its ancestors. This is the meaning of the term "appender additivity". However, if an ancestor of logger C, say P, has the additivity flag set to false, then C's output will be directed to all the appenders in C and it's ancestors upto and including P but not the appenders in any of the ancestors of P. Loggers have their additivity flag set to true by default. The table below shows an example: .. csv-table:: :header: "Logger Name", "Added Appenders", "Additivity Flag", "Output Targets", "Comment" :delim: | root|A1|not applicable|A1|The root logger is anonymous but can be accessed with the log4cxx::Logger::getRootLogger() method. There is no default appender attached to root. x|A-x1, A-x2|true|A1, A-x1, A-x2|Appenders of "x" and root. x.y|none|true|A1, A-x1, A-x2|Appenders of "x" and root. x.y.z|A-xyz1|true|A1, A-x1, A-x2, A-xyz1|Appenders in "x.y.z", "x" and root. security|A-sec|false|A-sec|No appender accumulation since the additivity flag is set to false. security.access|none| true| A-sec|Only appenders of "security" because the additivity flag in "security" is set to false. 半々くらいにおいては、きっとユーザは出力先だけではなく出力時の書式化もカスタマイズしたいと望むだろう。 これについては、 appender に「 *layout* 」 を関係付けることで可能だ。「レイアウト」の責務は ロギング要求をユーザの望みの通りに書式化することであり、appender はそれを注意深くその出力先へ 転送するのが役目だ。 log4cxx 標準配布物の一部である `PatternLayout `_ は、 C 言語の printf 関数に似た変換パターンに基づく書式化出力を、ユーザに指定させてくれる。 例えば PatternLayout を変換パターン "%r [%t] %-5p %c - %m%n" で使うと、出力は以下のようなぐあいになるだろう: .. code-block:: none 176 [main] INFO org.foo.Bar - Located nearest gas station. 最初のフィールドはプログラム開始からの経過ミリ秒である。2つめはログ要求を行ったスレッドを特定している。 3つめはログのレベル。4つめはロガー名。ハイフン以降のテキストが、ロギング要求メソッドに与えたメッセージだ。 Configuration ************************************************************************** アプリケーション実装にログ要求のコードを追加することは、かなりの量の計画性と努力が必要といえる。 観測によれば、アプリケーション全体のおよそ 4% がロギングに費やされている。 それゆえに、ほどほどの規模のアプリケーションでさえ、数千行ものロギング文が 埋め込まれていることになる。それだけあれば、必要がないならログ要求文を手で 変更することなく管理することは不可欠だ。 log4cxx 環境はプログラム的に完全に設定することが出来る。が、設定ファイルを使って 制御する方がはるかに柔軟だ。現在のところ、設定ファイルは XML または Java のプロパティ ファイル形式 (key=value) で書くことが出来る。 これを log4cxx を使ってどんなあんばいで行うのか、想像上のアプリケーション「MyApp」にご登場願って 示してみよう: .. code-block:: c++ #include "com/foo/bar.h" using namespace com::foo; // include log4cxx header files. #include "log4cxx/logger.h" #include "log4cxx/basicconfigurator.h" #include "log4cxx/helpers/exception.h" using namespace log4cxx; using namespace log4cxx::helpers; LoggerPtr logger(Logger::getLogger("MyApp")); int main(int argc, char **argv) { int result = EXIT_SUCCESS; try { // コンソールにログを出す単純な設定。 BasicConfigurator::configure(); LOG4CXX_INFO(logger, "Entering application."); Bar bar; bar.doIt(); LOG4CXX_INFO(logger, "Exiting application."); } catch(Exception&) { result = EXIT_FAILURE; } return result; } MyApp は log4cxx ヘッダをインクルードするところから始まる。 次にclass を完全に識別する名前で命名されたロガーインスタンスの static 変数の定義。 MyApp はヘッダファイル「 com/foo/bar.h 」で定義されている「Bar」 class を使う。 .. code-block:: c++ // file com/foo/bar.h #include "log4cxx/logger.h" namespace com { namespace foo { class Bar { static log4cxx::LoggerPtr logger; public: void doIt(); } } } .. code-block:: c++ // file bar.cpp #include "com/foo/bar.h" using namespace com::foo; using namespace log4cxx; LoggerPtr Bar::logger(Logger::getLogger("com.foo.bar")); void Bar::doIt() { LOG4CXX_DEBUG(logger, "Did it again!") } `BasicConfigurator::configure `_ メソッドの呼び出しは、かなり単純な log4cxx セットアップをする。このメソッドはルートロガーに問答無用で `ConsoleAppender `_ を追加する。出力は `PatternLayout `_ に "%-4r [%t] %-5p %c %x - %m%n" をセットした出力となる。 デフォルトではルートロガーは ``Level::getDebug()`` になっていることに注意すること。 MyApp の出力はこんな: .. code-block:: none 0 [12345] INFO MyApp - Entering application. 36 [12345] DEBUG com.foo.Bar - Did it again! 51 [12345] INFO MyApp - Exiting application. 今示した例は、何度動かしたところで同じ情報しか出力しない。幸い、出力を実行時に制御可能なように MyApp を修正するのは容易である。以下が、この点においてわずかばかり改善したバージョンである: .. code-block:: c++ // file MyApp2.cpp #include "com/foo/bar.h" using namespace com::foo; // include log4cxx header files. #include "log4cxx/logger.h" #include "log4cxx/basicconfigurator.h" #include "log4cxx/propertyconfigurator.h" #include "log4cxx/helpers/exception.h" using namespace log4cxx; using namespace log4cxx::helpers; // Define a static logger variable so that it references the // Logger instance named "MyApp". LoggerPtr logger(Logger::getLogger("MyApp")); int main(int argc, char **argv) { int result = EXIT_SUCCESS; try { // MyApp プログラムに java プロパティ形式のファイル名を // 渡すとそれを使う…的な if (argc > 1) { // BasicConfigurator replaced with PropertyConfigurator. PropertyConfigurator::configure(argv[1]); } else { BasicConfigurator::configure(); } LOG4CXX_INFO(logger, "Entering application.") Bar bar bar.doIt(); LOG4CXX_INFO(logger, "Exiting application.") } catch(Exception&) { result = EXIT_FAILURE; } return result; } このバージョンの MyApp は `PropertyConfigurator` に設定ファイル解析とそれに基づく 設定を指示している。 以下が、先の `BasicConfigurator` だけで行った出力と完全に等価な設定ファイルの サンプルだ: .. code-block:: bash # Set root logger level to DEBUG and its only appender to A1. log4j.rootLogger=DEBUG, A1 # A1 is set to be a ConsoleAppender. log4j.appender.A1=org.apache.log4j.ConsoleAppender # A1 uses PatternLayout. log4j.appender.A1.layout=org.apache.log4j.PatternLayout log4j.appender.A1.layout.ConversionPattern=%-4r [%t] %-5p %c %x - %m%n `PropertyConfigurator` 形式ファイルフォーマットは log4j と同じであることは 特筆しておこうか。 `com::foo` パッケージ以下のあらゆるコンポーネントの出力にもはや興味がなくなった、 ということを想像してみる。以下設定はこの目的を達成する手段の一つだ: .. code-block:: bash log4j.rootLogger=DEBUG, A1 log4j.appender.A1=org.apache.log4j.ConsoleAppender log4j.appender.A1.layout=org.apache.log4j.PatternLayout # Print the date in ISO 8601 format log4j.appender.A1.layout.ConversionPattern=%d [%t] %-5p %c - %m%n # com.foo 以下に関しては警告(WARN)かそれよりやばいもの以外は黙っておけ: log4j.logger.com.foo=WARN この設定で MyApp を起動したログはこんな具合: .. code-block:: none 2000-09-07 14:07:41,508 [12345] INFO MyApp - Entering application. 2000-09-07 14:07:41,529 [12345] INFO MyApp - Exiting application. ロガー ``com.foo.Bar`` にはレベルが明示されていないので、 ``com.foo`` から レベルが継承されていて、ゆえ、 ``com.foo.Bar`` のレベルは設定に基づき `WARN` である。 `Bar::doIt` 内のログ文は `DEBUG` であるからして、 すなわち `WARN` よりレベルが低い。しかるに、 `Bar::doIt` メソッドからの ログ要求は揉み消されている。 複数 appender をやりたければこんな具合: .. code-block:: bash log4j.rootLogger=debug, stdout, R log4j.appender.stdout=org.apache.log4j.ConsoleAppender log4j.appender.stdout.layout=org.apache.log4j.PatternLayout # Pattern to output the caller's file name and line number. log4j.appender.stdout.layout.ConversionPattern=%5p [%t] (%F:%L) - %m%n log4j.appender.R=org.apache.log4j.RollingFileAppender log4j.appender.R.File=example.log log4j.appender.R.MaxFileSize=100KB # Keep one backup file log4j.appender.R.MaxBackupIndex=1 log4j.appender.R.layout=org.apache.log4j.PatternLayout log4j.appender.R.layout.ConversionPattern=%p %t %c - %m%n Calling the enhanced MyApp with the this configuration file will output the following on the console. .. code-block:: none INFO [12345] (MyApp2.cpp:31) - Entering application. DEBUG [12345] (Bar.h:16) - Doing it again! INFO [12345] (MyApp2.cpp:34) - Exiting application. 加えて、as the root logger has been allocated a second appender、出力は `example.log` ファイルにも書き出される。このファイルはサイズが 100KB に達すると roll-over される。roll-over が発生すると、古いほうは `example.log.1` に 自動的にリネームされる。 これらロギングの異なる振る舞いを得るのに、プログラムを再コンパイルする必要が ないことに注目のこと。UNIX の Syslog デーモンに向けるのも簡単だし、 `com.foo` 出力を全部 NT Event ロガーに全部リダイレクトしてみたりだとか、 リモートの log4cxx サーバに向けてみたりだとか、そういったことを「ローカル サーバのポリシー」、たとえばログは second log4cxx サーバに向けるべし、 のようなものに基づいて行う、こんなことも容易である。 Default Initialization Procedure ************************************************************************** log4cxx ライブラリは、それが動作する環境について、何ら前提を置かない。とりわけ、 log4cxx appender にはデフォルトがない。ただし、ある特定のよく定義された環境に おいては、Logger クラスの静的イニシャライザは、log4j 設定を自動で行おうと 試みる。 デフォルトの初期化アルゴリズムの正確な定義は以下: 1. 環境変数 ``LOG4CXX_CONFIGURATION`` がセットされていれば、 configurationOptionStr をその値に、 なければ、環境変数 ``log4j.configuration`` または ``LOG4CXX_CONFIGURATION`` ([#f3]_) の値、これらもない場合には、カレントディレクトリにある "log4cxx.xml"、 "log4cxx.properties"、 "log4j.xml"、 "log4j.properties" の最初に見つかったものを。この結果で configurationOptionStr がそれでも空ならば、ロギングは無効となる。 2. 環境変数 ``LOG4CXX_CONFIGURATOR_CLASS`` または ``log4j.configuratorClass`` を使うことに よりカスタムコンフィギュレータが指定されない限りは、ファイル名が `.xml` で終わらない場合は `PropertyConfigurator` が、 `.xml` の場合は `DOMConfigurator` が log4cxx 設定に使われる。カスタムコンフィギュレータが指定 する場合は、環境変数の値は `Configurator` インターフェイスを実装した class を完全に特定する class 名 でなければならない。 .. [#f3] 文脈から、LOG4J_CONFIGURATION のミスペルではないかという気がする。 Nested Diagnostic Contexts ************************************************************************** 多くの現実世界のシステムは、複数のクライアントを同時に扱う必要がある。その種のシステムの 典型的なマルチスレッド実装では、別々のスレッドが別々のクライアントを処理する。ロギングは 複雑に配置されたアプリケーションのトレース、デバッグにとりわけ適している。あるクライアント からの出力を他のクライアントからの出力から分離するよくあるアプローチは、新しいクライアントごとに ロガーを新規にインスタンス化することである。これはロガーの増殖とロギング管理のオーバヘッド増加を 促進してしまう。 もっと「軽い」テクニックとして、同じクライアントからのリクエストの個々のログに、固有の スタンプを付与する、というものがある。 Neil Harrison described this method in the book "Patterns for Logging Diagnostic Messages," in Pattern Languages of Program Design 3, edited by R. Martin, D. Riehle, and F. Buschmann (Addison-Wesley, 1997). 個々のリクエストへの固有のスタンプには、ユーザは NDC (`Nested Diagnostic Context`) に文脈依存情報を「push」する。NDC クラスは以下: .. code-block:: c++ namespace log4cxx { class NDC { public: // pushes the value on construction and pops on destruction. NDC(const std::string& value); NDC(const std::wstring& value); // Remove the top of the context from the NDC. static LogString pop(); // Add diagnostic context for the current thread. static void push(const std::string& message); static void push(const std::wstring& message); } NDC は、スレッドごとの文脈依存情報の `stack` として管理される。 `log4cxx::NDC` class の 全てのメソッドは `static` であることに注意。いま NDC 出力が有効だと仮定して、ログリクエストが 発生するたびに、合致する log4cxx コンポーネントはカレントスレッドについての *完全な* NDC stack をそのログ出力に取り込む。これはユーザによる介入なしに行われ、ユーザはただ NDC に対して正しい情報を 置くだけで良い。これは push, pop メソッドを、数少ない「コード上のよく定義された位置」にて 呼び出すことで行う。これは先の「クライアントごとロガー」アプローチが実装の広範囲な変更を伴うのとは 対照的である。 この点を示すのに、おびただしい数のクライアントに対してコンテンツを配信するようなサーブレットを 例に考えてみよう。この場合のサーブレットは、リクエストを受けるたびに、他のコードの実行に先立って NDC を構築することが出来る。文脈情報としてはクライアントのホスト名とその他リクエストに固有の情報、 典型的には cookie 内の情報、これらを使えるであろう。ゆえ、同一コードにより初期化されたログ、 つまり同一ロガー、これはサーブレットが複数クライアントを同時に処理する場合でも区別可能である。 個々のクライアントごとのリクエストは異なる NDC stack を持つのであるから。 Contrast this with the complexity of passing a freshly instantiated logger to all code exercised during the client's request. Nevertheless, some sophisticated applications, such as virtual hosting web servers, must log differently depending on the virtual host context and also depending on the software component issuing the request. Recent log4cxx releases support multiple hierarchy trees. This enhancement allows each virtual host to possess its own copy of the logger hierarchy. Performance ************************************************************************** ロギングについてしばしば引用される議論の一つには、その演算コストがある。これは 数千のログ要求をしうるような、控えめなサイズのアプリケーションであろうと、ごもっとな 懸念である。ロギングのパフォーマンスについては、その計測と調整に多大な労力が支払われて きた。Log4cxx は「速くて柔軟」であることを謳う: スピード優先, 柔軟性はその次。 ユーザは以下に述べる性能問題に注意を払うべきである: 1. ロギング出力を OFF にした場合のロギングの性能 * ロギングを完全に OFF にする、あるいは必要レベルだけの ON にする場合に、ログ出力要求の コストは、メソッド呼び出しのコストと数値比較([#f4]_)の和である。 `LOG4CXX_DEBUG` やこれに類似のマクロはログ要求が disabled の場合に不要な評価コストを避けている。 2. ロギング出力を ON にした場合の、出力するのかしないのかの判定の性能 * 本質的にはこれは、ロガーの階層を渡り歩く性能である。ロギングが ON になっている場合でも、 log4cxx はログリクエストのレベルとロガーのレベルの比較が必要である。しかしながら 仮にロガーにレベルが割り当てられていないとすると、ロガーの階層から継承される。ゆえ、 レベルの継承の前にロガーは祖先を探し当てる必要がある。 * 階層を渡り歩くのを、出来るだけ、可能な限り速くすることには真剣な努力が支払われてきた。 たとえば、子のロガーは存在する祖先だけにリンクする。先の `BasicConfigurator` 例 における `com.foo.Bar` ロガーは直接ルートロガーに接続するすることで、存在しない `com` 、 `com.foo` ロガーへのリンクは迂回される。このことは、階層渡り歩きを劇的に 改善する。とりわけ「疎な」階層では。 * 階層渡り歩きのコストで、典型的にはロギングを完全に OFF にする場合の3倍遅くする。 3. 実際の出力メッセージ * これは出力の書式化のコストと、実際の出力先への転送コストである。ここにおいても、 layouts (formatters) が可能な限り高速になるように多大な努力が支払われてきた。 appender についても同様である。 .. [#f4] 「if (level < ??)」のようなという判定コストのこと。 Conclusions ************************************************************************** Apache Log4cxx is a popular logging package written in C++. One of its distinctive features is the notion of inheritance in loggers. Using a logger hierarchy it is possible to control which log statements are output at arbitrary granularity. This helps reduce the volume of logged output and minimize the cost of logging. One of the advantages of the log4cxx API is its manageability. Once the log statements have been inserted into the code, they can be controlled with configuration files. They can be selectively enabled or disabled, and sent to different and multiple output targets in user-chosen formats. The log4cxx package is designed so that log statements can remain in shipped code without incurring a heavy performance cost.