fluent-plugin-tagged_copy 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: 926a7eb28787523670d075f04e8fb1899e951942
4
+ data.tar.gz: 117ede3f43ccf1c475b1e93ef2630d1bc55ab328
5
+ SHA512:
6
+ metadata.gz: d55aad021eadd884789bad83839df945a85a66bbf936c54e632544ea02edd70298f01a9fa1cd97f480cccd36659c27d3f5584cdd869fed942bb2905c7a5ea0e0
7
+ data.tar.gz: 128b4977d657eba9473179b44b4c3c44919f6f7842b6d6769184a6d9358b35ee73bf2550244cd133cd408cbd03323e6b35a3e84eb35af86b58a40f57548b78fe
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,3 @@
1
+ ## 0.0.1
2
+
3
+ First version
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,62 @@
1
+ # fluent-plugin-tagged_copy
2
+
3
+ ## About
4
+
5
+ Fluentd out\_copy extension to do tagging before passing to chained plugins
6
+
7
+ ## Examples
8
+
9
+ ```apache
10
+ <match **>
11
+ type tagged_copy
12
+ <store>
13
+ <filter>
14
+ add_tag_prefix foo
15
+ remove_tag_prefix bar
16
+ </filter>
17
+ type stdout
18
+ </store>
19
+
20
+ <store>
21
+ <filter>
22
+ tag blah
23
+ </filter>
24
+ type stdout
25
+ </store>
26
+ </match>
27
+ ```
28
+
29
+ ## Parameters
30
+
31
+ Basically same with out\_copy plugin. See http://docs.fluentd.org/articles/out_copy
32
+
33
+ But, you can specify `filter` directive with following options
34
+
35
+ * tag
36
+
37
+ The tag name
38
+
39
+ * add_tag_prefix
40
+
41
+ Add tag prefix for output message
42
+
43
+ * remove_tag_prefix
44
+
45
+ Remove tag prefix for output message
46
+
47
+ ## Contributing
48
+
49
+ 1. Fork it
50
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
51
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
52
+ 4. Push to the branch (`git push origin my-new-feature`)
53
+ 5. Create new [Pull Request](../../pull/new/master)
54
+
55
+ ## ChangeLog
56
+
57
+ See [CHANGELOG.md](CHANGELOG.md) for details.
58
+
59
+ ## Copyright
60
+
61
+ * Copyright (c) 2014- Naotoshi Seo
62
+ * 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,24 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+
4
+ Gem::Specification.new do |s|
5
+ s.name = "fluent-plugin-tagged_copy"
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-tagged_copy"
10
+ s.summary = "Fluentd out_copy extension to do tagging before copy"
11
+ s.description = s.summary
12
+
13
+ s.rubyforge_project = "fluent-plugin-tagged_copy"
14
+
15
+ s.files = `git ls-files`.split("\n")
16
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
17
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
18
+ s.require_paths = ["lib"]
19
+
20
+ s.add_runtime_dependency "fluentd"
21
+ s.add_development_dependency "rake"
22
+ s.add_development_dependency "pry"
23
+ s.add_development_dependency "pry-nav"
24
+ end
@@ -0,0 +1,109 @@
1
+ require 'fluent/plugin/out_copy'
2
+
3
+ module Fluent
4
+ class TaggedCopyOutput < CopyOutput
5
+ Plugin.register_output('tagged_copy', self)
6
+
7
+ def initialize
8
+ super
9
+ @tag_procs = []
10
+ end
11
+
12
+ # Override to handle filter:tag options
13
+ def configure(conf)
14
+ conf.elements.select {|e|
15
+ e.name == 'store'
16
+ }.each {|e|
17
+ type = e['type']
18
+ unless type
19
+ raise ConfigError, "Missing 'type' parameter on <store> directive"
20
+ end
21
+ log.debug "adding store type=#{type.dump}"
22
+
23
+ f = e.elements.select {|i| i.name == 'filter'}.first || {}
24
+ tag_proc = generate_tag_proc(f['tag'], f['add_tag_prefix'], f['remove_tag_prefix'])
25
+ @tag_procs << tag_proc
26
+
27
+ output = Plugin.new_output(type)
28
+ output.configure(e)
29
+ @outputs << output
30
+ }
31
+ end
32
+
33
+ # Override to use TaggedOutputChain
34
+ def emit(tag, es, chain)
35
+ unless es.repeatable?
36
+ m = MultiEventStream.new
37
+ es.each {|time,record|
38
+ m.add(time, record)
39
+ }
40
+ es = m
41
+ end
42
+ if @deep_copy
43
+ chain = TaggedCopyOutputChain.new(@outputs, @tag_procs, tag, es, chain)
44
+ else
45
+ chain = TaggedOutputChain.new(@outputs, @tag_procs, tag, es, chain)
46
+ end
47
+ chain.next
48
+ end
49
+
50
+ private
51
+
52
+ def generate_tag_proc(tag, add_tag_prefix, remove_tag_prefix)
53
+ tag_prefix = "#{add_tag_prefix}." if add_tag_prefix
54
+ tag_prefix_match = "#{remove_tag_prefix}." if remove_tag_prefix
55
+ if tag
56
+ Proc.new {|t| tag }
57
+ elsif tag_prefix and tag_prefix_match
58
+ Proc.new {|t| "#{tag_prefix}#{lstrip(t, tag_prefix_match)}" }
59
+ elsif tag_prefix_match
60
+ Proc.new {|t| lstrip(t, tag_prefix_match) }
61
+ elsif tag_prefix
62
+ Proc.new {|t| "#{tag_prefix}#{t}" }
63
+ else
64
+ Proc.new {|t| t }
65
+ end
66
+ end
67
+
68
+ def lstrip(string, substring)
69
+ string.index(substring) == 0 ? string[substring.size..-1] : string
70
+ end
71
+ end
72
+
73
+ class TaggedOutputChain
74
+ def initialize(array, tag_procs, tag, es, chain=NullOutputChain.instance)
75
+ @array = array
76
+ @tag_procs = tag_procs
77
+ @tag = tag
78
+ @es = es
79
+ @offset = 0
80
+ @chain = chain
81
+ end
82
+
83
+ def next
84
+ if @array.length <= @offset
85
+ return @chain.next
86
+ end
87
+ @offset += 1
88
+ emit_tag = @tag_procs[@offset-1].call(@tag) # added
89
+ result = @array[@offset-1].emit(emit_tag, @es, self)
90
+ result
91
+ end
92
+ end
93
+
94
+ class TaggedCopyOutputChain < TaggedOutputChain
95
+ def next
96
+ if @array.length <= @offset
97
+ return @chain.next
98
+ end
99
+ @offset += 1
100
+ es = @array.length > @offset ? @es.dup : @es
101
+ emit_tag = @tag_procs[@offset-1].call(@tag) # added
102
+ result = @array[@offset-1].emit(emit_tag, es, self)
103
+ result
104
+ end
105
+ end
106
+
107
+ end
108
+
109
+
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_tagged_copy'
16
+
17
+ class Test::Unit::TestCase
18
+ end
@@ -0,0 +1,281 @@
1
+ require_relative '../helper'
2
+
3
+ class TaggedCopyOutputTest < 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
+ def create_driver(conf = CONFIG, tag = 'test')
24
+ Fluent::Test::OutputTestDriver.new(Fluent::TaggedCopyOutput, tag).configure(conf)
25
+ end
26
+
27
+ def test_configure
28
+ d = create_driver
29
+
30
+ outputs = d.instance.outputs
31
+ assert_equal 3, outputs.size
32
+ assert_equal Fluent::TestOutput, outputs[0].class
33
+ assert_equal Fluent::TestOutput, outputs[1].class
34
+ assert_equal Fluent::TestOutput, outputs[2].class
35
+ assert_equal "c0", outputs[0].name
36
+ assert_equal "c1", outputs[1].name
37
+ assert_equal "c2", outputs[2].name
38
+ end
39
+
40
+ def test_emit
41
+ d = create_driver
42
+
43
+ time = Time.parse("2011-01-02 13:14:15 UTC").to_i
44
+ d.emit({"a"=>1}, time)
45
+ d.emit({"a"=>2}, time)
46
+
47
+ d.instance.outputs.each {|o|
48
+ assert_equal [
49
+ [time, {"a"=>1}],
50
+ [time, {"a"=>2}],
51
+ ], o.events
52
+ }
53
+ end
54
+
55
+ def test_msgpack_es_emit_bug
56
+ d = Fluent::Test::OutputTestDriver.new(Fluent::CopyOutput)
57
+
58
+ outputs = %w(p1 p2).map do |pname|
59
+ p = Fluent::Plugin.new_output('test')
60
+ p.configure('name' => pname)
61
+ p.define_singleton_method(:emit) do |tag, es, chain|
62
+ es.each do |time, record|
63
+ super(tag, [[time, record]], chain)
64
+ end
65
+ end
66
+ p
67
+ end
68
+
69
+ d.instance.instance_eval { @outputs = outputs }
70
+
71
+ es = if defined?(MessagePack::Packer)
72
+ time = Time.parse("2013-05-26 06:37:22 UTC").to_i
73
+ packer = MessagePack::Packer.new
74
+ packer.pack([time, {"a" => 1}])
75
+ packer.pack([time, {"a" => 2}])
76
+ Fluent::MessagePackEventStream.new(packer.to_s)
77
+ else
78
+ events = "#{[time, {"a" => 1}].to_msgpack}#{[time, {"a" => 2}].to_msgpack}"
79
+ Fluent::MessagePackEventStream.new(events)
80
+ end
81
+
82
+ d.instance.emit('test', es, Fluent::NullOutputChain.instance)
83
+
84
+ d.instance.outputs.each { |o|
85
+ assert_equal [
86
+ [time, {"a"=>1}],
87
+ [time, {"a"=>2}],
88
+ ], o.events
89
+ }
90
+ end
91
+
92
+ def create_event_test_driver(is_deep_copy = false)
93
+ deep_copy_config = %[
94
+ deep_copy true
95
+ ]
96
+
97
+ output1 = Fluent::Plugin.new_output('test')
98
+ output1.configure('name' => 'output1')
99
+ output1.define_singleton_method(:emit) do |tag, es, chain|
100
+ es.each do |time, record|
101
+ record['foo'] = 'bar'
102
+ super(tag, [[time, record]], chain)
103
+ end
104
+ end
105
+
106
+ output2 = Fluent::Plugin.new_output('test')
107
+ output2.configure('name' => 'output2')
108
+ output2.define_singleton_method(:emit) do |tag, es, chain|
109
+ es.each do |time, record|
110
+ super(tag, [[time, record]], chain)
111
+ end
112
+ end
113
+
114
+ outputs = [output1, output2]
115
+
116
+ d = Fluent::Test::OutputTestDriver.new(Fluent::CopyOutput)
117
+ d = d.configure(deep_copy_config) if is_deep_copy
118
+ d.instance.instance_eval { @outputs = outputs }
119
+ d
120
+ end
121
+
122
+ def test_one_event
123
+ time = Time.parse("2013-05-26 06:37:22 UTC").to_i
124
+
125
+ d = create_event_test_driver(false)
126
+ es = Fluent::OneEventStream.new(time, {"a" => 1})
127
+ d.instance.emit('test', es, Fluent::NullOutputChain.instance)
128
+
129
+ assert_equal [
130
+ [[time, {"a"=>1, "foo"=>"bar"}]],
131
+ [[time, {"a"=>1, "foo"=>"bar"}]]
132
+ ], d.instance.outputs.map{ |o| o.events }
133
+
134
+ d = create_event_test_driver(true)
135
+ es = Fluent::OneEventStream.new(time, {"a" => 1})
136
+ d.instance.emit('test', es, Fluent::NullOutputChain.instance)
137
+
138
+ assert_equal [
139
+ [[time, {"a"=>1, "foo"=>"bar"}]],
140
+ [[time, {"a"=>1}]]
141
+ ], d.instance.outputs.map{ |o| o.events }
142
+ end
143
+
144
+ def test_multi_event
145
+ time = Time.parse("2013-05-26 06:37:22 UTC").to_i
146
+
147
+ d = create_event_test_driver(false)
148
+ es = Fluent::MultiEventStream.new
149
+ es.add(time, {"a" => 1})
150
+ es.add(time, {"b" => 2})
151
+ d.instance.emit('test', es, Fluent::NullOutputChain.instance)
152
+
153
+ assert_equal [
154
+ [[time, {"a"=>1, "foo"=>"bar"}], [time, {"b"=>2, "foo"=>"bar"}]],
155
+ [[time, {"a"=>1, "foo"=>"bar"}], [time, {"b"=>2, "foo"=>"bar"}]]
156
+ ], d.instance.outputs.map{ |o| o.events }
157
+
158
+ d = create_event_test_driver(true)
159
+ es = Fluent::MultiEventStream.new
160
+ es.add(time, {"a" => 1})
161
+ es.add(time, {"b" => 2})
162
+ d.instance.emit('test', es, Fluent::NullOutputChain.instance)
163
+
164
+ assert_equal [
165
+ [[time, {"a"=>1, "foo"=>"bar"}], [time, {"b"=>2, "foo"=>"bar"}]],
166
+ [[time, {"a"=>1}], [time, {"b"=>2}]]
167
+ ], d.instance.outputs.map{ |o| o.events }
168
+ end
169
+
170
+ ## Belows are special tests for tagged_copy
171
+
172
+ def test_tag_emit
173
+ config = %[
174
+ <store>
175
+ <filter>
176
+ tag first
177
+ </filter>
178
+ type test
179
+ name c0
180
+ </store>
181
+ <store>
182
+ <filter>
183
+ tag second
184
+ </filter>
185
+ type test
186
+ name c0
187
+ </store>
188
+ ]
189
+ d = create_driver(config)
190
+
191
+ time = Time.parse("2011-01-02 13:14:15 UTC").to_i
192
+ d.emit({"a"=>1}, time)
193
+ d.emit({"a"=>2}, time)
194
+
195
+ first = d.instance.outputs.first
196
+ assert_equal [
197
+ ['first', time, {"a"=>1}],
198
+ ['first', time, {"a"=>2}],
199
+ ], first.emits
200
+
201
+ second = d.instance.outputs[1]
202
+ assert_equal [
203
+ ['second', time, {"a"=>1}],
204
+ ['second', time, {"a"=>2}],
205
+ ], second.emits
206
+ end
207
+
208
+ def test_add_tag_prefix_emit
209
+ config = %[
210
+ <store>
211
+ <filter>
212
+ add_tag_prefix first
213
+ </filter>
214
+ type test
215
+ name c0
216
+ </store>
217
+ <store>
218
+ <filter>
219
+ add_tag_prefix second
220
+ </filter>
221
+ type test
222
+ name c0
223
+ </store>
224
+ ]
225
+ d = create_driver(config)
226
+
227
+ time = Time.parse("2011-01-02 13:14:15 UTC").to_i
228
+ d.emit({"a"=>1}, time)
229
+ d.emit({"a"=>2}, time)
230
+
231
+ first = d.instance.outputs.first
232
+ assert_equal [
233
+ ['first.test', time, {"a"=>1}],
234
+ ['first.test', time, {"a"=>2}],
235
+ ], first.emits
236
+
237
+ second = d.instance.outputs[1]
238
+ assert_equal [
239
+ ['second.test', time, {"a"=>1}],
240
+ ['second.test', time, {"a"=>2}],
241
+ ], second.emits
242
+ end
243
+
244
+ def test_remove_tag_prefix_emit
245
+ config = %[
246
+ <store>
247
+ <filter>
248
+ remove_tag_prefix first
249
+ </filter>
250
+ type test
251
+ name c0
252
+ </store>
253
+ <store>
254
+ <filter>
255
+ remove_tag_prefix second
256
+ </filter>
257
+ type test
258
+ name c0
259
+ </store>
260
+ ]
261
+ d = create_driver(config)
262
+
263
+ time = Time.parse("2011-01-02 13:14:15 UTC").to_i
264
+ d.tag = 'first.test'
265
+ d.emit({"a"=>1}, time)
266
+ d.tag = 'second.test'
267
+ d.emit({"a"=>2}, time)
268
+
269
+ first = d.instance.outputs.first
270
+ assert_equal [
271
+ ['test', time, {"a"=>1}],
272
+ ['second.test', time, {"a"=>2}],
273
+ ], first.emits
274
+
275
+ second = d.instance.outputs[1]
276
+ assert_equal [
277
+ ['first.test', time, {"a"=>1}],
278
+ ['test', time, {"a"=>2}],
279
+ ], second.emits
280
+ end
281
+ end
metadata ADDED
@@ -0,0 +1,112 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: fluent-plugin-tagged_copy
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-03-27 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 to do tagging before copy
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
+ - fluent-plugin-tagged_copy.gemspec
84
+ - lib/fluent/plugin/out_tagged_copy.rb
85
+ - test/helper.rb
86
+ - test/plugin/out_tagged_copy.rb
87
+ homepage: https://github.com/sonots/fluent-plugin-tagged_copy
88
+ licenses: []
89
+ metadata: {}
90
+ post_install_message:
91
+ rdoc_options: []
92
+ require_paths:
93
+ - lib
94
+ required_ruby_version: !ruby/object:Gem::Requirement
95
+ requirements:
96
+ - - '>='
97
+ - !ruby/object:Gem::Version
98
+ version: '0'
99
+ required_rubygems_version: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - '>='
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ requirements: []
105
+ rubyforge_project: fluent-plugin-tagged_copy
106
+ rubygems_version: 2.0.3
107
+ signing_key:
108
+ specification_version: 4
109
+ summary: Fluentd out_copy extension to do tagging before copy
110
+ test_files:
111
+ - test/helper.rb
112
+ - test/plugin/out_tagged_copy.rb