「Short introduction to Apache log4cxx」で抜けている説明や、わかりにくい箇所の補足。
log4cxx のインストール先が /usr/local とした場合、
me@host: ~$ cd /usr/local/lib
me@host: /usr/local/lib$ ls -l *log4cxx*
-rwxr-xr-x. 1 root root 13143893 Jan 16 21:03 liblog4cxx.so.10.0.0
lrwxrwxrwx. 1 root root 20 Jan 16 21:03 liblog4cxx.so -> liblog4cxx.so.10.0.0
-rw-r--r--. 1 root root 34497458 Jan 16 21:03 liblog4cxx.a
lrwxrwxrwx. 1 root root 20 Jan 16 21:03 liblog4cxx.so.10 -> liblog4cxx.so.10.0.0
-rwxr-xr-x. 1 root root 950 Jan 16 21:03 liblog4cxx.la
me@host: /usr/local/lib$ cd /usr/local/include
me@host: /usr/local/include$ ls -dl log4cxx
drwxr-xr-x. 14 root root 4096 Jan 16 21:03 log4cxx
すなわち、Makefile に記述する場合は
.SUFFIXES:
.SUFFIXES: .c .cpp .o
.PHONY: all clean
CC = gcc
CXX = g++
NOVERB =
CFLAGS = -O3 -DNDEBUG -I/usr/local/include
CXXFLAGS = $(CFLAGS)
#CXXFLAGS = $(CFLAGS) -std=c++0x -Weffc++
LFLAGS = -L/usr/local/lib -llog4cxx
OBJS = your_app.o
your_app: $(OBJS)
$(CXX) $(LFLAGS) $(OBJS) -oyour_app
などとする。
「Default Initialization Procedure」部分がわかりにくいので、実際にやってみる。
カレントディレクトリに、以下の(デフォルトと違う) log4cxx.properties を置いてみる:
# 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
log4j.appender.A1.layout.ConversionPattern=%-5p %c - %m%n
これを使うプログラムは以下:
// include log4cxx header files.
#include "log4cxx/logger.h"
#include "log4cxx/helpers/exception.h"
using namespace log4cxx;
using namespace log4cxx::helpers;
LoggerPtr logger(Logger::getLogger("your_app"));
int main(int argc, char **argv)
{
try {
LOG4CXX_INFO(logger, "Entering application.");
LOG4CXX_INFO(logger, "Exiting application.");
} catch(const log4cxx::helpers::Exception& e) {
return -1;
}
return 0;
}
つまり、 BasicConfigurator も PropertyConfigurator も ( DOMConfigurator も)何も記述しない。これでビルドして動かすと、
INFO your_app - Entering application.
INFO your_app - Exiting application.
となる。「これらもない場合には、カレントディレクトリにある~」が効いている。
今度は、 log4cxx_otr.properties という別名で、かつ、さっきと少しフォーマットが違うものを置いてみる:
# 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
log4j.appender.A1.layout.ConversionPattern=%d %-5p %c - %m%n
今度は、以下のようなシェル経由で起動してみる:
#! /bin/sh
export LOG4CXX_CONFIGURATION=./log4cxx_otr.properties
./your_app
今度は出力は以下のようになる:
2014-01-20 13:00:32,095 INFO your_app - Entering application.
2014-01-20 13:00:32,095 INFO your_app - Exiting application.
つまり「環境変数 LOG4CXX_CONFIGURATION があれば」の場合。
この方式が一番柔軟なのではないか。
「Short introduction to Apache log4cxx」に全て書かれてはいるのだが、わかりにくいかもしれないので、もっと単純化した例を示しておく。
Makefile:
.SUFFIXES:
.SUFFIXES: .c .cpp .o
.PHONY: all clean
CC = gcc
CXX = g++
NOVERB =
CFLAGS = -O3 -DNDEBUG -I/usr/local/include
CXXFLAGS = $(CFLAGS)
#CXXFLAGS = $(CFLAGS) -std=c++0x -Weffc++
LFLAGS = -L/usr/local/lib -llog4cxx
OBJS = your_class.o your_app.o
your_app: $(OBJS)
$(CXX) $(LFLAGS) $(OBJS) -oyour_app
your_app.cpp:
// include log4cxx header files.
#include "log4cxx/logger.h"
#include "log4cxx/helpers/exception.h"
#include "your_class.h"
using namespace log4cxx;
using namespace log4cxx::helpers;
LoggerPtr logger(Logger::getLogger("your_app"));
int main(int argc, char **argv)
{
try {
LOG4CXX_INFO(logger, "Entering application.");
YourClass c;
c.DoSomething();
LOG4CXX_INFO(logger, "Exiting application.");
} catch(const log4cxx::helpers::Exception& e) {
return -1;
}
return 0;
}
your_class.h:
/*
* your_class
*/
#ifndef __YOUR_CLASS_H_0A8D54BF1FB7414EA97785A1CE60F46C__
#define __YOUR_CLASS_H_0A8D54BF1FB7414EA97785A1CE60F46C__ /*DEFINED, BUT EMPTY*/
#include "log4cxx/logger.h"
class YourClass {
// 宣言。getLogger は実装部で。
static log4cxx::LoggerPtr logger;
public:
void DoSomething();
};
#endif /* ifndef __YOUR_CLASS_H_0A8D54BF1FB7414EA97785A1CE60F46C__ */
your_class.cpp:
#include "your_class.h"
using log4cxx::LoggerPtr;
using log4cxx::Logger;
// getLogger を static に。
LoggerPtr YourClass::logger(Logger::getLogger("YourClass"));
void YourClass::DoSomething()
{
LOG4CXX_INFO(logger, "do something");
}
log4cxx_otr.properties:
# 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
log4j.appender.A1.layout.ConversionPattern=%d %-5p %c (%F:%L) - %m%n
your_app.sh:
#! /bin/sh
export LOG4CXX_CONFIGURATION=./log4cxx_otr.properties
./your_app
これで実行させた場合の出力は
2014-01-20 13:42:46,365 INFO your_app (your_app.cpp:15) - Entering application.
2014-01-20 13:42:46,365 INFO YourClass (your_class.cpp:10) - do something
2014-01-20 13:42:46,365 INFO your_app (your_app.cpp:18) - Exiting application.
という具合。
「Short introduction to Apache log4cxx」でくどく書かれているのは
// getLogger を static に。
LoggerPtr YourClass::logger(Logger::getLogger("YourClass"));
部分のロガー命名の話。「あなたの」class が namespace に囲まれて管理されているならば、例えば namespace my { namespace app { class YourClass; } } の場合、ロガー名は
// getLogger を static に。
LoggerPtr YourClass::logger(Logger::getLogger("my.app.yourclass"));
の方が適切、という話である。これについてはアプリケーション・ライブラリの構造依存でもあり、どうすると管理しやすいかの話であり、任意とは言えるが、綺麗に構造化しないと使いにくいログになるので、その配慮はして欲しい。 (つまり、C++的に namespace に囲まれているいないに関わらず、ログは構造化する、などは考えて良い、と思う。)
一つ前の例から少し変えるだけの、ここでも非常に単純化した例を示しておく。
#include "your_class.h"
using log4cxx::LoggerPtr;
using log4cxx::Logger;
// getLogger を static に。
LoggerPtr YourClass::logger(Logger::getLogger("YourClass"));
void YourClass::DoSomething()
{
LOG4CXX_INFO(logger, "do something");
LOG4CXX_WARN(logger, "a little problem had occured");
}
log4cxx_otr.properties:
# 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
log4j.appender.A1.layout.ConversionPattern=%d %-5p %c (%F:%L) - %m%n
log4j.logger.YourClass=WARN
この場合、出力は
2014-01-20 13:50:52,413 INFO your_app (your_app.cpp:15) - Entering application.
2014-01-20 13:50:52,413 WARN YourClass (your_class.cpp:11) - a little problem had occured
2014-01-20 13:50:52,413 INFO your_app (your_app.cpp:18) - Exiting application.
となり、
void YourClass::DoSomething()
{
LOG4CXX_INFO(logger, "do something");
LOG4CXX_WARN(logger, "a little problem had occured");
}
この一つ目のログが揉み消されている。
「Short introduction to Apache log4cxx」は都度 API 仕様にリンクしているものの、インターネット上のリソースであるため、環境によっては参照出来ないであろう。これについては、 log4cxx をインストールすると、インストール先が /usr/local の場合は /usr/local/share/log4cxx に html ドキュメントがインストールされるので、適宜そちらを参照のこと。