fluent-plugin-copy_ex 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: e55e9309b56820ce9c6e3889d78a7adef469a666
4
+ data.tar.gz: 819fd35752ace0455f78083abfb998fa6dbd0a78
5
+ SHA512:
6
+ metadata.gz: afb6a9e80ba971b9a2b7916f797f1a15e82e8c45da068819e6af2badd5dc7cfed789b587444e2eac9f7d275919aeef3890e5c201d11d93311d640cb577c0c285
7
+ data.tar.gz: f2aefd89cbb13156b6b14215f8e3b32a7f6f85e406c8d71a508f98f0f1e3f223a85e64f7065f3c5d25608799f684b7b6fb5e21790ad3c2c29428e85c7241261d
data/.gitignore ADDED
@@ -0,0 +1,13 @@
1
+ /*.gem
2
+ ~*
3
+ #*
4
+ *~
5
+ .bundle
6
+ Gemfile.lock
7
+ .rbenv-version
8
+ vendor
9
+ doc/*
10
+ tmp/*
11
+ .yardoc
12
+ .ruby-version
13
+ pkg/*
data/.travis.yml ADDED
@@ -0,0 +1,6 @@
1
+ rvm:
2
+ - 1.9.3
3
+ - 2.0.0
4
+ - 2.1.*
5
+ gemfile:
6
+ - Gemfile
data/CHANGELOG.md ADDED
@@ -0,0 +1,4 @@
1
+ ## 0.0.1
2
+
3
+ First version
4
+
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source "http://rubygems.org"
2
+
3
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1 @@
1
+ Same with Fluentd
data/README.md ADDED
@@ -0,0 +1,63 @@
1
+ # fluent-plugin-copy_ex
2
+
3
+ ## About
4
+
5
+ Fluentd out\_copy extension
6
+
7
+ ## What is this for?
8
+
9
+ Think of `out_copy` configuration as folloings:
10
+
11
+ ```apache
12
+ <match **>
13
+ type copy
14
+ <store>
15
+ type plugin1
16
+ </store>
17
+ <store>
18
+ type plugin2
19
+ </store>
20
+ </match>
21
+ ```
22
+
23
+ In the current Fluentd, when plugin1 raises an error internally, the chain is broken and the plugin2 is not executed.
24
+
25
+ The `out_copy_ex` supplies `ignore_error` option so that it will not break the chain and the plugin2 is executed.
26
+
27
+ See https://github.com/fluent/fluentd/pull/303 for discussions.
28
+
29
+
30
+ ## Configuration
31
+
32
+ ```apache
33
+ <match **>
34
+ type copy_ex
35
+ <store ignore_error>
36
+ type plugin1
37
+ </store>
38
+ <store ignore_error>
39
+ type plugin2
40
+ </store>
41
+ </match>
42
+ ```
43
+
44
+ ## Parameters
45
+
46
+ Basically same with out\_copy plugin. See http://docs.fluentd.org/articles/out_copy
47
+
48
+ ## Contributing
49
+
50
+ 1. Fork it
51
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
52
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
53
+ 4. Push to the branch (`git push origin my-new-feature`)
54
+ 5. Create new [Pull Request](../../pull/new/master)
55
+
56
+ ## ChangeLog
57
+
58
+ See [CHANGELOG.md](CHANGELOG.md) for details.
59
+
60
+ ## Copyright
61
+
62
+ * Copyright (c) 2014- Naotoshi Seo
63
+ * See [LICENSE](LICENSE) for details.
data/Rakefile ADDED
@@ -0,0 +1,16 @@
1
+ # encoding: utf-8
2
+ require "bundler/gem_tasks"
3
+
4
+ require 'rake/testtask'
5
+ Rake::TestTask.new(:test) do |test|
6
+ test.libs << 'lib' << 'test'
7
+ test.pattern = 'test/**/*.rb'
8
+ test.verbose = true
9
+ end
10
+ task :default => :test
11
+
12
+ desc 'Open an irb session preloaded with the gem library'
13
+ task :console do
14
+ sh 'irb -rubygems -I lib'
15
+ end
16
+ task :c => :console
@@ -0,0 +1,9 @@
1
+ <match **>
2
+ type copy_ex
3
+ <store ignore-failure>
4
+ type stdout
5
+ </store>
6
+ <store ignore-failure>
7
+ type stdout
8
+ </store>
9
+ </match>
@@ -0,0 +1,25 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+
4
+ Gem::Specification.new do |s|
5
+ s.name = "fluent-plugin-copy_ex"
6
+ s.version = "0.0.1"
7
+ s.authors = ["Naotoshi Seo"]
8
+ s.email = ["sonots@gmail.com"]
9
+ s.homepage = "https://github.com/sonots/fluent-plugin-copy_ex"
10
+ s.summary = "Fluentd out_copy extension"
11
+ s.description = s.summary
12
+ s.licenses = ["MIT"]
13
+
14
+ s.rubyforge_project = "fluent-plugin-copy_ex"
15
+
16
+ s.files = `git ls-files`.split("\n")
17
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
+ s.require_paths = ["lib"]
20
+
21
+ s.add_runtime_dependency "fluentd"
22
+ s.add_development_dependency "rake"
23
+ s.add_development_dependency "pry"
24
+ s.add_development_dependency "pry-nav"
25
+ end
@@ -0,0 +1,72 @@
1
+ module Fluent
2
+ class CopyExOutput < MultiOutput
3
+ Plugin.register_output('copy_ex', self)
4
+
5
+ config_param :deep_copy, :bool, :default => false
6
+
7
+ def initialize
8
+ super
9
+ @outputs = []
10
+ @ignore_errors = []
11
+ end
12
+
13
+ attr_reader :outputs, :ignore_errors
14
+
15
+ def configure(conf)
16
+ super
17
+ conf.elements.select {|e|
18
+ e.name == 'store'
19
+ }.each {|e|
20
+ type = e['type']
21
+ unless type
22
+ raise ConfigError, "Missing 'type' parameter on <store> directive"
23
+ end
24
+ log.debug "adding store type=#{type.dump}"
25
+
26
+ output = Plugin.new_output(type)
27
+ output.configure(e)
28
+ @outputs << output
29
+
30
+ @ignore_errors << (e.arg == "ignore_error")
31
+ }
32
+ end
33
+
34
+ def start
35
+ @outputs.each {|o|
36
+ o.start
37
+ }
38
+ end
39
+
40
+ def shutdown
41
+ @outputs.each {|o|
42
+ o.shutdown
43
+ }
44
+ end
45
+
46
+ def emit(tag, es, chain)
47
+ unless es.repeatable?
48
+ m = MultiEventStream.new
49
+ es.each {|time,record|
50
+ m.add(time, record)
51
+ }
52
+ es = m
53
+ end
54
+
55
+ # Here, we do not use OutputChain for custom
56
+ @outputs.each_index do |idx|
57
+ _es = @deep_copy ? es.dup : es
58
+ begin
59
+ @outputs[idx].emit(tag, _es, NullOutputChain.instance)
60
+ rescue => e
61
+ if @ignore_errors[idx]
62
+ log.error :error_class => e.class, :error => e.message
63
+ else
64
+ raise e
65
+ end
66
+ end
67
+ end
68
+
69
+ chain.next
70
+ end
71
+ end
72
+ end
data/test/helper.rb ADDED
@@ -0,0 +1,18 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+ begin
4
+ Bundler.setup(:default, :development)
5
+ rescue Bundler::BundlerError => e
6
+ $stderr.puts e.message
7
+ $stderr.puts "Run `bundle install` to install missing gems"
8
+ exit e.status_code
9
+ end
10
+ require 'test/unit'
11
+
12
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
13
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
14
+ require 'fluent/test'
15
+ require 'fluent/plugin/out_copy_ex'
16
+
17
+ class Test::Unit::TestCase
18
+ end
@@ -0,0 +1,208 @@
1
+ require 'fluent/test'
2
+
3
+ class CopyExOutputTest < Test::Unit::TestCase
4
+ def setup
5
+ Fluent::Test.setup
6
+ end
7
+
8
+ CONFIG = %[
9
+ <store>
10
+ type test
11
+ name c0
12
+ </store>
13
+ <store>
14
+ type test
15
+ name c1
16
+ </store>
17
+ <store>
18
+ type test
19
+ name c2
20
+ </store>
21
+ ]
22
+
23
+ IGNORE_ERROR_CONFIG = %[
24
+ <store ignore_error>
25
+ type test
26
+ name c0
27
+ </store>
28
+ <store ignore_error>
29
+ type test
30
+ name c1
31
+ </store>
32
+ <store>
33
+ type test
34
+ name c2
35
+ </store>
36
+ ]
37
+
38
+ def create_driver(conf = CONFIG)
39
+ Fluent::Test::OutputTestDriver.new(Fluent::CopyExOutput).configure(conf)
40
+ end
41
+
42
+ def test_configure
43
+ d = create_driver
44
+
45
+ outputs = d.instance.outputs
46
+ assert_equal 3, outputs.size
47
+ assert_equal Fluent::TestOutput, outputs[0].class
48
+ assert_equal Fluent::TestOutput, outputs[1].class
49
+ assert_equal Fluent::TestOutput, outputs[2].class
50
+ assert_equal "c0", outputs[0].name
51
+ assert_equal "c1", outputs[1].name
52
+ assert_equal "c2", outputs[2].name
53
+ end
54
+
55
+ def test_configure_ignore_error
56
+ d = create_driver(IGNORE_ERROR_CONFIG)
57
+
58
+ outputs = d.instance.outputs
59
+ ignore_errors = d.instance.ignore_errors
60
+ assert_equal outputs.size, ignore_errors.size
61
+ assert_equal true, ignore_errors[0]
62
+ assert_equal true, ignore_errors[1]
63
+ assert_equal false, ignore_errors[2]
64
+ end
65
+
66
+ def test_emit
67
+ d = create_driver
68
+
69
+ time = Time.parse("2011-01-02 13:14:15 UTC").to_i
70
+ d.emit({"a"=>1}, time)
71
+ d.emit({"a"=>2}, time)
72
+
73
+ d.instance.outputs.each {|o|
74
+ assert_equal [
75
+ [time, {"a"=>1}],
76
+ [time, {"a"=>2}],
77
+ ], o.events
78
+ }
79
+ end
80
+
81
+ def test_msgpack_es_emit_bug
82
+ d = Fluent::Test::OutputTestDriver.new(Fluent::CopyExOutput)
83
+
84
+ outputs = %w(p1 p2).map do |pname|
85
+ p = Fluent::Plugin.new_output('test')
86
+ p.configure('name' => pname)
87
+ p.define_singleton_method(:emit) do |tag, es, chain|
88
+ es.each do |time, record|
89
+ super(tag, [[time, record]], chain)
90
+ end
91
+ end
92
+ p
93
+ end
94
+
95
+ d.instance.instance_eval { @outputs = outputs }
96
+
97
+ es = if defined?(MessagePack::Packer)
98
+ time = Time.parse("2013-05-26 06:37:22 UTC").to_i
99
+ packer = MessagePack::Packer.new
100
+ packer.pack([time, {"a" => 1}])
101
+ packer.pack([time, {"a" => 2}])
102
+ Fluent::MessagePackEventStream.new(packer.to_s)
103
+ else
104
+ events = "#{[time, {"a" => 1}].to_msgpack}#{[time, {"a" => 2}].to_msgpack}"
105
+ Fluent::MessagePackEventStream.new(events)
106
+ end
107
+
108
+ d.instance.emit('test', es, Fluent::NullOutputChain.instance)
109
+
110
+ d.instance.outputs.each { |o|
111
+ assert_equal [
112
+ [time, {"a"=>1}],
113
+ [time, {"a"=>2}],
114
+ ], o.events
115
+ }
116
+ end
117
+
118
+ def create_event_test_driver(is_deep_copy = false)
119
+ deep_copy_config = %[
120
+ deep_copy true
121
+ ]
122
+
123
+ output1 = Fluent::Plugin.new_output('test')
124
+ output1.configure('name' => 'output1')
125
+ output1.define_singleton_method(:emit) do |tag, es, chain|
126
+ es.each do |time, record|
127
+ record['foo'] = 'bar'
128
+ super(tag, [[time, record]], chain)
129
+ end
130
+ end
131
+
132
+ output2 = Fluent::Plugin.new_output('test')
133
+ output2.configure('name' => 'output2')
134
+ output2.define_singleton_method(:emit) do |tag, es, chain|
135
+ es.each do |time, record|
136
+ super(tag, [[time, record]], chain)
137
+ end
138
+ end
139
+
140
+ outputs = [output1, output2]
141
+
142
+ d = Fluent::Test::OutputTestDriver.new(Fluent::CopyExOutput)
143
+ d = d.configure(deep_copy_config) if is_deep_copy
144
+ d.instance.instance_eval { @outputs = outputs }
145
+ d
146
+ end
147
+
148
+ def test_one_event
149
+ time = Time.parse("2013-05-26 06:37:22 UTC").to_i
150
+
151
+ d = create_event_test_driver(false)
152
+ es = Fluent::OneEventStream.new(time, {"a" => 1})
153
+ d.instance.emit('test', es, Fluent::NullOutputChain.instance)
154
+
155
+ assert_equal [
156
+ [[time, {"a"=>1, "foo"=>"bar"}]],
157
+ [[time, {"a"=>1, "foo"=>"bar"}]]
158
+ ], d.instance.outputs.map{ |o| o.events }
159
+
160
+ d = create_event_test_driver(true)
161
+ es = Fluent::OneEventStream.new(time, {"a" => 1})
162
+ d.instance.emit('test', es, Fluent::NullOutputChain.instance)
163
+
164
+ assert_equal [
165
+ [[time, {"a"=>1, "foo"=>"bar"}]],
166
+ [[time, {"a"=>1}]]
167
+ ], d.instance.outputs.map{ |o| o.events }
168
+ end
169
+
170
+ def test_multi_event
171
+ time = Time.parse("2013-05-26 06:37:22 UTC").to_i
172
+
173
+ d = create_event_test_driver(false)
174
+ es = Fluent::MultiEventStream.new
175
+ es.add(time, {"a" => 1})
176
+ es.add(time, {"b" => 2})
177
+ d.instance.emit('test', es, Fluent::NullOutputChain.instance)
178
+
179
+ assert_equal [
180
+ [[time, {"a"=>1, "foo"=>"bar"}], [time, {"b"=>2, "foo"=>"bar"}]],
181
+ [[time, {"a"=>1, "foo"=>"bar"}], [time, {"b"=>2, "foo"=>"bar"}]]
182
+ ], d.instance.outputs.map{ |o| o.events }
183
+
184
+ d = create_event_test_driver(true)
185
+ es = Fluent::MultiEventStream.new
186
+ es.add(time, {"a" => 1})
187
+ es.add(time, {"b" => 2})
188
+ d.instance.emit('test', es, Fluent::NullOutputChain.instance)
189
+
190
+ assert_equal [
191
+ [[time, {"a"=>1, "foo"=>"bar"}], [time, {"b"=>2, "foo"=>"bar"}]],
192
+ [[time, {"a"=>1}], [time, {"b"=>2}]]
193
+ ], d.instance.outputs.map{ |o| o.events }
194
+ end
195
+
196
+ def test_ignore_error
197
+ d = create_driver(IGNORE_ERROR_CONFIG)
198
+
199
+ # override to raise an error
200
+ d.instance.outputs.first.define_singleton_method(:emit) do |tag, es, chain|
201
+ raise ArgumentError, 'Failed'
202
+ end
203
+
204
+ time = Time.parse("2011-01-02 13:14:15 UTC").to_i
205
+ assert_nothing_raised { d.emit({"a"=>1}, time) }
206
+ end
207
+ end
208
+
metadata ADDED
@@ -0,0 +1,114 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: fluent-plugin-copy_ex
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Naotoshi Seo
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-04-30 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: fluentd
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: pry
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: pry-nav
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ description: Fluentd out_copy extension
70
+ email:
71
+ - sonots@gmail.com
72
+ executables: []
73
+ extensions: []
74
+ extra_rdoc_files: []
75
+ files:
76
+ - ".gitignore"
77
+ - ".travis.yml"
78
+ - CHANGELOG.md
79
+ - Gemfile
80
+ - LICENSE
81
+ - README.md
82
+ - Rakefile
83
+ - example/fluent.conf
84
+ - fluent-plugin-copy_ex.gemspec
85
+ - lib/fluent/plugin/out_copy_ex.rb
86
+ - test/helper.rb
87
+ - test/plugin/test_out_copy_ex.rb
88
+ homepage: https://github.com/sonots/fluent-plugin-copy_ex
89
+ licenses:
90
+ - MIT
91
+ metadata: {}
92
+ post_install_message:
93
+ rdoc_options: []
94
+ require_paths:
95
+ - lib
96
+ required_ruby_version: !ruby/object:Gem::Requirement
97
+ requirements:
98
+ - - ">="
99
+ - !ruby/object:Gem::Version
100
+ version: '0'
101
+ required_rubygems_version: !ruby/object:Gem::Requirement
102
+ requirements:
103
+ - - ">="
104
+ - !ruby/object:Gem::Version
105
+ version: '0'
106
+ requirements: []
107
+ rubyforge_project: fluent-plugin-copy_ex
108
+ rubygems_version: 2.2.2
109
+ signing_key:
110
+ specification_version: 4
111
+ summary: Fluentd out_copy extension
112
+ test_files:
113
+ - test/helper.rb
114
+ - test/plugin/test_out_copy_ex.rb