fluent-plugin-grepcounter 0.4.2 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 3b6b29ceafed9d27f2a47aaae6f4509944a958c3
4
- data.tar.gz: a7efb1d61d2a585f49a81a22b0c3d3dfa0b122f4
3
+ metadata.gz: 905021d7ccf6788eb182017c59884b5071bd9e87
4
+ data.tar.gz: 25501e79fe3b478a3cbc3a2b5fa96beae5bbbe6b
5
5
  SHA512:
6
- metadata.gz: f6df4866a77929e798551961c6349ceb88d80b58f7959af60536b12cbcf4bd93e93311823da8abb882828f4cf6f3b313d2c50f34d682e700b6ae7f4f2054e19e
7
- data.tar.gz: 78b66fd77923e02947ae8a1a45da3e89e5c30eba12bb96f7970a80f109f91f8956f6233c3c0e400958f2c9c8500b66f7238b6ec471f32273dd005a6c9d7f23dd
6
+ metadata.gz: 084d066b3dbce3bb8686955ad34822332c963f57239ac6459761b04605de0c3bfa61288cfbe5b8c1b725a777cafc007f7ee044e9214c5c55a05796143e7e0a20
7
+ data.tar.gz: 8351db08a7cdde4d550ca1303f3352c003c24d0dd1ca304a258fd999be2253535434de78d6554e7977965b85647d811421d07f45183d5fdca98f651af3b4af29
@@ -1,3 +1,9 @@
1
+ ## 0.5.0 (2013/12/17)
2
+
3
+ Features
4
+
5
+ - Add `regexpN` and `excludeN` options
6
+
1
7
  ## 0.4.2 (2013/12/12)
2
8
 
3
9
  Features
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # fluent-plugin-grepcounter [![Build Status](https://secure.travis-ci.org/sonots/fluent-plugin-grepcounter.png?branch=master)](http://travis-ci.org/sonots/fluent-plugin-grepcounter) [![Dependency Status](https://gemnasium.com/sonots/fluent-plugin-grepcounter.png)](https://gemnasium.com/sonots/fluent-plugin-grepcounter)
1
+ # fluent-plugin-grepcounter [![Build Status](https://secure.travis-ci.org/sonots/fluent-plugin-grepcounter.png?branch=master)](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.4.2"
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 :threshold, :integer, :default => nil # obsolete
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
- @count_interval = @count_interval.to_i
40
- @input_key = @input_key.to_s
41
- @regexp = Regexp.compile(@regexp) if @regexp
42
- @exclude = Regexp.compile(@exclude) if @exclude
43
+ if @input_key
44
+ @regexp = Regexp.compile(@regexp) if @regexp
45
+ @exclude = Regexp.compile(@exclude) if @exclude
46
+ end
43
47
 
44
- @threshold = @threshold.to_i if @threshold
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
- unless ['>=', '<='].include?(@comparator)
47
- raise Fluent::ConfigError, "grepcounter: comparator allows >=, <="
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
- if @threshold and @comparator
55
- if @comparator == '>='
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
- value = record[@input_key]
129
- next unless match(value.to_s)
130
- matches << value
131
- count += 1
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
- output['message'] = @delimiter.nil? ? matches : matches.join(@delimiter)
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 false if @regexp and !@regexp.match(string)
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 "lack of requirements" do
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.2
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-12 00:00:00.000000000 Z
11
+ date: 2013-12-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: fluentd