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 +4 -4
- data/CHANGELOG.md +6 -0
- data/Gemfile +0 -1
- data/README.md +41 -3
- data/fluent-plugin-calc.gemspec +2 -2
- data/lib/fluent/plugin/out_calc.rb +132 -26
- data/spec/out_calc_spec.rb +77 -6
- data/spec/spec_helper.rb +0 -3
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: fb6d86f52bcefcd6d104c356cc6df3b707196ec8
|
4
|
+
data.tar.gz: 03b34ce3169a1f29c08823d1cfd8f2850822d6c9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1960a83a0a998933536bf6ea48c6129692258849e474f2ad701865c081b6fc268bdfc01e4d3591cbedc13b414a08837ea68fb6417f3097e49f0ba682c1e9fa0b
|
7
|
+
data.tar.gz: 6b97b056a3654180cbb8530e7744f75b606380d3cb513813f8b98a5327cb92fecf58ee69c49a5424025d48656533ebf2ca95aeb4c0cb9a452f59e48c42149061
|
data/CHANGELOG.md
CHANGED
data/Gemfile
CHANGED
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)
|
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
|
-
|
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":
|
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.
|
data/fluent-plugin-calc.gemspec
CHANGED
@@ -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.
|
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
|
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[
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
matches[key] = (matches[
|
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
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
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
|
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 =
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
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
|
data/spec/out_calc_spec.rb
CHANGED
@@ -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
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.
|
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-
|
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:
|
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:
|