fluent-plugin-stats-notifier 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: 6a10e45fed39a4a597b00a14d2cf9d9889351042
4
+ data.tar.gz: 87eb183f4e82343ccfcc0b30a23c668b962a9d4e
5
+ SHA512:
6
+ metadata.gz: b8f08b98979b725ba89c26577814572d0fed6ea7d89765435b38da82e853974746e9faf7843d198c0307767f83ad92eada13fc87b42b8e20aa093abd50bda331
7
+ data.tar.gz: c83b295784a032e281a1ac39cf58cb9bbb8c34670d625f4dd80c14bc7c37ae8f29f4ab0f4296783e9e63c8fde4fa9e1fa83013b1f8c60f6454037338308de260
data/.coveralls.yml ADDED
@@ -0,0 +1 @@
1
+ repo_token: i4DJCtdksuIwhBck1tukIjzKoMCxWIIvQ
data/.gitignore ADDED
@@ -0,0 +1,14 @@
1
+ /*.gem
2
+ ~*
3
+ #*
4
+ *~
5
+ .bundle
6
+ Gemfile.lock
7
+ .rbenv-version
8
+ vendor
9
+ doc/*
10
+ tmp/*
11
+ coverage
12
+ .yardoc
13
+ pkg/*
14
+ .ruby-version
data/.rdebugrc ADDED
@@ -0,0 +1,4 @@
1
+ set autolist
2
+ set autoeval
3
+ set autoreload
4
+
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/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/README.md ADDED
@@ -0,0 +1,87 @@
1
+ # fluent-plugin-stats-notifier [![Build Status](https://secure.travis-ci.org/sonots/fluent-plugin-stats-notifier.png?branch=master)](http://travis-ci.org/sonots/fluent-plugin-stats-notifier)
2
+
3
+ Fluentd plugin to calculate statistics and then thresholding
4
+
5
+ ## Configuration
6
+
7
+ <store>
8
+ type stats_notifier
9
+ tag notifier
10
+ interval 5
11
+ target_key 4xx_count
12
+ greater_equal 4
13
+ compare_with max
14
+ store_file /path/to/store_file.dat
15
+ </store>
16
+
17
+ Assuming following inputs are coming:
18
+
19
+ foo.bar1: {"4xx_count":1,"foobar":2"}
20
+ foo.bar2: {"4xx_count":6,"foobar":2"}
21
+
22
+ then this plugin emits an message because the max of `4xx_count` is greater than or equal to the specified value `4`. Output will be as following:
23
+
24
+ notifier: {"4xx_count":6.0}
25
+
26
+ ## Parameters
27
+
28
+ - target\_key (required)
29
+
30
+ The target key in the event record.
31
+
32
+ - interval
33
+
34
+ The interval time of calculation and bounding. Default is 60.
35
+
36
+ - less\_than
37
+
38
+ A `less than` threshold value, that is, emit if `target_key` value < specified value.
39
+
40
+ - less\_equal
41
+
42
+ A `less than or eqaul` threshold value, that is, emit if `target_key` value <= specified value.
43
+
44
+ - greater\_than
45
+
46
+ A `greater than` threshold value, that is, emit if `target_key` value > specified value.
47
+
48
+ - greater\_equal
49
+
50
+ A `greater than or eqaul` threshold value, that is, emit if `target_key` value >= specified value.
51
+
52
+ - compare\_with
53
+
54
+ `max`, `avg`, `min`, `sum` can be specified. Default is `max`.
55
+
56
+ - tag
57
+
58
+ The output tag name.
59
+
60
+ - add_tag_prefix
61
+
62
+ (not available yet) Add tag prefix for output message.
63
+
64
+ - aggragate
65
+
66
+ (not available yet) Do calculation by each `tag` or `all`. The default value is `tag`.
67
+
68
+ - store_file
69
+
70
+ Store internal data into a file of the given path on shutdown, and load on starting.
71
+
72
+ ## ChangeLog
73
+
74
+ See [CHANGELOG.md](CHANGELOG.md) for details.
75
+
76
+ ## Contributing
77
+
78
+ 1. Fork it
79
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
80
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
81
+ 4. Push to the branch (`git push origin my-new-feature`)
82
+ 5. Create new [Pull Request](../../pull/new/master)
83
+
84
+ ## Copyright
85
+
86
+ Copyright (c) 2013 Naotoshi Seo. See [LICENSE](LICENSE) for details.
87
+
data/Rakefile ADDED
@@ -0,0 +1,15 @@
1
+ # encoding: utf-8
2
+ require "bundler/gem_tasks"
3
+
4
+ require 'rspec/core'
5
+ require 'rspec/core/rake_task'
6
+ RSpec::Core::RakeTask.new(:spec) do |spec|
7
+ spec.pattern = FileList['spec/**/*_spec.rb']
8
+ end
9
+ task :default => :spec
10
+
11
+ desc 'Open an irb session preloaded with the gem library'
12
+ task :console do
13
+ sh 'irb -rubygems -I lib'
14
+ end
15
+ task :c => :console
@@ -0,0 +1,26 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+
4
+ Gem::Specification.new do |s|
5
+ s.name = "fluent-plugin-stats-notifier"
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-stats-notifier"
10
+ s.summary = "Fluentd plugin to calculate statistics and then thresholding"
11
+ s.description = s.summary
12
+ s.licenses = ["MIT"]
13
+
14
+ s.rubyforge_project = "fluent-plugin-stats-notifier"
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 "rspec"
24
+ s.add_development_dependency "pry"
25
+ s.add_development_dependency "pry-nav"
26
+ end
@@ -0,0 +1,209 @@
1
+ # encoding: UTF-8
2
+ class Fluent::StatsNotifierOutput < Fluent::Output
3
+ Fluent::Plugin.register_output('stats_notifier', self)
4
+
5
+ def initialize
6
+ super
7
+ require 'pathname'
8
+ end
9
+
10
+ config_param :target_key, :string
11
+ config_param :interval, :time, :default => 5
12
+ config_param :less_than, :float, :default => nil
13
+ config_param :less_equal, :float, :default => nil
14
+ config_param :greater_than, :float, :default => nil
15
+ config_param :greater_equal, :float, :default => nil
16
+ config_param :compare_with, :string, :default => "max"
17
+ config_param :tag, :string
18
+ config_param :store_file, :string, :default => nil
19
+
20
+ attr_accessor :counts
21
+ attr_accessor :matches
22
+ attr_accessor :saved_duration
23
+ attr_accessor :saved_at
24
+ attr_accessor :last_checked
25
+
26
+ def configure(conf)
27
+ super
28
+
29
+ @interval = @interval.to_i
30
+
31
+ if @less_than and @less_equal
32
+ raise Fluent::ConfigError, "out_stats_notiifer: Only either of `less_than` or `less_equal` can be specified."
33
+ end
34
+ if @greater_than and @greater_equal
35
+ raise Fluent::ConfigError, "out_stats_notiifer: Only either of `greater_than` or `greater_equal` can be specified."
36
+ end
37
+
38
+ case @compare_with
39
+ when "sum"
40
+ @compare_with = :sum
41
+ when "max"
42
+ @compare_with = :max
43
+ when "min"
44
+ @compare_with = :min
45
+ when "avg"
46
+ @compare_with = :avg
47
+ else
48
+ raise Fluent::ConfigError, "out_stats_notiifer: `compare_with` must be one of `sum`, `max`, `min`, `avg`"
49
+ end
50
+
51
+ @counts = {}
52
+ @matches = {}
53
+ @mutex = Mutex.new
54
+ end
55
+
56
+ def start
57
+ super
58
+ load_status(@store_file, @interval) if @store_file
59
+ @watcher = Thread.new(&method(:watcher))
60
+ end
61
+
62
+ def shutdown
63
+ super
64
+ @watcher.terminate
65
+ @watcher.join
66
+ save_status(@store_file) if @store_file
67
+ end
68
+
69
+ # Called when new line comes. This method actually does not emit
70
+ def emit(tag, es, chain)
71
+ key = @target_key
72
+
73
+ # stats
74
+ count = 0; matches = {}
75
+ es.each do |time,record|
76
+ if record[key]
77
+ # @todo: make an option for statsuation in the same tag. now only sum is supported
78
+ matches[key] = (matches[key] ? matches[key] + record[key] : record[key])
79
+ end
80
+ count += 1
81
+ end
82
+
83
+ # thread safe merge
84
+ @counts[tag] ||= 0
85
+ @matches[tag] ||= {}
86
+ @mutex.synchronize do
87
+ if matches[key]
88
+ # @todo: make an option for statsuation in the same tag. now only sum is supported
89
+ @matches[tag][key] = (@matches[tag][key] ? @matches[tag][key] + matches[key] : matches[key])
90
+ end
91
+ @counts[tag] += count
92
+ end
93
+
94
+ chain.next
95
+ rescue => e
96
+ $log.warn "#{e.class} #{e.message} #{e.backtrace.first}"
97
+ end
98
+
99
+ # thread callback
100
+ def watcher
101
+ # instance variable, and public accessable, for test
102
+ @last_checked = Fluent::Engine.now
103
+ # skip the passed time when loading @counts form file
104
+ @last_checked -= @passed_time if @passed_time
105
+ while true
106
+ sleep 0.5
107
+ begin
108
+ if Fluent::Engine.now - @last_checked >= @interval
109
+ now = Fluent::Engine.now
110
+ flush_emit(now - @last_checked)
111
+ @last_checked = now
112
+ end
113
+ rescue => e
114
+ $log.warn "#{e.class} #{e.message} #{e.backtrace.first}"
115
+ end
116
+ end
117
+ end
118
+
119
+ # This method is the real one to emit
120
+ def flush_emit(step)
121
+ time = Fluent::Engine.now
122
+ flushed_counts, flushed_matches, @counts, @matches = @counts, @matches, {}, {}
123
+
124
+ output = generate_output(flushed_counts, flushed_matches)
125
+ Fluent::Engine.emit(@tag, time, output) if output
126
+ end
127
+
128
+ def generate_output(counts, matches)
129
+ values = matches.values.map {|match| match[@target_key] }.compact
130
+
131
+ case @compare_with
132
+ when :sum
133
+ target_value = values.inject(:+)
134
+ when :max
135
+ target_value = values.max
136
+ when :min
137
+ target_value = values.min
138
+ when :avg
139
+ target_value = values.inject(:+) / values.count unless values.empty?
140
+ end
141
+
142
+ return nil if target_value.nil?
143
+ return nil if target_value == 0 # ignore 0 because standby nodes receive 0 message usually
144
+ return nil if @less_than and @less_than <= target_value
145
+ return nil if @less_equal and @less_equal < target_value
146
+ return nil if @greater_than and target_value <= @greater_than
147
+ return nil if @greater_equal and target_value < @greater_equal
148
+
149
+ output = {}
150
+ output[@target_key] = target_value
151
+ output
152
+ end
153
+
154
+ # Store internal status into a file
155
+ #
156
+ # @param [String] file_path
157
+ def save_status(file_path)
158
+ return unless file_path
159
+
160
+ begin
161
+ Pathname.new(file_path).open('wb') do |f|
162
+ @saved_at = Fluent::Engine.now
163
+ @saved_duration = @saved_at - @last_checked
164
+ Marshal.dump({
165
+ :counts => @counts,
166
+ :matches => @matches,
167
+ :saved_at => @saved_at,
168
+ :saved_duration => @saved_duration,
169
+ :target_key => @target_key,
170
+ }, f)
171
+ end
172
+ rescue => e
173
+ $log.warn "out_stats_notifier: Can't write store_file #{e.class} #{e.message}"
174
+ end
175
+ end
176
+
177
+ # Load internal status from a file
178
+ #
179
+ # @param [String] file_path
180
+ # @param [Interger] interval
181
+ def load_status(file_path, interval)
182
+ return unless (f = Pathname.new(file_path)).exist?
183
+
184
+ begin
185
+ f.open('rb') do |f|
186
+ stored = Marshal.load(f)
187
+ if stored[:target_key] == @target_key
188
+
189
+ if Fluent::Engine.now <= stored[:saved_at] + interval
190
+ @counts = stored[:counts]
191
+ @matches = stored[:matches]
192
+ @saved_at = stored[:saved_at]
193
+ @saved_duration = stored[:saved_duration]
194
+
195
+ # skip the saved duration to continue counting
196
+ @last_checked = Fluent::Engine.now - @saved_duration
197
+ else
198
+ $log.warn "out_stats_notifier: stored data is outdated. ignore stored data"
199
+ end
200
+ else
201
+ $log.warn "out_stats_notiifer: configuration param was changed. ignore stored data"
202
+ end
203
+ end
204
+ rescue => e
205
+ $log.warn "out_stats_notifier: Can't load store_file #{e.class} #{e.message}"
206
+ end
207
+ end
208
+
209
+ end
@@ -0,0 +1,294 @@
1
+ # encoding: UTF-8
2
+ require_relative 'spec_helper'
3
+
4
+ class Fluent::Test::OutputTestDriver
5
+ def emit_with_tag(record, time=Time.now, tag = nil)
6
+ @tag = tag if tag
7
+ emit(record, time)
8
+ end
9
+ end
10
+
11
+ describe Fluent::StatsNotifierOutput do
12
+ before { Fluent::Test.setup }
13
+ CONFIG = %[
14
+ target_key 5xx_count
15
+ tag foo
16
+ ]
17
+ let(:tag) { 'foo.bar' }
18
+ let(:driver) { Fluent::Test::OutputTestDriver.new(Fluent::StatsNotifierOutput, tag).configure(config) }
19
+
20
+ describe 'test configure' do
21
+ describe 'bad configuration' do
22
+ context "nothing" do
23
+ let(:config) { '' }
24
+ it { expect { driver }.to raise_error(Fluent::ConfigError) }
25
+ end
26
+
27
+ context "less_than and less_equal" do
28
+ let(:config) { CONFIG + %[less_than 2 \n less_equal 3] }
29
+ it { expect { driver }.to raise_error(Fluent::ConfigError) }
30
+ end
31
+
32
+ context "greater_than and greater_equal" do
33
+ let(:config) { CONFIG + %[greater_than 2 \n greater_equal 3] }
34
+ it { expect { driver }.to raise_error(Fluent::ConfigError) }
35
+ end
36
+ end
37
+
38
+ describe 'good configuration' do
39
+ context 'required' do
40
+ let(:config) { CONFIG }
41
+ it { expect { driver }.to_not raise_error }
42
+ end
43
+ end
44
+ end
45
+
46
+ describe 'test emit' do
47
+ let(:time) { Time.now.to_i }
48
+ let(:messages) do
49
+ [
50
+ {"4xx_count"=>1,"5xx_count"=>2,"reqtime_max"=>6,"reqtime_min"=>1,"reqtime_avg"=>3},
51
+ {"4xx_count"=>2,"5xx_count"=>2,"reqtime_max"=>5,"reqtime_min"=>2,"reqtime_avg"=>2},
52
+ {"4xx_count"=>3,"5xx_count"=>2,"reqtime_max"=>1,"reqtime_min"=>3,"reqtime_avg"=>4},
53
+ ]
54
+ end
55
+ let(:emit) do
56
+ driver.run { messages.each {|message| driver.emit(message, time) } }
57
+ driver.instance.flush_emit(0)
58
+ end
59
+ let(:config) { CONFIG } # 5xx_count, sum
60
+ let(:expected) do
61
+ {
62
+ "5xx_count"=>6,
63
+ }
64
+ end
65
+
66
+ context "threshold" do
67
+ context 'no threshold' do # should emit
68
+ before do
69
+ Fluent::Engine.stub(:now).and_return(time)
70
+ Fluent::Engine.should_receive(:emit).with("foo", time, expected)
71
+ end
72
+ it { emit }
73
+ end
74
+
75
+ context 'greather than' do
76
+ let(:config) { CONFIG + %[greater_than 5] }
77
+ before do
78
+ Fluent::Engine.stub(:now).and_return(time)
79
+ Fluent::Engine.should_receive(:emit).with("foo", time, expected)
80
+ end
81
+ it { emit }
82
+ end
83
+
84
+ context 'not greather than' do
85
+ let(:config) { CONFIG + %[greater_than 6] }
86
+ before do
87
+ Fluent::Engine.stub(:now).and_return(time)
88
+ Fluent::Engine.should_not_receive(:emit)
89
+ end
90
+ it { emit }
91
+ end
92
+
93
+ context 'greather than or equal to' do
94
+ let(:config) { CONFIG + %[greater_equal 6] }
95
+ before do
96
+ Fluent::Engine.stub(:now).and_return(time)
97
+ Fluent::Engine.should_receive(:emit).with("foo", time, expected)
98
+ end
99
+ it { emit }
100
+ end
101
+
102
+ context 'not greather than or equal to' do
103
+ let(:config) { CONFIG + %[greater_equal 7] }
104
+ before do
105
+ Fluent::Engine.stub(:now).and_return(time)
106
+ Fluent::Engine.should_not_receive(:emit)
107
+ end
108
+ it { emit }
109
+ end
110
+
111
+ context 'less than or equal to' do
112
+ let(:config) { CONFIG + %[less_equal 6] }
113
+ before do
114
+ Fluent::Engine.stub(:now).and_return(time)
115
+ Fluent::Engine.should_receive(:emit).with("foo", time, expected)
116
+ end
117
+ it { emit }
118
+ end
119
+
120
+ context 'not less than or equal to' do
121
+ let(:config) { CONFIG + %[less_equal 5] }
122
+ before do
123
+ Fluent::Engine.stub(:now).and_return(time)
124
+ Fluent::Engine.should_not_receive(:emit)
125
+ end
126
+ it { emit }
127
+ end
128
+
129
+ context 'between' do
130
+ let(:config) { CONFIG + %[greater_equal 1 \n less_equal 9] }
131
+ before do
132
+ Fluent::Engine.stub(:now).and_return(time)
133
+ Fluent::Engine.should_receive(:emit).with("foo", time, expected)
134
+ end
135
+ it { emit }
136
+ end
137
+
138
+ context 'not between' do
139
+ let(:config) { CONFIG + %[greater_equal 1 \n less_equal 4] }
140
+ before do
141
+ Fluent::Engine.stub(:now).and_return(time)
142
+ Fluent::Engine.should_not_receive(:emit)
143
+ end
144
+ it { emit }
145
+ end
146
+ end
147
+
148
+ context 'compare_with' do
149
+ let(:emit) do
150
+ driver.run do
151
+ driver.emit_with_tag({"5xx_count"=>2}, time, 'foo.bar1')
152
+ driver.emit_with_tag({"5xx_count"=>6}, time, 'foo.bar2')
153
+ end
154
+ driver.instance.flush_emit(0)
155
+ end
156
+
157
+ context 'avg' do
158
+ let(:config) { CONFIG + %[less_equal 4 \n compare_with avg] }
159
+ let(:expected) do
160
+ {
161
+ "5xx_count" => 4.0
162
+ }
163
+ end
164
+ before do
165
+ Fluent::Engine.stub(:now).and_return(time)
166
+ Fluent::Engine.should_receive(:emit).with("foo", time, expected)
167
+ end
168
+ it { emit }
169
+ end
170
+
171
+ context 'sum' do
172
+ let(:config) { CONFIG + %[less_equal 8 \n compare_with sum] }
173
+ let(:expected) do
174
+ {
175
+ "5xx_count" => 8.0
176
+ }
177
+ end
178
+ before do
179
+ Fluent::Engine.stub(:now).and_return(time)
180
+ Fluent::Engine.should_receive(:emit).with("foo", time, expected)
181
+ end
182
+ it { emit }
183
+ end
184
+
185
+ context 'min' do
186
+ let(:config) { CONFIG + %[less_equal 2 \n compare_with min] }
187
+ let(:expected) do
188
+ {
189
+ "5xx_count" => 2.0
190
+ }
191
+ end
192
+ before do
193
+ Fluent::Engine.stub(:now).and_return(time)
194
+ Fluent::Engine.should_receive(:emit).with("foo", time, expected)
195
+ end
196
+ it { emit }
197
+ end
198
+
199
+ context 'max' do
200
+ let(:config) { CONFIG + %[less_equal 6 \n compare_with max] }
201
+ let(:expected) do
202
+ {
203
+ "5xx_count" => 6.0
204
+ }
205
+ end
206
+ before do
207
+ Fluent::Engine.stub(:now).and_return(time)
208
+ Fluent::Engine.should_receive(:emit).with("foo", time, expected)
209
+ end
210
+ it { emit }
211
+ end
212
+ end
213
+
214
+ context 'abnormal case (no data)' do
215
+ let(:emit) do
216
+ driver.run do
217
+ end
218
+ driver.instance.flush_emit(0)
219
+ end
220
+
221
+ context 'avg' do
222
+ let(:config) { CONFIG + %[less_equal 4 \n compare_with avg] }
223
+ before do
224
+ Fluent::Engine.stub(:now).and_return(time)
225
+ Fluent::Engine.should_not_receive(:emit)
226
+ end
227
+ it { emit }
228
+ end
229
+
230
+ context 'sum' do
231
+ let(:config) { CONFIG + %[less_equal 8 \n compare_with sum] }
232
+ before do
233
+ Fluent::Engine.stub(:now).and_return(time)
234
+ Fluent::Engine.should_not_receive(:emit)
235
+ end
236
+ it { emit }
237
+ end
238
+
239
+ context 'min' do
240
+ let(:config) { CONFIG + %[less_equal 2 \n compare_with min] }
241
+ before do
242
+ Fluent::Engine.stub(:now).and_return(time)
243
+ Fluent::Engine.should_not_receive(:emit)
244
+ end
245
+ it { emit }
246
+ end
247
+
248
+ context 'max' do
249
+ let(:config) { CONFIG + %[less_equal 6 \n compare_with max] }
250
+ before do
251
+ Fluent::Engine.stub(:now).and_return(time)
252
+ Fluent::Engine.should_not_receive(:emit)
253
+ end
254
+ it { emit }
255
+ end
256
+ end
257
+
258
+ describe "store_file" do
259
+ let(:store_file) do
260
+ dirname = "tmp"
261
+ Dir.mkdir dirname unless Dir.exist? dirname
262
+ filename = "#{dirname}/test.dat"
263
+ File.unlink filename if File.exist? filename
264
+ filename
265
+ end
266
+ let(:config) { CONFIG + %[greater_equal 0 \n store_file #{store_file}] }
267
+
268
+ it 'stored_data and loaded_data should equal' do
269
+ driver.run { messages.each {|message| driver.emit(message, time) } }
270
+ driver.instance.shutdown
271
+ stored_counts = driver.instance.counts
272
+ stored_matches = driver.instance.matches
273
+ stored_saved_at = driver.instance.saved_at
274
+ stored_saved_duration = driver.instance.saved_duration
275
+ driver.instance.counts = {}
276
+ driver.instance.matches = {}
277
+ driver.instance.saved_at = nil
278
+ driver.instance.saved_duration = nil
279
+
280
+ driver.instance.start
281
+ loaded_counts = driver.instance.counts
282
+ loaded_matches = driver.instance.matches
283
+ loaded_saved_at = driver.instance.saved_at
284
+ loaded_saved_duration = driver.instance.saved_duration
285
+
286
+ loaded_counts.should == stored_counts
287
+ loaded_matches.should == stored_matches
288
+ loaded_saved_at.should == stored_saved_at
289
+ loaded_saved_duration.should == stored_saved_duration
290
+ end
291
+ end
292
+ end
293
+ end
294
+
@@ -0,0 +1,16 @@
1
+ # encoding: UTF-8
2
+ require 'rubygems'
3
+ require 'bundler'
4
+ Bundler.setup(:default, :test)
5
+ Bundler.require(:default, :test)
6
+
7
+ #require 'coveralls'
8
+ #Coveralls.wear!
9
+
10
+ require 'fluent/test'
11
+ require 'rspec'
12
+ require 'pry'
13
+
14
+ $TESTING=true
15
+ $:.unshift File.join(File.dirname(__FILE__), '..', 'lib')
16
+ require 'fluent/plugin/out_stats_notifier'
metadata ADDED
@@ -0,0 +1,129 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: fluent-plugin-stats-notifier
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: 2013-12-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: 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
+ - !ruby/object:Gem::Dependency
56
+ name: pry
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
+ - !ruby/object:Gem::Dependency
70
+ name: pry-nav
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - '>='
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - '>='
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ description: Fluentd plugin to calculate statistics and then thresholding
84
+ email:
85
+ - sonots@gmail.com
86
+ executables: []
87
+ extensions: []
88
+ extra_rdoc_files: []
89
+ files:
90
+ - .coveralls.yml
91
+ - .gitignore
92
+ - .rdebugrc
93
+ - .rspec
94
+ - .travis.yml
95
+ - CHANGELOG.md
96
+ - Gemfile
97
+ - README.md
98
+ - Rakefile
99
+ - fluent-plugin-stats-notifier.gemspec
100
+ - lib/fluent/plugin/out_stats_notifier.rb
101
+ - spec/out_stats_notifier_spec.rb
102
+ - spec/spec_helper.rb
103
+ homepage: https://github.com/sonots/fluent-plugin-stats-notifier
104
+ licenses:
105
+ - MIT
106
+ metadata: {}
107
+ post_install_message:
108
+ rdoc_options: []
109
+ require_paths:
110
+ - lib
111
+ required_ruby_version: !ruby/object:Gem::Requirement
112
+ requirements:
113
+ - - '>='
114
+ - !ruby/object:Gem::Version
115
+ version: '0'
116
+ required_rubygems_version: !ruby/object:Gem::Requirement
117
+ requirements:
118
+ - - '>='
119
+ - !ruby/object:Gem::Version
120
+ version: '0'
121
+ requirements: []
122
+ rubyforge_project: fluent-plugin-stats-notifier
123
+ rubygems_version: 2.0.3
124
+ signing_key:
125
+ specification_version: 4
126
+ summary: Fluentd plugin to calculate statistics and then thresholding
127
+ test_files:
128
+ - spec/out_stats_notifier_spec.rb
129
+ - spec/spec_helper.rb