App::RunCron
(3)
App::RunCronとは?
cronにおけるログとエラーハンドリングの問題点
cronで実行したコマンドのエラー処理は悩ましいところです。パイプで出力を後続のコマンドに渡したところで、
かといって、
App::RunCronによる解決
こういった背景を踏まえ、
- ラッパスクリプトによって指定したコマンドの終了コードを判別する
- 実行コマンドの終了コードに応じて処理の分岐や出力先の切り替えを行う
- 複数の出力先を指定できる
- メールやIRC
(Internet Relay Chat) などの独自の出力先を指定できる
たとえば、
cronの実行結果に応じて処理を分岐すると言えば、
App::RunCronの一般的な使い方
インストール
App::RunCronはCPANに上がっているので、
% 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
にはさまざまなオプションを指定できますが、-c
オプションで渡すこともできますが、
% 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_
、error_
、reporter
オプションにそれぞれ、+
があるものは独自レポーターモジュールで、
上記のruncron.
- 出力にタイムスタンプを付ける
- Fluentd、
ファイル、 IRCに常時出力する - エラー終了時はそれに加えてメールを飛ばす
独自レポーターの書き方
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オブジェクトが作られます。引数として必須の属性を持ち、
run
メソッドにはcronの実行情報が格納されたApp::RunCron のオブジェクトが渡ってきます。App::RunCronの各種アクセサに関してはApp::RunCronのドキュメントを参照してください。
独自レポーターやruncron.ymlをテストする方法
runcron.
Test::App::RunCronをuse
すると、runcron_
関数と、run
メソッドに渡されるApp::RunCronオブジェクトのモックを生成するmock_
関数がインポートされます。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はまだユーザも少なく、
cron実行に寄与する小さなツール群
crontabで指定するジョブはUNIXコマンドなので、
timeout──タイムアウトの時間を設定する
実行コマンドのタイムアウト時間を設定したい場合に便利なのがtimeoutコマンドです。GNU Coreutilsに含まれています。
*/1 * * * * timeout 45 command1
上記のように指定すると、
setlock──排他的に処理を実行する
たとえば毎分実行するようなジョブの実行時間が1分を超えてしまったような場合であっても、
setlockは次のようにロックファイルを指定して、
*/1 * * * * setlock -n /tmp/command1.lock command1
これはcommand1 を毎分実行するcronですが、
setlockに-n
オプションを指定すると、-n
を指定しない場合は、
ほかのジョブと同じロックファイルを指定することで、
*/1 * * * * setlock -nx /tmp/command1.lock command1
30 * * * * setlock /tmp/command1.lock command2
setlockの-x
オプションは、
この場合、
応用例としてリスト3のようにsetlockを重ねることで、
*/1 * * * * setlock -nx /tmp/lock1.lock setlock -nx /tmp/lock2.lock command
daemontoolsにはsetlockのほかにsoftlimitというコマンドも含まれています。これは、
まとめ
多くの人が何気なく利用してきたcronですが、
ただ、
さて、