[ C++で開発 ]

log4cppでロギング

ロギングライブラリの1つで、Java言語用の有名なオープンソース・ロギングライブラリ log4j をC++言語用に移植したものです。

初期化

ロギングの初期化は、プログラムで記述する方法と設定ファイルに記述して読み込ませる方法があります。

設定ファイル

ロギング設定をファイルに記述し、プログラムの起動時に読み込みます。設定ファイルの記述には歴史的経緯で2種類の書式があります。

  1. SimpleConfigurator用書式
  2. PropertyConfigurator用書式

1.の書式は簡易的に用いられ、正式なドキュメントには定義されていません。2.の書式は途中で追加され、log4jと同じ書式です。1.の方法は今後のバージョンで廃止が予定されている”deprecated”なAPIです。

1.SimpleConfigurator用書式

samplemain.cpp
#include <log4cpp/Category.hh>
#include <log4cpp/SimpleConfigurator.hh>

bool initLog4cpp() {
  try {
    log4cpp::SimpleConfigurator::configure("logging.conf");
    return true;
  } catch (log4cpp::ConfigureFailure& ex) {
    return false;
  }
}

int main(int argc, char *argv[])
{
  bool configured = initLog4cpp();
  if (!configured) {
    return -1;
  }

  log4cpp::Category& logger = log4cpp::Category::getInstance("main");
  logger.info("Hello, log4cpp");


  return 0;
}

log4cpp::SimpleConfiguratorクラスのconfigureメンバ関数でロギング設定ファイルを指定すると、その内容を読み込み初期化します。設定ファイルの名前は任意で構いません。

logging.confの書式と説明
logging.conf記述 記述の説明
appender root basic console
priority root ERROR
appender main basic file sample.log
priority main DEBUG
(1) rootカテゴリの出力はbasicフォーマットでコンソールへ
(2) rootカテゴリはERRORレベル以上を出力
(3) mainカテゴリの出力はbasicフォーマットでファイルsample.logへ
(4) mainカテゴリはDEBUGレベル以上を出力

フォーマットの種類は、simple, basic, patternがあります。patternの場合、フォーマットをマクロ定義で指定します。

appenderの出力先は、file, rolling, console, stdout, stderr, syslog, nteventlog(WIN32のみ), remotesyslogが選べます。

フォーマット種類 フォーマット例 定義
simple
ERROR   : Hello, log4cpp world!
%p - %m%n
basic
1217167556 ERROR  : Hello, log4cpp world!
%R %p %c %x: %m%n

フォーマットを設定ファイルで定義することができます。

logging.conf
appender main pattern console %d %c - %m%n 
priority main DEBUG
表示例は以下
2008-07-28 00:17:44,031 main - Hello, log4cpp world!

フォーマット定義のメッセージマクロ

%m メッセージ文字列 引数で指定した文字列
%n 改行
%c カテゴリ名
%d 日付時刻
%p プライオリティ
%r エポックからの経過ミリ秒
%R エポックからの経過秒数
%t スレッド名
%u プロセッサ時間
%x インデント

2.PropertyConfigurator用書式

初期化およびログ出力のコードは、1.SimpleConfiguratorのときと、Configuratorクラス名が違うだけどほとんど同じです。

samplemain.cpp
#include <log4cpp/Category.hh>
#include <log4cpp/PropertyConfigurator.hh>

bool initLog4cpp() {
  try {
    log4cpp::PropertyConfigurator::configure("logging.conf");
    return true;
  } catch (log4cpp::ConfigureFailure& ex) {
    return false;
  }
}

int main(int argc, char *argv[])
{
  bool configured = initLog4cpp();
  if (!configured) {
    return -1;
  }

  log4cpp::Category& logger = log4cpp::Category::getInstance("sample");
  logger.info("Hello, log4cpp");

  return 0;
}

log4cpp::PropertyConfiguratorクラスのconfigureメンバ関数でロギング設定ファイルを指定すると、その内容を読み込み初期化します。設定ファイルの名前は任意で構いません。

logging.confの書式と説明
logging.conf記述 記述の説明
# カテゴリの設定
log4cpp.rootCategory=DEBUG, rootAppender
log4cpp.category.sample=INFO, sampleAppender
log4cpp.category.sample.calc=NOTICE

# アペンダの設定
# rootAppenderの設定
log4cpp.appender.rootAppender=ConsoleAppender
log4cpp.appender.rootAppender.layout=BasicLayout
# sampleAppenderの設定
log4cpp.appender.sampleAppender=RollingFileAppender
log4cpp.appender.sampleAppender.fileName=sample.log
log4cpp.appender.sampleAppender.MaxFileSize=1024KB
log4cpp.appender.sampleAppender.MacBackupIndex=8
log4cpp.appender.sampleAppender.layout=BasicLayout
(1)
(2)
(3)



(4)
(5)

(6)
(7)
(8)
(9)
(10)

(1) rootカテゴリの出力はDEBUGレベル以上とし、出力先Appender名は、"rootAppender"とする
(2) sampleカテゴリの出力はINFOレベル以上とし、出力先Appender名は、"sampleAppender"とする
(3) sample.calcカテゴリの出力はNOTICEレベル以上とする
(4) "rootAppender"は、コンソール出力Appenderである
(5) "rootAppender"のレイアウトはBasicLayoutとする
(6) "sampleAppender"の出力先ファイルは、ローテーション可能なファイルとする
(7) "sampleAppender"の出力先ファイル名は、"sample.log"とする
(8) "sampleAppender"の出力先ファイルは最大で1024KB(1MB)とする
(9) "sampleAppender"の出力先ファイルのローテーション数は8とする
(10) "sampleAppender"のレイアウトはBasicLayoutとする

出力Appender名は、設定ファイル内で一致していれば任意の名前を付けてかまいません。

上記で、"sample.calc"カテゴリに出力したログは、まず(3)のレベルでフィルターされます。そして、"sample.calc"にはAppenderが設定されていないので、階層が1つ上のカテゴリ"sample"のAppenderに出力されます。

出力する書式を指定するには、レイアウトにPatternLayoutを指定し、ConversionPatternで設定します。

出力書式の指定
log4cpp.appender.sampleAppender.layout=PatterLayout
log4cpp.appender.sampleAppender.layout.ConversionPattern= %d %-5p %c - %m%n 

フォーマット定義のメッセージマクロ

%の直後には、'-'、数値(0-9の1桁)、または'.'を指定することができる。
数値の場合、その情報の出力最低桁数を指定する。
-の場合は左詰めを指定する
.の場合は、最大桁数を指定する

記号 種類 内容
%m メッセージ文字列 引数で指定した文字列
%n 改行
%c カテゴリ名 %c{1}と階層レベルを数字で指定すると、ピリオドで区切られた階層名から指定された階層名が出力される
%d 日付時刻
%d{XXX}の形でオプション指定可能。XXXには、
ISO8601 : %Y-%m-%d %H:%M:%S,%l (省略時はこれがデフォルト)
ABSOLUTE : %H:%M:%S,%l
DATE : %d %b %Y %H:%M:%S,%l
または、%指定子による書式指定が可能。%指定子の意味は、std::strftime
で指定可能なもの。
%p プライオリティ 桁数を合わせるため、%6p、%-6p(左詰の場合-を付ける)のように桁指定できる。
%r プログラム起動してからの経過秒
%R エポックからの経過秒数
%t スレッド名 スレッドID(アドレス?)
%x インデント スタックレベルによるインデントを追加する

処理時間

CPU、ディスクI/O性能によって大きく処理時間は変わります。ここは参考値を示します。

簡単な計測例

AMD Athlon XP 2600+/CentOS 5.2/ATA100 HDD

rootカテゴリ、appenderにbasicフォーマットでconsoleのみ指定、ERRORレベル以上を指定

ERROR以上のログ出力: 40μ秒
WARNING以下(DEBUGを除く)の出力: 4μ秒
DEBUGの出力: 0.2μ秒

mainカテゴリ、appenderにpattern指定でファイルとコンソール出力指定、DEBUGレベル以上を指定

DEBUG以上の出力: 120μ秒

ロギング・メンバ関数

Categoryクラス

通常は、Categoryクラスのオブジェクトに対してメンバ関数を呼びロギングを行います。

汎用ロギングメンバ関数

ログレベルと出力メッセージを指定するメンバ関数で、以下の3形式があります。

  1. void log4cpp::Category::log(log4cpp::Priority::PriorityLevel, const std::string&) throw()
  2. void log4cpp::Category::log(log4cpp::Priority::PriorityLevel, const char*, ...) throw()
  3. log4cpp::CategoryStream log4cpp::Category::operator<<(log4cpp::Priority::PriorityLevel)

printfライクな書式指定文字列が2.で、std::coutライクな<<のストリーム形式が3.で利用可能です。

log4cpp::Category& logger = log4cpp::Category::getInstance("main");

logger.log(log4cpp::Priority::INFO, "Hello, log4cpp");
logger.log(log4cpp::Priority::DEBUG, "start %d, end %d", start, end);
logger << log4cpp::Priority::WARN << "failed in " << place_ << "value=" << x  
       << log4cpp::eol;

ログレベルごとの専用ロギングメンバ関数

ログレベルごとに、レベル名にちなんだメンバ関数があります。ログレベルの指定が簡便になったものです。

以下は、DEBUGレベル用のメンバ関数です。

  1. void log4cpp::Category::debug(const std::string& message) throw()
  2. void log4cpp::Category::debug(const char* format, ...) throw()
  3. log4cpp::CategoryStream log4cpp::Category::debugStream()