前に自分で書いた fluentdのためのプラグインをイチから書く手順 - tagomorisのメモ置き場 はたいへん重宝していたのだが、書いたすこし後になって実は現在すでに bundle gem コマンドを使うやりかたが良さそうだということがわかってしまったがばたばたしてて移行してなかった。
で、またひとつプラグインを書くことにしたのでついでに bundle を使った手順をざっくりまとめておく。以下のエントリをたいへん参考にさせてもらった。
T-POINTを取得するスクリプトをGistから移動, Bundlerを使ったGem作成メモ (自分用) - ただのにっき(2012-02-18)
準備とディレクトリツリーの作成
bundler は必要なので、なにはなくとも入れておこう。
gem install bundler
そしてプラグイン用ディレクトリツリーを作成する。今回は DataCounterOutput というプラグインを作ることにして、パッケージ名は fluent-plugin-datacounter にする。
$ bundle gem fluent-plugin-datacounter
create fluent-plugin-datacounter/Gemfile
create fluent-plugin-datacounter/Rakefile
create fluent-plugin-datacounter/.gitignore
create fluent-plugin-datacounter/fluent-plugin-datacounter.gemspec
create fluent-plugin-datacounter/lib/fluent-plugin-datacounter.rb
create fluent-plugin-datacounter/lib/fluent-plugin-datacounter/version.rb
Initializating git repo in /Users/tagomoris/Documents/fluent-plugin-datacounter
ファイル配置が fluentd plugin ぽくないので修正するためにコマンドを叩く。あとバージョン管理に version.rb を使うのは rake build とかしたとき色々面倒だったので削除。バージョン番号は gemspec に直接書くことにする。
$ cd fluent-plugin-datacounter/ $ mkdir -p lib/fluent/plugin $ mv lib/fluent-plugin-datacounter.rb lib/fluent/plugin/out_datacounter.rb $ rm lib/fluent-plugin-datacounter/version.rb $ rmdir lib/fluent-plugin-datacounter $ mkdir -p test/plugin $ touch test/plugin/test_out_datacounter.rb
out_datacounter本体のモジュール名だけとりいそぎ更新。requireのパスとモジュール名だけ書き換えてある(あとでちゃんと直す)。
module Fluent class DataCounterOutput < Fluent::BufferedOutput # Your code goes here... end end
これにあわせて fluent-plugin-flowcounter.gemspec を更新。versionやhomepage, summary, descriptionを適当に変更する。また version.rb をrequireしている行を削除する。
# -*- encoding: utf-8 -*- $:.push File.expand_path("../lib", __FILE__) Gem::Specification.new do |s| s.name = "fluent-plugin-datacounter" s.version = "0.0.1" s.authors = ["TAGOMORI Satoshi"] s.email = ["tagomoris@gmail.com"] s.homepage = "https://github.com/tagomoris/fluent-plugin-datacounter" s.summary = %q{Output filter plugin to count messages that matches specified conditions} s.description = %q{Output filter plugin to count messages that matches specified conditions} s.rubyforge_project = "fluent-plugin-datacounter" # ... s.add_development_dependency "rake" s.add_runtime_dependency "fluentd" end
基本的にはこれでいいはず。gemspecに依存モジュールが書いてあり Gemfile はgemspecを読み込んでるので bundle install すれば必要なモジュール(ここではrake, fluentdとその依存モジュールだけ)はインストールされる。
なお前は vendor/fluentd にsubmoduleしてたけど、最近はさすがにもうそんなことしなくてもいいなーと思ってて、割とさっくりそのままリリース版のgemからfluentdを入れてる。ので、そのへんは省略。
ついでにライセンスをどうするか決めて LICENSE.txt でも用意して書いておく。fluentd が Apache License v2.0 なので、あわせておくならそのへんの文面を突っ込んでおけばいい。
また .gitignore に最低限のリストがあるけど、エディタのバックアップファイルなどは必要に応じて追記しておく。自分はjewelerが作っていたものから適当にコピーしてきてこんな感じ。
*.gem .bundle Gemfile.lock pkg/* # For TextMate, emacs, vim *.tmproj tmtags *~ \#* .\#* *.swp
ここまでやったらとりあえず全部 commit してpushする。(pushする前にgithub側でリポジトリを作っておくこと。)
$ git add lib/fluent $ git add test $ git commit -m 'init tree' -a $ git remote add origin git@github.com:tagomoris/fluent-plugin-datacounter.git $ git push -u origin master
プラグインの骨組みをつくる
ともあれプラグインとして最低限の体裁を整えてテストが走るようにしたい。こんな感じにしておく。まず out_datacounter.rb 本体の方。
class Fluent::DataCounterOutput < Fluent::BufferedOutput Fluent::Plugin.register_output('datacounter', self) # config_param :hoge, :string, :default => 'hoge' def initialize super # require 'hogepos' end def configure(conf) super # @path = conf['path'] end def start super # init end def shutdown super # destroy end def format(tag, time, record) [tag, time, record].to_msgpack end def write(chunk) records = [] chunk.msgpack_each { |record| # records << record } # write records end end
あとテスト。まず test/helper.rb を作っておく。ここに書いた fluent/test の読み込みがないと fluentd のTestDriverなどが読み込まれず、テストが走らない。またこれから書くプラグインもちゃんと書いておく。またテスト走行時に fluentd のログがコンソールに出力されないように logger をゴニョゴニョする。(VERBOSE=1 rake test すれば出てくるようにもする。)
require 'rubygems' require 'bundler' begin Bundler.setup(:default, :development) rescue Bundler::BundlerError => e $stderr.puts e.message $stderr.puts "Run `bundle install` to install missing gems" exit e.status_code end require 'test/unit' $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib')) $LOAD_PATH.unshift(File.dirname(__FILE__)) require 'fluent/test' unless ENV.has_key?('VERBOSE') nulllogger = Object.new nulllogger.instance_eval {|obj| def method_missing(method, *args) # pass end } $log = nulllogger end require 'fluent/plugin/out_datacounter' class Test::Unit::TestCase end
で、テストの骨組みを test/plugin/test_out_datacounter.rb に。create_driver に引数を追加し BufferedOutputTestDriver.new の引数にプラグインクラスの他にタグを渡すようにしている。これでテスト内で 'test' 以外のタグを扱いたくなっても対応できる。
require 'helper' class DataCounterOutputTest < Test::Unit::TestCase def setup Fluent::Test.setup end CONFIG = %[ ] # CONFIG = %[ # path #{TMP_DIR}/out_file_test # compress gz # utc # ] def create_driver(conf = CONFIG, tag='test') Fluent::Test::BufferedOutputTestDriver.new(Fluent::DataCounterOutput, tag).configure(conf) end def test_configure #### set configurations # d = create_driver %[ # path test_path # compress gz # ] #### check configurations # assert_equal 'test_path', d.instance.path # assert_equal :gz, d.instance.compress end def test_format d = create_driver # time = Time.parse("2011-01-02 13:14:15 UTC").to_i # d.emit({"a"=>1}, time) # d.emit({"a"=>2}, time) # d.expect_format %[2011-01-02T13:14:15Z\ttest\t{"a":1}\n] # d.expect_format %[2011-01-02T13:14:15Z\ttest\t{"a":2}\n] # d.run end def test_write d = create_driver # time = Time.parse("2011-01-02 13:14:15 UTC").to_i # d.emit({"a"=>1}, time) # d.emit({"a"=>2}, time) # ### FileOutput#write returns path # path = d.run # expect_path = "#{TMP_DIR}/out_file_test._0.log.gz" # assert_equal expect_path, path end end
Rakefileにテスト用の記述を足す。
diff --git a/Rakefile b/Rakefile index 2995527..f0f33ef 100644 --- a/Rakefile +++ b/Rakefile @@ -1 +1,11 @@ require "bundler/gem_tasks" + +require 'rake/testtask' +Rake::TestTask.new(:test) do |test| + test.libs << 'lib' << 'test' + test.pattern = 'test/**/test_*.rb' + test.verbose = true +end + +task :default => :test
ここまでできたらテストが走るはず。試してみよう。
$ rake test /Users/tagomoris/.rvm/rubies/ruby-1.9.2-p290/bin/ruby -I"lib:lib:test" -I"/Users/tagomoris/.rvm/gems/ruby-1.9.2-p290/gems/rake-0.9.2.2/lib" "/Users/tagomoris/.rvm/gems/ruby-1.9.2-p290/gems/rake-0.9.2.2/lib/rake/rake_test_loader.rb" "test/**/test_*.rb" /Users/tagomoris/.rvm/gems/ruby-1.9.2-p290/gems/fluentd-0.10.12/lib/fluent/plugin.rb:156: warning: already initialized constant Plugin 2012-02-21 18:17:36 +0900: registered output plugin 'datacounter' Loaded suite /Users/tagomoris/.rvm/gems/ruby-1.9.2-p290/gems/rake-0.9.2.2/lib/rake/rake_test_loader Started 2012-02-21 18:17:36 +0900: registered buffer plugin 'file' 2012-02-21 18:17:36 +0900: registered buffer plugin 'memory' 2012-02-21 18:17:36 +0900: registered input plugin 'exec' 2012-02-21 18:17:36 +0900: registered input plugin 'forward' 2012-02-21 18:17:36 +0900: registered input plugin 'http' 2012-02-21 18:17:36 +0900: registered input plugin 'tcp' 2012-02-21 18:17:36 +0900: registered input plugin 'unix' 2012-02-21 18:17:36 +0900: registered input plugin 'syslog' 2012-02-21 18:17:36 +0900: registered input plugin 'tail' 2012-02-21 18:17:36 +0900: registered output plugin 'copy' 2012-02-21 18:17:36 +0900: registered output plugin 'exec' 2012-02-21 18:17:36 +0900: registered output plugin 'exec_filter' 2012-02-21 18:17:36 +0900: registered output plugin 'file' 2012-02-21 18:17:36 +0900: registered output plugin 'forward' 2012-02-21 18:17:36 +0900: registered output plugin 'null' 2012-02-21 18:17:36 +0900: registered output plugin 'roundrobin' 2012-02-21 18:17:36 +0900: registered output plugin 'stdout' 2012-02-21 18:17:36 +0900: registered output plugin 'tcp' 2012-02-21 18:17:36 +0900: registered output plugin 'unix' 2012-02-21 18:17:36 +0900: registered output plugin 'test' ... Finished in 0.059279 seconds. 3 tests, 0 assertions, 0 failures, 0 errors, 0 skips Test run options: --seed 34276
やったやった! とりあえずもういちど commit & push しとく。
$ git add test/helper.rb $ git commit -m 'write skeleton of plugin and its tests' -a