fluent-plugin-calc 0.0.3 → 0.0.4

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: 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: