log4cppの調査
会社でlog4cppというC++のロギングライブラリを利用しているためmacで調査がてら使ってみた。
log4cppはlog4jというjavaのライブラリのC++版らしく詳しく解説されたページがある。
Log4J�O�������`�ڎ�
設定ファイルを読み込んでログ出力をハンドリングできる優れものみたいだ。
log4jに対してlog4cppの使用方法が記述されたドキュメントはネット上ではあまり見つからない。
とりあえずlog4jの設定ファイルがそのまま使えるらしいのでlog4j解説ページを参考に設定ファイルを作成した。
ファイルとsyslogに出力する設定ファイル log4cpp.conf
# CATEGORIES log4j.rootCategory=DEBUG, rootAppender log4j.category.cat.file=DEBUG, FILE log4j.category.cat.syslog=DEBUG, SYSLOG log4j.category.cat.all=DEBUG, SYSLOG, FILE # APPENDERS log4j.appender.rootAppender=org.apache.log4j.ConsoleAppender log4j.appender.rootAppender.layout=org.apache.log4j.BasicLayout # ログローテト機能付きのファイル出力 log4j.appender.FILE=org.apache.log4j.RollingFileAppender log4j.appender.FILE.FileName=test.log log4j.appender.FILE.MaxFileSize=1024 log4j.appender.FILE.MaxBackupIndex=3 log4j.appender.FILE.layout=org.apache.log4j.BasicLayout log4j.appender.FILE.layout.ConversionPattern=%5p %c{1} - %m%n # syslogのfacility=local6へ出力 log4j.appender.SYSLOG=org.apache.log4j.net.SyslogAppender log4j.appender.SYSLOG.facility=local6 log4j.appender.SYSLOG.SyslogHost=localhost log4j.appender.SYSLOG.layout=org.apache.log4j.PatternLayout log4j.appender.SYSLOG.layout.ConversionPattern=%5p %c{1} - %m%n
syslogに出力するためにはfacilityを設定してやる必要がある。(facilityはログの分類のようなもの)
facilityは/usr/include/sys/syslog.hに定義されている
//macだとこのように設定されている。今回はLOG_LOCAL6を利用する $ less /usr/include/sys/syslog.h #define LOG_LOCAL0 (16<<3) /* reserved for local use */ #define LOG_LOCAL1 (17<<3) /* reserved for local use */ #define LOG_LOCAL2 (18<<3) /* reserved for local use */ #define LOG_LOCAL3 (19<<3) /* reserved for local use */ #define LOG_LOCAL4 (20<<3) /* reserved for local use */ #define LOG_LOCAL5 (21<<3) /* reserved for local use */ #define LOG_LOCAL6 (22<<3) /* reserved for local use */ #define LOG_LOCAL7 (23<<3) /* reserved for local use */
facilityに対応するログ出力先の設定
$ sudo vi /etc/syslog.conf //以下を追加する local6.* /var/log/local6.log
syslogdの再起動
// 今回は関係ないがリモートのsyslogに転送する場合はsyslogdのオプションを追加してから再起動してやる必要がある $ sudo vi /System/Library/LaunchDaemons/com.apple.syslogd.plist <string>/usr/sbin/syslogd</string> <string>-u</string> //リモート転送の場合これを追加してやる // 再起動 $ sudo launchctl unload /System/Library/LaunchDaemons/com.apple.syslogd.plist $ sudo launchctl load /System/Library/LaunchDaemons/com.apple.syslogd.plist
これで事前準備は完了。
実際に使えるかテストしてみる。
サンプルプログラム test.cc
#include <iostream> #include <log4cpp/Category.hh> #include <log4cpp/PropertyConfigurator.hh> int main(int argc, char* argv[]) { const std::string CONFIG_FILE = "./log4cpp.conf"; try { log4cpp::PropertyConfigurator::configure(CONFIG_FILE); } catch (log4cpp::ConfigureFailure &e) { std::cerr << e.what() << std::endl; return -1; } //test.logに出力 log4cpp::Category::getInstance("cat.file").debugStream() << "output test.log"; log4cpp::Category::getInstance("cat.file").noticeStream() << "output test.log"; log4cpp::Category::getInstance("cat.file").warnStream() << "output test.log"; log4cpp::Category::getInstance("cat.file").errorStream() << "output test.log"; log4cpp::Category::getInstance("cat.file").critStream() << "output test.log"; log4cpp::Category::getInstance("cat.file").alertStream() << "output test.log"; log4cpp::Category::getInstance("cat.file").emergStream() << "output test.log"; log4cpp::Category::getInstance("cat.file").fatalStream() << "output test.log"; //syslogに出力 log4cpp::Category::getInstance("cat.syslog").debugStream() << "output /var/log/local6.log"; log4cpp::Category::getInstance("cat.syslog").noticeStream() << "output /var/log/local6.log"; log4cpp::Category::getInstance("cat.syslog").warnStream() << "output /var/log/local6.log"; log4cpp::Category::getInstance("cat.syslog").errorStream() << "output /var/log/local6.log"; log4cpp::Category::getInstance("cat.syslog").critStream() << "output /var/log/local6.log"; log4cpp::Category::getInstance("cat.syslog").alertStream() << "output /var/log/local6.log"; log4cpp::Category::getInstance("cat.syslog").emergStream() << "output /var/log/local6.log"; log4cpp::Category::getInstance("cat.syslog").fatalStream() << "output /var/log/local6.log"; //test.logとsyslogに出力 log4cpp::Category::getInstance("cat.all").debugStream() << "output test.log and /var/log/local6.log"; log4cpp::Category::getInstance("cat.all").noticeStream() << "output test.log and /var/log/local6.log"; log4cpp::Category::getInstance("cat.all").warnStream() << "output test.log and /var/log/local6.log"; log4cpp::Category::getInstance("cat.all").errorStream() << "output test.log and /var/log/local6.log"; log4cpp::Category::getInstance("cat.all").critStream() << "output test.log and /var/log/local6.log"; log4cpp::Category::getInstance("cat.all").alertStream() << "output test.log and /var/log/local6.log"; log4cpp::Category::getInstance("cat.all").emergStream() << "output test.log and /var/log/local6.log"; log4cpp::Category::getInstance("cat.all").fatalStream() << "output test.log and /var/log/local6.log"; log4cpp::Category::shutdown(); return 0; }
これだとうまく動かなかった。
- test.logは作成されず代わりにfoobarファイルが作成されて書き込まれた
- local6.logは作成されず
原因がわからないため設定ファイルを読み込むクラスPropertyConfiguratorのソースを読んでみた。
$ Emacs PropertyConfiguratorImpl.cpp else if (appenderType == "RollingFileAppender") { std::string fileName = _properties.getString(appenderPrefix + ".fileName", "foobar"); size_t maxFileSize = _properties.getInt(appenderPrefix + ".maxFileSize", 10*1024*1024); int maxBackupIndex = _properties.getInt(appenderPrefix + ".maxBackupIndex", 1); bool append = _properties.getBool(appenderPrefix + ".append", true); appender = new RollingFileAppender(appenderName, fileName, maxFileSize, maxBackupIndex, append); } else if (appenderType == "SyslogAppender") { std::string syslogName = _properties.getString(appenderPrefix + ".syslogName", "syslog"); std::string syslogHost = _properties.getString(appenderPrefix + ".syslogHost", "localhost"); int facility = _properties.getInt(appenderPrefix + ".facility", -1) * 8; // * 8 to get LOG_KERN, etc. compatible values. int portNumber = _properties.getInt(appenderPrefix + ".portNumber", -1); appender = new RemoteSyslogAppender(appenderName, syslogName, syslogHost, facility, portNumber); } #ifdef LOG4CPP_HAVE_SYSLOG else if (appenderType == "LocalSyslogAppender") { std::string syslogName = _properties.getString(appenderPrefix + ".syslogName", "syslog"); int facility = _properties.getInt(appenderPrefix + ".facility", -1) * 8; // * 8 to get LOG_KERN, etc. compatible values. appender = new SyslogAppender(appenderName, syslogName, facility); }
FileNameとかは認識されなくてfileNameで指定してやらないといけない。(だからfoobarファイルが作成される)
facilityにはlocal6じゃなくて直接番号を設定してやらないといけない。
localのsyslogに出力したい場合はSyslogAppenderじゃなくLocalSyslogAppenderを使うと動く
完成版の設定ファイル log4cpp.conf
# CATEGORIES log4j.rootCategory=DEBUG, rootAppender log4j.category.cat.file=DEBUG, FILE log4j.category.cat.syslog=DEBUG, SYSLOG log4j.category.cat.all=DEBUG, SYSLOG, FILE # APPENDERS log4j.appender.rootAppender=org.apache.log4j.ConsoleAppender log4j.appender.rootAppender.layout=org.apache.log4j.BasicLayout log4j.appender.FILE=org.apache.log4j.RollingFileAppender log4j.appender.FILE.fileName=test.log log4j.appender.FILE.maxFileSize=1024 log4j.appender.FILE.maxBackupIndex=3 log4j.appender.FILE.layout=org.apache.log4j.BasicLayout log4j.appender.FILE.layout.ConversionPattern=%5p %c{1} - %m%n log4j.appender.SYSLOG=org.apache.log4j.net.LocalSyslogAppender log4j.appender.SYSLOG.facility=22 log4j.appender.SYSLOG.syslogName=syslog log4j.appender.SYSLOG.layout=org.apache.log4j.PatternLayout log4j.appender.SYSLOG.layout.ConversionPattern=%5p %c{1} - %m%n
こちらの設定ファイルを使ってさきほどのテストプログラムを動かしたところ、
- test.logにもlocal6.logにもログが書き込まれる事が確認
- test.logのサイズが1024バイトを超えるとtest.log.1〜3まで作成されてログローテトもうまく動いた
想定通りの動きを示した。
総括
- log4jとlog4cppは使い方はほぼ同じ
- 設定ファイルのキー名が微妙に違うところに注意が必要