ククログ

株式会社クリアコード > ククログ > Fluentd pluginのconfigure内でのアンチパターン

Fluentd pluginのconfigure内でのアンチパターン

多くのFluentdのプラグインを見てきて知見が増えてきたのでまとめます。

config_sectionを使っていない

Fluentdのプラグインでは、configureメソッド内で設定を自力で解釈することによりconfig_sectionconfig_paramで定義していない設定値も扱えるようになっています。

以下のような設定ファイルを

<source>
  @type sample
  user hoge
  pass secret_passowrd
  <pattern>
    name foo
  </pattern>
  <pattern>
    name bar
  </pattern>
</source>

次のコードで自力で解釈することができます。

def configure(conf)
  super
  @patterns = conf.select {|element| element.name == "pattern" }
  @user = conf["user"]
  @pass = conf["pass"]
end

このように自力で解釈すると、テストも書かなければならないし、間違いも発生しやすくなるので以下のように、Fluentdの本体でよくテストされているconfig_sectionconfig_paramを組み合わせて使いましょう。config_sectionconfig_paramを使うと設定を宣言的に書くことができて、とてもわかりやすくなります。その上、fluent-plugin-config-formatというコマンドでMarkdownやJSONで出力することができます。descを使って説明も加えておくと、fluent-plugin-config-formatでも説明が出力されて便利です。

config_section :pattern, param_name: "patterns", multi: true do
  desc "name for pattern"
  config_param :name, :string
end
desc "user name for login"
config_param :user, :string
desc "password for login"
config_params :pass, :string, secret: true

def configure(conf)
  super
  # 何か追加の処理
end

config_paramのarrayを使っていない

<source>
  @type github-activities
  users ashie,cosmo0920,kenhys,kou
</source>

上記のような設定を以下のようなコードで、配列化して使っているプラグインがありました。

desc "user names e.g. user1,user2,user3"
config_param :users, :string, default: ""
def configure(conf)
  super

  @users = @users.split(",").map(&:strip)
end

これは以下のように書き換えることができます。

desc "user names e.g. user1,user2,user3"
config_param :users, :array, default: [], value_type: :string
def configure(conf)
  super
end

config_paramのenumを使っていない

<source>
  @type groonga
  protocol http
  # ...
</source>

上のような設定を次のようなコードでパースして使用しているプラグインがありました。

config_param :protocol, default: :http do |value|
  case value
  when "http", "gqtp", "command"
    value.to_sym 
  else
    rails Fluent::ConfigError, "must be http, gqtp or command: <#{value}>"
  end
end
def configure(conf)
  super
  # @protocol を使う
end

これは以下のように書き換えることができます。

config_param :protocol, :enum, list: [:http, :gqtp, :command], default: :http
def configure(conf)
  super
  # @protocol を使う
end

書き換えることで、コードがすっきりしました。

秘密の値をsecretにしていない

<match dummy.log>
  @type honeycomb
  writekey very-secret-key
  # ...
</match>

パスワードやAPIキーなどの秘密にしておきたい情報を設定ファイルに書かせるときは、secretオプションを付けましょう。 secretオプションをtrueにするとFluentdの起動時に表示されるダンプなどで値がマスクされます。

config_param :writekey, :string, secret: true

以下のようにsecretオプションを忘れてしまうと、Fluentd起動時のダンプなどにそのまま出力されてしまうので、バグ報告などのときにユーザーが自分でマスクしなければなりません。

config_param :writekey, :string

必須チェックを自分でしている

config_param :tag, :string, default: nil
def configure(conf)
  super
  raise Fluent::ConfigError, "tag is required" unless @tag
end

上のように必須の値のデフォルト値をnilにして、自分でチェックしているコードをたまに見かけます。 下のようにデフォルト値を省略するとその設定は必須になり、Fluentd起動時にチェックが実行されます。

config_param :tag, :string
def configure(conf)
  super
end

desc を書いていない

config_param :tag, :string

descを書くとfluent-plugin-config-formatというコマンドで、説明を見やすくフォーマットしたものを出力することができます。これはREADMEに設定の説明を書くときに使うととても便利です。

desc "tag for records"
config_param :tag, :string

まとめ

Fluentdで用意されている機能を活用して、メンテナンスを続けやすいプラグインを開発しましょう。