3.ACE Logging 機能

printf 関数や cerr ステートメントを利用したデバッグ出力はよく使われるログ機能である。ACE はこれらの機能をより強力にし、出力する情報の種類や出力先の変更などを行えるようにしている。
現在ではグラフィカルかつ強力なデバッガがあるので、テキストによるログ出力は不要と思うかもしれない。このような場合でも、次のような利用方法にはログ出力が重宝する。

3.1 基本的なログとトレース

ログ出力を行うために、一般的には次の3つのマクロが利用される。ACE_DEBUG、ACE_ERROR、ACE_TRACE である。前の二つは利用方法が同じであり、処理内容も同じである。唯一違うのは、規約として ACE_DEBUG はデバッグ用途に利用し、ACE_ERROR は警告やエラー表示に利用することだ。
これらのマクロは次のようにして利用する。
ACE_DEBUG(( 重要度, フォーマット引数群 ));
ACE_ERROR(( 重要度, フォーマット引数群 ));
「重要度」はメッセージの重要度を示し、全9種が用意されている。一般的に利用されるのは LM_DEBUG と LM_ERROR だろう。詳細度の高い方から LM_TRACE、LM_DEBUG、LM_INFO、LM_NOTICE、LM_WARNING、LM_ERROR、LM_CRITICAL、LM_ALERT、LM_EMERGENCY となる。
「フォーマット引数群」には printf 形式での引数指定(文字列と埋め込み用の各パラメータ)ができる。
これらのマクロには2つの括弧の組が必要であることに注意してほしい。これは ACE_NLOGGING マクロが定義されている場合に、該当部分の内容を空に置き換えることを想定している。

ACE_DEBUG と ACE_ERROR がマクロの位置に出力を埋め込むのに対し、ACE_TRACE はマクロの位置の他にブロックの終了時にも出力を行う点で異なっている。
ACE_TRACE を関数の先頭に置くことで、関数の駆動と終了のトレース表示を行うことができるのだ。しかしながら、この便利さと引き換えに、引数には単純な文字列しか利用できない。(つまり、printf ライクな引数リストは使えない)
ACE_TRACE の利用方法は次のようになる。
ACE_TRACE( ACE_TEXT("logging message") );
各マクロの利用サンプルコードは p.40 に記載されている。

これらのマクロを利用するためには、「ace/Log_Msg.h」をインクルードする必要がある。
他のマクロには以下のようなものがある。

ログ出力マクロ群は次のマクロの定義によってコンパイル時の有効・無効を切り替えることができる。
ACE_NTRACE(デフォルトでオフ:0)、ACE_NDEBUG(デフォルトでオン:1)、ACE_NLOGGING(デフォルトでオン:1)。この時、ACE_TRACE は ACE_NTRACE および ACE_NDEBUG の両方の影響を受けることに注意が必要だ。

3.2 表示するログレベルの調整

ACE では ACE_Log_Msg クラスがログメッセージのフォーマットを司っている。このクラスは ACE によって自動的にスレッドごとに準備され、ACE_LOG_MSG マクロによってそのインスタンスのポインタを取得することができる。
ログレベルは ACE_LOG_MSG->priority_mask( mask, TYPE = THREAD ) で設定することができ、この関数は以前のマスク値を返す。TYPE には ACE_Log_Msg::THREAD か ACE_Log_Msg::PROCESS を指定することができる。利用例は次のようになる。
ACE_LOG_MSG->priority_mask( LM_DEBUG | LM_ERROR, ACE_Log_Msg::THREAD );
デフォルトではプロセスレベルで全てのログをオンにしており、各スレッドのレベルでは0(全てオフ)である。ログ出力はどちらかのレベルでオンになっていれば出力されるので、初期状態では全てのログが出力されることになる。
スレッドレベルでのインスタンスのデフォルトは0(全てオフ)だが、これは次のメソッドで変更することができる。変更後に作成されたスレッドのログレベルは新規に設定したデフォルト値と同様になる。
ACE_LOG_MSG::disable_debug_messages(priority = DEBUG);
ACE_LOG_MSG::enable_debug_messages(priority = DEBUG);
また、ACE_Log_Msg クラスには状態を示す変数があり、それらを取得・変更するメソッドも存在している。これらは p.48 表3.4 を参照すること。

3.3 ACE ロギングマクロのカスタマイズ

ACE_DEBUG マクロをラップしてカスタマイズ版を作る例として p.49 が挙げられる。この __VA_ARGS__ 技法は最近の GNU C/C++ コンパイラでは有効だが、その他では動かない場合がある。その際にはエレガントさには欠けるが、p.51 のような方法がある。
また、ACE_TRACE マクロのカスタマイズについては p.51-54 に記載されている。簡単な例ではあるが、かなり参考になると思われる。

3.4 ログ出力先の変更

3.4.1 標準ログ出力

デフォルトでのログ出力先は標準エラー出力(STDERR)となっている。他の選択肢がある中で明示的に STDERR を利用するには次のようにすればよい。
ACE_LOG_MSG->open( argv[0], ACE_Log_Msg::STDERR );
または以下の関数を使って出力先フラグを変更してもよい。
ACE_LOG_MSG->set_flags( flag );
ACE_LOG_MSG->clr_flags( flag );
例えば、標準エラー出力をオンにするにはこうする。
ACE_LOG_MSG->set_flags( ACE_Log_Msg::STDERR );

3.4.2 システムロガー

システムロガーはプラットフォームでサポートされていない場合、何の影響も及ぼさない。windows NT4 以降(2000 や XP など)ではシステムのイベントログを示す。この時、省略可能な第三引数はイベントログに記入されるサービス名となる。
また、UNIX/Linux では syslog への出力となる。デフォルトでのファシリティーは LOG_USER であるが、これは config.h における ACE_DEFAULT_SYSLOG_FACILITY の定義で変更することができる。詳しくは syslog の man ページなどを参照のこと。
システムロガーへ出力したい場合は次の命令を利用する。
ACE_LOG_MSG->open( argv[0], ACE_Log_Msg::SYSLOG, ACE_TEXT("syslogTest") );
システムロガーへの出力を停止したい場合には、ACE_Log_Msg::SYSLOG 以外を指定した open を行えばよい。例えば、次の命令ではデフォルトの STDERR 出力へ戻し、システムロガーを閉じる。
ACE_LOG_MSG->open( argv[0] );
システムロガーへの出力の際には、初期化処理を必要とするため、set_flags() や clr_flags() による出力先指定は利用できない。open() を利用すること。(訳注:できるかもしれないが、条件がある模様)

3.4.3 出力ストリーム

ファイルなどの ostream オブジェクトへログ出力したい場合には次のように設定する。
ACE_OSTREAM_TYPE *output = new std::ofstream("ostream.output.txt");
ACE_LOG_MSG->msg_ostream( output, 1 );
ACE_LOG_MSG->set_flags( ACE_Log_Msg::OSTREAM );
ACE_LOG_MSG->clr_flags( ACE_Log_Msg::STDERR );

msg_ostream() 関数の第二引数はストリームオブジェクトの所有権を獲得するかどうかを示す。上のように設定すると、該当する ACE_Log_Msg オブジェクトが削除される時に new した ofstream も delete してもらえる。
msg_ostream() による出力ストリーム割り当ての前に、出力先を OSTREAM に指定しても問題は起きない。その間は出力されなくなるだけである。
また、std::ostream の代わりに ACE_OSTREAM_TYPE を利用しているのは移植性のためである。C++ のiostraem をサポートしない処理系においては FILE を代入して利用することもできる。

3.4.4 複合テクニック

これまでの内容を複合して利用し、複数の出力先へログを書き出すこともできる。例は p.60 にある。この中で注意が必要なのは ostream の出力先である output を delete する前に clr_flags(OSTREAM) で出力ストリームを出力先から外している点である。これを行わないと ACE_TRACE() によるトレース出力が delete された output にも出力されることになり、最悪の場合クラッシュしてしまう可能性がある。

3.5 コールバックの使い方

ログした出力が、出力先に出る前にチェックしたり変更したりしたい場合がある。そのような場合には ACE_Log_Msg_Callback が利用できる。使い方は次の通りである。
  1. コールバッククラスを ACE_Log_Msg_Callback から継承して、virtual void log(ACE_Log_Record &log_record)メソッドを実装する。
  2. 上で用意したコールバッククラスのオブジェクトを生成する。
  3. コールバックオブジェクトのポインタを ACE_Log_Msg::msg_callback() 関数で登録する。
  4. ACE_Log_Msg::set_flags() 関数でコールバックを有効にする。
一度登録され、有効化されると、このオブジェクトの log() メソッドは ACE_Log_Msg::log() が呼ばれるたび(全てのログ出力マクロが利用)に ACE_Log_Record を伴って呼び出される。
コールバックの利用に関する詳細は ACE_Log_Msg_Callback のリファレンスを参照すること。その中でも特にマルチスレッドを利用している場合と、ostream を出力先に利用している場合には注意が必要である。
サンプルコードは p.61-64 を参照。

3.6 ログクライアントとサーバのデーモン

ACE では netsvcs ロギングフレームワークにおいて、クライアント・サーバ型のデザインを利用している。あるホストでサーバを起動しておき、ログを取りたいホストではクライアントを起動しておいて、そこへログを出力する。このプロキシアプローチによって各プロキシホストがバッファとなり、サーバの処理能力オーバーを低減させることができる。
このサーバとクライアントを設定するには19章で説明する Service Configurator を利用する。必要最低限の説明はあるが、詳しく知りたい場合には19章を参照すること。
サーバとクライアントの設定・起動方法は p.65-66 で、これらへの出力サンプルは p.67 に、サーバへ直接送信するサンプルは p.67-69 にある。

3.7 LogManager クラス

ログ出力先の変更を容易にするための LogManager クラスの実装例が p.70-73 に記載されている。簡略化された実装ではあるが、叩き台としては有効なクラスである。
その他、ACE_Singleton の利用法や、古いコンパイラでのテンプレートの明示的なインスタンス化についても載っている。

3.8 ログ機能の実行時における設定変更

今までの内容でコンパイル時におけるログ出力制御は問題無いと思われる。しかしながら、状況によっては実行時に再コンパイル無しでログオプションを変更したい場合もあるだろう。
これらに対応するためにコマンドラインパラメータや設定ファイルを利用することもできるが、その実装とデバッグには手間がかかる。
ACE ではこのような場面に対応するために ACE_Logging_Strategy オブジェクトを利用することができる。それには次の内容の設定ファイルを用意し、svc.conf という名前で保存する。
dynamic Logger Service_Object * ACE:_make_ACE_Logging_Strategy() "-s log.out -f STDERR|OSTREAM -p INFO"
そして次の命令を ACE_TMAIN の先頭に入れておく。
if (ACE_Service_Config::open( argc, argv, ACE_DEFAULT_LOGGER_KEY, 1, 0, 1) < 0 ) { ACE_OS::exit(1); }
上の ACE_Service_Config::open() によって、実行ファイルと同じフォルダ内の設定ファイル svn.conf(「-f ファイル名」を指定することで別の名前も利用可能) が検索され開かれる。注意が必要なのは、open() の最後の引数が 1以外だとロギングフラグが open() 以前のものに戻ってしまうことである。
設定ファイル中の ACE_Logging_Strategy へのオプションには p.75 のものが利用できる。中でも -m と -N を利用することで、ログのローテートを行うことができる。

Index
Top