$shibayu36->blog;

クラスター株式会社のソフトウェアエンジニアです。エンジニアリングや読書などについて書いています。

horensoを使ってcronの実行時間を自動でmackerelに記録する

 最近cronの実行にはhorenso というツールを使って、実行エラーがあればSlackに通知するなどといったことをしている。今回はcronの実行時間をmackerelのサービスメトリックに自動で記録するということをやったのでメモ。

やりたいこと

  • crontabに以下の形式でスクリプトを登録すれば、自動でサービスメトリックに記録される
    • horenso --tag (tag名) --reporter=elapsed-time-to-mackerel-reporter.pl -- (実行したいコマンド)
  • 記録は秒数で、小数3桁まで記録する

horensoの動き

 horensoではreporterとして適当なスクリプトを指定することが出来て、そのスクリプトには標準入力に実行のログ等が渡ってくるようになってくる。例えば

$ horenso --reporter=test-reporter.pl -- perl -E 'say 1; warn 2;'

とすると、以下のようなJSONがtest-reporter.plに標準入力として渡ってくる。

{
  "command": "perl -E 'say 1; warn 2;",
  "commandArgs": [
    "perl",
    "-E",
    "say 1; warn 2;"
  ],
  "output": "1\n2 at -e line 1.\n",
  "stdout": "1\n",
  "stderr": "2 at -e line 1.\n",
  "exitCode": 0,
  "result": "command exited with code: 0",
  "pid": 95030,
  "startAt": "2015-12-28T00:37:10.494282399+09:00",
  "endAt": "2015-12-28T00:37:10.546466379+09:00",
  "hostname": "webserver.example.com",
  "systemTime": 0.034632,
  "userTime": 0.026523
}

 ここで実行開始時間を表すstartAtと実行終了時間を表すendAtの差分を利用すれば、実際に動いている時間を取ることが出来る。

 また、次のように--tagを使って名前を入力しておくことで、実行のJSONにtagというキーで指定した名前を取得できる。こちらはメトリックに投稿するためのラベルとして利用する。

$ horenso --tag say --reporter=test-reporter.pl -- perl -E 'say 1; warn 2;'

perlで実行時間をmackerelに投稿する

 先ほど紹介したとおり、reporterには標準入力に実行ログが入ってくるので、どんなスクリプトを指定してもよい。今回は自分が慣れているperlを利用してみる。

elapsed-time-to-mackerel-reporter.pl

#!/usr/bin/env perl
use strict;
use warnings;
use utf8;

use JSON::XS qw(encode_json decode_json);
use LWP::UserAgent;
use JSON::Types ();

use DateTime::Format::W3CDTF;

# 標準入力を読み込み、JSON decodeする
my $json = do { local $/; <> };
my $data = decode_json($json);

my $start_at = DateTime::Format::W3CDTF->parse_datetime($data->{startAt});
my $end_at   = DateTime::Format::W3CDTF->parse_datetime($data->{endAt});

# 小数点3桁までの実行秒数を計算する
my $elapsed_time = sprintf('%.3f', $end_at->hires_epoch - $start_at->hires_epoch);

# 自分が使っているservice名を利用
my $service_name = 'cron';
# グラフがcron_elapsed_time.*でまとまるように
# cron_elapsed_time.{tag名}という名前で投稿する
my $metric_name  = 'cron_elapsed_time.' . $data->{tag};
# api_key
my $api_key = '...';

my $ua = LWP::UserAgent->new;
$ua->post(
    "https://mackerel.io/api/v0/services/$service_name/tsdb",
    'X-Api-Key' => $api_key,
    'Content-Type' => 'application/json',
    Content => encode_json([{
        name  => JSON::Types::string($metric_name),
        time  => JSON::Types::number($start_at->epoch),
        value => JSON::Types::number($elapsed_time),
    }]),
);

reporterを利用してmackerelに実行時間を記録する

 あとはこのreporterと、適当なタグを指定して、horensoを使ってスクリプトを実行すれば良い。例えば以下の通り。

$ horenso --tag echo --reporter=elapsed-time-to-mackerel-reporter.pl -- sh -c 'echo 123;sleep 1'
$ horenso --tag sleep --reporter=elapsed-time-to-mackerel-reporter.pl -- perl -E 'sleep int rand(10);'

 1つ目は適当にechoして1秒sleepするようなコマンドで、これにはechoというタグを付けた。2つ目はランダムで1~9秒間sleepするようなもので、これにはsleepというタグを付けた。

 これを適当に何回か実行することで以下のようにmackerelに実行時間がプロットされる。

f:id:shiba_yu36:20160725060537p:plain

 あとはcrontabでは

horenso --tag (tag名) --reporter=elapsed-time-to-mackerel-reporter.pl -- ...

という形式でジョブを登録しておけば、全てのcronジョブの実行時間を記録できるようになる。

投稿したメトリックを監視する

 さらにサービスメトリックに投稿だけしておけば実行時間監視も監視できる。例えば以下のように設定しておけば、sleepタグの付いたジョブの実行時間が5秒を超えるとWarning通知し、10秒を超えるとCritical通知するということが出来るだろう。

f:id:shiba_yu36:20160725061012p:plain

まとめ

 今回はcronのジョブの実行時間を自動でmackerelに投稿することをやってみた。horensoはreporterとしていくつも登録できるので、例えばnagios監視と連携したり、エラーが起こった時にSlack通知したりといろんなことが出来る。非常に便利なので是非使ってみてください。