日向夏特殊応援部隊

俺様向けメモ

Catalyst::Log::Log4perlの出力先を任意のファイルにする。

始めに

Catalyst::Logを敢えてLog::Log4perlに変えるケースって良くあるとは思うんですが、デフォルトだとstderrへの書き込みのようなので、起動のさせ方に依っては嬉しくない記載になる可能性が高いです。*1

と言う訳できちんと外部のファイルに明示的に出力する方法です。

追記

path_toがsetup_home実行前だと使えないとかって記述が不適切*2だったので、修正しました。

Catalyst::Log::Log4perlでのLog4perlの呼び出しを確認

ドキュメントにも書いてありますが、

new($config, [%options])

This builds a new Catalyst::Log::Log4perl object. If you provide an argument to new(), it will be passed directly to Log::Log4perl::init.

snip ...

Without any arguments, new() will initialize a root logger with a single appender, Log::Log4perl::Appender::Screen, configured to have an identical layout to the default Catalyst::Log object.

基本的にはargumentsを指定するとLog::Log4perl->initに直接それを代入します。
またargumentsを指定しない場合は、基本的にはひとつのappenderとなり、AppenderオブジェクトはLog::Log4perl::Appender::Screenになります。

そしてその出力LayoutはCatalyst::Logと同等になるとあります。

newメソッドのargumentsを指定しない場合の処理は下記になります。

my $log      = Log::Log4perl->get_logger("");
my $layout   = Log::Log4perl::Layout::PatternLayout->new("[%d] [catalyst] [%p] %m%n");
my $appender = Log::Log4perl::Appender->new(
    "Log::Log4perl::Appender::Screen",
    'name'   => 'screenlog',
    'stderr' => 1,
);
$appender->layout($layout);
$log->add_appender($appender);
$log->level($DEBUG);

これを外部出力としたい場合は前述の通りargumentsを指定して、Log::Log4perlインスタンスを生成する事になります。

CatalystのLogオブジェクトとCatalyst::Log::Log4perl

基本的にはCatalyst::Log::Log4perlの使い方としては、

package MyApp;

use strict;
use warnings;

use Catalyst::Runtime '5.70';

use Catalyst qw/-Debug ConfigLoader Static::Simple/;
use Catalyst::Log::Log4perl;

our $VERSION = '0.01';

__PACKAGE__->config(
    name => 'MyApp',
);

__PACKAGE__->log(Catalyst::Log::Log4perl->new("$FindBin::Bin/../conf/log4perl.conf"));
__PACKAGE__->setup;

のようになります。

ところでCatalystの場合はsetupメソッド中のsetup_logメソッドによりlogインスタンスが初期化されます。

setupメソッドの当該処理の前後を見てみましょう。

# Process options
my $flags = {};

foreach (@arguments) {
    if (/^-Debug$/) {
        $flags->{log} =
          ( $flags->{log} ) ? 'debug,' . $flags->{log} : 'debug';
    }
    elsif (/^-(\w+)=?(.*)$/) {
        $flags->{ lc $1 } = $2;
    }
    else {
        push @{ $flags->{plugins} }, $_;
    }
}

$class->setup_home( delete $flags->{home} );
$class->setup_log( delete $flags->{log} );
  • Debugが指定された時の$flags->{log}への設定なんですが、

事前に$flags->{log}が設定されている場合はその値をカンマ区切りで追記する形になっています。

この予め指定されてる状況は-log=warnみたいな感じで@argumentsに入れた場合または、-Debugが複数指定された場合で、

use Catalyst qw/-Debug -log=warn,info/;

のように指定出来る模様。但しこれを指定した所で、デフォルトでの挙動は全然変わりません。*3
ここの$flags->{log}に関しては実際のsetup_logメソッドを見てみます。

sub setup_log {
    my ( $class, $debug ) = @_;

    unless ( $class->log ) {
        $class->log( Catalyst::Log->new );
    }

    my $app_flag = Catalyst::Utils::class2env($class) . '_DEBUG';

    if (
          ( defined( $ENV{CATALYST_DEBUG} ) || defined( $ENV{$app_flag} ) )
        ? ( $ENV{CATALYST_DEBUG} || $ENV{$app_flag} )
        : $debug
      )
    {
        no strict 'refs';
        *{"$class\::debug"} = sub { 1 };
        $class->log->debug('Debug messages enabled');
    }
}

渡した$flags->{log}はsetup_logメソッド内では$debug変数に格納されます。


まず$class->logが指定されていない場合は、Catalyst::Logが使用されます。
複数ある環境変数がtrueと解釈されるか、それらが指定されていないならば、
$debugがtrue扱いになっていれば、デバッグ扱いとなり、loggingが開始されると言う寸法です。


注意しなければならないのはsetup_logの直前にてsetup_homeが呼ばれていると言う事です。
即ちsetupが終了していない場合は、configで事前にhomeパラメーターを指定しない限りは、path_toメソッドが使えないって事です。但し予めhomeパラメータを何らかの方法で指定してあれば当然使えます。

良く考えたらCatalyst->importでsetup_home呼んでるから不要でした。
普通にpath_to使えます。

Catalyst::Log::Log4perlと同等のLog4perlの設定ファイル

大体下記のようになります。

log4perl.logger = DEBUG, A1
log4perl.appender.A1 = Log::Log4perl::Appender::File
log4perl.appender.A1.layout= Log::Log4perl::Layout::PatternLayout
log4perl.appender.A1.layout.ConversionPattern = [%d] [catalyst] [%p] %m%n
log4perl.appender.A1.filename = ../logs/myapp.log

ファイル名の部分は適当にアレンジして下さい。

MyAppにCatalyst::Log::Log4perlを設定する

path_toを使わないならば、こんな記述になるでしょう。

use FindBin;
use Catalyst::Log::Log4perl;

__PACKAGE__->log(Catalyst::Log::Log4perl->new("$FindBin::Bin/../conf/log4perl.conf"));

これでも十分動作します。

あるいはpath_toを使う場合は、

__PACKAGE__->log(Catalyst::Log::Log4perl->new(__PACKAGE__->path_to(qw/conf log4perl.conf/)->stringify));

stringifyメソッドを明示的に叩かないとLog4perl内で怒られます。w


多分、path_toを使った方がCatalyst風なんでこちらをお勧めしておきます。

*1:FastCGI by Apacheなど

*2:Catalyst->importで実行済みだから気にしなくて良い

*3:もし意味を持たせたいならば、setup_logメソッドを拡張する必要があります。