fluent-plugin-datacounter 0.4.1 → 0.4.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
@@ -1,8 +1,8 @@
1
- = fluent-plugin-datacounter
1
+ # fluent-plugin-datacounter
2
2
 
3
- == Component
3
+ ## Component
4
4
 
5
- === DataCounterOutput
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
- == Configuration
20
+ ## Configuration
21
21
 
22
- === DataCounterOutput
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
- == TODO
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
- == Copyright
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
- $:.push File.expand_path("../lib", __FILE__)
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
- Gem::Specification.new do |s|
5
- s.name = "fluent-plugin-datacounter"
6
- s.version = "0.4.1"
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
+ 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
- s.rubyforge_project = "fluent-plugin-datacounter"
17
+ gem.add_runtime_dependency "fluentd"
14
18
 
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
- 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 = Fluent::Engine.now
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
@@ -22,6 +22,7 @@ unless ENV.has_key?('VERBOSE')
22
22
  $log = nulllogger
23
23
  end
24
24
 
25
+ require 'delorean'
25
26
  require 'fluent/plugin/out_datacounter'
26
27
 
27
28
  class Test::Unit::TestCase
@@ -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.1
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-04-08 00:00:00.000000000 Z
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: fluentd
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: :runtime
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: Output filter plugin to count messages that matches specified conditions
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.rdoc
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: fluent-plugin-datacounter
83
- rubygems_version: 1.8.23
106
+ rubyforge_project:
107
+ rubygems_version: 2.0.3
84
108
  signing_key:
85
- specification_version: 3
86
- summary: Output filter plugin to count messages that matches specified conditions
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: