fluent-plugin-numeric-counter 0.2.1 → 0.2.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: bf34aed7a1bdec3baef3baca72cccc6ee8e858f3
4
+ data.tar.gz: e77034978932949f7f41d84ac426d6b8152d5d0e
5
+ SHA512:
6
+ metadata.gz: 748cbafe875ef82c2069db03b8fb24e8ff9bd008abb75dde27d1853b5379a155156f7ec627a0c2adafecdb73a07b4590c78998fa7f5fd1af8440867e062cc13e
7
+ data.tar.gz: 468015aeee1dffdad2679eb85b829789569728d84f36a7c179d915cc883f6fc7dbabd4c24b016eba7ef14dd5eb4f55e49bef050a77ce226dcc5cfe024f315c58
@@ -0,0 +1,4 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.3
4
+ - 2.0.0
data/README.md CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  ### NumericCounterOutput
6
6
 
7
- Fluentd plugin to count messages, matches for numeric range patterns, and emits its result (like fluent-plugin-datacounter).
7
+ [Fluentd](http://fluentd.org) plugin to count messages, matches for numeric range patterns, and emits its result (like fluent-plugin-datacounter).
8
8
 
9
9
  - Counts per min/hour/day
10
10
  - Counts per second (average every min/hour/day)
@@ -113,6 +113,53 @@ And you can get tested messages count with 'output\_messages' option:
113
113
  # => tag: 'num.foo' or 'num.bar'
114
114
  # message: {'messages' => xxx, 'SMALL_count' => 100, ... }
115
115
 
116
+ ## Parameters
117
+
118
+ * count\_key (required)
119
+
120
+ The key to count in the event record.
121
+
122
+ * tag
123
+
124
+ The output tag. Default is `numcount`.
125
+
126
+ * tag\_prefix
127
+
128
+ The prefix string which will be added to the input tag. `output_per_tag yes` must be specified together.
129
+
130
+ * input\_tag\_remove\_prefix
131
+
132
+ The prefix string which will be removed from the input tag.
133
+
134
+ * count\_interval
135
+
136
+ The interval time to count in seconds. Default is `60`.
137
+
138
+ * unit
139
+
140
+ The interval time to monitor specified an unit (either of `minute`, `hour`, or `day`).
141
+ Use either of `count_interval` or `unit`.
142
+
143
+ * aggregate
144
+
145
+ Calculate in each input `tag` separetely, or `all` records in a mass. Default is `tag`.
146
+
147
+ * ouput\_per\_tag
148
+
149
+ Emit for each input tag. `tag_prefix` must be specified together. Default is `no`.
150
+
151
+ * outcast\_unmatched
152
+
153
+ Specify `yes` if you do not want to include 'unmatched' counts into percentage. Default is `no`.
154
+
155
+ * output\_messages
156
+
157
+ Specify `yes` if you want to get tested messages. Default is `no`.
158
+
159
+ * store\_file
160
+
161
+ Store internal data into a file of the given path on shutdown, and load on starting.
162
+
116
163
  ## TODO
117
164
 
118
165
  * more tests
@@ -2,12 +2,13 @@
2
2
 
3
3
  Gem::Specification.new do |gem|
4
4
  gem.name = "fluent-plugin-numeric-counter"
5
- gem.version = "0.2.1"
5
+ gem.version = "0.2.2"
6
6
  gem.authors = ["TAGOMORI Satoshi"]
7
7
  gem.email = ["tagomoris@gmail.com"]
8
8
  gem.description = %q{Counts messages, with specified key and numeric value in specified range}
9
9
  gem.summary = %q{Fluentd plugin to count messages with specified numeric values}
10
10
  gem.homepage = "https://github.com/tagomoris/fluent-plugin-numeric-counter"
11
+ gem.license = "APLv2"
11
12
 
12
13
  gem.files = `git ls-files`.split($\)
13
14
  gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
@@ -15,5 +16,6 @@ Gem::Specification.new do |gem|
15
16
  gem.require_paths = ["lib"]
16
17
 
17
18
  gem.add_development_dependency "rake"
19
+ gem.add_development_dependency "delorean"
18
20
  gem.add_runtime_dependency "fluentd"
19
21
  end
@@ -1,6 +1,11 @@
1
1
  class Fluent::NumericCounterOutput < Fluent::Output
2
2
  Fluent::Plugin.register_output('numeric_counter', 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 => 60
@@ -13,6 +18,7 @@ class Fluent::NumericCounterOutput < 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 LOW HIGH
@@ -20,11 +26,18 @@ class Fluent::NumericCounterOutput < Fluent::Output
20
26
  (2..PATTERN_MAX_NUM).each do |i|
21
27
  config_param ('pattern' + i.to_s).to_sym, :string, :default => nil
22
28
  end
23
-
24
- attr_accessor :counts, :last_checked
25
29
 
30
+ attr_accessor :counts
31
+ attr_accessor :last_checked
32
+ attr_accessor :saved_duration
33
+ attr_accessor :saved_at
26
34
  attr_accessor :patterns
27
35
 
36
+ # Define `log` method for v0.10.42 or earlier
37
+ unless method_defined?(:log)
38
+ define_method("log") { $log }
39
+ end
40
+
28
41
  def parse_num(str)
29
42
  if str.nil?
30
43
  nil
@@ -58,7 +71,7 @@ class Fluent::NumericCounterOutput < Fluent::Output
58
71
 
59
72
  invalids = conf.keys.select{|k| k =~ /^pattern(\d+)$/ and not (1..PATTERN_MAX_NUM).include?($1.to_i)}
60
73
  if invalids.size > 0
61
- $log.warn "invalid number patterns (valid pattern number:1-#{PATTERN_MAX_NUM}):" + invalids.join(",")
74
+ log.warn "invalid number patterns (valid pattern number:1-#{PATTERN_MAX_NUM}):" + invalids.join(",")
62
75
  end
63
76
  (1..PATTERN_MAX_NUM).each do |i|
64
77
  next unless conf["pattern#{i}"]
@@ -88,12 +101,20 @@ class Fluent::NumericCounterOutput < Fluent::Output
88
101
  @removed_length = @removed_prefix_string.length
89
102
  end
90
103
 
104
+ if @store_file
105
+ f = Pathname.new(@store_file)
106
+ if (f.exist? && !f.writable_real?) || (!f.exist? && !f.parent.writable_real?)
107
+ raise Fluent::ConfigError, "#{@store_file} is not writable"
108
+ end
109
+ end
110
+
91
111
  @counts = count_initialized
92
112
  @mutex = Mutex.new
93
113
  end
94
114
 
95
115
  def start
96
116
  super
117
+ load_status(@store_file, @count_interval) if @store_file
97
118
  start_watch
98
119
  end
99
120
 
@@ -101,6 +122,7 @@ class Fluent::NumericCounterOutput < Fluent::Output
101
122
  super
102
123
  @watcher.terminate
103
124
  @watcher.join
125
+ save_status(@store_file) if @store_file
104
126
  end
105
127
 
106
128
  def count_initialized(keys=nil)
@@ -217,7 +239,7 @@ class Fluent::NumericCounterOutput < Fluent::Output
217
239
  end
218
240
 
219
241
  def watch
220
- @last_checked = Fluent::Engine.now
242
+ @last_checked ||= Fluent::Engine.now
221
243
  while true
222
244
  sleep 0.5
223
245
  if Fluent::Engine.now - @last_checked >= @count_interval
@@ -249,4 +271,60 @@ class Fluent::NumericCounterOutput < Fluent::Output
249
271
 
250
272
  chain.next
251
273
  end
274
+
275
+ # Store internal status into a file
276
+ #
277
+ # @param [String] file_path
278
+ def save_status(file_path)
279
+ begin
280
+ Pathname.new(file_path).open('wb') do |f|
281
+ @saved_at = Fluent::Engine.now
282
+ @saved_duration = @saved_at - @last_checked
283
+ Marshal.dump({
284
+ :counts => @counts,
285
+ :saved_at => @saved_at,
286
+ :saved_duration => @saved_duration,
287
+ :aggregate => @aggregate,
288
+ :count_key => @count_key,
289
+ :patterns => @patterns,
290
+ }, f)
291
+ end
292
+ rescue => e
293
+ log.warn "out_datacounter: Can't write store_file #{e.class} #{e.message}"
294
+ end
295
+ end
296
+
297
+ # Load internal status from a file
298
+ #
299
+ # @param [String] file_path
300
+ # @param [Interger] count_interval
301
+ def load_status(file_path, count_interval)
302
+ return unless (f = Pathname.new(file_path)).exist?
303
+
304
+ begin
305
+ f.open('rb') do |f|
306
+ stored = Marshal.load(f)
307
+ if stored[:aggregate] == @aggregate and
308
+ stored[:count_key] == @count_key and
309
+ stored[:patterns] == @patterns
310
+
311
+ if Fluent::Engine.now <= stored[:saved_at] + count_interval
312
+ @counts = stored[:counts]
313
+ @saved_at = stored[:saved_at]
314
+ @saved_duration = stored[:saved_duration]
315
+
316
+ # skip the saved duration to continue counting
317
+ @last_checked = Fluent::Engine.now - @saved_duration
318
+ else
319
+ log.warn "out_datacounter: stored data is outdated. ignore stored data"
320
+ end
321
+ else
322
+ log.warn "out_datacounter: configuration param was changed. ignore stored data"
323
+ end
324
+ end
325
+ rescue => e
326
+ log.warn "out_datacounter: Can't load store_file #{e.class} #{e.message}"
327
+ end
328
+ end
329
+
252
330
  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_numeric_counter'
26
27
 
27
28
  class Test::Unit::TestCase
@@ -496,4 +496,62 @@ class NumericCounterOutputTest < Test::Unit::TestCase
496
496
  d.instance.flush_emit(60)
497
497
  assert_equal 2, d.emits.size # +0
498
498
  end
499
+
500
+ def test_store_file
501
+ dir = "test/tmp"
502
+ Dir.mkdir dir unless Dir.exist? dir
503
+ file = "#{dir}/test.dat"
504
+ File.unlink file if File.exist? file
505
+
506
+ # test store
507
+ d = create_driver(CONFIG + %[store_file #{file}])
508
+ d.run do
509
+ d.instance.flush_emit(60)
510
+ d.emit({'target' => 1})
511
+ d.emit({'target' => 1})
512
+ d.emit({'target' => 1})
513
+ d.instance.shutdown
514
+ end
515
+ stored_counts = d.instance.counts
516
+ stored_saved_at = d.instance.saved_at
517
+ stored_saved_duration = d.instance.saved_duration
518
+ assert File.exist? file
519
+
520
+ # test load
521
+ d = create_driver(CONFIG + %[store_file #{file}])
522
+ d.run do
523
+ loaded_counts = d.instance.counts
524
+ loaded_saved_at = d.instance.saved_at
525
+ loaded_saved_duration = d.instance.saved_duration
526
+ assert_equal stored_counts, loaded_counts
527
+ assert_equal stored_saved_at, loaded_saved_at
528
+ assert_equal stored_saved_duration, loaded_saved_duration
529
+ end
530
+
531
+ # test not to load if config is changed
532
+ d = create_driver(CONFIG + %[count_key foobar store_file #{file}])
533
+ d.run do
534
+ loaded_counts = d.instance.counts
535
+ loaded_saved_at = d.instance.saved_at
536
+ loaded_saved_duration = d.instance.saved_duration
537
+ assert_equal({}, loaded_counts)
538
+ assert_equal(nil, loaded_saved_at)
539
+ assert_equal(nil, loaded_saved_duration)
540
+ end
541
+
542
+ # test not to load if stored data is outdated.
543
+ Delorean.jump 61 # jump more than count_interval
544
+ d = create_driver(CONFIG + %[store_file #{file}])
545
+ d.run do
546
+ loaded_counts = d.instance.counts
547
+ loaded_saved_at = d.instance.saved_at
548
+ loaded_saved_duration = d.instance.saved_duration
549
+ assert_equal({}, loaded_counts)
550
+ assert_equal(nil, loaded_saved_at)
551
+ assert_equal(nil, loaded_saved_duration)
552
+ end
553
+ Delorean.back_to_the_present
554
+
555
+ File.unlink file
556
+ end
499
557
  end
metadata CHANGED
@@ -1,46 +1,55 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fluent-plugin-numeric-counter
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
5
- prerelease:
4
+ version: 0.2.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: 2014-03-07 00:00:00.000000000 Z
13
12
  dependencies:
14
13
  - !ruby/object:Gem::Dependency
15
14
  name: rake
16
15
  requirement: !ruby/object:Gem::Requirement
17
- none: false
18
16
  requirements:
19
- - - ! '>='
17
+ - - ">="
20
18
  - !ruby/object:Gem::Version
21
19
  version: '0'
22
20
  type: :development
23
21
  prerelease: false
24
22
  version_requirements: !ruby/object:Gem::Requirement
25
- none: false
26
23
  requirements:
27
- - - ! '>='
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: delorean
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
+ - - ">="
28
39
  - !ruby/object:Gem::Version
29
40
  version: '0'
30
41
  - !ruby/object:Gem::Dependency
31
42
  name: fluentd
32
43
  requirement: !ruby/object:Gem::Requirement
33
- none: false
34
44
  requirements:
35
- - - ! '>='
45
+ - - ">="
36
46
  - !ruby/object:Gem::Version
37
47
  version: '0'
38
48
  type: :runtime
39
49
  prerelease: false
40
50
  version_requirements: !ruby/object:Gem::Requirement
41
- none: false
42
51
  requirements:
43
- - - ! '>='
52
+ - - ">="
44
53
  - !ruby/object:Gem::Version
45
54
  version: '0'
46
55
  description: Counts messages, with specified key and numeric value in specified range
@@ -50,7 +59,8 @@ executables: []
50
59
  extensions: []
51
60
  extra_rdoc_files: []
52
61
  files:
53
- - .gitignore
62
+ - ".gitignore"
63
+ - ".travis.yml"
54
64
  - Gemfile
55
65
  - LICENSE
56
66
  - README.md
@@ -60,28 +70,28 @@ files:
60
70
  - test/helper.rb
61
71
  - test/plugin/test_out_numeric_counter.rb
62
72
  homepage: https://github.com/tagomoris/fluent-plugin-numeric-counter
63
- licenses: []
73
+ licenses:
74
+ - APLv2
75
+ metadata: {}
64
76
  post_install_message:
65
77
  rdoc_options: []
66
78
  require_paths:
67
79
  - lib
68
80
  required_ruby_version: !ruby/object:Gem::Requirement
69
- none: false
70
81
  requirements:
71
- - - ! '>='
82
+ - - ">="
72
83
  - !ruby/object:Gem::Version
73
84
  version: '0'
74
85
  required_rubygems_version: !ruby/object:Gem::Requirement
75
- none: false
76
86
  requirements:
77
- - - ! '>='
87
+ - - ">="
78
88
  - !ruby/object:Gem::Version
79
89
  version: '0'
80
90
  requirements: []
81
91
  rubyforge_project:
82
- rubygems_version: 1.8.23
92
+ rubygems_version: 2.2.2
83
93
  signing_key:
84
- specification_version: 3
94
+ specification_version: 4
85
95
  summary: Fluentd plugin to count messages with specified numeric values
86
96
  test_files:
87
97
  - test/helper.rb