fluent-plugin-derive 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 51798346dc27a15ff0a23c51c4926e146c3af789
4
+ data.tar.gz: 16dc81bbc0e36994303602b7becc18ccf8091ff0
5
+ SHA512:
6
+ metadata.gz: d2a1c43275e27ea1e9a270c6dc35aba23fa89ffd200b6d23099920188ba8deb5e4528e74e767be24ff9cc3de82b911f4c492f4d813c578c2a5a69d16685ac411
7
+ data.tar.gz: c13f6fede3c12711518daeb96131062032876e5a2528bf0acc6fc976a5ccf445866c509067ea771b45d8a6da67c45b40174498913ff753a114a6d544d81b3a95
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --colour
2
+ --format documentation
data/.travis.yml ADDED
@@ -0,0 +1,6 @@
1
+ rvm:
2
+ - 1.9.2
3
+ - 1.9.3
4
+ - 2.0.0
5
+ gemfile:
6
+ - Gemfile
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in fluent-plugin-derive.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Nobuhiro Nikushi
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,104 @@
1
+ # fluent-plugin-derive
2
+
3
+ Calculate per second value for the increasing/decreasing value between the last and the current, like derive in RRDTool.
4
+
5
+ For example, imagine interface counter values that are inputted by SNMP polling, What we want to know is essentially the bps not the raw value. By this plug-in, the last inputted values of the specific keys and time are cached, and the per second rate for each key are culculated and re-emitted when the next record is inputted.
6
+
7
+ Note that Fluentd does not guarantee the order of arrival of messages, it may not be able to calculate accurately if the messages which are tagged with same name are inputted at short intervals. So DO NOT USE this plugin in that case.
8
+
9
+ I am using the derive plugin in combination with [fluent-plugin-snmp](https://github.com/iij/fluent-plugin-snmp).
10
+
11
+ ## Configuration
12
+
13
+ ### Example 1
14
+
15
+ <match foo.bar.**>
16
+ type derive
17
+ add_tag_prefix derive
18
+ key1 foo_count
19
+ key2 bar_count
20
+ </match>
21
+
22
+ Assuming following inputs are coming:
23
+
24
+ 2013-12-19 20:01:00 +0900 foo.bar: {"foo_count": 100, "bar_count": 200}
25
+ 2013-12-19 20:02:00 +0900 foo.bar: {"foo_count": 700, "bar_count": 1400}
26
+ 2013-12-19 20:03:10 +0900 foo.bar: {"foo_count": 700, "bar_count": 1470}
27
+ 2013-12-19 20:04:10 +0900 foo.bar: {"foo_count": 1300, "bar_count": 870}
28
+
29
+ then output becomes as below:
30
+
31
+ 2013-12-19 20:01:00 +0900 derive.foo.bar: {"foo_count": nil, "bar_count": nil}
32
+ 2013-12-19 20:02:00 +0900 derive.foo.bar: {"foo_count": 10, "bar_count": 20}
33
+ 2013-12-19 20:03:10 +0900 derive.foo.bar: {"foo_count": 0, "bar_count": 1}
34
+ 2013-12-19 20:04:10 +0900 derive.foo.bar: {"foo_count": 10, "bar_count": -10}
35
+
36
+ Cacled as a per sec rate. See below how calced.
37
+
38
+ (700/100)/(20:02:00 - 20:01:00) => 10
39
+
40
+ ### Example 2
41
+
42
+ <match foo.bar.**>
43
+ type derive
44
+ add_tag_prefix derive
45
+ key1 foo_count *1000
46
+ key2 bar_count *1000
47
+ </match>
48
+
49
+ Assuming following inputs are coming:
50
+
51
+ 2013-12-19 20:01:00 +0900 foo.bar: {"foo_count": 100, "bar_count": 200}
52
+ 2013-12-19 20:02:00 +0900 foo.bar: {"foo_count": 700, "bar_count": 1400}
53
+ 2013-12-19 20:03:10 +0900 foo.bar: {"foo_count": 700, "bar_count": 1470}
54
+ 2013-12-19 20:04:10 +0900 foo.bar: {"foo_count": 1300, "bar_count": 870}
55
+
56
+ then output becomes as below:
57
+
58
+ 2013-12-19 20:01:00 +0900 derive.foo.bar: {"foo_count": nil, "bar_count": nil}
59
+ 2013-12-19 20:02:00 +0900 derive.foo.bar: {"foo_count": 10000, "bar_count": 20000}
60
+ 2013-12-19 20:03:10 +0900 derive.foo.bar: {"foo_count": 0, "bar_count": 1000}
61
+ 2013-12-19 20:04:10 +0900 derive.foo.bar: {"foo_count": 10000, "bar_count": -10000}
62
+
63
+ ## Paramteres
64
+ * key[1-20] [Adjustment]
65
+
66
+ A pair of a field name of the input record, and to be calculated. key1 or key_pattern is required. `Adjustment` is optional.
67
+
68
+ Use `Adjustment` like follow:
69
+
70
+ key1 foo_count *3600000 => output the rate as K/h
71
+ key1 foo_count *8 => shift unit (e.g. Byteps to bps)
72
+ key1 foo_count /1000 => shift unit (e.g. M to K)
73
+
74
+ * key_pattern [Adjustment]
75
+
76
+ A pair of a regular expression to specify field names of the input record, and to be calculated. key1 or key_pattern is required. `Adjustment` is optional.
77
+
78
+ * tag
79
+
80
+ The output tag name
81
+
82
+ * add_tag_prefix
83
+
84
+ Add tag prefix for output message
85
+
86
+ * remove_tag_prefix
87
+
88
+ Remove tag prefix for output message
89
+
90
+ * min
91
+
92
+ Define the expected range value. If min and/or max are specified any value outside the defined range will be truncated.
93
+
94
+ * max
95
+
96
+ Define the expected range value. If min and/or max are specified any value outside the defined range will be truncated.
97
+
98
+ ## Contributing
99
+
100
+ 1. Fork it
101
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
102
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
103
+ 4. Push to the branch (`git push origin my-new-feature`)
104
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,23 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "fluent-plugin-derive"
7
+ spec.version = "0.0.1"
8
+ spec.authors = ["Nobuhiro Nikushi"]
9
+ spec.email = ["deneb.ge@gmail.com"]
10
+ spec.description = spec.summary
11
+ spec.summary = "fluentd plugin to derive rate"
12
+ spec.homepage = "https://github.com/niku4i/fluent-plugin-derive"
13
+ spec.license = "MIT"
14
+
15
+ spec.files = `git ls-files`.split($/)
16
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
17
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
18
+ spec.require_paths = ["lib"]
19
+
20
+ spec.add_runtime_dependency "fluentd"
21
+ spec.add_development_dependency "rake"
22
+ spec.add_development_dependency "rspec"
23
+ end
@@ -0,0 +1,175 @@
1
+ class Fluent::DeriveOutput < Fluent::Output
2
+ Fluent::Plugin.register_output('derive', self)
3
+
4
+ KEY_MAX_NUM = 20
5
+ (1..KEY_MAX_NUM).each {|i| config_param "key#{i}".to_sym, :string, :default => nil }
6
+ config_param :key_pattern, :string, :default => nil
7
+ config_param :tag, :string, :default => nil
8
+ config_param :add_tag_prefix, :string, :default => nil
9
+ config_param :remove_tag_prefix, :string, :default => nil
10
+ config_param :min, :integer, :default => nil
11
+ config_param :max, :integer, :default => nil
12
+
13
+ # for test
14
+ attr_reader :key_pattern
15
+ attr_reader :key_pattern_adjustment
16
+ attr_reader :keys
17
+ attr_reader :prev
18
+
19
+ def configure(conf)
20
+ super
21
+
22
+ if @key_pattern
23
+ key_pattern, @key_pattern_adjustment = @key_pattern.split(/ +/, 2)
24
+ @key_pattern_adjustment = parse_adjustment(@key_pattern_adjustment)
25
+ @key_pattern = Regexp.compile(key_pattern)
26
+ else
27
+ @keys = {}
28
+ (1..KEY_MAX_NUM).each do |i|
29
+ next unless conf["key#{i}"]
30
+ key, adjustment = conf["key#{i}"].split(/ +/, 2)
31
+ adjustment = parse_adjustment(adjustment)
32
+ @keys[key] = adjustment
33
+ end
34
+ end
35
+ raise Fluent::ConfigError, "Either of `key_pattern` or `key1` must be specified" if (@key_pattern.nil? and @keys.empty?)
36
+
37
+ raise Fluent::ConfigError, "Either of `tag`, `add_tag_prefix`, or `remove_tag_prefix` must be specified" if (@tag.nil? and @add_tag_prefix.nil? and @remove_tag_prefix.nil?)
38
+ @tag_prefix = "#{@add_tag_prefix}." if @add_tag_prefix
39
+ @tag_prefix_match = "#{@remove_tag_prefix}." if @remove_tag_prefix
40
+ @tag_proc =
41
+ if @tag
42
+ Proc.new {|tag| @tag }
43
+ elsif @tag_prefix and @tag_prefix_match
44
+ Proc.new {|tag| "#{@tag_prefix}#{lstrip(tag, @tag_prefix_match)}" }
45
+ elsif @tag_prefix_match
46
+ Proc.new {|tag| lstrip(tag, @tag_prefix_match) }
47
+ elsif @tag_prefix
48
+ Proc.new {|tag| "#{@tag_prefix}#{tag}" }
49
+ else
50
+ Proc.new {|tag| tag }
51
+ end
52
+
53
+ raise Fluent::ConfigError, "`max` must be greater than `min`" if (@min && @max && @min >= @max)
54
+
55
+ @prev = {}
56
+ @mutex = Mutex.new
57
+ rescue => e
58
+ raise Fluent::ConfigError, "#{e.class} #{e.message} #{e.backtrace.first}"
59
+ end
60
+
61
+ def start
62
+ super
63
+ end
64
+
65
+ def shutdown
66
+ super
67
+ end
68
+
69
+ def emit(tag, es, chain)
70
+ emit_tag = @tag_proc.call(tag)
71
+
72
+ if @key_pattern
73
+ es.each do |time, record|
74
+ record.each do |key, value|
75
+ next unless key =~ @key_pattern
76
+ value = value.to_i
77
+ prev_time, prev_value = get_prev_value(tag, key)
78
+ unless prev_time && prev_value
79
+ save_to_prev(time, tag, key, value)
80
+ record[key] = nil
81
+ next
82
+ end
83
+ # adjustment
84
+ rate = calc_rate(tag, key, value, prev_value, time, prev_time, @key_pattern_adjustment)
85
+ rate = truncate_min(rate, @min) if @min
86
+ rate = truncate_max(rate, @max) if @max
87
+ # Set new value
88
+ record[key] = rate
89
+ save_to_prev(time, tag, key, value)
90
+ end
91
+ Fluent::Engine.emit(emit_tag, time, record)
92
+ end
93
+ else #keys
94
+ es.each do |time, record|
95
+ @keys.each do |key, adjustment|
96
+ next unless value = record[key]
97
+ value = value.to_i
98
+ prev_time, prev_value = get_prev_value(tag, key)
99
+ unless prev_time && prev_value
100
+ save_to_prev(time, tag, key, value)
101
+ record[key] = nil
102
+ next
103
+ end
104
+ # adjustment
105
+ rate = calc_rate(tag, key, value, prev_value, time, prev_time, adjustment)
106
+ rate = truncate_min(rate, @min) if @min
107
+ rate = truncate_max(rate, @max) if @max
108
+ # Set new value
109
+ record[key] = rate
110
+ save_to_prev(time, tag, key, value)
111
+ end
112
+ Fluent::Engine.emit(emit_tag, time, record)
113
+ end
114
+ end
115
+
116
+ chain.next
117
+ rescue => e
118
+ $log.warn e.message
119
+ $log.warn e.backtrace.join(', ')
120
+ end
121
+
122
+ # @return [Array] time, value
123
+ def get_prev_value(tag, key)
124
+ @prev["#{tag}:#{key}"] || []
125
+ end
126
+
127
+ def save_to_prev(time, tag, key, value)
128
+ @mutex.synchronize { @prev["#{tag}:#{key}"] = [time, value] }
129
+ end
130
+
131
+ private
132
+
133
+ def lstrip(string, substring)
134
+ string.index(substring) == 0 ? string[substring.size..-1] : string
135
+ end
136
+
137
+ def parse_adjustment(str)
138
+ case str
139
+ when /^\*(\d+)$/
140
+ ['*', $1.to_i]
141
+ when /^\/(\d+)$/
142
+ ['/', $1.to_i]
143
+ else
144
+ nil
145
+ end
146
+ end
147
+
148
+ def calc_rate(tag, key, cur_value, prev_value, cur_time, prev_time, adjustment = nil)
149
+ if cur_time - prev_time <= 0
150
+ $log.warn "Could not calculate the rate. multiple input less than one second or minus delta of seconds on tag=#{tag}, key=#{key}"
151
+ return nil
152
+ end
153
+ rate = (cur_value - prev_value)/(cur_time - prev_time)
154
+ if adjustment && adjustment[0] == '*'
155
+ rate * adjustment[1]
156
+ elsif adjustment && adjustment[0] == '/'
157
+ rate / adjustment[1]
158
+ else
159
+ rate
160
+ end
161
+ end
162
+
163
+ def truncate_min(value, min)
164
+ return nil unless value
165
+ (value < min) ? min : value
166
+ end
167
+
168
+ def truncate_max(value, max)
169
+ return nil unless value
170
+ (value > max) ? max : value
171
+ end
172
+
173
+
174
+ end
175
+
@@ -0,0 +1,49 @@
1
+ # encoding: UTF-8
2
+ require_relative 'spec_helper'
3
+
4
+ # setup
5
+ Fluent::Test.setup
6
+ config = %[
7
+ remove_tag_prefix foo
8
+ add_tag_prefix hoge
9
+ key1 foooooo_baaaaaa_count *1000
10
+ key2 hogeeee_fugaaaa_count *1500
11
+ min 0
12
+ max 1000000000
13
+ ]
14
+
15
+ time = Time.now.to_i
16
+ tag = 'foo.bar'
17
+ driver = Fluent::Test::OutputTestDriver.new(Fluent::DeriveOutput, tag).configure(config)
18
+
19
+ # bench
20
+ require 'benchmark'
21
+ message = "2013/01/13T07:02:11.124202 INFO GET /ping"
22
+ n = 100000
23
+ Benchmark.bm(7) do |x|
24
+ x.report { driver.run { n.times do |i|
25
+ time = time + i*60
26
+ driver.emit({'foooooo_baaaaaa_count' => 1234, 'hogeeee_fugaaaa_count' => 5000, 'unmached_keeeeee' => "abvc" }, time)
27
+ end
28
+ } }
29
+ end
30
+
31
+ # key_pattern without adjustment
32
+ # user system total real
33
+ # 2.920000 0.030000 2.950000 ( 3.466375)
34
+ #
35
+ # key_pattern with adjustment
36
+ # user system total real
37
+ # 3.000000 0.040000 3.040000 ( 3.550168)
38
+ #
39
+ # key1 without adjustment and key2 without adjustment
40
+ # user system total real
41
+ # 2.520000 0.030000 2.550000 ( 3.058561)
42
+ #
43
+ # key1 with adjustment and key2 with adjustment
44
+ # user system total real
45
+ # 2.630000 0.030000 2.660000 ( 3.180860)
46
+ #
47
+ # key1 with adjustment, key2 with adjustment, min and max
48
+ # user system total real
49
+ # 2.650000 0.040000 2.690000 ( 3.188757)
@@ -0,0 +1,289 @@
1
+ # encoding: UTF-8
2
+ require_relative 'spec_helper'
3
+
4
+ describe Fluent::DeriveOutput do
5
+ before { Fluent::Test.setup }
6
+ let(:tag) { 'counter.host1' }
7
+ let(:driver) { Fluent::Test::OutputTestDriver.new(Fluent::DeriveOutput, tag).configure(config) }
8
+
9
+ describe 'test configure' do
10
+ describe 'bad configuration' do
11
+ context 'none of key_pattern or key1 are included' do
12
+ let(:config) { "" }
13
+ it { expect { driver }.to raise_error(Fluent::ConfigError) }
14
+ end
15
+ context 'none of tag, add_tag_prefix, or remove_tag_prefix are included' do
16
+ let(:config) { "key1 foo" }
17
+ end
18
+ context 'min greater than max' do
19
+ let(:config) {%[
20
+ tag rate
21
+ key1 foo_count
22
+ min 1
23
+ max 0
24
+ ]}
25
+ it { expect { driver }.to raise_error(Fluent::ConfigError) }
26
+ end
27
+ context 'min == max' do
28
+ let(:config) {%[
29
+ tag rate
30
+ key1 foo_count
31
+ min 1
32
+ max 1
33
+ ]}
34
+ it { expect { driver }.to raise_error(Fluent::ConfigError) }
35
+ end
36
+ end
37
+
38
+ describe 'good configuration' do
39
+ subject { driver.instance }
40
+
41
+ context "check default" do
42
+ let(:config) { %[
43
+ tag rate
44
+ key1 foo
45
+ ] }
46
+ its(:tag) { should eq "rate" }
47
+ its(:add_tag_prefix) { should be_nil }
48
+ its(:remove_tag_prefix) { should be_nil }
49
+ its(:min) { should be_nil }
50
+ its(:max) { should be_nil }
51
+ end
52
+
53
+ context "key_pattern" do
54
+ let(:config) {%[
55
+ tag rate
56
+ key_pattern _count$
57
+ ]}
58
+ it { subject.key_pattern.should == Regexp.compile("_count$") }
59
+ end
60
+
61
+ context "key_pattern_adjustment" do
62
+ let(:config) {%[
63
+ tag rate
64
+ key_pattern _count$ *1000
65
+ ]}
66
+ it { subject.key_pattern_adjustment.should eq ['*', 1000] }
67
+ end
68
+
69
+ context "keys" do
70
+ let(:config) {%[
71
+ tag rate
72
+ key1 foo_count
73
+ key2 bar_count
74
+ ]}
75
+ it { subject.keys["foo_count"].should be_nil }
76
+ it { subject.keys["bar_count"].should be_nil }
77
+ end
78
+
79
+ context "keys adjustment" do
80
+ let(:config) {%[
81
+ tag rate
82
+ key1 foo_count *1000
83
+ key2 bar_count /1000
84
+ ]}
85
+ it { subject.keys["foo_count"].should eq ['*', 1000] }
86
+ it { subject.keys["bar_count"].should eq ['/', 1000] }
87
+ end
88
+
89
+ context 'min < max' do
90
+ let(:config) {%[
91
+ tag rate
92
+ key1 foo_count
93
+ min 0
94
+ max 1
95
+ ]}
96
+ it { expect { driver }.not_to raise_error(Fluent::ConfigError) }
97
+ end
98
+
99
+ end
100
+ end
101
+
102
+ describe 'test emit' do
103
+ let(:time) { Time.now.to_i }
104
+
105
+ context 'keys' do
106
+
107
+ context 'normal' do
108
+ let(:config) { %[
109
+ tag rate
110
+ key1 foo_count
111
+ key2 bar_count
112
+ ]}
113
+ before do
114
+ driver.run {
115
+ driver.emit({'foo_count'=> 100, 'bar_count' =>200, 'other_key' => 'abc'}, time)
116
+ driver.emit({'foo_count'=> 700, 'bar_count' =>1400}, time + 60)
117
+ driver.emit({'foo_count'=> 700, 'bar_count' =>800}, time + 120)
118
+ }
119
+ end
120
+ it {
121
+ driver.emits[0].should == ['rate', time, {'foo_count' => nil, 'bar_count' => nil, 'other_key' => 'abc'}]
122
+ driver.emits[1].should == ['rate', time + 60, {'foo_count' => 10, 'bar_count' => 20}]
123
+ driver.emits[2].should == ['rate', time + 120, {'foo_count' => 0, 'bar_count' => -10}]
124
+ driver.instance.prev.should == {"#{tag}:foo_count"=>[time+120, 700], "#{tag}:bar_count"=>[time+120, 800]}
125
+ }
126
+ end
127
+
128
+ context 'multiple records in same time' do
129
+ let(:config) { %[
130
+ tag rate
131
+ key1 foo_count
132
+ key2 bar_count
133
+ ]}
134
+ before do
135
+ driver.run {
136
+ driver.emit({'foo_count'=> 100, 'bar_count' =>200}, time)
137
+ driver.emit({'foo_count'=> 100, 'bar_count' =>200}, time)
138
+ }
139
+ end
140
+ it {
141
+ driver.emits[1].should == ['rate', time, {'foo_count' => nil, 'bar_count' => nil}]
142
+ }
143
+ end
144
+
145
+ context 'adjustment' do
146
+ let(:config) { %[
147
+ tag rate
148
+ key1 foo_count
149
+ key2 bar_count
150
+ key3 baz_count *1000
151
+ ]}
152
+ before do
153
+ driver.run {
154
+ driver.emit({'foo_count'=> 100, 'bar_count' =>200, 'baz_count' => 300}, time)
155
+ driver.emit({'foo_count'=> 700, 'bar_count' =>1400, 'baz_count' => 900}, time + 60)
156
+ }
157
+ end
158
+ it {
159
+ driver.emits[0].should == ['rate', time, {'foo_count' => nil, 'bar_count' => nil, 'baz_count' => nil}]
160
+ driver.emits[1].should == ['rate', time + 60, {'foo_count' => 10, 'bar_count' => 20, 'baz_count' => 10000}]
161
+ }
162
+ end
163
+
164
+ context 'min/max' do
165
+ let(:config) { %[
166
+ tag rate
167
+ key1 foo_count
168
+ key2 bar_count *1000000
169
+ min 0
170
+ max 1000
171
+ ]}
172
+ before do
173
+ driver.run {
174
+ driver.emit({'foo_count'=> 100, 'bar_count'=>0}, time)
175
+ driver.emit({'foo_count'=> 0, 'bar_count'=>6000}, time + 60)
176
+ }
177
+ end
178
+ it {
179
+ driver.emits[1].should == ['rate', time + 60, {'foo_count' => 0, 'bar_count' => 1000}]
180
+ }
181
+ end
182
+
183
+ context 'add_tag_prefix' do
184
+ let(:config) {%[
185
+ add_tag_prefix rate
186
+ key1 foo_count
187
+ key2 bar_count
188
+ ]}
189
+ before do
190
+ driver.run {
191
+ driver.emit({'foo_count'=> 100, 'bar_count' =>200, 'other_key' => 'abc'}, time)
192
+ }
193
+ end
194
+ it { driver.emits[0][0].should == "rate.#{tag}" }
195
+ end
196
+
197
+ context 'remove_tag_prefix' do
198
+ let(:config) {%[
199
+ remove_tag_prefix counter
200
+ key1 foo_count
201
+ key2 bar_count
202
+ ]}
203
+ before do
204
+ driver.run {
205
+ driver.emit({'foo_count'=> 100, 'bar_count' =>200, 'other_key' => 'abc'}, time)
206
+ }
207
+ end
208
+ it { driver.emits[0][0].should == 'host1' }
209
+ end
210
+ end
211
+
212
+ context 'key_pattern' do
213
+
214
+ context 'normal' do
215
+ let(:config) { %[
216
+ tag rate
217
+ key_pattern .*_count$
218
+ ]}
219
+ before do
220
+ driver.run {
221
+ driver.emit({'foo_count'=> 100, 'bar_count' =>200, 'other_key' => 'abc'}, time)
222
+ driver.emit({'foo_count'=> 700, 'bar_count' =>1400}, time + 60)
223
+ driver.emit({'foo_count'=> 700, 'bar_count' =>800}, time + 120)
224
+ }
225
+ end
226
+ it {
227
+ driver.emits[0].should == ['rate', time, {'foo_count' => nil, 'bar_count' => nil, 'other_key' => 'abc'}]
228
+ driver.emits[1].should == ['rate', time + 60, {'foo_count' => 10, 'bar_count' => 20}]
229
+ driver.emits[2].should == ['rate', time + 120, {'foo_count' => 0, 'bar_count' => -10}]
230
+ driver.instance.prev.should == {"#{tag}:foo_count"=>[time+120, 700], "#{tag}:bar_count"=>[time+120, 800]}
231
+ }
232
+ end
233
+
234
+ context 'multiple records in same time' do
235
+ let(:config) { %[
236
+ tag rate
237
+ key_pattern .*_count$
238
+ ]}
239
+ before do
240
+ driver.run {
241
+ driver.emit({'foo_count'=> 100, 'bar_count' =>200}, time)
242
+ driver.emit({'foo_count'=> 100, 'bar_count' =>200}, time)
243
+ }
244
+ end
245
+ it {
246
+ driver.emits[1].should == ['rate', time, {'foo_count' => nil, 'bar_count' => nil}]
247
+ }
248
+ end
249
+
250
+ context 'adjustment' do
251
+ let(:config) { %[
252
+ tag rate
253
+ key_pattern .*_count$ /10
254
+ ]}
255
+ before do
256
+ driver.run {
257
+ driver.emit({'foo_count'=> 100, 'bar_count' =>200, 'baz_count' => 300}, time)
258
+ driver.emit({'foo_count'=> 700, 'bar_count' =>1400, 'baz_count' => 900}, time + 60)
259
+ }
260
+ end
261
+ it {
262
+ driver.emits[0].should == ['rate', time, {'foo_count' => nil, 'bar_count' => nil, 'baz_count' => nil}]
263
+ driver.emits[1].should == ['rate', time + 60, {'foo_count' => 1, 'bar_count' => 2, 'baz_count' => 1}]
264
+ }
265
+ end
266
+
267
+ context 'min/max' do
268
+ let(:config) { %[
269
+ tag rate
270
+ key_pattern .*_count$ *10000
271
+ min 0
272
+ max 1000
273
+ ]}
274
+ before do
275
+ driver.run {
276
+ driver.emit({'foo_count'=> 100, 'bar_count'=>0}, time)
277
+ driver.emit({'foo_count'=> 0, 'bar_count'=>6000}, time + 60)
278
+ }
279
+ end
280
+ it {
281
+ driver.emits[1].should == ['rate', time + 60, {'foo_count' => 0, 'bar_count' => 1000}]
282
+ }
283
+ end
284
+
285
+
286
+ end
287
+
288
+ end
289
+ end
@@ -0,0 +1,13 @@
1
+ # encoding: UTF-8
2
+ require 'rubygems'
3
+ require 'bundler'
4
+ Bundler.setup(:default, :test)
5
+ Bundler.require(:default, :test)
6
+
7
+ require 'fluent/test'
8
+ require 'rspec'
9
+
10
+ $TESTING=true
11
+ $:.unshift File.join(File.dirname(__FILE__), '..', 'lib')
12
+ require 'fluent/plugin/out_derive'
13
+
metadata ADDED
@@ -0,0 +1,101 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: fluent-plugin-derive
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Nobuhiro Nikushi
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-12-23 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: rspec
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
+ description: ''
56
+ email:
57
+ - deneb.ge@gmail.com
58
+ executables: []
59
+ extensions: []
60
+ extra_rdoc_files: []
61
+ files:
62
+ - .gitignore
63
+ - .rspec
64
+ - .travis.yml
65
+ - Gemfile
66
+ - LICENSE.txt
67
+ - README.md
68
+ - Rakefile
69
+ - fluent-plugin-derive.gemspec
70
+ - lib/fluent/plugin/out_derive.rb
71
+ - spec/out_derive_bench.rb
72
+ - spec/out_derive_spec.rb
73
+ - spec/spec_helper.rb
74
+ homepage: https://github.com/niku4i/fluent-plugin-derive
75
+ licenses:
76
+ - MIT
77
+ metadata: {}
78
+ post_install_message:
79
+ rdoc_options: []
80
+ require_paths:
81
+ - lib
82
+ required_ruby_version: !ruby/object:Gem::Requirement
83
+ requirements:
84
+ - - '>='
85
+ - !ruby/object:Gem::Version
86
+ version: '0'
87
+ required_rubygems_version: !ruby/object:Gem::Requirement
88
+ requirements:
89
+ - - '>='
90
+ - !ruby/object:Gem::Version
91
+ version: '0'
92
+ requirements: []
93
+ rubyforge_project:
94
+ rubygems_version: 2.1.10
95
+ signing_key:
96
+ specification_version: 4
97
+ summary: fluentd plugin to derive rate
98
+ test_files:
99
+ - spec/out_derive_bench.rb
100
+ - spec/out_derive_spec.rb
101
+ - spec/spec_helper.rb