fluent-plugin-calc 0.0.3 → 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: c1a0fa5a4f57cb4b44e75759fdf27f0db87fe498
4
- data.tar.gz: 994beab4aca4623426effc9fbd6d1d0e8dfcaf91
3
+ metadata.gz: fb6d86f52bcefcd6d104c356cc6df3b707196ec8
4
+ data.tar.gz: 03b34ce3169a1f29c08823d1cfd8f2850822d6c9
5
5
  SHA512:
6
- metadata.gz: e1a71b149904a8a9768d32ca86a1f632f2f31dad02a95bc421323db9807b014e10abe3f7c47bbec675982fcebc3db386131286f10b182af6406ad7baf4858e8a
7
- data.tar.gz: b8b9562c574be42c7e984b4d7c67ad9064a8009984389890d4d336cfe95fa803882c2d461ea74bcebd5b25e9e35eb5eb39368e863cb2aca9d77a6192376bde7f
6
+ metadata.gz: 1960a83a0a998933536bf6ea48c6129692258849e474f2ad701865c081b6fc268bdfc01e4d3591cbedc13b414a08837ea68fb6417f3097e49f0ba682c1e9fa0b
7
+ data.tar.gz: 6b97b056a3654180cbb8530e7744f75b606380d3cb513813f8b98a5327cb92fecf58ee69c49a5424025d48656533ebf2ca95aeb4c0cb9a452f59e48c42149061
data/CHANGELOG.md CHANGED
@@ -1,3 +1,9 @@
1
+ ## 0.0.4 (2013/08/30)
2
+
3
+ Enhancement:
4
+
5
+ - add `sum_suffix`, `max_suffix`, `min_suffix`, `avg_suffix`.
6
+
1
7
  ## 0.0.2 (2013/05/06)
2
8
 
3
9
  Bugfixes:
data/Gemfile CHANGED
@@ -1,4 +1,3 @@
1
1
  source "http://rubygems.org"
2
2
 
3
- gem 'simplecov', git: 'https://github.com/colszowka/simplecov.git'
4
3
  gemspec
data/README.md CHANGED
@@ -1,10 +1,12 @@
1
- # fluent-plugin-calc [![Build Status](https://secure.travis-ci.org/sonots/fluent-plugin-calc.png?branch=master)](http://travis-ci.org/sonots/fluent-plugin-calc) [![Dependency Status](https://gemnasium.com/sonots/fluent-plugin-calc.png)](https://gemnasium.com/sonots/fluent-plugin-calc) [![Coverage Status](https://coveralls.io/repos/sonots/fluent-plugin-calc/badge.png?branch=master)](https://coveralls.io/r/sonots/fluent-plugin-calc)
1
+ # fluent-plugin-calc [![Build Status](https://secure.travis-ci.org/sonots/fluent-plugin-calc.png?branch=master)](http://travis-ci.org/sonots/fluent-plugin-calc) [![Dependency Status](https://gemnasium.com/sonots/fluent-plugin-calc.png)](https://gemnasium.com/sonots/fluent-plugin-calc)
2
2
 
3
3
  Simple fluentd plugin to calculate messages.
4
4
 
5
5
  ## Configuration
6
6
 
7
- Example (sum for xxx_count, max for xxx_max, min for xxx_min, avg for xxx_avg):
7
+ Example1)
8
+
9
+ sum for xxx_count, max for xxx_max, min for xxx_min, avg for xxx_avg
8
10
 
9
11
  <match foo.**>
10
12
  type calc
@@ -24,7 +26,35 @@ Assuming following inputs are coming:
24
26
 
25
27
  then output bocomes as belows:
26
28
 
27
- calc.foo.bar: {"4xx_count":5,"5xx_count":4","reqtime_max":24831,"reqtime_min":82,"reqtime_avg":270.46}
29
+ calc.foo.bar: {"4xx_count":5,"5xx_count":4","reqtime_max":24831,"reqtime_min":10,"reqtime_avg":270.46}
30
+
31
+ Example2)
32
+
33
+ sum, max, min, avg for the same key
34
+
35
+ <match foo.**>
36
+ type calc
37
+ interval 5s
38
+ add_tag_prefix calc
39
+
40
+ sum ^reqtime$
41
+ max ^reqtime$
42
+ min ^reqtime$
43
+ avg ^reqtime$
44
+ sum_suffix _sum
45
+ max_suffix _max
46
+ min_suffix _min
47
+ avg_suffix _avg
48
+ </match>
49
+
50
+ Assuming following inputs are coming:
51
+
52
+ foo.bar: {"reqtime":1.000}
53
+ foo.bar: {"reqtime":2.000}
54
+
55
+ then output bocomes as belows:
56
+
57
+ calc.foo.bar: {"reqtime_sum":3.000,"reqtime_max":2.000,"reqtime_min":1.000,"reqtime_avg":1.500}
28
58
 
29
59
  ## Parameters
30
60
 
@@ -32,6 +62,10 @@ then output bocomes as belows:
32
62
 
33
63
  Calculation. Specify input keys by a regular expression
34
64
 
65
+ - sum\_suffix, min\_suffix, max\_suffix, avg\_suffix
66
+
67
+ Add a suffix to keys of the output record
68
+
35
69
  - interval
36
70
 
37
71
  The interval to calculate in seconds. Default is 5s.
@@ -48,6 +82,10 @@ then output bocomes as belows:
48
82
 
49
83
  Calculate by each `tag` or `all`. The default value is `tag`.
50
84
 
85
+ - store_file
86
+
87
+ Store internal data into a file of the given path on shutdown, and load on starting.
88
+
51
89
  ## ChangeLog
52
90
 
53
91
  See [CHANGELOG.md](CHANGELOG.md) for details.
@@ -3,7 +3,7 @@ $:.push File.expand_path("../lib", __FILE__)
3
3
 
4
4
  Gem::Specification.new do |s|
5
5
  s.name = "fluent-plugin-calc"
6
- s.version = "0.0.3"
6
+ s.version = "0.0.4"
7
7
  s.authors = ["Naotoshi SEO"]
8
8
  s.email = ["sonots@gmail.com"]
9
9
  s.homepage = "https://github.com/sonots/fluent-plugin-calc"
@@ -21,5 +21,5 @@ Gem::Specification.new do |s|
21
21
  s.add_development_dependency "rake"
22
22
  s.add_development_dependency "rspec"
23
23
  s.add_development_dependency "pry"
24
- s.add_development_dependency 'coveralls'
24
+ s.add_development_dependency "pry-nav"
25
25
  end
@@ -2,16 +2,29 @@
2
2
  class Fluent::CalcOutput < Fluent::Output
3
3
  Fluent::Plugin.register_output('calc', self)
4
4
 
5
+ def initialize
6
+ super
7
+ require 'pathname'
8
+ end
9
+
5
10
  config_param :sum, :string, :default => nil
6
11
  config_param :max, :string, :default => nil
7
12
  config_param :min, :string, :default => nil
8
13
  config_param :avg, :string, :default => nil
14
+ config_param :sum_suffix, :string, :default => ""
15
+ config_param :max_suffix, :string, :default => ""
16
+ config_param :min_suffix, :string, :default => ""
17
+ config_param :avg_suffix, :string, :default => ""
9
18
  config_param :interval, :time, :default => 5
10
19
  config_param :tag, :string, :default => nil
11
20
  config_param :add_tag_prefix, :string, :default => 'calc'
12
21
  config_param :aggregate, :string, :default => 'tag'
22
+ config_param :store_file, :string, :default => nil
13
23
 
24
+ attr_accessor :counts
14
25
  attr_accessor :matches
26
+ attr_accessor :saved_duration
27
+ attr_accessor :saved_at
15
28
  attr_accessor :last_checked
16
29
 
17
30
  def configure(conf)
@@ -39,6 +52,7 @@ class Fluent::CalcOutput < Fluent::Output
39
52
 
40
53
  def start
41
54
  super
55
+ load_status(@store_file, @interval) if @store_file
42
56
  @watcher = Thread.new(&method(:watcher))
43
57
  end
44
58
 
@@ -46,6 +60,7 @@ class Fluent::CalcOutput < Fluent::Output
46
60
  super
47
61
  @watcher.terminate
48
62
  @watcher.join
63
+ save_status(@store_file) if @store_file
49
64
  end
50
65
 
51
66
  # Called when new line comes. This method actually does not emit
@@ -53,17 +68,20 @@ class Fluent::CalcOutput < Fluent::Output
53
68
  tag = 'all' if @aggregate == 'all'
54
69
 
55
70
  # calc
56
- count = 0; matches = {}
57
- es.each do |time,record|
71
+ count = 0; matches = { :sum => {}, :max => {}, :min => {}, :avg => {} }
72
+ es.each do |time, record|
58
73
  record.keys.each do |key|
59
74
  if @sum and @sum.match(key)
60
- matches[key] = (matches[key] ? matches[key] + record[key] : record[key])
61
- elsif @max and @max.match(key)
62
- matches[key] = (matches[key] ? [matches[key], record[key]].max : record[key])
63
- elsif @min and @min.match(key)
64
- matches[key] = (matches[key] ? [matches[key], record[key]].min : record[key])
65
- elsif @avg and @avg.match(key)
66
- matches[key] = (matches[key] ? matches[key] + record[key] : record[key]) # sum yet
75
+ matches[:sum][key] = sum(matches[:sum][key], record[key])
76
+ end
77
+ if @max and @max.match(key)
78
+ matches[:max][key] = max(matches[:max][key], record[key])
79
+ end
80
+ if @min and @min.match(key)
81
+ matches[:min][key] = min(matches[:min][key], record[key])
82
+ end
83
+ if @avg and @avg.match(key)
84
+ matches[:avg][key] = sum(matches[:avg][key], record[key]) # sum yet
67
85
  end
68
86
  end
69
87
  count += 1
@@ -71,18 +89,19 @@ class Fluent::CalcOutput < Fluent::Output
71
89
 
72
90
  # thread safe merge
73
91
  @counts[tag] ||= 0
74
- @matches[tag] ||= {}
92
+ @matches[tag] ||= { :sum => {}, :max => {}, :min => {}, :avg => {} }
75
93
  @mutex.synchronize do
76
- matches.keys.each do |key|
77
- if @sum and @sum.match(key)
78
- @matches[tag][key] = (@matches[tag][key] ? @matches[tag][key] + matches[key] : matches[key])
79
- elsif @max and @max.match(key)
80
- @matches[tag][key] = (@matches[tag][key] ? [@matches[tag][key], matches[key]].max : matches[key])
81
- elsif @min and @min.match(key)
82
- @matches[tag][key] = (@matches[tag][key] ? [@matches[tag][key], matches[key]].min : matches[key])
83
- elsif @avg and @avg.match(key)
84
- @matches[tag][key] = (@matches[tag][key] ? @matches[tag][key] + matches[key] : matches[key]) # sum yet
85
- end
94
+ matches[:sum].keys.each do |key|
95
+ @matches[tag][:sum][key] = sum(@matches[tag][:sum][key], matches[:sum][key])
96
+ end
97
+ matches[:max].keys.each do |key|
98
+ @matches[tag][:max][key] = max(@matches[tag][:max][key], matches[:max][key])
99
+ end
100
+ matches[:min].keys.each do |key|
101
+ @matches[tag][:min][key] = min(@matches[tag][:min][key], matches[:min][key])
102
+ end
103
+ matches[:avg].keys.each do |key|
104
+ @matches[tag][:avg][key] = sum(@matches[tag][:avg][key], matches[:avg][key]) # sum yet
86
105
  end
87
106
  @counts[tag] += count
88
107
  end
@@ -95,7 +114,7 @@ class Fluent::CalcOutput < Fluent::Output
95
114
  # thread callback
96
115
  def watcher
97
116
  # instance variable, and public accessable, for test
98
- @last_checked = Fluent::Engine.now
117
+ @last_checked ||= Fluent::Engine.now
99
118
  while true
100
119
  sleep 0.5
101
120
  begin
@@ -126,13 +145,100 @@ class Fluent::CalcOutput < Fluent::Output
126
145
 
127
146
  def generate_output(count, matches)
128
147
  return nil if matches.empty?
129
- output = matches.dup
130
- output.keys.each do |key|
131
- if @avg and @avg.match(key)
132
- output[key] = matches[key] / count.to_f # compute avg
133
- end
148
+ output = {}
149
+ matches[:sum].keys.each do |key|
150
+ output[key + @sum_suffix] = matches[:sum][key]
151
+ end
152
+ matches[:max].keys.each do |key|
153
+ output[key + @max_suffix] = matches[:max][key]
154
+ end
155
+ matches[:min].keys.each do |key|
156
+ output[key + @min_suffix] = matches[:min][key]
157
+ end
158
+ matches[:avg].keys.each do |key|
159
+ output[key + @avg_suffix] = matches[:avg][key] / count.to_f # compute avg here
134
160
  end
135
161
  output
136
162
  end
137
163
 
164
+ def sum(a, b)
165
+ [a, b].compact.inject(:+)
166
+ end
167
+
168
+ def max(a, b)
169
+ [a, b].compact.max
170
+ end
171
+
172
+ def min(a, b)
173
+ [a, b].compact.min
174
+ end
175
+
176
+ # Store internal status into a file
177
+ #
178
+ # @param [String] file_path
179
+ def save_status(file_path)
180
+ return unless file_path
181
+
182
+ begin
183
+ Pathname.new(file_path).open('wb') do |f|
184
+ @saved_at = Fluent::Engine.now
185
+ @saved_duration = @saved_at - @last_checked
186
+ Marshal.dump({
187
+ :counts => @counts,
188
+ :matches => @matches,
189
+ :saved_at => @saved_at,
190
+ :saved_duration => @saved_duration,
191
+ :aggregate => @aggregate,
192
+ :sum => @sum,
193
+ :max => @max,
194
+ :min => @min,
195
+ :avg => @avg,
196
+ }, f)
197
+ end
198
+ rescue => e
199
+ $log.warn "out_calc: Can't write store_file #{e.class} #{e.message}"
200
+ end
201
+ end
202
+
203
+ # Load internal status from a file
204
+ #
205
+ # @param [String] file_path
206
+ # @param [Interger] interval
207
+ def load_status(file_path, interval)
208
+ return unless (f = Pathname.new(file_path)).exist?
209
+
210
+ begin
211
+ f.open('rb') do |f|
212
+ stored = Marshal.load(f)
213
+ if stored[:aggregate] == @aggregate and
214
+ stored[:sum] == @sum and
215
+ stored[:max] == @max and
216
+ stored[:min] == @min and
217
+ stored[:avg] == @avg
218
+
219
+ if !stored[:matches].first[1].has_key?(:max)
220
+ $log.warn "out_calc: stored data does not have compatibility with the current version. ignore stored data"
221
+ return
222
+ end
223
+
224
+ if Fluent::Engine.now <= stored[:saved_at] + interval
225
+ @counts = stored[:counts]
226
+ @matches = stored[:matches]
227
+ @saved_at = stored[:saved_at]
228
+ @saved_duration = stored[:saved_duration]
229
+
230
+ # skip the saved duration to continue counting
231
+ @last_checked = Fluent::Engine.now - @saved_duration
232
+ else
233
+ $log.warn "out_calc: stored data is outdated. ignore stored data"
234
+ end
235
+ else
236
+ $log.warn "out_calc: configuration param was changed. ignore stored data"
237
+ end
238
+ end
239
+ rescue => e
240
+ $log.warn "out_calc: Can't load store_file #{e.class} #{e.message}"
241
+ end
242
+ end
243
+
138
244
  end
@@ -78,10 +78,6 @@ describe Fluent::CalcOutput do
78
78
  driver.instance.flush_emit(0)
79
79
  end
80
80
 
81
- context 'interval' do
82
- pending
83
- end
84
-
85
81
  context 'sum/max/min/avg' do
86
82
  let(:config) do
87
83
  CONFIG + %[
@@ -100,15 +96,46 @@ describe Fluent::CalcOutput do
100
96
  it { emit }
101
97
  end
102
98
 
99
+ context 'sum/max/min/avg_suffix' do
100
+ let(:config) do
101
+ CONFIG + %[
102
+ sum ^(reqtime|reqsize)$
103
+ max ^reqtime$
104
+ min ^reqtime$
105
+ avg ^reqtime$
106
+ sum_suffix _sum
107
+ max_suffix _max
108
+ min_suffix _min
109
+ avg_suffix _avg
110
+ ]
111
+ end
112
+ let(:messages) do
113
+ [
114
+ {"reqtime"=>1.000,"reqsize"=>10},
115
+ {"reqtime"=>2.000,"reqsize"=>20},
116
+ ]
117
+ end
118
+ before do
119
+ Fluent::Engine.stub(:now).and_return(time)
120
+ Fluent::Engine.should_receive(:emit).with("calc.#{tag}", time, {
121
+ "reqtime_sum"=>3.000,"reqtime_max"=>2.000,"reqtime_min"=>1.000,"reqtime_avg"=>1.500,"reqsize_sum"=>30
122
+ })
123
+ end
124
+ it { emit }
125
+ end
126
+
103
127
  context 'tag' do
104
128
  let(:config) do
105
129
  CONFIG + %[
106
130
  tag foo
131
+ sum _count$
107
132
  ]
108
133
  end
109
134
  before do
110
135
  Fluent::Engine.stub(:now).and_return(time)
111
- Fluent::Engine.should_receive(:emit).with("foo", time, {})
136
+ Fluent::Engine.should_receive(:emit).with("foo", time, {
137
+ "4xx_count"=>6,"5xx_count"=>6
138
+ })
112
139
  end
113
140
  it { emit }
114
141
  end
@@ -117,11 +144,14 @@ describe Fluent::CalcOutput do
117
144
  let(:config) do
118
145
  CONFIG + %[
119
146
  add_tag_prefix foo
147
+ sum _count$
120
148
  ]
121
149
  end
122
150
  before do
123
151
  Fluent::Engine.stub(:now).and_return(time)
124
- Fluent::Engine.should_receive(:emit).with("foo.#{tag}", time, {})
152
+ Fluent::Engine.should_receive(:emit).with("foo.#{tag}", time, {
153
+ "4xx_count"=>6,"5xx_count"=>6
154
+ })
125
155
  end
126
156
  it { emit }
127
157
  end
@@ -176,6 +206,47 @@ describe Fluent::CalcOutput do
176
206
  it { emit }
177
207
  end
178
208
  end
209
+
210
+ describe "store_file" do
211
+ let(:store_file) do
212
+ dirname = "tmp"
213
+ Dir.mkdir dirname unless Dir.exist? dirname
214
+ filename = "#{dirname}/test.dat"
215
+ File.unlink filename if File.exist? filename
216
+ filename
217
+ end
218
+
219
+ let(:config) do
220
+ CONFIG + %[
221
+ sum _count$
222
+ store_file #{store_file}
223
+ ]
224
+ end
225
+
226
+ it 'stored_data and loaded_data should equal' do
227
+ driver.run { messages.each {|message| driver.emit(message, time) } }
228
+ driver.instance.shutdown
229
+ stored_counts = driver.instance.counts
230
+ stored_matches = driver.instance.matches
231
+ stored_saved_at = driver.instance.saved_at
232
+ stored_saved_duration = driver.instance.saved_duration
233
+ driver.instance.counts = {}
234
+ driver.instance.matches = {}
235
+ driver.instance.saved_at = nil
236
+ driver.instance.saved_duration = nil
237
+
238
+ driver.instance.start
239
+ loaded_counts = driver.instance.counts
240
+ loaded_matches = driver.instance.matches
241
+ loaded_saved_at = driver.instance.saved_at
242
+ loaded_saved_duration = driver.instance.saved_duration
243
+
244
+ loaded_counts.should == stored_counts
245
+ loaded_matches.should == stored_matches
246
+ loaded_saved_at.should == stored_saved_at
247
+ loaded_saved_duration.should == stored_saved_duration
248
+ end
249
+ end
179
250
  end
180
251
  end
181
252
 
data/spec/spec_helper.rb CHANGED
@@ -4,9 +4,6 @@ require 'bundler'
4
4
  Bundler.setup(:default, :test)
5
5
  Bundler.require(:default, :test)
6
6
 
7
- require 'coveralls'
8
- Coveralls.wear!
9
-
10
7
  require 'fluent/test'
11
8
  require 'rspec'
12
9
  require 'pry'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fluent-plugin-calc
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.3
4
+ version: 0.0.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Naotoshi SEO
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-06-18 00:00:00.000000000 Z
11
+ date: 2013-08-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: fluentd
@@ -67,7 +67,7 @@ dependencies:
67
67
  - !ruby/object:Gem::Version
68
68
  version: '0'
69
69
  - !ruby/object:Gem::Dependency
70
- name: coveralls
70
+ name: pry-nav
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
73
  - - '>='
@@ -127,3 +127,4 @@ summary: fluentd plugin to calc messages
127
127
  test_files:
128
128
  - spec/out_calc_spec.rb
129
129
  - spec/spec_helper.rb
130
+ has_rdoc: