こんにちは @sonots です。
Haikanko OSS化への道、連載第一弾です。Haikanko は大分できあがった感があるんですが、OSS化するためには内部モジュールの gem 化やテスト、ドキュメントの整備作業していないとむりだなー、と感じているので、連載と称して少しずつやっていこうと思ってます。※ Haikanko についてはこちら => HaikankoというFluentdクラスタ管理ツールの話をしてきた(1) #fluentdcasual

ということで、その第一弾と称して、fluent-plugin-grepcounter という fluentd のプラグインを gem 化して rubygems に置いたので紹介します。

何するもの?

ログメッセージを grep のように正規表現で絞り込むと同時に、マッチしたメッセージの数をカウントしてくれるものです。カウントがある一定数を超えた場合のみ出力する、といった制御もできます。

やりたかったこと/できること

fluent-agent-lite を使ってアプリが出力しているログを fluentd に送り、5分の間に出力された "warn" という文字列にヒットするログメッセージが xx 件以上ならば、IRCおよびメールで通知、といったようなことがやりたかったので作りました。メールの本文にはヒットしたログメッセージ全件を貼付けたかったので、既存の fluent-plugin-datacounter + fluent-plugin-notifier ではそれはできないなー、と思って作成しました。

使い方

https://github.com/sonots/fluent-plugin-grepcounter の README にも書いていますが。

例えば、以下のようなメッセージが fluent-agent-lite から送られてくるとして
syslog.host1: {"message":"2013/01/13T07:02:11.124202 INFO GET /ping" }
syslog.host1: {"message":"2013/01/13T07:02:13.232645 WARN POST /auth" }
syslog.host1: {"message":"2013/01/13T07:02:21.542145 WARN GET /favicon.ico" }
syslog.host1: {"message":"2013/01/13T07:02:43.632145 WARN POST /login" }

こんなかんじで設定すると

<match syslog.**>
  type grepcounter
  input_key message
  regexp WARN
  count_interval 60
  exclude favicon.ico
  threshold 1
  add_tag_prefix warn.count
</source>

message (input_key)フィールドの文字列のうち、WARN (regexp)という文字がヒットする数を60秒間(count_interval)カウントします。exclude で指定した文字は除外され、カウント数が threshold で指定した値を超えた場合だけ、出力されます。add_tag_prefix で出力メッセージのタグを改変できます。

出力はこんなかんじになります。count はもちろん出力されますが、自分の利便性のために、入力のタグ名と、その最後の要素も出力されるようになっています。ホスト名を取りたかったんですよね。


warn.count.syslog.host1: {
  "count":2,
  "input_tag":"syslog.host1",
  "input_tag_last":"host1"
  "message":["2013/01/13T07:02:13.232645 WARN POST /auth","2013/01/13T07:02:43.632145 WARN POST /login"],
}


ヒットしたメッセージはデフォルトでは出力されないので、もし、出力したい場合は output_matched_message オプションを true にして、delimiter を指定してください。

(2013年05月05日 加筆) デフォルトでヒットしたメッセージも出力されるようになりました。
(2013年11月30日 加筆) delimiter オプションで delimiter を指定すると message が join されて配列ではなく文字列として出力されます。

<match syslog.**>
  type grepcounter
  count_interval 60
  input_key message
  regexp WARN
  exclude favicon.ico
  threshold 1
  add_tag_prefix warn.count
  delimiter \n
</source>

出力はこんなかんじになります。

warn.count.syslog.host1: {
  "count":2,
  "input_tag":"syslog.host1",
  "input_tag_last":"host1",
  "message":"2013/01/13T07:02:13.232645 WARN POST /auth\n2013/01/13T07:02:43.632145 WARN POST /login"
}


Acknowledgements

fluent-plugin-grepcounter を作成するにあたって、例によって @tagomoris さんの  https://github.com/tagomoris/fluent-plugin-datacounter を大分参考にさせていただきました。ありがとうございます。

また、今回テストは fluentd のプラグインにしては珍しく、rspec で書いています。rspec でテストを書くにあたって、@SpringMT さんの https://github.com/SpringMT/fluent-plugin-resque_stat を参考にさせていただきました。ありがとうございます。

おわりに

fluent-plugin-grepcounter は以前書いたこちらの記事 HaikankoというFluentdクラスタ管理ツールの話をしてきた(2) - Fluentd側の話 #fluentdcasual で fluent-plugin-watchcatcounter と呼んでいたやつですね。それを汎用的なプラグインとして整理して、それなりの名前を付け直してリリースしました。Haikanko でログのキーワード監視機能を実現するために使用しています。

こんな感じで、整理作業を進めていくのでよろしくお願いいたします。かしこ。