mackerel-check-plugins v0.22.1 で入った、check-log への変更について

こんにちは。Mackerelチーム CRE の井上(id:a-know)です。

昨日、同じくCREの三浦(id:missasan)より、Mackerelのアップデート告知をお知らせしましたが、その中で以下のようなアップデートが含まれていたかと思います。

go-check-plugins v0.22.1 のリリースで、check-log プラグインでログファイルを追尾する際にinode番号を参照するよう変更しました。 これによりログローテーションされた際のログファイルの追尾精度が向上しました。

今日のこのブログ記事では、その変更内容について少し詳細に解説したいと思います。

当記事の要約

check-log のチェック対象ログファイルの追跡を、これまでの「ファイル名のみ」に加えてinode番号も加味して追跡するようにしたことで、追尾可能なケースを増やしました。

従来までの check-log プラグインのおおまかな仕様と、その課題

Mackerel(mackerel-agent)には、チェック監視をおこなう仕組みがあります。

mackerel.io

command で指定したコマンドを実行したその終了ステータスにより、WARNING CRITICAL UNKNOWN アラートを発報させることができます。通常、このチェック処理は毎分実行されます(check_interval オプションを指定することで実行間隔を指定可能)。そして、このチェック監視に便利に使っていただけるものとして、公式として用意しているプラグイン集が mackerel-check-plugins であり、check-log プラグインもこれに含まれる形となります。

そして、check-log の仕様をおおまかに書くと、以下のようなものとなっていました。

  • check-log によるチェック対象のファイルは、--file オプションなどで指定する
  • チェック完了した位置(バイト数)をstateファイルとして出力しておく
    • stateファイルは「チェック対象のファイル」と「check-log コマンドに渡している引数」毎に作成され、管理される(該当ソースコード
  • 前回チェック時以降の出力差分に対して、チェックを実施する
    • stateファイルに記録されているバイト数分、読み飛ばすことで実現しています。
    • stateファイルに記録されているバイト数よりも読み込み対象ファイルのバイト数が小さければ、ログローテーションが発生したとみなして先頭から読み直すようにもなっています。

「stateファイルに記録されているバイト数よりも読み込み対象ファイルのバイト数が小さければ、ログローテーションが発生したとみなして先頭から読み直す」という作りにはなっているものの、ログの出力とログローテーションのタイミングによっては、チェック対象から漏れてしまうログ出力行が発生し得ます。例えば以下のようなケースです。

  • 12時00分00秒:ログチェック処理実施
  • 12時00分10秒:ログ出力(※1)
  • 12時00分20秒:ログローテーション発生
    • これまで出力していたファイルの名前を変更し(production.log → production_yyyyMMdd.log など)、新たにファイルを作成する(いわゆる copytruncate ではないログのローテーション)
  • 12時00分30秒:ログ出力(※2)
  • 12時01分00秒:ログチェック処理実施

上記のようなケースでは、※2の部分で出力されるログに関してはきちんとチェック対象となるものの、※1の部分で出力されるログについてはチェック対象から外れてしまいます。従来までの check-log には、このような課題がありました。

今回 check-log に対しておこなった変更について

上記のような課題に対して、今回以下の2つの Pull Request によって対処をおこなっています。

この2つについて、以下で掘り下げてみたいと思います。

[check-log] Jsonize status file

この Pull Request によって、stateファイルの構造をJSON形式に変更しています。それまでは、読み込み済みのバイト数だけが出力されたフラットな形式でした。

Mackerel では常に互換性を意識して開発を進めているため、今回のこの変更も「まずJSON形式のstateファイルの有無をチェックし、あればそれを用いて読み込み済みバイト数情報を取得。JSON形式のファイルがなければ、以前の形式のstateファイルを読み込む」という挙動となるように実施しており、これまでの check-log を使っている環境でも問題なく利用できるようにしています。

[check-log] Trace an old file after logrotation with the inode number

上で説明した課題への対策としては、この Pull Request がメインとなります。内容の概要としては、以下の通りです。

  • stateファイルに、チェック対象ログファイルの inode 番号も常に保存するようにする
    • これを実施したいがためのJSON形式への構造変更、というわけですね。
  • 「ログローテーションが発生していないか否か」を、「stateファイルに保存されたinode番号とファイルパスで指定されるファイルのもつinode番号の一致」を確認する形で、毎回実施する
    • ログファイルのinode変更を検知して、ローテーションされたファイルをinode番号で特定し、そのファイル末尾までのチェックを実施します。

これら2つの Pull Request により、上述したようなログローテーション時の課題に対して対処できるようになりました。

今回の変更に関する注意点について

ただし、注意点もあります。以下のような事柄です。

  • logrotate でいうところの copytruncate 方式のログローテーションには対応していません。
    • ローテーション時にログファイル自身のコピーをおこない、ログファイルのリセットを行う場合、ログファイル自身のinode番号は変わらないため。
  • 一時的にチェック対象スコープから外す意図でファイル名を変更した場合でも、inode番号により追跡されます(チェック対象から外すことはできません)。
  • ローテーション検知時の古い方のファイルのinode番号による追跡は、元のログファイルと同一ディレクトリ内に限ります。
  • 現時点では、Windows環境には対応していません。
    • ただし、stateファイルがJSON形式となったことで、inode番号以外のファイル識別子を保持することはこれまでよりも容易になりました。

まとめ

mackerel-check-plugins v0.22.1 で入った、check-log への変更内容についてご紹介しました。ここで紹介した Pull Request が閲覧できることでもわかるとおり、Mackerel のプラグイン、エージェントなどは OSS になっています。これらの公式リポジトリについては、もちろん今後も私たち Mackerel チームが責任を持って開発・メンテナンスをおこなっていきますが、Mackerelをご利用の皆さんからの Pull Request・issue起案なども歓迎しています!

GitHub リポジトリでのコミュニケーションについては基本的に英語でお願いをしているのですが、以下の Mackerel Users Group のslackチームには Mackerel の開発エンジニアも参加しており、ここでは日本語でコミュニケーションをすることが可能です。

slackin - Mackerel UG slack チームに参加する

「こういう Pull Request を送ろうと思っているのだけど、ちょっと自信がない......」、そういった場合でもお気軽に相談していただけますので、ぜひこちらへのご参加もご検討ください!