fluent-plugin-datacounter 0.1.0
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.
- data/.gitignore +11 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +13 -0
- data/README.rdoc +72 -0
- data/Rakefile +11 -0
- data/example.conf +24 -0
- data/fluent-plugin-datacounter.gemspec +25 -0
- data/lib/fluent/plugin/out_datacounter.rb +185 -0
- data/test/helper.rb +28 -0
- data/test/plugin/test_out_datacounter.rb +205 -0
- metadata +79 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
Copyright (c) 2012- TAGOMORI Satoshi
|
2
|
+
|
3
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
you may not use this file except in compliance with the License.
|
5
|
+
You may obtain a copy of the License at
|
6
|
+
|
7
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
|
9
|
+
Unless required by applicable law or agreed to in writing, software
|
10
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
See the License for the specific language governing permissions and
|
13
|
+
limitations under the License.
|
data/README.rdoc
ADDED
@@ -0,0 +1,72 @@
|
|
1
|
+
= fluent-plugin-datacounter
|
2
|
+
|
3
|
+
== Component
|
4
|
+
|
5
|
+
=== DataCounterOutput
|
6
|
+
|
7
|
+
Count messages with data matches any of specified regexp patterns in specified attribute.
|
8
|
+
|
9
|
+
- Counts per min/hour/day
|
10
|
+
- Counts per second (average every min/hour/day)
|
11
|
+
- Percentage of each pattern in total counts of messages
|
12
|
+
|
13
|
+
DataCounterOutput emits messages contains results data, so you can output these message (with 'datacount' tag by default) to any outputs you want.
|
14
|
+
|
15
|
+
output ex1 (aggragates all inputs): {"pattern1_count":20, "pattern1_rate":0.333, "pattern1_percentage":25.0, "pattern2_count":40, "pattern2_rate":0.666, "pattern2_percentage":50.0, "unmatched_count":20, "unmatched_rate":0.333, "unmatched_percentage":25.0}
|
16
|
+
output ex2 (aggragates per tag): {"test_pattern1_count":10, "test_pattern1_rate":0.333, "test_pattern1_percentage":25.0, "test_pattern2_count":40, "test_pattern2_rate":0.666, "test_pattern2_percentage":50.0, "test_unmatched_count":20, "test_unmatched_rate":0.333, "test_unmatched_percentage":25.0}
|
17
|
+
|
18
|
+
'input_tag_remove_prefix' option available if you want to remove tag prefix from output field names.
|
19
|
+
|
20
|
+
== Configuration
|
21
|
+
|
22
|
+
=== DataCounterOutput
|
23
|
+
|
24
|
+
Count messages that have attribute 'referer' as 'google.com', 'yahoo.com' and 'facebook.com' from all messages matched, per minutes.
|
25
|
+
|
26
|
+
<match accesslog.**>
|
27
|
+
type datacounter
|
28
|
+
unit minute
|
29
|
+
aggregate all
|
30
|
+
count_key referer
|
31
|
+
# patternX: X(1-9)
|
32
|
+
pattern1 google google.com
|
33
|
+
pattern2 yahoo yahoo.com
|
34
|
+
pattern3 facebook facebook.com
|
35
|
+
# but patterns above matches 'this-is-facebookXcom.xxxsite.com' ...
|
36
|
+
</match>
|
37
|
+
|
38
|
+
Or, more exact match pattern, output per tags (default 'aggregate tag'), per hours.
|
39
|
+
|
40
|
+
<match accesslog.**>
|
41
|
+
type datacounter
|
42
|
+
unit hour
|
43
|
+
count_key referer
|
44
|
+
# patternX: X(1-9)
|
45
|
+
pattern1 google ^http://www\.google\.com/.*
|
46
|
+
pattern2 yahoo ^http://www\.yahoo\.com/.*
|
47
|
+
pattern3 twitter ^https://twitter.com/.*
|
48
|
+
</match>
|
49
|
+
|
50
|
+
HTTP status code patterns.
|
51
|
+
|
52
|
+
<match accesslog.**>
|
53
|
+
type datacounter
|
54
|
+
unit min
|
55
|
+
count_key status
|
56
|
+
# patternX: X(1-9)
|
57
|
+
pattern1 2xx ^2\d\d$
|
58
|
+
pattern2 3xx ^3\d\d$
|
59
|
+
pattern3 404 ^404$ # we want only 404 counts...
|
60
|
+
pattern4 4xx ^4\d\d$ # pattern4 doesn't matches messages matches pattern[123]
|
61
|
+
pattern5 5xx ^5\d\d$
|
62
|
+
</match>
|
63
|
+
|
64
|
+
== TODO
|
65
|
+
|
66
|
+
- consider what to do next
|
67
|
+
- patches welcome!
|
68
|
+
|
69
|
+
== Copyright
|
70
|
+
|
71
|
+
Copyright:: Copyright (c) 2012- TAGOMORI Satoshi (tagomoris)
|
72
|
+
License:: Apache License, Version 2.0
|
data/Rakefile
ADDED
data/example.conf
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
<source>
|
2
|
+
type forward
|
3
|
+
</source>
|
4
|
+
|
5
|
+
# out_sampling_filter strongly recommended
|
6
|
+
<match test.**>
|
7
|
+
type sampling_filter
|
8
|
+
interval 2
|
9
|
+
remove_prefix test
|
10
|
+
add_prefix sampled
|
11
|
+
</match>
|
12
|
+
|
13
|
+
<match sampled.**>
|
14
|
+
type datacounter
|
15
|
+
tag result
|
16
|
+
aggregate all
|
17
|
+
count_key field
|
18
|
+
pattern1 ok ^OK
|
19
|
+
pattern2 ng ^NG
|
20
|
+
</match>
|
21
|
+
|
22
|
+
<match result>
|
23
|
+
type stdout
|
24
|
+
</match>
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |s|
|
5
|
+
s.name = "fluent-plugin-datacounter"
|
6
|
+
s.version = "0.1.0"
|
7
|
+
s.authors = ["TAGOMORI Satoshi"]
|
8
|
+
s.email = ["tagomoris@gmail.com"]
|
9
|
+
s.homepage = "https://github.com/tagomoris/fluent-plugin-datacounter"
|
10
|
+
s.summary = %q{Output filter plugin to count messages that matches specified conditions}
|
11
|
+
s.description = %q{Output filter plugin to count messages that matches specified conditions}
|
12
|
+
|
13
|
+
s.rubyforge_project = "fluent-plugin-datacounter"
|
14
|
+
|
15
|
+
s.files = `git ls-files`.split("\n")
|
16
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
17
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
18
|
+
s.require_paths = ["lib"]
|
19
|
+
|
20
|
+
# specify any dependencies here; for example:
|
21
|
+
# s.add_development_dependency "rspec"
|
22
|
+
# s.add_runtime_dependency "rest-client"
|
23
|
+
s.add_development_dependency "fluentd"
|
24
|
+
s.add_runtime_dependency "fluentd"
|
25
|
+
end
|
@@ -0,0 +1,185 @@
|
|
1
|
+
class Fluent::DataCounterOutput < Fluent::Output
|
2
|
+
Fluent::Plugin.register_output('datacounter', self)
|
3
|
+
|
4
|
+
config_param :unit, :string, :default => 'minute'
|
5
|
+
config_param :aggregate, :string, :default => 'tag'
|
6
|
+
config_param :tag, :string, :default => 'datacount'
|
7
|
+
config_param :input_tag_remove_prefix, :string, :default => nil
|
8
|
+
config_param :count_key, :string
|
9
|
+
|
10
|
+
# pattern0 reserved as unmatched counts
|
11
|
+
config_param :pattern1, :string # string: NAME REGEXP
|
12
|
+
(2..9).each do |i|
|
13
|
+
config_param ('pattern' + i.to_s).to_sym, :string, :default => nil # NAME REGEXP
|
14
|
+
end
|
15
|
+
|
16
|
+
attr_accessor :counts
|
17
|
+
attr_accessor :last_checked
|
18
|
+
|
19
|
+
def configure(conf)
|
20
|
+
super
|
21
|
+
|
22
|
+
@unit = case @unit
|
23
|
+
when 'minute' then :minute
|
24
|
+
when 'hour' then :hour
|
25
|
+
when 'day' then :day
|
26
|
+
else
|
27
|
+
raise Fluent::ConfigError, "flowcounter unit allows minute/hour/day"
|
28
|
+
end
|
29
|
+
@aggregate = case @aggregate
|
30
|
+
when 'tag' then :tag
|
31
|
+
when 'all' then :all
|
32
|
+
else
|
33
|
+
raise Fluent::ConfigError, "flowcounter aggregate allows tag/all"
|
34
|
+
end
|
35
|
+
|
36
|
+
@patterns = [[0, 'unmatched', nil]]
|
37
|
+
pattern_names = ['unmatched']
|
38
|
+
(1..9).each do |i|
|
39
|
+
next unless conf["pattern#{i}"]
|
40
|
+
name,regexp = conf["pattern#{i}"].split(' ', 2)
|
41
|
+
@patterns.push([i, name, Regexp.new(regexp)])
|
42
|
+
pattern_names.push(name)
|
43
|
+
end
|
44
|
+
pattern_index_list = conf.keys.select{|s| s =~ /^pattern\d$/}.map{|v| (/^pattern(\d)$/.match(v))[1].to_i}
|
45
|
+
unless pattern_index_list.reduce(true){|v,i| v and @patterns[i]}
|
46
|
+
raise Fluent::ConfigError, "jump of pattern index found"
|
47
|
+
end
|
48
|
+
unless @patterns.length == pattern_names.uniq.length
|
49
|
+
raise Fluent::ConfigError, "duplicated pattern names"
|
50
|
+
end
|
51
|
+
|
52
|
+
if @input_tag_remove_prefix
|
53
|
+
@removed_prefix_string = @input_tag_remove_prefix + '.'
|
54
|
+
@removed_length = @removed_prefix_string.length
|
55
|
+
end
|
56
|
+
|
57
|
+
@counts = count_initialized
|
58
|
+
end
|
59
|
+
|
60
|
+
def start
|
61
|
+
super
|
62
|
+
@counts = count_initialized
|
63
|
+
start_watch
|
64
|
+
end
|
65
|
+
|
66
|
+
def shutdown
|
67
|
+
super
|
68
|
+
@watcher.terminate
|
69
|
+
@watcher.join
|
70
|
+
end
|
71
|
+
|
72
|
+
def count_initialized(keys=nil)
|
73
|
+
# counts['tag'][num] = count
|
74
|
+
if @aggregate == :all
|
75
|
+
{'all' => ([0] * @patterns.length)}
|
76
|
+
elsif keys
|
77
|
+
values = Array.new(keys.length) {|i|
|
78
|
+
Array.new(@patterns.length){|j| 0 }
|
79
|
+
}
|
80
|
+
Hash[[keys, values].transpose]
|
81
|
+
else
|
82
|
+
{}
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def countups(tag, counts)
|
87
|
+
if @aggregate == :all
|
88
|
+
tag = 'all'
|
89
|
+
end
|
90
|
+
@counts[tag] ||= [0] * @patterns.length
|
91
|
+
|
92
|
+
counts.each_with_index do |count, i|
|
93
|
+
@counts[tag][i] += count
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def stripped_tag(tag)
|
98
|
+
return tag unless @input_tag_remove_prefix
|
99
|
+
return tag[@removed_length..-1] if tag.start_with?(@removed_prefix_string) and tag.length > @removed_length
|
100
|
+
return tag[@removed_length..-1] if tag == @input_tag_remove_prefix
|
101
|
+
tag
|
102
|
+
end
|
103
|
+
|
104
|
+
def generate_output(counts, step)
|
105
|
+
output = {}
|
106
|
+
if @aggregate == :all
|
107
|
+
sum = counts['all'].inject(:+)
|
108
|
+
counts['all'].each_with_index do |count,i|
|
109
|
+
name = @patterns[i][1]
|
110
|
+
output[name + '_count'] = count
|
111
|
+
output[name + '_rate'] = ((count * 100.0) / (1.00 * step)).floor / 100.0
|
112
|
+
output[name + '_percentage'] = count * 100.0 / (1.00 * sum) if sum > 0
|
113
|
+
end
|
114
|
+
return output
|
115
|
+
end
|
116
|
+
|
117
|
+
counts.keys.each do |tag|
|
118
|
+
t = stripped_tag(tag)
|
119
|
+
sum = counts[tag].inject(:+)
|
120
|
+
counts[tag].each_with_index do |count,i|
|
121
|
+
name = @patterns[i][1]
|
122
|
+
output[t + '_' + name + '_count'] = count
|
123
|
+
output[t + '_' + name + '_rate'] = ((count * 100.0) / (1.00 * step)).floor / 100.0
|
124
|
+
output[t + '_' + name + '_percentage'] = count * 100.0 / (1.00 * sum) if sum > 0
|
125
|
+
end
|
126
|
+
end
|
127
|
+
output
|
128
|
+
end
|
129
|
+
|
130
|
+
def flush(step)
|
131
|
+
flushed,@counts = @counts,count_initialized(@counts.keys.dup)
|
132
|
+
generate_output(flushed, step)
|
133
|
+
end
|
134
|
+
|
135
|
+
def flush_emit(step)
|
136
|
+
Fluent::Engine.emit(@tag, Fluent::Engine.now, flush(step))
|
137
|
+
end
|
138
|
+
|
139
|
+
def start_watch
|
140
|
+
# for internal, or tests only
|
141
|
+
@watcher = Thread.new(&method(:watch))
|
142
|
+
end
|
143
|
+
|
144
|
+
def watch
|
145
|
+
# instance variable, and public accessable, for test
|
146
|
+
@last_checked = Fluent::Engine.now
|
147
|
+
tick = case @unit
|
148
|
+
when :minute then 60
|
149
|
+
when :hour then 3600
|
150
|
+
when :day then 86400
|
151
|
+
else
|
152
|
+
raise RuntimeError, "@unit must be one of minute/hour/day"
|
153
|
+
end
|
154
|
+
while true
|
155
|
+
sleep 0.5
|
156
|
+
if Fluent::Engine.now - @last_checked >= tick
|
157
|
+
now = Fluent::Engine.now
|
158
|
+
flush_emit(now - @last_checked)
|
159
|
+
@last_checked = now
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
def emit(tag, es, chain)
|
165
|
+
c = [0] * @patterns.length
|
166
|
+
|
167
|
+
es.each do |time,record|
|
168
|
+
value = record[@count_key]
|
169
|
+
next if value.nil?
|
170
|
+
|
171
|
+
value = value.to_s
|
172
|
+
matched = false
|
173
|
+
@patterns.each do |index, name, regexp|
|
174
|
+
next unless regexp and regexp.match(value)
|
175
|
+
c[index] += 1
|
176
|
+
matched = true
|
177
|
+
break
|
178
|
+
end
|
179
|
+
c[0] += 1 unless matched
|
180
|
+
end
|
181
|
+
countups(tag, c)
|
182
|
+
|
183
|
+
chain.next
|
184
|
+
end
|
185
|
+
end
|
data/test/helper.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler'
|
3
|
+
begin
|
4
|
+
Bundler.setup(:default, :development)
|
5
|
+
rescue Bundler::BundlerError => e
|
6
|
+
$stderr.puts e.message
|
7
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
8
|
+
exit e.status_code
|
9
|
+
end
|
10
|
+
require 'test/unit'
|
11
|
+
|
12
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
13
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
14
|
+
require 'fluent/test'
|
15
|
+
unless ENV.has_key?('VERBOSE')
|
16
|
+
nulllogger = Object.new
|
17
|
+
nulllogger.instance_eval {|obj|
|
18
|
+
def method_missing(method, *args)
|
19
|
+
# pass
|
20
|
+
end
|
21
|
+
}
|
22
|
+
$log = nulllogger
|
23
|
+
end
|
24
|
+
|
25
|
+
require 'fluent/plugin/out_datacounter'
|
26
|
+
|
27
|
+
class Test::Unit::TestCase
|
28
|
+
end
|
@@ -0,0 +1,205 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
class DataCounterOutputTest < Test::Unit::TestCase
|
4
|
+
def setup
|
5
|
+
Fluent::Test.setup
|
6
|
+
end
|
7
|
+
|
8
|
+
CONFIG = %[
|
9
|
+
unit minute
|
10
|
+
aggregate tag
|
11
|
+
input_tag_remove_prefix test
|
12
|
+
count_key target
|
13
|
+
pattern1 status2xx ^2\\d\\d$
|
14
|
+
pattern2 status3xx ^3\\d\\d$
|
15
|
+
pattern3 status4xx ^4\\d\\d$
|
16
|
+
pattern4 status5xx ^5\\d\\d$
|
17
|
+
]
|
18
|
+
|
19
|
+
def create_driver(conf = CONFIG, tag='test.input')
|
20
|
+
Fluent::Test::OutputTestDriver.new(Fluent::DataCounterOutput, tag).configure(conf)
|
21
|
+
end
|
22
|
+
|
23
|
+
def test_configure
|
24
|
+
assert_raise(Fluent::ConfigError) {
|
25
|
+
d = create_driver('')
|
26
|
+
}
|
27
|
+
assert_raise(Fluent::ConfigError) {
|
28
|
+
d = create_driver %[
|
29
|
+
count_key field
|
30
|
+
]
|
31
|
+
}
|
32
|
+
assert_raise(Fluent::ConfigError) {
|
33
|
+
d = create_driver %[
|
34
|
+
pattern1 hoge ^1\\d\\d$
|
35
|
+
]
|
36
|
+
}
|
37
|
+
assert_raise(Fluent::ConfigError) {
|
38
|
+
d = create_driver %[
|
39
|
+
count_key field
|
40
|
+
pattern2 hoge ^1\\d\\d$
|
41
|
+
]
|
42
|
+
}
|
43
|
+
assert_raise(Fluent::ConfigError) {
|
44
|
+
d = create_driver %[
|
45
|
+
count_key field
|
46
|
+
pattern1 hoge ^1\\d\\d$
|
47
|
+
pattern4 pos ^4\\d\\d$
|
48
|
+
]
|
49
|
+
}
|
50
|
+
assert_raise(Fluent::ConfigError) {
|
51
|
+
d = create_driver %[
|
52
|
+
count_key field
|
53
|
+
pattern1 hoge ^1\\d\\d$
|
54
|
+
pattern2 hoge ^4\\d\\d$
|
55
|
+
]
|
56
|
+
}
|
57
|
+
d = create_driver %[
|
58
|
+
count_key field
|
59
|
+
pattern1 ok ^2\\d\\d$
|
60
|
+
]
|
61
|
+
assert_equal :minute, d.instance.unit
|
62
|
+
assert_equal :tag, d.instance.aggregate
|
63
|
+
assert_equal 'datacount', d.instance.tag
|
64
|
+
assert_nil d.instance.input_tag_remove_prefix
|
65
|
+
assert_equal 'field', d.instance.count_key
|
66
|
+
assert_equal 'ok ^2\d\d$', d.instance.pattern1
|
67
|
+
end
|
68
|
+
|
69
|
+
def test_count_initialized
|
70
|
+
d = create_driver %[
|
71
|
+
aggregate all
|
72
|
+
count_key field
|
73
|
+
pattern1 hoge 1\d\d
|
74
|
+
pattern2 moge 2\d\d
|
75
|
+
]
|
76
|
+
assert_equal [0,0,0], d.instance.counts['all']
|
77
|
+
end
|
78
|
+
|
79
|
+
def test_countups
|
80
|
+
d = create_driver
|
81
|
+
assert_nil d.instance.counts['test.input']
|
82
|
+
|
83
|
+
d.instance.countups('test.input', [0, 0, 0, 0, 0])
|
84
|
+
assert_equal [0,0,0,0,0], d.instance.counts['test.input']
|
85
|
+
d.instance.countups('test.input', [1, 1, 1, 0, 0])
|
86
|
+
assert_equal [1,1,1,0,0], d.instance.counts['test.input']
|
87
|
+
d.instance.countups('test.input', [0, 5, 1, 0, 0])
|
88
|
+
assert_equal [1,6,2,0,0], d.instance.counts['test.input']
|
89
|
+
end
|
90
|
+
|
91
|
+
def test_stripped_tag
|
92
|
+
d = create_driver
|
93
|
+
assert_equal 'input', d.instance.stripped_tag('test.input')
|
94
|
+
assert_equal 'test.input', d.instance.stripped_tag('test.test.input')
|
95
|
+
assert_equal 'input', d.instance.stripped_tag('input')
|
96
|
+
end
|
97
|
+
|
98
|
+
def test_generate_output
|
99
|
+
d = create_driver
|
100
|
+
r1 = d.instance.generate_output({'test.input' => [60,240,120,180,0], 'test.input2' => [0,600,0,0,0]}, 60)
|
101
|
+
assert_equal 60, r1['input_unmatched_count']
|
102
|
+
assert_equal 1.0, r1['input_unmatched_rate']
|
103
|
+
assert_equal 10.0, r1['input_unmatched_percentage']
|
104
|
+
assert_equal 240, r1['input_status2xx_count']
|
105
|
+
assert_equal 4.0, r1['input_status2xx_rate']
|
106
|
+
assert_equal 40.0, r1['input_status2xx_percentage']
|
107
|
+
assert_equal 120, r1['input_status3xx_count']
|
108
|
+
assert_equal 2.0, r1['input_status3xx_rate']
|
109
|
+
assert_equal 20.0, r1['input_status3xx_percentage']
|
110
|
+
assert_equal 180, r1['input_status4xx_count']
|
111
|
+
assert_equal 3.0, r1['input_status4xx_rate']
|
112
|
+
assert_equal 30.0, r1['input_status4xx_percentage']
|
113
|
+
assert_equal 0, r1['input_status5xx_count']
|
114
|
+
assert_equal 0.0, r1['input_status5xx_rate']
|
115
|
+
assert_equal 0.0, r1['input_status5xx_percentage']
|
116
|
+
|
117
|
+
assert_equal 0, r1['input2_unmatched_count']
|
118
|
+
assert_equal 0.0, r1['input2_unmatched_rate']
|
119
|
+
assert_equal 0.0, r1['input2_unmatched_percentage']
|
120
|
+
assert_equal 600, r1['input2_status2xx_count']
|
121
|
+
assert_equal 10.0, r1['input2_status2xx_rate']
|
122
|
+
assert_equal 100.0, r1['input2_status2xx_percentage']
|
123
|
+
assert_equal 0, r1['input2_status3xx_count']
|
124
|
+
assert_equal 0.0, r1['input2_status3xx_rate']
|
125
|
+
assert_equal 0.0, r1['input2_status3xx_percentage']
|
126
|
+
assert_equal 0, r1['input2_status4xx_count']
|
127
|
+
assert_equal 0.0, r1['input2_status4xx_rate']
|
128
|
+
assert_equal 0.0, r1['input2_status4xx_percentage']
|
129
|
+
assert_equal 0, r1['input2_status5xx_count']
|
130
|
+
assert_equal 0.0, r1['input2_status5xx_rate']
|
131
|
+
assert_equal 0.0, r1['input2_status5xx_percentage']
|
132
|
+
|
133
|
+
d = create_driver %[
|
134
|
+
aggregate all
|
135
|
+
count_key field
|
136
|
+
pattern1 hoge xxx\d\d
|
137
|
+
]
|
138
|
+
r2 = d.instance.generate_output({'all' => [60,240]}, 60)
|
139
|
+
assert_equal 60, r2['unmatched_count']
|
140
|
+
assert_equal 1.0, r2['unmatched_rate']
|
141
|
+
assert_equal 20.0, r2['unmatched_percentage']
|
142
|
+
assert_equal 240, r2['hoge_count']
|
143
|
+
assert_equal 4.0, r2['hoge_rate']
|
144
|
+
assert_equal 80.0, r2['hoge_percentage']
|
145
|
+
end
|
146
|
+
|
147
|
+
def test_emit
|
148
|
+
d1 = create_driver(CONFIG, 'test.tag1')
|
149
|
+
d1.run do
|
150
|
+
60.times do
|
151
|
+
d1.emit({'target' => '200'})
|
152
|
+
d1.emit({'target' => '100'})
|
153
|
+
d1.emit({'target' => '200'})
|
154
|
+
d1.emit({'target' => '400'})
|
155
|
+
end
|
156
|
+
end
|
157
|
+
r1 = d1.instance.flush(60)
|
158
|
+
assert_equal 120, r1['tag1_status2xx_count']
|
159
|
+
assert_equal 2.0, r1['tag1_status2xx_rate']
|
160
|
+
assert_equal 50.0, r1['tag1_status2xx_percentage']
|
161
|
+
|
162
|
+
assert_equal 60, r1['tag1_status4xx_count']
|
163
|
+
assert_equal 1.0, r1['tag1_status4xx_rate']
|
164
|
+
assert_equal 25.0, r1['tag1_status4xx_percentage']
|
165
|
+
|
166
|
+
assert_equal 60, r1['tag1_unmatched_count']
|
167
|
+
assert_equal 1.0, r1['tag1_unmatched_rate']
|
168
|
+
assert_equal 25.0, r1['tag1_unmatched_percentage']
|
169
|
+
|
170
|
+
assert_equal 0, r1['tag1_status3xx_count']
|
171
|
+
assert_equal 0.0, r1['tag1_status3xx_rate']
|
172
|
+
assert_equal 0.0, r1['tag1_status3xx_percentage']
|
173
|
+
assert_equal 0, r1['tag1_status5xx_count']
|
174
|
+
assert_equal 0.0, r1['tag1_status5xx_rate']
|
175
|
+
assert_equal 0.0, r1['tag1_status5xx_percentage']
|
176
|
+
|
177
|
+
d2 = create_driver(%[
|
178
|
+
aggregate all
|
179
|
+
count_key target
|
180
|
+
pattern1 ok 2\\d\\d
|
181
|
+
pattern2 redirect 3\\d\\d
|
182
|
+
], 'test.tag2')
|
183
|
+
d2.run do
|
184
|
+
60.times do
|
185
|
+
d2.emit({'target' => '200'})
|
186
|
+
d2.emit({'target' => '300 200'})
|
187
|
+
end
|
188
|
+
end
|
189
|
+
d2.instance.flush_emit(120)
|
190
|
+
emits = d2.emits
|
191
|
+
assert_equal 1, emits.length
|
192
|
+
data = emits[0]
|
193
|
+
assert_equal 'datacount', data[0] # tag
|
194
|
+
assert_equal 120, data[2]['ok_count']
|
195
|
+
assert_equal 1.0, data[2]['ok_rate']
|
196
|
+
assert_equal 100.0, data[2]['ok_percentage']
|
197
|
+
assert_equal 0, data[2]['redirect_count']
|
198
|
+
assert_equal 0.0, data[2]['redirect_rate']
|
199
|
+
assert_equal 0.0, data[2]['redirect_percentage']
|
200
|
+
assert_equal 0, data[2]['unmatched_count']
|
201
|
+
assert_equal 0.0, data[2]['unmatched_rate']
|
202
|
+
assert_equal 0.0, data[2]['unmatched_percentage']
|
203
|
+
end
|
204
|
+
|
205
|
+
end
|
metadata
ADDED
@@ -0,0 +1,79 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: fluent-plugin-datacounter
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- TAGOMORI Satoshi
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-02-23 00:00:00.000000000Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: fluentd
|
16
|
+
requirement: &2153804860 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :development
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *2153804860
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: fluentd
|
27
|
+
requirement: &2153804420 !ruby/object:Gem::Requirement
|
28
|
+
none: false
|
29
|
+
requirements:
|
30
|
+
- - ! '>='
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '0'
|
33
|
+
type: :runtime
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: *2153804420
|
36
|
+
description: Output filter plugin to count messages that matches specified conditions
|
37
|
+
email:
|
38
|
+
- tagomoris@gmail.com
|
39
|
+
executables: []
|
40
|
+
extensions: []
|
41
|
+
extra_rdoc_files: []
|
42
|
+
files:
|
43
|
+
- .gitignore
|
44
|
+
- Gemfile
|
45
|
+
- LICENSE.txt
|
46
|
+
- README.rdoc
|
47
|
+
- Rakefile
|
48
|
+
- example.conf
|
49
|
+
- fluent-plugin-datacounter.gemspec
|
50
|
+
- lib/fluent/plugin/out_datacounter.rb
|
51
|
+
- test/helper.rb
|
52
|
+
- test/plugin/test_out_datacounter.rb
|
53
|
+
homepage: https://github.com/tagomoris/fluent-plugin-datacounter
|
54
|
+
licenses: []
|
55
|
+
post_install_message:
|
56
|
+
rdoc_options: []
|
57
|
+
require_paths:
|
58
|
+
- lib
|
59
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
60
|
+
none: false
|
61
|
+
requirements:
|
62
|
+
- - ! '>='
|
63
|
+
- !ruby/object:Gem::Version
|
64
|
+
version: '0'
|
65
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
66
|
+
none: false
|
67
|
+
requirements:
|
68
|
+
- - ! '>='
|
69
|
+
- !ruby/object:Gem::Version
|
70
|
+
version: '0'
|
71
|
+
requirements: []
|
72
|
+
rubyforge_project: fluent-plugin-datacounter
|
73
|
+
rubygems_version: 1.8.6
|
74
|
+
signing_key:
|
75
|
+
specification_version: 3
|
76
|
+
summary: Output filter plugin to count messages that matches specified conditions
|
77
|
+
test_files:
|
78
|
+
- test/helper.rb
|
79
|
+
- test/plugin/test_out_datacounter.rb
|