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 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