Ruby
fluentd

fluentdのconfのDSL記法は通常のconfからinclude出来ない

More than 1 year has passed since last update.

古いサーバのリプレース作業中に、行数の多いfluentdのconfを見つけました。
中身はsource指定で多数のログファイルをtailしている部分が大部分を占めているため、
Ruby DSL記法で書くと良いかもしれない、と試してみました。
参考: FluentdでRuby DSLを使う
以下のようになりました(架空のものです)

config.rb
sources = %w(device request response)

sources.each do |log_type|
    source {
        type :tail
        path "/var/log/game/#{log_type}.%Y%m%d"
        tag log_type
    }
end

書きつつコマンドラインから-cで指定して、読み込ませて想定通りのconfになっていることを確認し
いざ配置してみるとエラー、スタックトレースを見ると…
あれ?通常のconf用のv1_parserが動いてる?

実はこのサーバの大本のconfでは最低限の設定を記述し、各サーバで必要な部分については別ファイルに書いたものをincludeするようにされていました。
通常のconfを読み取るv1_parserのソースを見てみると

v1_parser.rb
def eval_include(attrs, elems, uri)
  u = URI.parse(uri)
  if u.scheme == 'file' || (!u.scheme.nil? && u.scheme.length == 1) || u.path == uri # file path
    # When the Windows absolute path then u.scheme.length == 1
    # e.g. C:
    path = u.path
    if path[0] != ?/
      pattern = File.expand_path("#{@include_basepath}/#{path}")
    else
      pattern = path
    end
    Dir.glob(pattern).sort.each { |entry|
      basepath = File.dirname(entry)
      fname = File.basename(entry)
      data = File.read(entry)
      data.force_encoding('UTF-8')
      ss = StringScanner.new(data)
      V1Parser.new(ss, basepath, fname, @eval_context).parse_element(true, nil, attrs, elems)
    }
  else
    require 'open-uri'
    basepath = '/'
    fname = path
    data = open(uri) { |f| f.read }
    data.force_encoding('UTF-8')
    ss = StringScanner.new(data)
    V1Parser.new(ss, basepath, fname, @eval_context).parse_element(true, nil, attrs, elems)
end

https://github.com/fluent/fluentd/blob/master/lib/fluent/config/v1_parser.rb
と、includeされたファイルをV1Parserに渡していました。
一方DSLパーサのソースでは

dsl.rb
def include(*args)
  ::Kernel.raise ::ArgumentError, "#{name} block requires arguments for include path" if args.nil? || args.size != 1
  if args.first =~ /\.rb$/
    path = File.expand_path(args.first)
    data = File.read(path)
    self.instance_eval(data, path)
  else
    ss = StringScanner.new('')
    Config::V1Parser.new(ss, @proxy.include_basepath, '', nil).eval_include(@attrs, @elements, args.first)
  end
end

https://github.com/fluent/fluentd/blob/master/lib/fluent/config/dsl.rb
のように、includeされるファイル名の末尾を見て、V1Parserに渡すかDSLに渡すかを分けていました。

今回のケースでは、ベースのconfから個別のconfをincludeする運用は変更したくなかったため、
DSL記法を止め、confの整理をすることとしました。
v1_parserのinclude部分を修正することも試してみたいと思います。