App::RunCron
(3)
App::RunCronとは?
cronにおけるログとエラーハンドリングの問題点
cronで実行したコマンドのエラー処理は悩ましいところです。パイプで出力を後続のコマンドに渡したところで、
かといって、
App::RunCronによる解決
こういった背景を踏まえ、
- ラッパスクリプトによって指定したコマンドの終了コードを判別する
- 実行コマンドの終了コードに応じて処理の分岐や出力先の切り替えを行う
- 複数の出力先を指定できる
- メールやIRC(Internet Relay Chat) などの独自の出力先を指定できる 
たとえば、
cronの実行結果に応じて処理を分岐すると言えば、
App::RunCronの一般的な使い方
インストール
App::RunCronはCPANに上がっているので、
% cpanm App::RunCronruncronコマンド
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:0runcron.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 command2setlockの-xオプションは、
この場合、
応用例としてリスト3のようにsetlockを重ねることで、
*/1 * * * * setlock -nx /tmp/lock1.lock setlock -nx /tmp/lock2.lock commanddaemontoolsにはsetlockのほかにsoftlimitというコマンドも含まれています。これは、
まとめ
多くの人が何気なく利用してきたcronですが、
ただ、
さて、