LoginSignup
12
15

More than 5 years have passed since last update.

fluentdのカスタムParser Pluginを作ってみる

Posted at

なぜParser Pluginが必要なのか。

独自フォーマットのログをパースするときに、デフォルトのin_tail pluginの正規表現では対応が難しい場合ががあります。
例えば、下記のログが挙げられます。
このログは、Key=Valueが順不同で発生したり、そもそも発生してたりしていなかったりしてます。
さらに要求として、keyをカラム名にValueを値という形で使いたい場合にはどうすればよいでしょうか。

[2013-01-01 00:00:00] key1=value1&key2=value2
[2013-01-01 00:00:00] key2=value2&key1=value1&key3=value3

このような場合では、fluentdのParser Pluginを作るとで対応することができます。
その他の案として、Tailインプットプラグインパーサーをカスタマイズすることもできますが、
こちらの方法だとread_from_headerなどの最近追加されたオプションが使えません。
オプションが追加されたNewTailInput pluginを同様の方法で作成する方法もありますが、
少々NewTailInputが複雑そうなので今回はやめておきます。
それぞれの方法のサンプルとなるpluginを挙げます。

Parser Pluginを作る

それでは、Parser Pluginを作ります。
サンプルは下記です。

# filename: parser_test_format.rb
require 'fluent/parser'

module Fluent
  class TextParser
    class TestFormatParser # TestFormatParserを任意変更
      include Configurable

      def initialize
        super
      end
      def configure(conf)
        super
        @time_format = conf['time_format'] || '%Y-%m-%d %H:%M:%S'
      end
      def call(text)
        # begin: 1レコードを編集処理
        elements = /^\[(.+)\] (.+)$/.match(text)

        time = elements[1]
        time = Time.strptime(time, @time_format).to_i

        # key1=value1&key2=value2&... -> [key1=value1, key2=value2, ...]
        messages = elements[2].split('&')

        # [k1, v1, k2, v2, ...] -> {k1=>v1, k2=>v2, ...}
        record = {}
        messages.each do |message|
          kv = message.split('=') 
          while (k = kv.shift) && (v = kv.shift)
            record[k] = v
          end
        end
        # end

        if time && record
          if block_given?
            yield time, record
            return
          else
            return time, record
          end
        end
      rescue # ignore parser error
      end
    end

    # test_formatとTestFormatParserを任意変更
    register_template('test_format', Proc.new { TestFormatParser.new })
  end
end

Parser Pluginを使う

設定ファイルは下記の通りに記載します。
Gem化していない場合は、td-agentの場合には、/etc/fluent/pluginフォルダ以下に格納します。
あとは通常通りにfluentdを起動すれば大丈夫です。
NewTailInputを使うことができるので、read_from_head等のオプションも利用できます。

<source>
  type tail
  tag test.parser
  format test_format # registerで指定した名前
  path /var/log/test/test.log
  pos_file /var/log/td-agent/test.pos
  read_from_head true
</source>

まとめ

ログは、ログをパースすることも考えて、プログラミングでインプットが容易なログのフォーマットで保存するようにしましょう。
ログサイズは大きくなりますが、LTSV形式とかをおすすめしておきます。
ストレージを気にする時代でもないので、1レコードのサイズが大きくなっても問題ないでしょう。

12
15
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
12
15