fluent-plugin-datacounter 0.4.1 → 0.4.2
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 +7 -0
- data/{README.rdoc → README.md} +54 -7
- data/fluent-plugin-datacounter.gemspec +17 -17
- data/lib/fluent/plugin/out_datacounter.rb +74 -1
- data/test/helper.rb +1 -0
- data/test/plugin/test_out_datacounter.rb +58 -0
- metadata +49 -24
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 1a2ccf7898022261c52e17eb5aa9375df17fcac1
|
4
|
+
data.tar.gz: 19dbfaf01f24fe21fcfc76429d9be3c77d3c4167
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 4739c24aabcd790e73038e60956a9bc7fdd00434d910f07e2f6cfbe55b28790fbc1859fd2027c9a8d01c9170f040726c1bdbb8fd394b6d38b6e57582d9011c46
|
7
|
+
data.tar.gz: 14729172930a3b75453916c757c2be4415f428a372bdce4c1ae0120152389b7e845bdc942b06f48f5c9848eaa4045799f918a7884504ce67554f9c7abefd5d37
|
data/{README.rdoc → README.md}
RENAMED
@@ -1,8 +1,8 @@
|
|
1
|
-
|
1
|
+
# fluent-plugin-datacounter
|
2
2
|
|
3
|
-
|
3
|
+
## Component
|
4
4
|
|
5
|
-
|
5
|
+
### DataCounterOutput
|
6
6
|
|
7
7
|
Count messages with data matches any of specified regexp patterns in specified attribute.
|
8
8
|
|
@@ -17,9 +17,9 @@ DataCounterOutput emits messages contains results data, so you can output these
|
|
17
17
|
|
18
18
|
'input_tag_remove_prefix' option available if you want to remove tag prefix from output field names.
|
19
19
|
|
20
|
-
|
20
|
+
## Configuration
|
21
21
|
|
22
|
-
|
22
|
+
### DataCounterOutput
|
23
23
|
|
24
24
|
Count messages that have attribute 'referer' as 'google.com', 'yahoo.com' and 'facebook.com' from all messages matched, per minutes.
|
25
25
|
|
@@ -116,12 +116,59 @@ And you can get tested messages count with 'output_messages' option:
|
|
116
116
|
# => tag: 'datacount.baz'
|
117
117
|
# message: {'messages' => xxx, 'OK_count' => ...}
|
118
118
|
|
119
|
-
|
119
|
+
## Parameters
|
120
|
+
|
121
|
+
* count\_key (required)
|
122
|
+
|
123
|
+
The key to count in the event record.
|
124
|
+
|
125
|
+
* tag
|
126
|
+
|
127
|
+
The output tag. Default is `datacount`.
|
128
|
+
|
129
|
+
* tag\_prefix
|
130
|
+
|
131
|
+
The prefix string which will be added to the input tag. `output_per_tag yes` must be specified together.
|
132
|
+
|
133
|
+
* input\_tag\_remove\_prefix
|
134
|
+
|
135
|
+
The prefix string which will be removed from the input tag.
|
136
|
+
|
137
|
+
* count\_interval
|
138
|
+
|
139
|
+
The interval time to count in seconds. Default is `60`.
|
140
|
+
|
141
|
+
* unit
|
142
|
+
|
143
|
+
The interval time to monitor specified an unit (either of `minute`, `hour`, or `day`).
|
144
|
+
Use either of `count_interval` or `unit`.
|
145
|
+
|
146
|
+
* aggregate
|
147
|
+
|
148
|
+
Calculate in each input `tag` separetely, or `all` records in a mass. Default is `tag`.
|
149
|
+
|
150
|
+
* ouput\_per\_tag
|
151
|
+
|
152
|
+
Emit for each input tag. `tag_prefix` must be specified together. Default is `no`.
|
153
|
+
|
154
|
+
* outcast\_unmatched
|
155
|
+
|
156
|
+
Specify `yes` if you do not want to include 'unmatched' counts into percentage. Default is `no`.
|
157
|
+
|
158
|
+
* output\_messages
|
159
|
+
|
160
|
+
Specify `yes` if you want to get tested messages. Default is `no`.
|
161
|
+
|
162
|
+
* store\_file
|
163
|
+
|
164
|
+
Store internal data into a file of the given path on shutdown, and load on starting.
|
165
|
+
|
166
|
+
## TODO
|
120
167
|
|
121
168
|
- consider what to do next
|
122
169
|
- patches welcome!
|
123
170
|
|
124
|
-
|
171
|
+
## Copyright
|
125
172
|
|
126
173
|
Copyright:: Copyright (c) 2012- TAGOMORI Satoshi (tagomoris)
|
127
174
|
License:: Apache License, Version 2.0
|
@@ -1,22 +1,22 @@
|
|
1
1
|
# -*- encoding: utf-8 -*-
|
2
|
-
|
2
|
+
Gem::Specification.new do |gem|
|
3
|
+
gem.name = "fluent-plugin-datacounter"
|
4
|
+
gem.version = "0.4.2"
|
5
|
+
gem.authors = ["TAGOMORI Satoshi"]
|
6
|
+
gem.email = ["tagomoris@gmail.com"]
|
7
|
+
gem.homepage = "https://github.com/tagomoris/fluent-plugin-datacounter"
|
8
|
+
gem.summary = %q{Fluentd plugin to count records with specified regexp patterns}
|
9
|
+
gem.description = %q{To count records with string fields by regexps (To count records with numbers, use numeric-counter)}
|
10
|
+
gem.license = "APLv2"
|
3
11
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
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
|
+
gem.files = `git ls-files`.split("\n")
|
13
|
+
gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
14
|
+
gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
15
|
+
gem.require_paths = ["lib"]
|
12
16
|
|
13
|
-
|
17
|
+
gem.add_runtime_dependency "fluentd"
|
14
18
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
s.require_paths = ["lib"]
|
19
|
-
|
20
|
-
s.add_development_dependency "rake"
|
21
|
-
s.add_runtime_dependency "fluentd"
|
19
|
+
gem.add_development_dependency "bundler"
|
20
|
+
gem.add_development_dependency "rake"
|
21
|
+
gem.add_development_dependency "delorean"
|
22
22
|
end
|
@@ -1,6 +1,11 @@
|
|
1
1
|
class Fluent::DataCounterOutput < Fluent::Output
|
2
2
|
Fluent::Plugin.register_output('datacounter', self)
|
3
3
|
|
4
|
+
def initialize
|
5
|
+
super
|
6
|
+
require 'pathname'
|
7
|
+
end
|
8
|
+
|
4
9
|
PATTERN_MAX_NUM = 20
|
5
10
|
|
6
11
|
config_param :count_interval, :time, :default => nil
|
@@ -13,6 +18,7 @@ class Fluent::DataCounterOutput < Fluent::Output
|
|
13
18
|
config_param :count_key, :string
|
14
19
|
config_param :outcast_unmatched, :bool, :default => false
|
15
20
|
config_param :output_messages, :bool, :default => false
|
21
|
+
config_param :store_file, :string, :default => nil
|
16
22
|
|
17
23
|
# pattern0 reserved as unmatched counts
|
18
24
|
config_param :pattern1, :string # string: NAME REGEXP
|
@@ -22,6 +28,8 @@ class Fluent::DataCounterOutput < Fluent::Output
|
|
22
28
|
|
23
29
|
attr_accessor :tick
|
24
30
|
attr_accessor :counts
|
31
|
+
attr_accessor :saved_duration
|
32
|
+
attr_accessor :saved_at
|
25
33
|
attr_accessor :last_checked
|
26
34
|
|
27
35
|
def configure(conf)
|
@@ -78,12 +86,20 @@ class Fluent::DataCounterOutput < Fluent::Output
|
|
78
86
|
@removed_length = @removed_prefix_string.length
|
79
87
|
end
|
80
88
|
|
89
|
+
if @store_file
|
90
|
+
f = Pathname.new(@store_file)
|
91
|
+
if (f.exist? && !f.writable_real?) || (!f.exist? && !f.parent.writable_real?)
|
92
|
+
raise Fluent::ConfigError, "#{@store_file} is not writable"
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
81
96
|
@counts = count_initialized
|
82
97
|
@mutex = Mutex.new
|
83
98
|
end
|
84
99
|
|
85
100
|
def start
|
86
101
|
super
|
102
|
+
load_status(@store_file, @tick) if @store_file
|
87
103
|
start_watch
|
88
104
|
end
|
89
105
|
|
@@ -91,6 +107,7 @@ class Fluent::DataCounterOutput < Fluent::Output
|
|
91
107
|
super
|
92
108
|
@watcher.terminate
|
93
109
|
@watcher.join
|
110
|
+
save_status(@store_file) if @store_file
|
94
111
|
end
|
95
112
|
|
96
113
|
def count_initialized(keys=nil)
|
@@ -210,7 +227,7 @@ class Fluent::DataCounterOutput < Fluent::Output
|
|
210
227
|
|
211
228
|
def watch
|
212
229
|
# instance variable, and public accessable, for test
|
213
|
-
@last_checked
|
230
|
+
@last_checked ||= Fluent::Engine.now
|
214
231
|
while true
|
215
232
|
sleep 0.5
|
216
233
|
if Fluent::Engine.now - @last_checked >= @tick
|
@@ -242,4 +259,60 @@ class Fluent::DataCounterOutput < Fluent::Output
|
|
242
259
|
|
243
260
|
chain.next
|
244
261
|
end
|
262
|
+
|
263
|
+
# Store internal status into a file
|
264
|
+
#
|
265
|
+
# @param [String] file_path
|
266
|
+
def save_status(file_path)
|
267
|
+
begin
|
268
|
+
Pathname.new(file_path).open('wb') do |f|
|
269
|
+
@saved_at = Fluent::Engine.now
|
270
|
+
@saved_duration = @saved_at - @last_checked
|
271
|
+
Marshal.dump({
|
272
|
+
:counts => @counts,
|
273
|
+
:saved_at => @saved_at,
|
274
|
+
:saved_duration => @saved_duration,
|
275
|
+
:aggregate => @aggregate,
|
276
|
+
:count_key => @count_key,
|
277
|
+
:patterns => @patterns,
|
278
|
+
}, f)
|
279
|
+
end
|
280
|
+
rescue => e
|
281
|
+
$log.warn "out_datacounter: Can't write store_file #{e.class} #{e.message}"
|
282
|
+
end
|
283
|
+
end
|
284
|
+
|
285
|
+
# Load internal status from a file
|
286
|
+
#
|
287
|
+
# @param [String] file_path
|
288
|
+
# @param [Interger] tick The count interval
|
289
|
+
def load_status(file_path, tick)
|
290
|
+
return unless (f = Pathname.new(file_path)).exist?
|
291
|
+
|
292
|
+
begin
|
293
|
+
f.open('rb') do |f|
|
294
|
+
stored = Marshal.load(f)
|
295
|
+
if stored[:aggregate] == @aggregate and
|
296
|
+
stored[:count_key] == @count_key and
|
297
|
+
stored[:patterns] == @patterns
|
298
|
+
|
299
|
+
if Fluent::Engine.now <= stored[:saved_at] + tick
|
300
|
+
@counts = stored[:counts]
|
301
|
+
@saved_at = stored[:saved_at]
|
302
|
+
@saved_duration = stored[:saved_duration]
|
303
|
+
|
304
|
+
# skip the saved duration to continue counting
|
305
|
+
@last_checked = Fluent::Engine.now - @saved_duration
|
306
|
+
else
|
307
|
+
$log.warn "out_datacounter: stored data is outdated. ignore stored data"
|
308
|
+
end
|
309
|
+
else
|
310
|
+
$log.warn "out_datacounter: configuration param was changed. ignore stored data"
|
311
|
+
end
|
312
|
+
end
|
313
|
+
rescue => e
|
314
|
+
$log.warn "out_datacounter: Can't load store_file #{e.class} #{e.message}"
|
315
|
+
end
|
316
|
+
end
|
317
|
+
|
245
318
|
end
|
data/test/helper.rb
CHANGED
@@ -653,4 +653,62 @@ class DataCounterOutputTest < Test::Unit::TestCase
|
|
653
653
|
d.instance.flush_emit(60)
|
654
654
|
assert_equal 2, d.emits.size # +0
|
655
655
|
end
|
656
|
+
|
657
|
+
def test_store_file
|
658
|
+
dir = "test/tmp"
|
659
|
+
Dir.mkdir dir unless Dir.exist? dir
|
660
|
+
file = "#{dir}/test.dat"
|
661
|
+
File.unlink file if File.exist? file
|
662
|
+
|
663
|
+
# test store
|
664
|
+
d = create_driver(CONFIG + %[store_file #{file}])
|
665
|
+
d.run do
|
666
|
+
d.instance.flush_emit(60)
|
667
|
+
d.emit({'target' => 1})
|
668
|
+
d.emit({'target' => 1})
|
669
|
+
d.emit({'target' => 1})
|
670
|
+
d.instance.shutdown
|
671
|
+
end
|
672
|
+
stored_counts = d.instance.counts
|
673
|
+
stored_saved_at = d.instance.saved_at
|
674
|
+
stored_saved_duration = d.instance.saved_duration
|
675
|
+
assert File.exist? file
|
676
|
+
|
677
|
+
# test load
|
678
|
+
d = create_driver(CONFIG + %[store_file #{file}])
|
679
|
+
d.run do
|
680
|
+
loaded_counts = d.instance.counts
|
681
|
+
loaded_saved_at = d.instance.saved_at
|
682
|
+
loaded_saved_duration = d.instance.saved_duration
|
683
|
+
assert_equal stored_counts, loaded_counts
|
684
|
+
assert_equal stored_saved_at, loaded_saved_at
|
685
|
+
assert_equal stored_saved_duration, loaded_saved_duration
|
686
|
+
end
|
687
|
+
|
688
|
+
# test not to load if config is changed
|
689
|
+
d = create_driver(CONFIG + %[count_key foobar store_file #{file}])
|
690
|
+
d.run do
|
691
|
+
loaded_counts = d.instance.counts
|
692
|
+
loaded_saved_at = d.instance.saved_at
|
693
|
+
loaded_saved_duration = d.instance.saved_duration
|
694
|
+
assert_equal({}, loaded_counts)
|
695
|
+
assert_equal(nil, loaded_saved_at)
|
696
|
+
assert_equal(nil, loaded_saved_duration)
|
697
|
+
end
|
698
|
+
|
699
|
+
# test not to load if stored data is outdated.
|
700
|
+
Delorean.jump 61 # jump more than count_interval
|
701
|
+
d = create_driver(CONFIG + %[store_file #{file}])
|
702
|
+
d.run do
|
703
|
+
loaded_counts = d.instance.counts
|
704
|
+
loaded_saved_at = d.instance.saved_at
|
705
|
+
loaded_saved_duration = d.instance.saved_duration
|
706
|
+
assert_equal({}, loaded_counts)
|
707
|
+
assert_equal(nil, loaded_saved_at)
|
708
|
+
assert_equal(nil, loaded_saved_duration)
|
709
|
+
end
|
710
|
+
Delorean.back_to_the_present
|
711
|
+
|
712
|
+
File.unlink file
|
713
|
+
end
|
656
714
|
end
|
metadata
CHANGED
@@ -1,49 +1,73 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fluent-plugin-datacounter
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.4.
|
5
|
-
prerelease:
|
4
|
+
version: 0.4.2
|
6
5
|
platform: ruby
|
7
6
|
authors:
|
8
7
|
- TAGOMORI Satoshi
|
9
8
|
autorequire:
|
10
9
|
bindir: bin
|
11
10
|
cert_chain: []
|
12
|
-
date: 2013-
|
11
|
+
date: 2013-08-27 00:00:00.000000000 Z
|
13
12
|
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: fluentd
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - '>='
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - '>='
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: bundler
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - '>='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - '>='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
14
41
|
- !ruby/object:Gem::Dependency
|
15
42
|
name: rake
|
16
43
|
requirement: !ruby/object:Gem::Requirement
|
17
|
-
none: false
|
18
44
|
requirements:
|
19
|
-
- -
|
45
|
+
- - '>='
|
20
46
|
- !ruby/object:Gem::Version
|
21
47
|
version: '0'
|
22
48
|
type: :development
|
23
49
|
prerelease: false
|
24
50
|
version_requirements: !ruby/object:Gem::Requirement
|
25
|
-
none: false
|
26
51
|
requirements:
|
27
|
-
- -
|
52
|
+
- - '>='
|
28
53
|
- !ruby/object:Gem::Version
|
29
54
|
version: '0'
|
30
55
|
- !ruby/object:Gem::Dependency
|
31
|
-
name:
|
56
|
+
name: delorean
|
32
57
|
requirement: !ruby/object:Gem::Requirement
|
33
|
-
none: false
|
34
58
|
requirements:
|
35
|
-
- -
|
59
|
+
- - '>='
|
36
60
|
- !ruby/object:Gem::Version
|
37
61
|
version: '0'
|
38
|
-
type: :
|
62
|
+
type: :development
|
39
63
|
prerelease: false
|
40
64
|
version_requirements: !ruby/object:Gem::Requirement
|
41
|
-
none: false
|
42
65
|
requirements:
|
43
|
-
- -
|
66
|
+
- - '>='
|
44
67
|
- !ruby/object:Gem::Version
|
45
68
|
version: '0'
|
46
|
-
description:
|
69
|
+
description: To count records with string fields by regexps (To count records with
|
70
|
+
numbers, use numeric-counter)
|
47
71
|
email:
|
48
72
|
- tagomoris@gmail.com
|
49
73
|
executables: []
|
@@ -53,7 +77,7 @@ files:
|
|
53
77
|
- .gitignore
|
54
78
|
- Gemfile
|
55
79
|
- LICENSE.txt
|
56
|
-
- README.
|
80
|
+
- README.md
|
57
81
|
- Rakefile
|
58
82
|
- example.conf
|
59
83
|
- fluent-plugin-datacounter.gemspec
|
@@ -61,29 +85,30 @@ files:
|
|
61
85
|
- test/helper.rb
|
62
86
|
- test/plugin/test_out_datacounter.rb
|
63
87
|
homepage: https://github.com/tagomoris/fluent-plugin-datacounter
|
64
|
-
licenses:
|
88
|
+
licenses:
|
89
|
+
- APLv2
|
90
|
+
metadata: {}
|
65
91
|
post_install_message:
|
66
92
|
rdoc_options: []
|
67
93
|
require_paths:
|
68
94
|
- lib
|
69
95
|
required_ruby_version: !ruby/object:Gem::Requirement
|
70
|
-
none: false
|
71
96
|
requirements:
|
72
|
-
- -
|
97
|
+
- - '>='
|
73
98
|
- !ruby/object:Gem::Version
|
74
99
|
version: '0'
|
75
100
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
76
|
-
none: false
|
77
101
|
requirements:
|
78
|
-
- -
|
102
|
+
- - '>='
|
79
103
|
- !ruby/object:Gem::Version
|
80
104
|
version: '0'
|
81
105
|
requirements: []
|
82
|
-
rubyforge_project:
|
83
|
-
rubygems_version:
|
106
|
+
rubyforge_project:
|
107
|
+
rubygems_version: 2.0.3
|
84
108
|
signing_key:
|
85
|
-
specification_version:
|
86
|
-
summary:
|
109
|
+
specification_version: 4
|
110
|
+
summary: Fluentd plugin to count records with specified regexp patterns
|
87
111
|
test_files:
|
88
112
|
- test/helper.rb
|
89
113
|
- test/plugin/test_out_datacounter.rb
|
114
|
+
has_rdoc:
|