Log4perl でデバッグログだけ別ファイルに保存する修行
Lo4perl 周り。基本的には Log4J でも同じはず。
やりたいことは、次のとおり。
もしかするとデバッグログは日次よりも細かく、たとえば分次とかでローリングしてもいいかもしれない。どれくらい細かく、すなわち大量にログるかって話だが。
etc/logger.conf は次のようにしてみた。
log4perl.rootLogger = INFO, CONSOLE, FIRST_FILE log4perl.logger.Sample.DEBUG = TRACE, DEBUG_FILE log4perl.additivity.Sample.DEBUG = 0 log4perl.appender.CONSOLE = Log::Log4perl::Appender::Screen log4perl.appender.CONSOLE.layout = Log::Log4perl::Layout::PatternLayout log4perl.appender.CONSOLE.layout.ConversionPattern = <%P> %p %l - %m%n log4perl.appender.FIRST_FILE.Threshold = INFO log4perl.appender.FIRST_FILE = Log::Dispatch::File::Rolling log4perl.appender.FIRST_FILE.filename = var/log/first-%d{yyyyMMdd}.log log4perl.appender.FIRST_FILE.mode = append log4perl.appender.FIRST_FILE.layout = Log::Log4perl::Layout::PatternLayout log4perl.appender.FIRST_FILE.layout.ConversionPattern = %d{yyyy/MM/dd HH:mm:ss.SSS} %p %m%n log4perl.appender.DEBUG_FILE.Threshold = TRACE log4perl.appender.DEBUG_FILE = Log::Dispatch::File::Rolling log4perl.appender.DEBUG_FILE.filename = var/log/debug-%d{yyyyMMdd}.log log4perl.appender.DEBUG_FILE.mode = append log4perl.appender.DEBUG_FILE.layout = Log::Log4perl::Layout::PatternLayout log4perl.appender.DEBUG_FILE.layout.ConversionPattern = %d{yyyy/MM/dd HH:mm:ss.SSS} %p %m%n
デフォルトのルートロガーの他に Sample.DEBUG ロガーも作る。ルートロガーは INFO レベル以上を受けつけるようにして CONSOLE アペンダと FIRST_FILE アペンダに出力する。Sample.DEBUG ロガーは TRACE レベル以上、つまり全レベルのログを受けつけ DEBUG_FILE アペンダに出力する。そのままだと Sample.DEBUG ロガーがルートロガーのアペンダを継承してしまう、すなわち、デバッグログも CONSOLE アペンダ、FIRST_FILE アペンダに流れてしまうので、additivity を 0 にしておく。
日次でローリングさせるために、FIRST_FILE アペンダ、DEBUG_FILE アペンダともに Log::Dispatch::File::Rolling を利用。
プログラムのそれぞれの場所から use して使う Sample クラスを作って、ロガーもそこに仕込む。
package Sample; use Log::Log4perl; Log::Log4perl::init('etc/logger.conf'); my $logger = Log::Log4perl->get_logger(q{}); my $debug_logger = Log::Log4perl->get_logger('Sample::DEBUG'); sub TRACE { local $Log::Log4perl::caller_depth = $Log::Log4perl::caller_depth + 1; $debug_logger->trace(@_); } sub DEBUG { local $Log::Log4perl::caller_depth = $Log::Log4perl::caller_depth + 1; $debug_logger->debug(@_); } sub INFO { local $Log::Log4perl::caller_depth = $Log::Log4perl::caller_depth + 1; $logger->info(@_); } sub WARN { local $Log::Log4perl::caller_depth = $Log::Log4perl::caller_depth + 1; $logger->warn(@_); } sub ERROR { local $Log::Log4perl::caller_depth = $Log::Log4perl::caller_depth + 1; $logger->error(@_); } sub FATAL { local $Log::Log4perl::caller_depth = $Log::Log4perl::caller_depth + 1; $logger->fatal(@_); } sub import { no strict 'refs'; *{caller()."::TRACE"} = *TRACE; *{caller()."::DEBUG"} = *DEBUG; *{caller()."::INFO"} = *INFO; *{caller()."::WARN"} = *WARN; *{caller()."::ERROR"} = *ERROR; *{caller()."::FATAL"} = *FATAL; }
ルートロガーとして $logger インスタンスを作成し、INFO 以上のログメソッドからはそれを利用するようにした。このとき Log4perl->get_logger() と引数を省略すると望んだロガーが得られない*1。ここでは空文字列を渡している。デバッグロガーとして引数で 'Sample.DEBUG' を指定して $debbug_logger インスタンスを作成。こちらは TRACE と DEBUG の場合に出力対象になる。TRACE、DEBUG などの関数は import 時に import 元のパッケージに名前をさらしてお手軽に使えるようにするのは、Log4perl でコンソールとファイルにロギング - 昨日知ったことでやったとおり。
複数のロガーを使った例はググってもなかなか引っ掛らない。ので書いておいてみた。
課題
コストの高いデバッグログ情報の生成は、ロガーの設定レベルを調べてから行うのが定石だ。
if ($logger->is_debug()) { my $message = create_heavy_information(); $logger->debug($message); } 先の例に、こういう仕組みを入れるにはどうすればいいか、考える必要あり。 単に >|perl| sub IS_DEBUG { $debug_logger.is_debug; }
を用意すればいいのか。でも、そもそもデバッグログを抑制するのであればデバッグログオブジェクトを生成する必要すらない。そこまでやるとなると logger.conf だけでは対応できないだろうな。
*1:Data::Dumper で中身をみたら意図していないものだった、というだけで詳しく解析してない