log4cpp †ログを吐き出すライブラリ。使い方をマスターすれば便利そうだが、とっかかりは非常に面倒ぽい。 ソース †ここからダウンロードできる。ソースなのでビルドして使わにゃ。 ビルド †Windows なので、VC++ 6 (.NET) などでビルドして使用する( nmake もあるかな?)。 留意点
ビルドすると log4cpp.lib と log4cpp.dll が作成されるので、これをプロジェクトから見える位置に配置する。 「追加のインクルードディレクトリ」などに追加しちゃっても良いし、pragma でソースに書いても良い。 VC++8(VS2005)以降でコンパイルできない場合 なので、dsp をテキストエディタなどで開いて、これらの場所を修正してからコンバートすればOK(コンバートした後なら vcproj ファイルの同じ場所を修正)。VC++8以降の場合、デフォルトのままインストールしていれば、(Program Files)\Microsoft Visual Studio 8\Common7\Tools\Bin とか (Program Files)\Microsoft Visual Studio 8\VC\bin あたりに見つかるはず(なければ検索で)。 使用してみる †ライブラリへリンク。 #pragma comment( lib, "log4cpp.lib" ) ヘッダをインクルード。(log4cpp というディレクトリをインクルードしていると仮定。) #define LOG4CPP_HAVE_INT64_T #include <log4cpp/Category.hh> #include <log4cpp/FileAppender.hh> #include <log4cpp/OstreamAppender.hh> #include <log4cpp/Win32DebugAppender.hh> #include <log4cpp/SimpleConfigurator.hh> これだけあれば足りると思う。フィルタを使う場合は次のヘッダも必要。 #include <log4cpp/Filter.hh> 定義する。 static log4cpp::Category& logroot = log4cpp::Category::getRoot(); static log4cpp::Category& logger = log4cpp::Category::getInstance("category"); ログだけなら logger だけでいいか。category には、アプリの名前なりクラスの名前なり解りやすい名前を設定しておく。 で、初期化。 #ifdef(_DEBUG) logroot.setPriority( log4cpp::Priority::DEBUG ); #else logroot.setPriority( log4cpp::Priority::INFO ); #endif log4cpp::Appender* pDebugAppender = new log4cpp::Win32DebugAppender("w32dbg"); log4cpp::Appender* pFileAppender = new log4cpp::FileAppender("file", "debug.log"); logroot.addAppender(pDebugAppender); logroot.addAppender(pFileAppender); try { log4cpp::SimpleConfigurator::configure( "sample.config" ); } catch(log4cpp::ConfigureFailure e) { logroot.warn("FAILED: Loading %s: %s", "sample.config", e.what()); } 出力レベルを設定して、インスタンスをつくって、設定ファイルを読み込んでいる。 sample.config は設定ファイルなので、ここに設定を書いておく。 例えばこんな感じ。 <sample.config> # ログレベルの設定: # priority {カテゴリ名} {ログレベル} # 設定したログレベルはカテゴリ階層へカスケードされる。 priority root WARN priority category DEBUG # 書式の設定: appender category pattern file debug.log %d [%c] %p - %m%n root というカテゴリは WARN レベルまで、category というカテゴリは DEBUG レベルまで出力されるよ、という意味。 高 EMERG, FATAL(システムが使用不可) ↑ ALERT(ただちに行動を起こさなければならない) CRIT(危険な状態) ERROR(エラーの状態) WARN(ワーニングの状態) NOTICE(通常だが重要な状態) ↓ INFO(インフォメーションメッセージ) 低 DEBUG(デバッグレベルのメッセージ) 書式の意味は次の通り。 %% : %一個 %c : カテゴリ %d : 日付と時刻 %m : メッセージ %n : 改行 %p : プライオリティ %r : レイアウトインスタンスが作られてからの時間(ミリ秒単位) %R : 1970年元旦からの秒数 %u : プロセスが起動してからのclock tics %x : NDC 書式は、ソースで PatternLayout? で設定することもできるっぽい。 ex) メッセージのみを出力する例 #include <PatternLayout.hh> log4cpp::Appender* appender = new log4cpp::FileAppender("file", "debug.log"); log4cpp::PatternLayout* layout = new log4cpp::PatternLayout(); layout->setConversionPattern( "%m" ); appender->setLayout( layout ); 設定しなければ、デフォルトで "%m%n" となる。 あとは、 logger.debug("ごにょごにょ"); logger.info("ほげほげ"); とかで出力する。 使い終わったらシャットダウン。 logger.shutdown(); logroot.shutdown(); フィルタの使い方 †全てのログを書き出すのではなく、ピンポイントで欲しいものだけ書き出す、 といったことをしたいという要請が大抵の場合に出てくる。 そういうときに使うのがログフィルタ。 例えば、カテゴリ名でフィルタしたいときは、次のようなクラスで。 #include <Filter.hh> class CategoryFilter : public log4cpp::Filter { public: CategoryLogFilter( const std::string& category ) : m_category( category ) { } virtual ~CategoryLogFilter() { } protected: virtual log4cpp::Filter::Decision _decide( const log4cpp::LoggingEvent& event ) { if ( event.categoryName.find( m_category ) != std::string::npos ) { return Filter::Decision::ACCEPT; } else { return Filter::Decision::DENY; } } private: std::string m_category; }; _decide() メソッドをオーバーライドして、 その中で書き出したいログ種別の場合にACCEPTを返し、 それ以外の場合にDENYを返せば良いということ。 自作 Appender †パッケージには デフォルトでいくつかの標準的な Appender が同梱されているが、 ファイルや標準入出力以外にもログを出してみたいことがある。 そんなときは自分で Appender を作ってしまえばよろしい。 LayoutAppender? を継承すれば、簡単に文字列を出力する Appender をつくることができる。 class CustomAppender : public log4cpp::LayoutAppender { public: CustomAppender(const std::string& name) : log4cpp::LayoutAppender(name) { } virtual ~CustomAppender() { close(); } virtual void close() { } protected: virtual void _append(const log4cpp::LoggingEvent& event) { // TODO: ここをお好みに書き換える。 // これはデバッグウィンドウに文字列を出す例。 std::string message( _getLayout().format(event) ); ::OutputDebugString( CString( message.c_str() ) ); } }; ただ、log4cpp はアプリケーションとは別のスレッドで動作している為、 例えばエディットコントロールなどに文字列を出力しようとする場合は、 スレッドの同期処理が必要になる。 ウィンドウを持つアプリケーションの場合は、 メインウィンドウに対して PostMessage?() で通知するのが一番安全。 通知する側: virtual void _append(const log4cpp::LoggingEvent& event) { // ウィンドウにポストする例。 std::string message( _getLayout().format(event) ); CString * msg = new CString( message.c_str() ); ::PostMessage( hWnd, WM_XXX, 0, (LPARAM)(msg) ); } メッセージはメインウィンドウの WindowProc?() (DefWindowProc?() ) で拾えば良い。 通知される側: BOOL OnLogging( WPARAM wParam, LPARAM lParam ) { CString * msg = (CString*)lParam; CListBox::AddString( *msg ); delete msg; } いわずもがなだが、new したポインタの delete はお忘れなく。 参考サイト †log4ファミリ † |