fluent-plugin-grepcounter 0.4.2 → 0.5.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +6 -0
- data/README.md +13 -5
- data/fluent-plugin-grepcounter.gemspec +1 -1
- data/lib/fluent/plugin/out_grepcounter.rb +81 -45
- data/spec/out_grepcounter_spec.rb +46 -3
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 905021d7ccf6788eb182017c59884b5071bd9e87
|
4
|
+
data.tar.gz: 25501e79fe3b478a3cbc3a2b5fa96beae5bbbe6b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 084d066b3dbce3bb8686955ad34822332c963f57239ac6459761b04605de0c3bfa61288cfbe5b8c1b725a777cafc007f7ee044e9214c5c55a05796143e7e0a20
|
7
|
+
data.tar.gz: 8351db08a7cdde4d550ca1303f3352c003c24d0dd1ca304a258fd999be2253535434de78d6554e7977965b85647d811421d07f45183d5fdca98f651af3b4af29
|
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# fluent-plugin-grepcounter [](http://travis-ci.org/sonots/fluent-plugin-grepcounter)
|
1
|
+
# fluent-plugin-grepcounter [](http://travis-ci.org/sonots/fluent-plugin-grepcounter)
|
2
2
|
|
3
3
|
Fluentd plugin to count the number of matched messages, and emit if exeeds the `threshold`.
|
4
4
|
|
@@ -63,18 +63,26 @@ Then, output bocomes as belows (indented). You can see the `message` field is jo
|
|
63
63
|
|
64
64
|
The interval time to count in seconds. Default is 60.
|
65
65
|
|
66
|
-
- input\_key
|
66
|
+
- input\_key *field\_key*
|
67
67
|
|
68
|
-
The target field key to grep out
|
68
|
+
The target field key to grep out. Use with regexp or exclude.
|
69
69
|
|
70
|
-
- regexp
|
70
|
+
- regexp *regexp*
|
71
71
|
|
72
72
|
The filtering regular expression
|
73
73
|
|
74
|
-
- exclude
|
74
|
+
- exclude *regexp*
|
75
75
|
|
76
76
|
The excluding regular expression like grep -v
|
77
77
|
|
78
|
+
- regexp[1-20] *field\_key* *regexp* (experimental)
|
79
|
+
|
80
|
+
The target field key and the filtering regular expression to grep out.
|
81
|
+
|
82
|
+
- exclude[1-20] *field_key* *regexp* (experimental)
|
83
|
+
|
84
|
+
The target field key and the excluding regular expression like grep -v
|
85
|
+
|
78
86
|
- threshold
|
79
87
|
|
80
88
|
The threshold number to emit. Emit if `count` value >= specified value.
|
@@ -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-grepcounter"
|
6
|
-
s.version = "0.
|
6
|
+
s.version = "0.5.0"
|
7
7
|
s.authors = ["Naotoshi Seo"]
|
8
8
|
s.email = ["sonots@gmail.com"]
|
9
9
|
s.homepage = "https://github.com/sonots/fluent-plugin-grepcounter"
|
@@ -2,16 +2,20 @@
|
|
2
2
|
class Fluent::GrepCounterOutput < Fluent::Output
|
3
3
|
Fluent::Plugin.register_output('grepcounter', self)
|
4
4
|
|
5
|
+
REGEXP_MAX_NUM = 20
|
6
|
+
|
5
7
|
def initialize
|
6
8
|
super
|
7
9
|
require 'pathname'
|
8
10
|
end
|
9
11
|
|
10
|
-
config_param :input_key, :string
|
12
|
+
config_param :input_key, :string, :default => nil
|
11
13
|
config_param :regexp, :string, :default => nil
|
12
|
-
config_param :count_interval, :time, :default => 5
|
13
14
|
config_param :exclude, :string, :default => nil
|
14
|
-
config_param :
|
15
|
+
(1..REGEXP_MAX_NUM).each {|i| config_param :"regexp#{i}", :string, :default => nil }
|
16
|
+
(1..REGEXP_MAX_NUM).each {|i| config_param :"exclude#{i}", :string, :default => nil }
|
17
|
+
config_param :count_interval, :time, :default => 5
|
18
|
+
config_param :threshold, :integer, :default => nil # not obsolete, though
|
15
19
|
config_param :comparator, :string, :default => '>=' # obsolete
|
16
20
|
config_param :less_than, :float, :default => nil
|
17
21
|
config_param :less_equal, :float, :default => nil
|
@@ -36,57 +40,56 @@ class Fluent::GrepCounterOutput < Fluent::Output
|
|
36
40
|
def configure(conf)
|
37
41
|
super
|
38
42
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
+
if @input_key
|
44
|
+
@regexp = Regexp.compile(@regexp) if @regexp
|
45
|
+
@exclude = Regexp.compile(@exclude) if @exclude
|
46
|
+
end
|
43
47
|
|
44
|
-
@
|
48
|
+
@regexps = {}
|
49
|
+
(1..REGEXP_MAX_NUM).each do |i|
|
50
|
+
next unless conf["regexp#{i}"]
|
51
|
+
key, regexp = conf["regexp#{i}"].split(/ /, 2)
|
52
|
+
raise Fluent::ConfigError, "regexp#{i} does not contain 2 parameters" unless regexp
|
53
|
+
raise Fluent::ConfigError, "regexp#{i} contains a duplicated key, #{key}" if @regexps[key]
|
54
|
+
@regexps[key] = Regexp.compile(regexp)
|
55
|
+
end
|
45
56
|
|
46
|
-
|
47
|
-
|
57
|
+
@excludes = {}
|
58
|
+
(1..REGEXP_MAX_NUM).each do |i|
|
59
|
+
next unless conf["exclude#{i}"]
|
60
|
+
key, exclude = conf["exclude#{i}"].split(/ /, 2)
|
61
|
+
raise Fluent::ConfigError, "exclude#{i} does not contain 2 parameters" unless exclude
|
62
|
+
raise Fluent::ConfigError, "exclude#{i} contains a duplicated key, #{key}" if @excludes[key]
|
63
|
+
@excludes[key] = Regexp.compile(exclude)
|
48
64
|
end
|
49
65
|
|
66
|
+
if @input_key and (!@regexps.empty? or !@excludes.empty?)
|
67
|
+
raise Fluent::ConfigError, "Classic style `input_key`, and new style `regexpN`, `excludeN` can not be used together"
|
68
|
+
end
|
69
|
+
|
70
|
+
# to support obsolete options
|
71
|
+
@tag ||= @output_tag
|
72
|
+
@delimiter ||= @output_with_joined_delimiter
|
73
|
+
|
50
74
|
# to support obsolete `threshold` and `comparator` options
|
51
75
|
if @threshold.nil? and @less_than.nil? and @less_equal.nil? and @greater_than.nil? and @greater_equal.nil?
|
52
76
|
@threshold = 1
|
53
77
|
end
|
54
|
-
|
55
|
-
|
78
|
+
unless %w[>= <=].include?(@comparator)
|
79
|
+
raise Fluent::ConfigError, "grepcounter: comparator allows >=, <="
|
80
|
+
end
|
81
|
+
if @threshold
|
82
|
+
case @comparator
|
83
|
+
when '>='
|
56
84
|
@greater_equal = @threshold
|
57
85
|
else
|
58
86
|
@less_equal = @threshold
|
59
87
|
end
|
60
88
|
end
|
61
89
|
|
62
|
-
# to support osolete `output_tag` option
|
63
|
-
@tag = @output_tag if !@tag and @output_tag
|
64
|
-
|
65
|
-
# to support obsolete `output_with_joined_delimiter` option
|
66
|
-
@delimiter = @output_with_joined_delimiter if !@delimiter and @output_with_joined_delimiter
|
67
|
-
|
68
|
-
unless ['tag', 'all'].include?(@aggregate)
|
69
|
-
raise Fluent::ConfigError, "grepcounter: aggregate allows tag/all"
|
70
|
-
end
|
71
|
-
|
72
|
-
case @aggregate
|
73
|
-
when 'all'
|
74
|
-
raise Fluent::ConfigError, "grepcounter: output_tag must be specified with aggregate all" if @output_tag.nil?
|
75
|
-
when 'tag'
|
76
|
-
# raise Fluent::ConfigError, "grepcounter: add_tag_prefix must be specified with aggregate tag" if @add_tag_prefix.nil?
|
77
|
-
end
|
78
|
-
|
79
|
-
if @store_file
|
80
|
-
f = Pathname.new(@store_file)
|
81
|
-
if (f.exist? && !f.writable_real?) || (!f.exist? && !f.parent.writable_real?)
|
82
|
-
raise Fluent::ConfigError, "#{@store_file} is not writable"
|
83
|
-
end
|
84
|
-
end
|
85
|
-
|
86
90
|
if @tag.nil? and @add_tag_prefix.nil? and @remove_tag_prefix.nil?
|
87
91
|
@add_tag_prefix = 'count' # not ConfigError to support lower version compatibility
|
88
92
|
end
|
89
|
-
|
90
93
|
@tag_prefix = "#{@add_tag_prefix}." if @add_tag_prefix
|
91
94
|
@tag_prefix_match = "#{@remove_tag_prefix}." if @remove_tag_prefix
|
92
95
|
@tag_proc =
|
@@ -102,6 +105,22 @@ class Fluent::GrepCounterOutput < Fluent::Output
|
|
102
105
|
Proc.new {|tag| tag }
|
103
106
|
end
|
104
107
|
|
108
|
+
case @aggregate
|
109
|
+
when 'all'
|
110
|
+
raise Fluent::ConfigError, "grepcounter: `tag` must be specified with aggregate all" if @tag.nil?
|
111
|
+
when 'tag'
|
112
|
+
# raise Fluent::ConfigError, "grepcounter: add_tag_prefix must be specified with aggregate tag" if @add_tag_prefix.nil?
|
113
|
+
else
|
114
|
+
raise Fluent::ConfigError, "grepcounter: aggregate allows tag/all"
|
115
|
+
end
|
116
|
+
|
117
|
+
if @store_file
|
118
|
+
f = Pathname.new(@store_file)
|
119
|
+
if (f.exist? && !f.writable_real?) || (!f.exist? && !f.parent.writable_real?)
|
120
|
+
raise Fluent::ConfigError, "#{@store_file} is not writable"
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
105
124
|
@matches = {}
|
106
125
|
@counts = {}
|
107
126
|
@mutex = Mutex.new
|
@@ -125,10 +144,23 @@ class Fluent::GrepCounterOutput < Fluent::Output
|
|
125
144
|
count = 0; matches = []
|
126
145
|
# filter out and insert
|
127
146
|
es.each do |time,record|
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
147
|
+
catch(:break_loop) do
|
148
|
+
if key = @input_key
|
149
|
+
value = record[key].to_s
|
150
|
+
throw :break_loop if @regexp and !match(@regexp, value)
|
151
|
+
throw :break_loop if @exclude and match(@exclude, value)
|
152
|
+
matches << value # old style stores as an array of values
|
153
|
+
else
|
154
|
+
@regexps.each do |key, regexp|
|
155
|
+
throw :break_loop unless match(regexp, record[key].to_s)
|
156
|
+
end
|
157
|
+
@excludes.each do |key, exclude|
|
158
|
+
throw :break_loop if match(exclude, record[key].to_s)
|
159
|
+
end
|
160
|
+
matches << record # new style stores as an array of hashes, but how to utilize it?
|
161
|
+
end
|
162
|
+
count += 1
|
163
|
+
end
|
132
164
|
end
|
133
165
|
# thread safe merge
|
134
166
|
@counts[tag] ||= 0
|
@@ -196,7 +228,12 @@ class Fluent::GrepCounterOutput < Fluent::Output
|
|
196
228
|
return nil if @greater_equal and count < @greater_equal
|
197
229
|
output = {}
|
198
230
|
output['count'] = count
|
199
|
-
|
231
|
+
if @input_key
|
232
|
+
output['message'] = @delimiter ? matches.join(@delimiter) : matches
|
233
|
+
else
|
234
|
+
# I will think of good format later...
|
235
|
+
output['message'] = @delimiter ? matches.map{|hash| hash.to_hash}.join(@delimiter) : matches
|
236
|
+
end
|
200
237
|
if tag
|
201
238
|
output['input_tag'] = tag
|
202
239
|
output['input_tag_last'] = tag.split('.').last
|
@@ -208,10 +245,9 @@ class Fluent::GrepCounterOutput < Fluent::Output
|
|
208
245
|
string.index(substring) == 0 ? string[substring.size..-1] : string
|
209
246
|
end
|
210
247
|
|
211
|
-
def match(string)
|
248
|
+
def match(regexp, string)
|
212
249
|
begin
|
213
|
-
return
|
214
|
-
return false if @exclude and @exclude.match(string)
|
250
|
+
return regexp.match(string)
|
215
251
|
rescue ArgumentError => e
|
216
252
|
raise e unless e.message.index("invalid byte sequence in") == 0
|
217
253
|
string = replace_invalid_byte(string)
|
@@ -18,8 +18,8 @@ describe Fluent::GrepCounterOutput do
|
|
18
18
|
|
19
19
|
describe 'test configure' do
|
20
20
|
describe 'bad configuration' do
|
21
|
-
context
|
22
|
-
let(:config) {
|
21
|
+
context 'should not use classic style and new style together' do
|
22
|
+
let(:config) { %[input_key message\nregexp1 message foo] }
|
23
23
|
it { expect { driver }.to raise_error(Fluent::ConfigError) }
|
24
24
|
end
|
25
25
|
|
@@ -109,6 +109,22 @@ describe Fluent::GrepCounterOutput do
|
|
109
109
|
it { emit }
|
110
110
|
end
|
111
111
|
|
112
|
+
context 'regexpN' do
|
113
|
+
let(:config) { %[ regexp1 message WARN ] }
|
114
|
+
before do
|
115
|
+
Fluent::Engine.stub(:now).and_return(time)
|
116
|
+
Fluent::Engine.should_receive(:emit).with("count.#{tag}", time, expected.merge({
|
117
|
+
"count"=>3,
|
118
|
+
"message"=>[
|
119
|
+
{"message"=>"2013/01/13T07:02:13.232645 WARN POST /auth"},
|
120
|
+
{"message"=>"2013/01/13T07:02:21.542145 WARN GET /favicon.ico"},
|
121
|
+
{"message"=>"2013/01/13T07:02:43.632145 WARN POST /login"},
|
122
|
+
],
|
123
|
+
}))
|
124
|
+
end
|
125
|
+
it { emit }
|
126
|
+
end
|
127
|
+
|
112
128
|
context 'exclude' do
|
113
129
|
let(:config) { CONFIG + %[regexp WARN \n exclude favicon] }
|
114
130
|
before do
|
@@ -124,6 +140,21 @@ describe Fluent::GrepCounterOutput do
|
|
124
140
|
it { emit }
|
125
141
|
end
|
126
142
|
|
143
|
+
context 'excludeN' do
|
144
|
+
let(:config) { %[regexp1 message WARN \n exclude1 message favicon] }
|
145
|
+
before do
|
146
|
+
Fluent::Engine.stub(:now).and_return(time)
|
147
|
+
Fluent::Engine.should_receive(:emit).with("count.#{tag}", time, expected.merge({
|
148
|
+
"count"=>2,
|
149
|
+
"message"=>[
|
150
|
+
{"message"=>"2013/01/13T07:02:13.232645 WARN POST /auth"},
|
151
|
+
{"message"=>"2013/01/13T07:02:43.632145 WARN POST /login"},
|
152
|
+
],
|
153
|
+
}))
|
154
|
+
end
|
155
|
+
it { emit }
|
156
|
+
end
|
157
|
+
|
127
158
|
context "threshold and comparator (obsolete)" do
|
128
159
|
context '>= threshold' do
|
129
160
|
let(:config) { CONFIG + %[threshold 4] }
|
@@ -301,7 +332,7 @@ describe Fluent::GrepCounterOutput do
|
|
301
332
|
it { emit }
|
302
333
|
end
|
303
334
|
|
304
|
-
context 'delimiter' do
|
335
|
+
context 'delimiter for old style (input_key)' do
|
305
336
|
# \\n shall be \n in config file
|
306
337
|
let(:config) { CONFIG + %[delimiter \\n] }
|
307
338
|
before do
|
@@ -312,6 +343,18 @@ describe Fluent::GrepCounterOutput do
|
|
312
343
|
it { emit }
|
313
344
|
end
|
314
345
|
|
346
|
+
context 'delimiter for new style (regexpN or excludeN)' do
|
347
|
+
# \\n shall be \n in config file
|
348
|
+
let(:config) { %[regexp1 message .\ndelimiter \\n] }
|
349
|
+
before do
|
350
|
+
Fluent::Engine.stub(:now).and_return(time)
|
351
|
+
# I will think of good format later ...
|
352
|
+
message = "{\"message\"=>\"2013/01/13T07:02:11.124202 INFO GET /ping\"}\\n{\"message\"=>\"2013/01/13T07:02:13.232645 WARN POST /auth\"}\\n{\"message\"=>\"2013/01/13T07:02:21.542145 WARN GET /favicon.ico\"}\\n{\"message\"=>\"2013/01/13T07:02:43.632145 WARN POST /login\"}"
|
353
|
+
Fluent::Engine.should_receive(:emit).with("count.#{tag}", time, expected.merge("message" => message))
|
354
|
+
end
|
355
|
+
it { emit }
|
356
|
+
end
|
357
|
+
|
315
358
|
context 'aggregate all' do
|
316
359
|
let(:messages) { ['foobar', 'foobar'] }
|
317
360
|
let(:emit) do
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fluent-plugin-grepcounter
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.0
|
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-12-
|
11
|
+
date: 2013-12-16 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: fluentd
|