fluent-plugin-route 0.2.0 → 1.0.0

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 7740164b77dfc4121d2abb21a984958cc4578cc8
4
- data.tar.gz: 22a5547cf3d120b4e939b232b858f9482935f2f0
3
+ metadata.gz: a20d74801e0b2189c8de4e61be4123e131986800
4
+ data.tar.gz: 68d0a470e298074ce667651a6f4acccf79738d09
5
5
  SHA512:
6
- metadata.gz: 990730d0d8956c835ccb2e9b7e1e88e9d7ad0df1feb88f1736db30279d371ad42908eb9b349ddd64f0e019d7a3242dedc4120091daa0b03aacc20302378c6c93
7
- data.tar.gz: f840ec3d77b45fe782871621793ef2ff4401c3dddcbce08107072aa808f2537e5c9b207745ac28e0ef8ad240c293e33a0fb08fd3dec2545bd6dae105366d54be
6
+ metadata.gz: 64ad0ac9468fb83c7c6c7e0a35c2e02b9325c5c26ccd7ddff6f86ef71e644e2657b0530003b82d0023b288ff1d51f0a36313544c3bddc3eeac3179389460160d
7
+ data.tar.gz: fb4e5e8fa31fdce80f2a4e577c392d734fe04c59d3e268600139044e991ef8456f33780760f4a858e1a9cbdf695f9be176bdb644e3c22df4e9aa930ef70447af
@@ -1,7 +1,7 @@
1
1
  # -*- encoding: utf-8 -*-
2
2
  Gem::Specification.new do |gem|
3
3
  gem.name = "fluent-plugin-route"
4
- gem.version = "0.2.0"
4
+ gem.version = "1.0.0"
5
5
  gem.authors = ["TAGOMORI Satoshi", "FURUHASHI Sadayuki"]
6
6
  gem.email = ["tagomoris@gmail.com", "frsyuki@gmail.com"]
7
7
  gem.summary = %q{Fluentd plugin to route messages in fluentd processes}
@@ -14,7 +14,7 @@ Gem::Specification.new do |gem|
14
14
  gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
15
15
  gem.require_paths = ["lib"]
16
16
 
17
- gem.add_runtime_dependency "fluentd", "< 0.14.0"
17
+ gem.add_runtime_dependency "fluentd", ">= 0.14.0"
18
18
  gem.add_development_dependency "rake"
19
19
  gem.add_development_dependency "test-unit"
20
20
  end
@@ -15,153 +15,131 @@
15
15
  # See the License for the specific language governing permissions and
16
16
  # limitations under the License.
17
17
  #
18
- require 'fluent/output'
18
+ require 'fluent/plugin/bare_output'
19
+ require 'fluent/match'
19
20
 
20
- module Fluent
21
- class RouteOutput < MultiOutput
22
- Plugin.register_output('route', self)
21
+ module Fluent::Plugin
22
+ class RouteOutput < BareOutput
23
+ Fluent::Plugin.register_output('route', self)
23
24
 
24
- class Route
25
- include Configurable
25
+ helpers :event_emitter
26
26
 
27
- config_param :remove_tag_prefix, :string, :default => nil
28
- config_param :add_tag_prefix, :string, :default => nil
29
- # TODO tag_transform regexp
30
- attr_accessor :copy
27
+ config_section :route, param_name: :route_configs, multi: true, required: true do
28
+ config_argument :pattern, :string, default: '**'
29
+ config_param :@label, :string, default: nil
30
+ config_param :remove_tag_prefix, :string, default: nil
31
+ config_param :add_tag_prefix, :string, default: nil
32
+ config_param :copy, :bool, default: false
33
+ end
31
34
 
32
- def initialize(pattern, router)
33
- super()
34
- if !pattern || pattern.empty?
35
- pattern = '**'
36
- end
37
- @router = router
38
- @pattern = MatchPattern.create(pattern)
39
- @tag_cache = {}
40
- end
35
+ config_param :remove_tag_prefix, :string, default: nil
36
+ config_param :add_tag_prefix, :string, default: nil
41
37
 
42
- def match?(tag)
43
- @pattern.match(tag)
44
- end
38
+ config_param :match_cache_size, :integer, default: 256
39
+ config_param :tag_cache_size, :integer, default: 256
45
40
 
46
- def configure(conf)
47
- super
48
- if conf['copy']
49
- @copy = true
50
- else
51
- @copy = false
52
- end
53
- if label_name = conf['@label']
54
- label = Fluent::Engine.root_agent.find_label(label_name)
55
- @router = label.event_router
56
- end
57
- if @remove_tag_prefix
58
- @prefix_match = /^#{Regexp.escape(@remove_tag_prefix)}\.?/
59
- else
60
- @prefix_match = //
61
- end
62
- if @add_tag_prefix
63
- @tag_prefix = "#{@add_tag_prefix}."
64
- else
65
- @tag_prefix = ""
66
- end
67
- end
68
-
69
- def copy?
70
- @copy
71
- end
41
+ attr_reader :routes
72
42
 
73
- def emit(tag, es)
74
- ntag = @tag_cache[tag]
75
- unless ntag
76
- ntag = tag.sub(@prefix_match, @tag_prefix)
77
- if @tag_cache.size < 1024 # TODO size limit
78
- @tag_cache[tag] = ntag
43
+ def tag_modifier(remove_tag_prefix, add_tag_prefix)
44
+ tag_cache_size = @tag_cache_size
45
+ cache = {}
46
+ mutex = Mutex.new
47
+ removed_prefix = remove_tag_prefix ? remove_tag_prefix + "." : ""
48
+ added_prefix = add_tag_prefix ? add_tag_prefix + "." : ""
49
+ ->(tag){
50
+ if cached = cache[tag]
51
+ cached
52
+ else
53
+ modified = tag.start_with?(removed_prefix) ? tag.sub(removed_prefix, added_prefix) : added_prefix + tag
54
+ mutex.synchronize do
55
+ if cache.size >= tag_cache_size
56
+ remove_keys = cache.keys[0...(tag_cache_size / 2)]
57
+ cache.delete_if{|key, _value| remove_keys.include?(key) }
58
+ end
59
+ cache[tag] = modified
79
60
  end
61
+ modified
80
62
  end
81
- @router.emit_stream(ntag, es)
82
- end
83
- end
84
-
85
- def initialize
86
- super
87
- @routes = []
88
- @tag_cache = {}
89
- @match_cache = {}
63
+ }
90
64
  end
91
65
 
92
- config_param :remove_tag_prefix, :string, :default => nil
93
- config_param :add_tag_prefix, :string, :default => nil
94
- # TODO tag_transform regexp
95
-
96
- attr_reader :routes
66
+ def configure(conf)
67
+ if conf.elements(name: 'store').size > 0
68
+ raise Fluent::ConfigError, "<store> section is not available in route plugin"
69
+ end
97
70
 
98
- # Define `log` method for v0.10.42 or earlier
99
- unless method_defined?(:log)
100
- define_method("log") { $log }
101
- end
71
+ super
102
72
 
103
- # Define `router` method of v0.12 to support v0.10 or earlier
104
- unless method_defined?(:router)
105
- define_method("router") { ::Fluent::Engine }
73
+ @match_cache = {}
74
+ @routes = []
75
+ @route_configs.each do |rc|
76
+ route_router = event_emitter_router(rc['@label'])
77
+ modifier = tag_modifier(rc.remove_tag_prefix, rc.add_tag_prefix)
78
+ @routes << Route.new(rc.pattern, route_router, modifier, rc.copy)
79
+ end
80
+ @default_tag_modifier = (@remove_tag_prefix || @add_tag_prefix) ? tag_modifier(@remove_tag_prefix, @add_tag_prefix) : nil
81
+ @mutex = Mutex.new
106
82
  end
107
83
 
108
- def configure(conf)
109
- super
84
+ class Route
85
+ def initialize(pattern, router, tag_modifier, copy)
86
+ @router = router
87
+ @pattern = Fluent::MatchPattern.create(pattern)
88
+ @tag_modifier = tag_modifier
89
+ @copy = copy
90
+ end
110
91
 
111
- if @remove_tag_prefix
112
- @prefix_match = /^#{Regexp.escape(@remove_tag_prefix)}\.?/
113
- else
114
- @prefix_match = //
92
+ def match?(tag)
93
+ @pattern.match(tag)
115
94
  end
116
- if @add_tag_prefix
117
- @tag_prefix = "#{@add_tag_prefix}."
118
- else
119
- @tag_prefix = ""
95
+
96
+ def copy?
97
+ @copy
120
98
  end
121
99
 
122
- conf.elements.select {|e|
123
- e.name == 'route'
124
- }.each {|e|
125
- route = Route.new(e.arg, router)
126
- route.configure(e)
127
- @routes << route
128
- }
100
+ def emit(tag, es)
101
+ tag = @tag_modifier.call(tag)
102
+ @router.emit_stream(tag, es)
103
+ end
129
104
  end
130
105
 
131
- def emit(tag, es, chain)
132
- ntag, targets = @match_cache[tag]
106
+ def process(tag, es)
107
+ modified_tag, targets = @match_cache[tag]
133
108
  unless targets
134
- ntag = tag.sub(@prefix_match, @tag_prefix)
109
+ modified_tag = @default_tag_modifier ? @default_tag_modifier.call(tag) : tag
135
110
  targets = []
136
- @routes.each {|r|
137
- if r.match?(ntag)
111
+ @routes.each do |r|
112
+ if r.match?(modified_tag)
138
113
  targets << r
139
114
  break unless r.copy?
140
115
  end
141
- }
142
- if @match_cache.size < 1024 # TODO size limit
143
- @match_cache[tag] = [ntag, targets]
116
+ end
117
+
118
+ @mutex.synchronize do
119
+ if @match_cache.size >= @match_cache_size
120
+ remove_keys = @match_cache.keys[0...(@match_cache_size / 2)]
121
+ @match_cache.delete_if{|key, _value| remove_keys.include?(key) }
122
+ end
123
+ @match_cache[tag] = [modified_tag, targets]
144
124
  end
145
125
  end
146
126
 
147
127
  case targets.size
148
128
  when 0
149
- return
129
+ # do nothing
150
130
  when 1
151
- targets.first.emit(ntag, es)
152
- chain.next
131
+ targets.first.emit(modified_tag, es)
153
132
  else
154
- unless es.repeatable?
155
- m = MultiEventStream.new
156
- es.each {|time,record|
157
- m.add(time, record)
158
- }
159
- es = m
133
+ targets.each do |target|
134
+ dup_es = if es.respond_to?(:dup)
135
+ es.dup
136
+ else
137
+ m_es = MultiEventStream.new
138
+ es.each{|t,r| m_es.add(t, r) }
139
+ m_es
140
+ end
141
+ target.emit(modified_tag, dup_es)
160
142
  end
161
- targets.each {|t|
162
- t.emit(ntag, es)
163
- }
164
- chain.next
165
143
  end
166
144
  end
167
145
  end
@@ -1,6 +1,12 @@
1
- require 'fluent/test'
2
1
  require 'fluent/plugin/out_route'
3
2
 
3
+ require 'fluent/test'
4
+ require 'fluent/test/driver/multi_output'
5
+ require 'fluent/test/driver/event_feeder'
6
+
7
+ require 'fluent/test/helpers'
8
+ include Fluent::Test::Helpers
9
+
4
10
  class RouteOutputTest < Test::Unit::TestCase
5
11
  def setup
6
12
  Fluent::Test.setup
@@ -25,14 +31,15 @@ class RouteOutputTest < Test::Unit::TestCase
25
31
  </route>
26
32
  ]
27
33
 
28
- def create_driver(conf, tag)
29
- d = Fluent::Test::OutputTestDriver.new(Fluent::RouteOutput, tag)
34
+ def create_driver(conf)
35
+ d = Fluent::Test::Driver::BaseOwner.new(Fluent::Plugin::RouteOutput)
36
+ d.extend(Fluent::Test::Driver::EventFeeder)
30
37
  Fluent::Engine.root_agent.define_singleton_method(:find_label) do |label_name|
31
38
  obj = Object.new
32
39
  obj.define_singleton_method(:event_router){ d.instance.router } # for test...
33
40
  obj
34
41
  end
35
- d.configure(conf, true)
42
+ d.configure(conf)
36
43
  end
37
44
 
38
45
  def test_configure
@@ -40,52 +47,52 @@ class RouteOutputTest < Test::Unit::TestCase
40
47
  end
41
48
 
42
49
  def test_emit_t1
43
- d = create_driver(CONFIG, "t.t1.test")
50
+ d = create_driver(CONFIG)
44
51
 
45
- time = Time.parse("2011-11-11 11:11:11 UTC").to_i
46
- d.run do
47
- d.emit({"a" => 1}, time)
48
- d.emit({"a" => 2}, time)
52
+ time = event_time("2011-11-11 11:11:11 UTC")
53
+ d.run(default_tag: "t.t1.test", expect_records: 2) do
54
+ d.feed(time, {"a" => 1})
55
+ d.feed(time, {"a" => 2})
49
56
  end
50
57
 
51
- emits = d.emits
52
- assert_equal 2, emits.size
58
+ events = d.events
59
+ assert_equal 2, events.size
53
60
 
54
- assert_equal ["yay.test", time, {"a" => 1}], emits[0]
55
- assert_equal ["yay.test", time, {"a" => 2}], emits[1]
61
+ assert_equal ["yay.test", time, {"a" => 1}], events[0]
62
+ assert_equal ["yay.test", time, {"a" => 2}], events[1]
56
63
  end
57
64
 
58
65
  def test_emit_t2
59
- d = create_driver(CONFIG, "t.t2.test")
66
+ d = create_driver(CONFIG)
60
67
 
61
- time = Time.parse("2011-11-11 11:11:11 UTC").to_i
62
- d.run do
63
- d.emit({"a" => 1}, time)
64
- d.emit({"a" => 2}, time)
68
+ time = event_time("2011-11-11 11:11:11 UTC")
69
+ d.run(default_tag: "t.t2.test", expect_records: 2) do
70
+ d.feed(time, {"a" => 1})
71
+ d.feed(time, {"a" => 2})
65
72
  end
66
73
 
67
- emits = d.emits
68
- assert_equal 2, emits.size
74
+ events = d.events
75
+ assert_equal 2, events.size
69
76
 
70
- assert_equal ["foo.test", time, {"a" => 1}], emits[0]
71
- assert_equal ["foo.test", time, {"a" => 2}], emits[1]
77
+ assert_equal ["foo.test", time, {"a" => 1}], events[0]
78
+ assert_equal ["foo.test", time, {"a" => 2}], events[1]
72
79
  end
73
80
 
74
81
  def test_emit_others
75
- d = create_driver(CONFIG, "t.t3.test")
82
+ d = create_driver(CONFIG)
76
83
 
77
- time = Time.parse("2011-11-11 11:11:11 UTC").to_i
78
- d.run do
79
- d.emit({"a" => 1}, time)
80
- d.emit({"a" => 2}, time)
84
+ time = event_time("2011-11-11 11:11:11 UTC")
85
+ d.run(default_tag: "t.t3.test", expect_records: 4) do
86
+ d.feed(time, {"a" => 1})
87
+ d.feed(time, {"a" => 2})
81
88
  end
82
89
 
83
- emits = d.emits
84
- assert_equal 4, emits.size
90
+ events = d.events
91
+ assert_equal 4, events.size
85
92
 
86
- assert_equal ["t3.test", time, {"a" => 1}], emits[0]
87
- assert_equal ["t3.test", time, {"a" => 1}], emits[1]
88
- assert_equal ["t3.test", time, {"a" => 2}], emits[2]
89
- assert_equal ["t3.test", time, {"a" => 2}], emits[3]
93
+ assert_equal ["t3.test", time, {"a" => 1}], events[0]
94
+ assert_equal ["t3.test", time, {"a" => 1}], events[1]
95
+ assert_equal ["t3.test", time, {"a" => 2}], events[2]
96
+ assert_equal ["t3.test", time, {"a" => 2}], events[3]
90
97
  end
91
98
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fluent-plugin-route
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - TAGOMORI Satoshi
@@ -9,20 +9,20 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2017-01-31 00:00:00.000000000 Z
12
+ date: 2017-02-01 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: fluentd
16
16
  requirement: !ruby/object:Gem::Requirement
17
17
  requirements:
18
- - - "<"
18
+ - - ">="
19
19
  - !ruby/object:Gem::Version
20
20
  version: 0.14.0
21
21
  type: :runtime
22
22
  prerelease: false
23
23
  version_requirements: !ruby/object:Gem::Requirement
24
24
  requirements:
25
- - - "<"
25
+ - - ">="
26
26
  - !ruby/object:Gem::Version
27
27
  version: 0.14.0
28
28
  - !ruby/object:Gem::Dependency