Perl Hackers Hub

第11回ログでアプリケーションの改善プロセスを回す(3)

ログの管理

ログを書き出し始めると、次に気になるのはログのファイルサイズの増大でしょう。大規模なサービスでは数日分のログを保存するだけでディスク容量を使い切ってしまうことがあります。大規模ではないWebサービスでも長く続けていればログは数十GBの単位で溜まっていることでしょう。ログの容量が大きくなると、記録されている内容を確認することが難しくなります。そこでログを検索しやすく、またディスク容量を大きく使わないように、ファイルの切り分けと世代管理をすることが重要です。

ログローテーション

ログファイルが肥大化してサーバのディスク容量を圧迫することを防いだり、ログの検索を行いやすくするために、ログを一定のサイズや間隔で分割し、またログファイルの世代管理を行い設定された保存期間を過ぎたログファイルを削除することを、一般的にログのローテーションと言います。アプリケーションによってはログの分割だけをサポートし、古いログファイルの削除に別途cronの設定などが必要となるものもあります。

Linuxディストリビューションのバイナリパッケージ版のApacheであれば、多くの場合は午前4時にcronからlogroateが起動され、ログファイルがローテーションされます。またdaemontoolssupervisorを使用してプロセスを起動している場合、それらのプロセス管理ツールがサポートしているログのローテーション機能を使用するのがよいでしょう。

supervisorでは、

command = /path/to/app.pl
redirect_stderr = true
stdout_logfile = /path/to/app.log
stdout_logfile_maxbytes = 10MB
stdout_logfile_backups = 7

のように設定を追加することで10MBごとにログファイルを切り分けて、7世代分保存できます。

Perlで独自にデーモンプロセスを書いている場合は注意が必要です。筆者はとあるアプリケーションでログがローテーションされないまま数十GBになってしまい、ディクス容量を圧迫しているのを見たことがあります。このときはアプリケーションエンジニアと相談し、ログファイルの名前を変更し、ログを吐き出しているプロセスをいったん停止させ、起動しなおすことでログファイルのローテーションを行いました。

手動で起動・終了する運用はミスも誘発しますし、いったん停止することでサービスに影響が出てしまうことも考えられます。独自にログを出力するデーモンプロセスには自動でファイルを切り分けたり、ログローテーションを行える機構が入っていると安心して運用ができます。リスト12は、リスト11にHUPシグナル[1]を受け取ったらファイルを開き直す機能を追加したものです。

リスト12 log_minimal_file_rotate.pl
my $fh;
open( $fh, '>>', 'app.log');
# シグナルを受け取ったらファイルを開き直す
local $SIG{HUP} = sub {
    undef $fh;
    open( $fh, '>>', 'app.log');
};
local $Log::Minimal::PRINT = sub {
    my ( $time, $type, $message, $trace) = @_;
    print $fh "$time [$type] $message at $trace\n";
}

次のシェルスクリプトと組み合わせることで、ログのローテーションがプロセスの終了なしで実現できます。

#/bin/bash

DATE=`date +"%Y%m%d"`
mv app.log app.log.$DATE
# ファイルを再オープン
kill -HUP $PID

# 古いファイルの削除
sleep 5
find -name app.log.* -mtime +30 | xargs rm -f

このほかにもtokuhirom氏によるFile::Stampedモジュールを使うと、自動でタイムスタンプ付きのファイルを生成し、任意のタイミングでログを書き出すファイルを新しいファイルに切り替えることができます。

先述したLog::Log4perlやLog::Dispatchでも拡張モジュールでローテーションがサポートされているので、採用を検討してください。

ログレベルを適切に付け、無駄を減らす

ログの中から重要なログを見つけ出すのは、ログのサイズが大きくなるにつれて難しくなってきます。ログローテーションにより重要なログが失われることも多くなるでしょう。そのため無駄なログを減らし、重要なログがまぎれてしまわないようにすることが重要です。たとえば、開発環境でのみ必要なデバッグメッセージを本番環境では出さないようにしたり、スタックトレースを出す場所を絞ったり、ログレベルを適切に付けることでログを見る人がどこに注目するべきなのかを示すことが重要です。

加えて、Perlで未定義値を使った際に出力されるuse of uninitialized valueなどのアラートは、バグの温床にもなるので速やかに解決し、ログに残り続けないようにしましょう。

ログ集計ツールの作成

問題となりそうな警告はなるべく早く見つけ、修正していくことが重要です。しかし、24時間ログをtailコマンドで監視することは現実的ではありません。サーバ上のログファイルを収集、集計するツールを作成し、1日に1回程度担当するエンジニアに向けてメールで送ったり、グラフ上にプロットすると、改善すべき点と改善した結果が可視化できるのでお勧めです。

まとめと次回予告今回はアプリケーションの開発や運用をスムーズに回していくために必須となるログの話題として、ログに必要な要素とログの管理方法、Perlで簡易的にログを出力するための方法、CPANに登録されているログモジュールの紹介をしました。適切なログを書き出すことでオペレーションエンジニアとアプリケーションエンジニアがスムーズにコミュニケーションが行えるようになり、サービスの改善サイクルのスピードを上げていくことができるでしょう。

次回は特別編として、小飼弾さんによるインタビュー「Perlハッカーに逢ってきた」を予定しています。お楽しみに!

おすすめ記事

記事・ニュース一覧