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 +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 [](http://travis-ci.org/sonots/fluent-plugin-calc) [](https://gemnasium.com/sonots/fluent-plugin-calc)
|
1
|
+
# fluent-plugin-calc [](http://travis-ci.org/sonots/fluent-plugin-calc) [](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:
|