こんにちは、maruchangです。
今日はログ設定のお話。
といっても一般的な話ではなく、Perlで作られたWebApplicationFrameworkのAmon2と、同じくPerlで作られたWebServerのStarmanを使ってウェブアプリを作った場合の、アクセスログ設定方法のお話。
この記事では、Amon2の中でログ設定を完結させ、なおかつローテートさせる方法を紹介するよ!
Amon2やStarmanについては、それぞれのサイトやググって調べてみてね。ここでは詳細を省きます。
- Amon2 – Web application framework for Rapid web development
- Amon2 – search.cpan.org
- Starman – search.cpan.org
plackupでアプリを起動した場合は、コンソールにアクセスログが出力されるわけだけど、Starmanを使ってアプリを起動してデーモン化した場合は、アクセスログがどこにも出力されなくなる。–access-logオプションを使えば出せるんだけど、ログ設定はアプリ側で行いたい!しかも、この方法だとローテートは別途用意しなくちゃならない。
でも、以下の手法を使うと、デーモン化したときに(plackupで起動したときも)アクセスログを所定の場所(ファイルでもコンソールでも)に出力することができるようになります!やったぜ!
使用するのは、Log::DispatchとPlack::Middleware::AccessLog。
だけど、そのまんま使うわけではなく、Amon2が提供する機能にのっけて設定します。
まず、Amon2のアプリを用意しておいてください。ここでは、フレーバーをLiteにして作ったアプリを前提に進めます。
[sourcecode] amon2-setup.pl --flavor=Lite MyApp [/sourcecode]
で、app.psgiファイルを開きます。単にアクセスログを所定のログファイルやコンソールに出すだけであれば、下のコードを追加すれば終わりという簡単さ!
[sourcecode] use Log::Dispatch; # http://search.cpan.org/~drolsky/Log-Dispatch-2.35/lib/Log/Dispatch.pmから引用 my $access_log = Log::Dispatch->new( outputs => [ [ 'File', min_level => 'debug', filename => 'logfile' ], [ 'Screen', min_level => 'warning' ], ], ); [/sourcecode]
何をやっているかというと、まずは指定したオプションでLog::Dispatchオブジェクトを作っている。そして、__PACKAGE__->enable_middlewareを使ってアプリ起動時にPlack::Middleware::AccessLogを指定したオプションで読み込んでいる。この時に、最初に作成したLog::Dispatchオブジェクトをloggerに指定している。
なお、Log::DispatchやPlack::Middleware::AccessLogについては、それぞれのSYNOPSISを参照してくれ。
では起動して確認してみよう。
[sourcecode] starman -D --port 5000 app.psgi [/sourcecode]
ばっちりアクセスログが指定したファイルに出力されているはずだ。
では、ここまでのコード全文を載せておくよ。あ、話は続くから気を付けて!
[sourcecode] use strict; use warnings; use utf8; use File::Spec; use File::Basename; use lib File::Spec->catdir(dirname(__FILE__), 'extlib', 'lib', 'perl5'); use lib File::Spec->catdir(dirname(__FILE__), 'lib'); use Amon2::Lite; our $VERSION = '0.01'; use Log::Dispatch; my $access_log = Log::Dispatch->new( outputs => [ [ 'File', min_level => 'debug', filename => 'logfile' ], [ 'Screen', min_level => 'warning' ], ], ); # put your configuration here sub load_config { my $c = shift; my $mode = $c->mode_name || 'development'; +{ 'DBI' => [ 'dbi:SQLite:dbname=$mode.db', '', '', ], } } get '/' => sub { my $c = shift; return $c->render('index.tt'); }; # load plugins __PACKAGE__->load_plugin('Web::CSRFDefender'); # __PACKAGE__->load_plugin('DBI'); # __PACKAGE__->load_plugin('Web::FillInFormLite'); # __PACKAGE__->load_plugin('Web::JSON'); __PACKAGE__->enable_session(); __PACKAGE__->enable_middleware('AccessLog', format => 'combined', logger => sub {$access_log->log(level => 'debug', message => @_)}); __PACKAGE__->to_app(handle_static => 1); __DATA__ @@ index.tt <!doctype html> <html> <head> <meta charset="utf-8"> <title>MyApp</title> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.8.0/jquery.min.js"></script> <script type="text/javascript" src="[% uri_for('/static/js/main.js') %]"></script> <link rel="stylesheet" href="http://twitter.github.com/bootstrap/1.4.0/bootstrap.min.css"> <link rel="stylesheet" href="[% uri_for('/static/css/main.css') %]"> </head> <body> <div class="container"> <header><h1>MyApp</h1></header> <section class="row"> This is a MyApp </section> <footer>Powered by <a href="http://amon.64p.org/">Amon2::Lite</a></footer> </div> </body> </html> @@ /static/js/main.js @@ /static/css/main.css footer { text-align: right; } [/sourcecode]
これだけでもいいんだけど
本稼働した後は日々のログで肥大化していくし、ローテートさせたいという要望もでてくるよね。そこで登場するのが、Log::Dispatch::FileRotate!
こいつはファイルサイズまたは日付でローテートができてしまう優れものであり、なおかつさっきのコードで使ったLog::Dispatchがそのまま使えてしまうのだ。
SYNOPSISを参考に、さっきのLog::Dispatchオブジェクトを作るコードを下記に書き換えてみよう。use Log::Dispatch::FileRotate;に書き換えるのも忘れずに!
[sourcecode] my $access_log = Log::Dispatch::FileRotate->new( name => 'file1', min_level => 'debug', filename => 'logfile', mode => 'append', max => 10, TZ => 'JST', DatePattern => 'yyyy-dd-HH', ); [/sourcecode]
Screenは省略した。上の例は、1時間ごとにローテートし、最大10ファイル、ローテートしたログを保持する設定。
書き換えたら、さっきのstarmanコマンドを実行してみよう。すぐにはわからないが、オプションに設定した条件をクリアしたら、ちゃんとログがローテートされているはずだ。
いかがだったかな?
とても簡単に実現できることがわかってもらえたと思う。なんだか偉そうな口ぶりだね!
このあたりの話題に触れているブログが少なかったので、自分で調べたことを書いてみた。もっといい方法とかあれば教えてほしい。
なお、設定を外だししたければ、 Log::Dispatch::Configを使えばいけるはず。