Perl Hackers Hub

第25回 cron周りのベストプラクティス(3)

この記事を読むのに必要な時間:およそ 3.5 分

(1)こちら⁠2)こちらから。

App::RunCron

(3)では,前回(2)に出てきたcron実行結果の通知処理やエラーハンドリングを統一的に行うことができる拙作のフレームワークApp::RunCronを紹介します。

App::RunCronとは?

cronにおけるログとエラーハンドリングの問題点

cronで実行したコマンドのエラー処理は悩ましいところです。パイプで出力を後続のコマンドに渡したところで,コマンドの成否自体は後続のコマンドからは知るすべはなく,出力結果から推測するしかありません。ログ処理とも共通することですが,コマンド終了コードがわからないまま,成功時も失敗時の別なく出力が流れてくることから,ログが埋もれてしまうというジレンマをcronは抱えています。

かといって,ジョブごとにエラー処理を書くのは,正しく書くのも難しく,書けたところで各ジョブ内に同じようなコードが書かれてしまいます。

App::RunCronによる解決

こういった背景を踏まえ,コマンドの終了コードを見て処理を切り分けるためのしくみが外部的に必要ではないかと思い,筆者が作ったのがApp::RunCronです。App::RunCronが持っている機能は次のとおりです。

  • ラッパスクリプトによって指定したコマンドの終了コードを判別する
  • 実行コマンドの終了コードに応じて処理の分岐や出力先の切り替えを行う
  • 複数の出力先を指定できる
  • メールやIRC(Internet Relay Chat)などの独自の出力先を指定できる

たとえば,コマンドの成功時にはIRCの通知にとどめ,失敗した際にはそれに加えてアラートメールを投げたり,それに加えて成功時も失敗時も出力を一律Fluentdに投げるといったことが実現できます。

cronの実行結果に応じて処理を分岐すると言えば,奥一穂さんのcronlogが知られています。実際,App::RunCronのコア部分はcronlogの実装をかなり参考にしています。cronlogは監視用途に寄った安定した一枚岩のPerlスクリプトですが,App::RunCronは独自の処理をプラガブルに記述できる点が異なります。

App::RunCronの一般的な使い方

インストール

App::RunCronはCPANに上がっているので,cpanmコマンドでインストールできます。

% cpanm App::RunCron
runcronコマンド

App::RunCronをインストールすると,runcronコマンドがインストールされます。次のように引数として実行するコマンドを受け取ります。

% runcron -- perl -E 'say "Hello"'

runcronは標準ではcronlogと同じ動きをします。つまり,コマンドにエラーがなかった場合は何も出力せず,エラーがあったときのみ出力を行います。先ほどの例では何も出力されなかったと思いますが,次のようにすれば出力されるはずです。

% runcron -- perl -E 'die "Hello"'
example.local tag:[] starting: perl -E die "Hello"
Hello at -e line 1.
command exited with code:255

正常終了した場合も出力したい場合は,runcron--reporterオプションを指定します。

% runcron --reporter=Stdout -- perl -E 'say "Hello"'
example.local tag:[] starting: perl -E say "Hello"
Hello
command exited with code:0
runcron.yml

runcronにはさまざまなオプションを指定できますが,指定が長くなってしまうため,直接オプションを指定するよりYAMLYAML Ain't Markup Language形式のconfigファイルを渡すほうがよいでしょう。configは-cオプションで渡すこともできますが,実行ディレクトリにruncron.ymlがある場合にはそれを自動的に読み出してくれます。以下にruncron.ymlのサンプルを記載します。

% cat runcron.yml
timestamp: 1
common_reporter:
  - Fluentd
  - File
  - file: tmp/log/cron%Y%m%d.log
  - "+MyApp::Reporter::IRC"
error_reporter:
  - "+MyApp::Reporter::AlertMail"

timestampを付けると,ミリ秒単位で実行時間をログに書き出してくれます。

common_reportererror_reporterそれと上記には記述されていませんがreporterオプションにそれぞれ,共通の通知先,エラー時の通知先,正常終了時の通知先を設定できます。先頭に+があるものは独自レポーターモジュールで,そうではないものはApp::RunCron::Reporter::*以下のモジュールが参照されます。

上記のruncron.ymlは次の設定となります。

  • 出力にタイムスタンプを付ける
  • Fluentd,ファイル,IRCに常時出力する
  • エラー終了時はそれに加えてメールを飛ばす

独自レポーターの書き方

App::RunCronには標準でいくつかのレポーターが同梱されていますが,基本的な機能しかありません。プロジェクト固有の要件に合わせて独自のレポーターを書くことで,App::RunCronは本来の力を発揮します。

レポーターはPerlモジュール形式で記述します。レポーターモジュールには次の2つを定義してください。

  • コンストラクタメソッドnew
  • オブジェクトメソッドrun

以下がレポーターのひな型です。

package MyApp::Reporter::Sample;
use strict;
use warnings;
use utf8;

sub new {
    my $class = shift;
    my %args = @_ == 1 ? %{$_[0]} : @_;
    bless \%args, $class;
}
sub run {
    my ($self, $runcron) = @_;
    print 'sample';
}
1;

newメソッドに設定項目が渡されてReporterオブジェクトが作られます。引数として必須の属性を持ち,それが足りない場合には例外を投げるようにしておいたほうが,runcron.ymlのテストを行った場合にエラーを検知できるのでよいでしょう。

runメソッドにはcronの実行情報が格納されたApp::RunCron のオブジェクトが渡ってきます。App::RunCronの各種アクセサに関してはApp::RunCronのドキュメントを参照してください。

独自レポーターやruncron.ymlをテストする方法

runcron.ymlや独自レポーターに不備があってcronが動かなかったら大変です。runcronはその特性上,runcron.ymlや独自レポーターに不備があった場合も指定したコマンドは極力実行されるようになっています。しかしそれらは本来誤りなく動いてほしいものなので,テストなどで検知できるに越したことはありません。ですので,App::RunCronにはruncron.ymlと独自レポーターをテストするためのテストフレームワークTest::App::RunCronを同梱しています。

Test::App::RunCronをuseすると,runcron.ymlのテストを行うruncron_yml_ok関数と,カスタムレポーターをテストするためにrunメソッドに渡されるApp::RunCronオブジェクトのモックを生成するmock_runcron関数がインポートされます。Test::App::RunCronを利用すると次のようにテストを書くことができます。

use strict;
use warnings;
use Test::More;
use Test::App::RunCron;

subtest 'runcron.yml のテスト' => sub {
  runcron_yml_ok;
  runcron_yml_ok(other_runcron.yml);
};

subtest 'MyApp::Reporter::Sample のテスト' => sub {
  use_ok 'MyApp::Reporter::Sample';
  my $mock = mock_runcron;
  MyApp::Reporter::Sample->new->run($mock);
  pass 'run ok';
};
done_testing;

App::RunCronはまだユーザも少なく,発展途上のモジュールですが,Perl以外のユーザでも汎用的に使える便利なモジュールですので,ぜひ使ってみてフィードバックをいただけると幸いです。単にcronで実行するジョブに限らず,そのほかのジョブの終了の通知処理などをラップする場合にも便利です。

著者プロフィール

松木雅幸(まつきまさゆき)

好きな言語はPerlと中国語。持ちネタは中国語LT。Songmuの由来は「松木」の中国語発音表記である。

大学で中国語と機械翻訳の研究に注力した勢いで卒業後,中国に渡る。中国でITベンチャーの立ち上げに携わった後に帰国。語学学校のシス担,印刷系SIerでの何でも屋業を経て,2011年1月より(株)カヤック所属。

前職までの多言語ぶりとは打って変わり,現職ではソーシャルゲーム開発のリードエンジニアとしてひたすらPerlを書く日々を過ごしている。CPANに上げたモジュール数は50近いが,無駄にたくさんあげているとの噂も。

最近はRijiというBlogツールをゴリ押しで普及させようとしている。

Blog:http://www.songmu.jp/riji/

コメント

コメントの記入