はじめに
- 前回の続き
- ログを
elasticsearchに投げる部分をfluentdでやってみることにする(厳密にはtd-agent)
参考
準備
fluentd のプラグインインストール
fluent-plugin-elasticsearch
elasticsearch にログを投げる為に...
/usr/lib/fluent/ruby/bin/fluent-gem install fluent-plugin-elasticsearch --no-ri --no-rdoc -V
fluent-plugin-typecast
elasticsearch に送るデータの型を変換する為に...理由は後述。
/usr/lib/fluent/ruby/bin/fluent-gem install fluent-plugin-typecast --no-ri --no-rdoc -V
ちなみに一連のセットアップについてはこちらに cookbook 化した(テストはまだ!)。
正規表現を頑張る
siege が出力するログを正規化する為に慣れない正規表現を頑張ってみた。尚、siege が出力するログは以下のようなフォーマット。
Date & Time, Trans, Elap Time, Data Trans, Resp Time, Trans Rate, Throughput, Concurrent, OKAY, Failed 2013-11-16 07:51:06, 25, 0.94, 0, 0.16, 26.60, 0.00, 4.21, 25, 0 2013-11-16 07:58:56, 25, 0.74, 0, 0.14, 33.78, 0.00, 4.74, 25, 0 2013-11-16 07:59:39, 25, 0.75, 0, 0.14, 33.33, 0.00, 4.65, 25, 0 2013-11-16 08:00:16, 25, 0.67, 0, 0.13, 37.31, 0.00, 4.69, 25, 0 2013-11-16 08:00:31, 25, 0.76, 0, 0.14, 32.89, 0.00, 4.67, 25, 0 2013-11-16 08:05:52, 25, 0.86, 0, 0.16, 29.07, 0.00, 4.60, 25, 0
デバッグに際してはこちらやこちらを参考にさせて頂いて以下のようなスクリプトでデバッグを行った。
#!/usr/bin/env ruby # -*- coding: utf-8 -*- require 'time' require 'fluent/log' require 'fluent/config' require 'fluent/engine' require 'fluent/parser' $log ||= Fluent::Log.new # debug log = '2013-11-17 07:18:02, 25, 0.76, 0, 0.14, 32.89, 0.00, 4.54, 25, 0' format = /^(?<date>(\d{4})-(\d{2})-(\d{2}) (\d{2}:)(\d{2}):(\d{2})),\s{1,}(?<Trans>\d+(\.\d+)?),\s{1,}(?<Elap_Time>\d+(\.\d+)?),\s{1,}(?<Data_Trans>\d+(\.\d+)?),\s{1,}(?<Resp_Time>\d+(\.\d+)?),\s{1,}(?<Trans_Rate>\d+(\.\d+)?),\s{1,}(?<Throughput>\d+(\.\d+)?),\s{1,}(?<Concurrent>\d+(\.\d+)?),\s{1,}(?<OKAY>\d+),\s{1,}(?<Failed>\d+)$/ time_format = '' parser = Fluent::TextParser::RegexpParser.new(format, 'time_format' => time_format) puts parser.call(log)
もっと良い方法があれば教えて欲しいよー...
siege を叩くシェルを修正
たったコレだけ!
#!/bin/bash /usr/bin/siege -b -i -r 3 -c 3 http://hoge.huga.com/ --log=/tmp/test_log
工夫して複数の Web サイトで調査出来るようになるかも。
ハマった
現状
kibana でレスポンスタイムのデータをヒストグラムで見ようとしたら以下のようなエラーとなってしまった。
ClassCastException[org.elasticsearch.index.fielddata.plain.PagedBytesIndexFieldData cannot be cast to org.elasticsearch.index.fielddata.IndexNumericFieldData]
原因
シェルスクリプトから elasticsearch に突っ込んだら type: double なっていたがfluentd(fluent-plugin-elasticsearch) 経由で elasticsearch に突っ込んだら type:string となっていた。
参考にさせて頂いているブログ記事を引用させて頂くと...
type apache2のparserでは size 等にはto_iの処理が施されている(ApacheParserが利用されている)- format を自作した場合には
RegexpParserが利用されてtime以外が文字列として処理される
とのことなので独自に format を書いた場合には全てのフィールドを string 型で処理するらしい。
対応
fluent-plugin-typecast を利用することでフィールドの型を変換してしまう。
<match siege.access> type typecast item_types Trans:integer,Elap_Time:float,Data_Trans:integer,Resp_Time:float,Trans_Rate:float,Throughput:float,Concurrent:float,OKAY:integer,Failed:integer tag filtered.siege.access </match>
fluent-plugin-typecast で扱えるタイプは以下の通り。
- integer
- float
- string
- time
- bool
- array
fluent-plugin-typecast でパースして elasticsearch に投げると以下のようなデータ型でデータが登録されていた。

elasticsearch 側でデータのタイプを自動判断してくれているようだ。
最終的に...
td-agent.conf
以下のような td-agent.conf となった。
<source> type tail path /tmp/test_log pos_file /tmp/test.pos tag siege.access format /^(?<date>(\d{4})-(\d{2})-(\d{2}) (\d{2}:)(\d{2}):(\d{2})),\s{1,}(?<Trans>\d+(\.\d+)?),\s{1,}(?<Elap_Time>\d+(\.\d+)?),\s{1,}(?<Data_Trans>\d+(\.\d+)?),\s{1,}(?<Resp_Time>\d+(\.\d+)?),\s{1,}(?<Trans_Rate>\d+(\.\d+)?),\s{1,}(?<Throughput>\d+(\.\d+)?),\s{1,}(?<Concurrent>\d+(\.\d+)?),\s{1,}(?<OKAY>\d+),\s{1,}(?<Failed>\d+)$/ </source> <match siege.access> type typecast item_types Trans:integer,Elap_Time:float,Data_Trans:integer,Resp_Time:float,Trans_Rate:float,Throughput:float,Concurrent:float,OKAY:integer,Failed:integer tag filtered.siege.access </match> <match filtered.siege.access> type elasticsearch include_tag_key true tag_key @log_name host 127.0.0.1 port 9200 logstash_format true flush_interval 10s </match>
kibana で確認
ログの中から Resp_Time から Elap_Time をヒストグラムで表示させてみた。

もう少しデータ集めたいところ。
最後に
- 正規表現勉強になった
fluentdでオレオレ format を作った時にはデータ型には注意するelasticsearchでデータを突っ込んだ時にデータ型は自動判別してくれる(みたい)elasticsearch-headがあると手軽にelasticsearchのデータが確認出来るのが素晴らしい!