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 ADDED
@@ -0,0 +1,11 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
5
+ # For TextMate, emacs, vim
6
+ *.tmproj
7
+ tmtags
8
+ *~
9
+ \#*
10
+ .\#*
11
+ *.swp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in fluent-plugin-datacounter.gemspec
4
+ gemspec
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
@@ -0,0 +1,11 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ require 'rake/testtask'
4
+ Rake::TestTask.new(:test) do |test|
5
+ test.libs << 'lib' << 'test'
6
+ test.pattern = 'test/**/test_*.rb'
7
+ test.verbose = true
8
+ end
9
+
10
+ task :default => :test
11
+
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