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.
@@ -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: