Kentaro Kuribayashi's blog

Software Engineering, Management, Books, and Daily Journal.

fluent-plugin-rewriteというプラグインを作成した #fluentd

fluent-plugin-rewriteというfluentdのプラグインを作成した。以下、このプラグインの解決する問題について述べる。

問題

あるサービスのレスポンスタイム改善をしていて、まずは状況の可視化のために、fluentdを用いることにした。その際、たとえば

  • トップページ
  • ユーザページ
  • 書籍検索ページ
  • 書籍詳細ページ
  • ...
  • その他

といったグループにわけて、レスポンスタイムの各種統計を取りたい。また、ログを全部集計すればいいというものではなく、除外すべきmessageも複数種類あるので、柔軟にフィルタルールを設定したい。

既存のプラグインだとout_exec_filterを使うことによって達成可能。ただし、以下のような問題がある。

  • パフォーマンス的に不安
  • システムを構成するファイルがひとつ増える(filter用のスクリプト)
    • gem installするべきプラグインがひとつ増える(+1)より、異なる名前であり得るスクリプトが増える(+n)方が、システムの複雑性は増大する
  • 同じような処理を横展開していくにあたって、何度も似たようなスクリプトを書くことになる。設定でできるなら、その方がDRYでよい。

「問題」に対する追記

「パフォーマンス的に不安」と書いたが、実際に大規模環境で試したわけではなく、この点についてはFUDであった。out_exec_filterには、大規模環境下での、以下のような実績があることを教えてもらった。


ただし、先に挙げた他2つの問題についてはout_exec_filterでは解決されない。

解決

そこで、fluent-plugin-rewriteを作成した。このプラグインを用いると、ひとつひとつのmessageについて、指定したkeyのvalueが特定のパタンにマッチしたら、

  • valueを書き換えて、再emitする
  • そのmessageを無視する
  • マッチした文字列をtagに追加して、再emitする

ということができる。また、それらのルールを複数指定することも可能。イメージ的にApachemod_rewriteみたいな感じなので、fluent-plugin-rewriteという名前にした。

具体例を以下に述べる。

前段

まず、fluent-agent-liteにより、こういうメッセージが投げられてくるとする。

raw.apache.access {"message":"example.com xxx.xxx.xxx.xxx - - [29/Jun/2012:04:02:06 +0900] \"GET /book/38790 HTTP/1.0\" 200 1643 13043 \"\" \"Mozilla/5.0 (Windows NT 5.1; rv:13.0) Gecko/20100101 Firefox/13.0.1\" 161576"}

それを、中間fluentdで受けて、fluent-plugin-parserでバラす。

<source>
  type forward
</source>

<match raw.apache.access>
  type parser
  remove_prefix raw
  format /^[^ ]+ [^ ]+ [^ ]+ [^ ]+ \[(?<time>[^\]]+)\] "(?<method>[^ ]+) (?<path>[^ ]+) [^"]+" (?<status>[^ ]+) [^ ]+ [^ ]+ "[^"]+" "[^"]+" (?<response_time>[^ ]+)$/
  time_format %d/%b/%Y:%H:%M:%S %z
  key_name message
</match>

これにより、messageがたとえば以下のように整理される。

apache.access: {"method":"GET","path":"/book/38790","status":"200","response_time":"161576"}

fluent-plugin-rewrite

いったんmessageがkey/valueの形になったら、fluent-plugin-rewriteを適用できる。Webアプリのレスポンス改善をしたいのであるから、

  • image/css/jsなどの静的ファイルを除外する

必要がある。また、今回は参照されるページの改善をしたいので、あわせて

  • ステータスコードが200以外のものを除外する
  • リクエストメソッドがGET以外のものを除外する

といったフィルタリングをする必要がある。また、URLをグルーピングして可視化したいのであるから、

  • URLを、数種類にグルーピングし、グルーピングから外れたものはothersとしてまとめる
  • 上でグルーピングした名前で新たにタグをふりなおして、再emitする

ということもしたい。以下、設定。

<match apache.access>
  type rewrite

  remove_prefix apache.access
  add_prefix    filtered

  <rule>
    key     path
    pattern ^\/(?:image|css|js)
    ignore  true
  </rule>
  <rule>
    key     status
    pattern ^(?!200)\\d+$
    ignore  true
  </rule>
  <rule>
    key     method
    pattern ^(?!GET).+$
    ignore  true
  </rule>
  <rule>
    key     path
    pattern ^/$
    replace /top
  </rule>
  <rule>
    key           path
    pattern       ^/(top|books|book|admin|users|api|authors|cart)
    append_to_tag true
    fallback      others
  </rule>
</match>

このように設定しておくと、除外すべきものが除外され、URLごとにグルーピングされたmessageを得られる(以下はbookグループの例)。また、グルーピング対象にならなかったものはothersというグループに分類される。

filtered.book: {"method":"GET","path":"/book/38790","status":"200","response_time":"161576"}

これで問題に挙げていた事柄が解決できる。fluent-plugin-rewriteの説明は以上である。使い方の詳細についてはドキュメントを参照のこと。

集計・グラフ化

集計には、たとえば

のふたつのプラグインを使う。以下、設定。

<match filtered.**>
  type    forest
  subtype numeric_monitor
  remove_prefix filtered

  <template>
    unit minute
    tag  response_time.__TAG__
    aggregate all
    monitor_key response_time
    percentiles 90,95
  </template>
</match>

すると、fluent-plugin-rewriteでグルーピングしたURLの分だけ、以下のような数値を得られる。

response_time.book: {"min":24150.0,"max":41587529.0,"avg":1481909,"percentile_90":98058.0,"percentile_95":614745.0}
response_time.users ...
response_time.admin ...

あとはこれをfluent-plugin-growthforecastでGrowthForecastに投げれば、いい感じにグラフが作成されるでしょう。

<match response_time.**>
  type    forest
  subtype growthforecast

  <template>
    gfapi_url http://localhost:5125/api/
    service   a_service
    name_keys avg,max,min,percentile_90,percentile_95
    tag_for section
    remove_prefix response_time
  </template>
</match>

まとめ

  • fluent-plugin-rewrite、けっこう便利な気がする
    • ただし、これはこれでパフォーマンスの点で気掛りではある。
  • つか、tagomorisさんにお世話になりっぱなし

どうぞご利用ください。