fluent-plugin-flowcounter 1.0.0 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 873629b9804e92822b8a2b854a5df3da2798bf45
4
- data.tar.gz: f3d79ae17a63763f9dc0ca8073982022d8b0759e
3
+ metadata.gz: 2d5e0837aff2b5b9122daa256b9eac720886e653
4
+ data.tar.gz: 5af91ae11eb092d76b87dc3669af751b6141cca7
5
5
  SHA512:
6
- metadata.gz: 9b92f7be19ad17f125244c10efab034df307f0581f6db4ba82d06551eee513e3002453080221494b8ec5e746601ee3a55a5540f87e93ded866e9222df1391d33
7
- data.tar.gz: dd56f20d75dfb69f309170fdb59a520a69da0fca58e5188c46cc98106c8448e1c20d7c00e79ea4fbf98aeccda6835d98ca3caddfe322cb440ea26f8a83958b9f
6
+ metadata.gz: 95de380ed6362234b5053bd341df61e24040c174f638cc486267a1a98a2a0d286da701f552d37223655726b45859a6499da95f9ec39a7a5d99e7ee8130a54a12
7
+ data.tar.gz: a4180307283337fb425e9aee62cb4b803c420aef397d1da4022e5d13fbb4d7f8fadb0631100bddb879bb78a184594eac6c8f394d80615a7622cbbe97e8b26b67
data/README.md CHANGED
@@ -96,6 +96,29 @@ Counts active tag, stop count records if the tag message stoped(when aggragates
96
96
  delete_idle true
97
97
  </match>
98
98
 
99
+ ### Generate Output at Zero Time
100
+
101
+ If you want to generate count results at every 0 second (for unit:minute), at every 00:00 (for unit:hour) or at every 00:00:00 (for unit:day), specify `timestamp_counting true` in your configuration.
102
+
103
+ <match target.**>
104
+ @type flowcounter
105
+ count_keys *
106
+ aggregate all
107
+ unit hour
108
+ timestamp_counting true
109
+ </match>
110
+
111
+ The configuration above emits output at 00:00:00, 01:00:00, 02:00:00, .... every day. In this use case, `unit: day` requires to configure `timestamp_timezone` to set the timezone to determine the beginning of the day.
112
+
113
+ <match target.**>
114
+ @type flowcounter
115
+ count_keys *
116
+ aggregate all
117
+ unit day
118
+ timestamp_counting true
119
+ timestamp_timezone -03:00
120
+ </match>
121
+
99
122
  ### Embedding Hostname
100
123
 
101
124
  The current version of this plugin doesn't support `${hostname}` placeholders. Use ruby code embedding for such purpose:
@@ -1,7 +1,7 @@
1
1
  # -*- encoding: utf-8 -*-
2
2
  Gem::Specification.new do |gem|
3
3
  gem.name = "fluent-plugin-flowcounter"
4
- gem.version = "1.0.0"
4
+ gem.version = "1.1.0"
5
5
  gem.authors = ["TAGOMORI Satoshi"]
6
6
  gem.email = ["tagomoris@gmail.com"]
7
7
  gem.summary = %q{Fluent plugin to count message flow}
@@ -16,5 +16,6 @@ Gem::Specification.new do |gem|
16
16
 
17
17
  gem.add_development_dependency "rake"
18
18
  gem.add_development_dependency "test-unit"
19
+ gem.add_development_dependency "timecop"
19
20
  gem.add_runtime_dependency "fluentd", "~> 0.14.0"
20
21
  end
@@ -1,4 +1,5 @@
1
1
  require 'fluent/plugin/output'
2
+ require 'time'
2
3
 
3
4
  class Fluent::Plugin::FlowCounterOutput < Fluent::Plugin::Output
4
5
  Fluent::Plugin.register_output('flowcounter', self)
@@ -6,11 +7,13 @@ class Fluent::Plugin::FlowCounterOutput < Fluent::Plugin::Output
6
7
  helpers :event_emitter, :timer
7
8
 
8
9
  config_param :unit, :enum, list: [:second, :minute, :hour, :day], default: :minute
10
+ config_param :timestamp_counting, :bool, default: false
11
+ config_param :timestamp_timezone, :string, default: nil
9
12
  config_param :aggregate, :enum, list: [:tag, :all], default: :tag
10
13
  config_param :output_style, :enum, list: [:joined, :tagged], default: :joined
11
14
  config_param :tag, :string, default: 'flowcount'
12
15
  config_param :input_tag_remove_prefix, :string, default: nil
13
- config_param :count_keys, :string, default: nil
16
+ config_param :count_keys, :array, value_type: :string, default: []
14
17
  config_param :delimiter, :string, default: '_'
15
18
  config_param :delete_idle, :bool, default: false
16
19
 
@@ -38,21 +41,51 @@ class Fluent::Plugin::FlowCounterOutput < Fluent::Plugin::Output
38
41
  @removed_length = @removed_prefix_string.length
39
42
  end
40
43
  @count_all = false
41
- if @count_keys
42
- @count_keys = @count_keys.split(',').map(&:strip)
44
+ if @count_keys && !@count_keys.empty?
43
45
  @count_all = (@count_keys == ['*'])
44
46
  @count_bytes = true
45
47
  else
46
48
  @count_bytes = false
47
49
  end
48
50
 
51
+ if @timestamp_counting
52
+ @timestamp_timezone_offset = 0
53
+ if @unit == :second
54
+ raise Fluent::ConfigError, "timestamp_counting cannot be enabled with unit: second"
55
+ elsif @unit == :day
56
+ unless @timestamp_timezone
57
+ raise Fluent::ConfigError, "timestamp_counting requires timestamp_timezone to be configured (e.g., '-0700') if unit is day"
58
+ end
59
+ @timestamp_timezone_offset = Time.zone_offset(@timestamp_timezone)
60
+ unless @timestamp_timezone_offset
61
+ raise Fluent::ConfigError, "invalid timestamp_timezone value (specify like '-0700')"
62
+ end
63
+ end
64
+ @last_checked = nil
65
+ @initializer = ->{ now = Fluent::EventTime.now.to_i; @last_checked = now - (now % @tick) - @timestamp_timezone_offset }
66
+ @checker = ->{ Fluent::EventTime.now.to_i - @last_checked >= @tick }
67
+ @updater = ->{ @last_checked += @tick; return Fluent::EventTime.new(@last_checked, 0), @tick }
68
+ else
69
+ @last_checked = nil
70
+ @initializer = ->{ @last_checked = Fluent::Clock.now }
71
+ @checker = ->{ Fluent::Clock.now - @last_checked >= @tick }
72
+ @updater = ->{ prev = @last_checked; @last_checked = Fluent::Clock.now; return Fluent::EventTime.now, @last_checked - prev }
73
+ end
74
+
49
75
  @counts = count_initialized
50
76
  @mutex = Mutex.new
51
77
  end
52
78
 
53
79
  def start
54
80
  super
55
- start_watch
81
+
82
+ @initializer.call
83
+ timer_execute(:out_flowcounter_watcher, 0.5) do
84
+ if @checker.call
85
+ now, interval = @updater.call
86
+ flush_emit(now, interval)
87
+ end
88
+ end
56
89
  end
57
90
 
58
91
  def count_initialized(keys=nil)
@@ -114,27 +147,13 @@ class Fluent::Plugin::FlowCounterOutput < Fluent::Plugin::Output
114
147
  }
115
148
  end
116
149
 
117
- def flush_emit(step)
150
+ def flush_emit(now, step)
118
151
  if @output_style == :tagged
119
152
  tagged_flush(step).each do |data|
120
- router.emit(@tag, Fluent::Engine.now, data)
153
+ router.emit(@tag, now, data)
121
154
  end
122
155
  else
123
- router.emit(@tag, Fluent::Engine.now, flush(step))
124
- end
125
- end
126
-
127
- def start_watch
128
- @last_checked = Fluent::Engine.now
129
- # for internal, or tests only
130
- timer_execute(:out_flowcounter_watcher, 0.5, &method(:watch))
131
- end
132
-
133
- def watch
134
- if Fluent::Engine.now - @last_checked >= @tick
135
- now = Fluent::Engine.now
136
- flush_emit(now - @last_checked)
137
- @last_checked = now
156
+ router.emit(@tag, now, flush(step))
138
157
  end
139
158
  end
140
159
 
@@ -17,3 +17,13 @@ require 'fluent/plugin/out_flowcounter'
17
17
 
18
18
  class Test::Unit::TestCase
19
19
  end
20
+
21
+ def waiting(seconds)
22
+ begin
23
+ Timeout.timeout(seconds) do
24
+ yield
25
+ end
26
+ rescue Timeout::Error
27
+ raise "Timed out with timeout second #{seconds}"
28
+ end
29
+ end
@@ -1,10 +1,16 @@
1
1
  require 'helper'
2
+ require 'timecop'
2
3
 
3
4
  class FlowCounterOutputTest < Test::Unit::TestCase
4
5
  def setup
5
6
  Fluent::Test.setup
6
7
  end
7
8
 
9
+ def teardown
10
+ Fluent::Clock.return
11
+ Timecop.return
12
+ end
13
+
8
14
  CONFIG = %[
9
15
  unit day
10
16
  aggregate tag
@@ -245,7 +251,7 @@ count_keys message
245
251
  d2.feed(time, {'f1' => 'abcde', 'f2' => 'vwxyz', 'f3' => '0123456789'})
246
252
  d2.feed(time, {'f1' => 'abcde', 'f2' => 'vwxyz', 'f3' => '0123456789'})
247
253
  end
248
- d2.instance.flush_emit(60)
254
+ d2.instance.flush_emit(Fluent::EventTime.now, 60)
249
255
  end
250
256
  events = d2.events
251
257
  assert_equal 1, events.length
@@ -272,7 +278,7 @@ count_keys message
272
278
  d3.feed(time, {'f1' => 'abcde', 'f2' => 'vwxyz', 'f3' => '0123456789'})
273
279
  d3.feed(time, {'f1' => 'abcde', 'f2' => 'vwxyz', 'f3' => '0123456789'})
274
280
  end
275
- d3.instance.flush_emit(60)
281
+ d3.instance.flush_emit(Fluent::EventTime.now, 60)
276
282
  end
277
283
  events = d3.events
278
284
  assert_equal 1, events.length
@@ -364,7 +370,7 @@ count_keys message
364
370
  d3.feed(time, {'f1' => 'abcde', 'f2' => 'vwxyz', 'f3' => '0123456789'})
365
371
  d3.feed(time, {'f1' => 'abcde', 'f2' => 'vwxyz', 'f3' => '0123456789'})
366
372
  end
367
- d3.instance.flush_emit(60)
373
+ d3.instance.flush_emit(Fluent::EventTime.now, 60)
368
374
  end
369
375
  events = d3.events
370
376
  assert_equal 1, events.length
@@ -373,4 +379,198 @@ count_keys message
373
379
  assert_equal 60*5, data[2]['count']
374
380
  assert_equal 0, data[2]['bytes']
375
381
  end
382
+
383
+ data(
384
+ minute: ["minute", 59, 61, 90, 100, 110, 122],
385
+ hour: ["hour", 3500, 3601, 3800, 6900, 7180, 7202],
386
+ day: ["day", 86350, 86401, 86400+20000, 86400+65000, 86400+86350, 86400+86402],
387
+ )
388
+ test 'emit timing' do |data|
389
+ unit, time1, time2, time3, time4, time5, time6 = data
390
+
391
+ d = create_driver %[
392
+ unit #{unit}
393
+ timestamp_counting false
394
+ aggregate all
395
+ tag flowcount
396
+ count_keys message
397
+ ]
398
+
399
+ start = Fluent::Clock.now
400
+ Fluent::Clock.freeze(start)
401
+
402
+ first_emit_before = nil
403
+ first_emit_after = nil
404
+ second_emit_before = nil
405
+ second_emit_after = nil
406
+
407
+ prev_emit_count = 0
408
+ wait_next_emit = ->{
409
+ waiting(10) do
410
+ sleep 0.1 while d.emit_count == prev_emit_count
411
+ prev_emit_count = d.emit_count
412
+ end
413
+ }
414
+
415
+ d.run(default_tag: 'test', timeout: 300_000) do
416
+ 3600.times do
417
+ d.feed({'message'=> 'a' * 100})
418
+ d.feed({'message'=> 'b' * 100})
419
+ d.feed({'message'=> 'c' * 100})
420
+ end
421
+
422
+ Fluent::Clock.freeze(start + time1)
423
+ sleep 2 # nothing emitted here - clock is frozen
424
+ assert_equal 0, d.emit_count
425
+
426
+ 3600.times do
427
+ d.feed({'message'=> 'a' * 100})
428
+ d.feed({'message'=> 'b' * 100})
429
+ d.feed({'message'=> 'c' * 100})
430
+ end
431
+
432
+ first_emit_before = Fluent::EventTime.now
433
+ Fluent::Clock.freeze(start + time2)
434
+ wait_next_emit.call # emitted from 7200 * 3 events
435
+ first_emit_after = Fluent::EventTime.now
436
+
437
+ Fluent::Clock.freeze(start + time3)
438
+
439
+ 3600.times do
440
+ d.feed({'message'=> 'a' * 100})
441
+ d.feed({'message'=> 'b' * 100})
442
+ d.feed({'message'=> 'c' * 100})
443
+ end
444
+
445
+ Fluent::Clock.freeze(start + time4)
446
+ sleep 1
447
+ assert_equal prev_emit_count, d.emit_count
448
+
449
+ Fluent::Clock.freeze(start + time5)
450
+ sleep 1
451
+ assert_equal prev_emit_count, d.emit_count
452
+
453
+ second_emit_before = Fluent::EventTime.now
454
+ Fluent::Clock.freeze(start + time6)
455
+ wait_next_emit.call # emitted from 3600 events
456
+ second_emit_after = Fluent::EventTime.now
457
+ end
458
+
459
+ events = d.events
460
+ assert_equal 2, events.size
461
+ tag, t1, r1 = events[0]
462
+ assert{ t1 >= first_emit_before }
463
+ assert{ t1 <= first_emit_after }
464
+ assert_equal 3600*3*2, r1["count"]
465
+ tag, t2, r2 = events[1]
466
+ assert{ t2 >= second_emit_before }
467
+ assert{ t2 <= second_emit_after }
468
+ assert_equal 3600*3, r2["count"]
469
+ end
470
+
471
+ data(
472
+ minute: [
473
+ "minute",
474
+ "",
475
+ Time.utc(2017,7,26,13,49, 3),
476
+ Time.utc(2017,7,26,13,49,55),
477
+ Time.utc(2017,7,26,13,50, 0),
478
+ Time.utc(2017,7,26,13,50, 3),
479
+ Time.utc(2017,7,26,13,50,49),
480
+ Time.utc(2017,7,26,13,51, 0)],
481
+ hour: [
482
+ "hour",
483
+ "",
484
+ Time.utc(2017,7,26,13,49, 3),
485
+ Time.utc(2017,7,26,13,59,58),
486
+ Time.utc(2017,7,26,14, 0, 0),
487
+ Time.utc(2017,7,26,14,15,49),
488
+ Time.utc(2017,7,26,14,58, 0),
489
+ Time.utc(2017,7,26,15, 0, 0)],
490
+ day: [
491
+ "day",
492
+ "timestamp_timezone +0000",
493
+ Time.utc(2017,7,26,13,49, 3),
494
+ Time.utc(2017,7,26,23,59,58),
495
+ Time.utc(2017,7,27, 0, 0, 0),
496
+ Time.utc(2017,7,27, 1,30,10),
497
+ Time.utc(2017,7,27,23,38,50),
498
+ Time.utc(2017,7,28, 0, 0, 0)],
499
+ dayz: [
500
+ "day",
501
+ "timestamp_timezone -0700",
502
+ Time.new(2017,7,26,13,49, 3, "-07:00"),
503
+ Time.new(2017,7,26,23,59,58, "-07:00"),
504
+ Time.new(2017,7,27, 0, 0, 0, "-07:00"),
505
+ Time.new(2017,7,27, 1,30,10, "-07:00"),
506
+ Time.new(2017,7,27,23,38,50, "-07:00"),
507
+ Time.new(2017,7,28, 0, 0, 0, "-07:00")],
508
+ )
509
+ test 'emit timing with timestamp_counting' do |data|
510
+ unit, conf, start, time1, time2, time3, time4, time5 = data
511
+
512
+ d = create_driver %[
513
+ unit #{unit}
514
+ timestamp_counting true
515
+ #{conf}
516
+ aggregate all
517
+ tag flowcount
518
+ count_keys message
519
+ ]
520
+
521
+ Timecop.freeze(start)
522
+
523
+ prev_emit_count = 0
524
+ wait_next_emit = ->{
525
+ waiting(10) do
526
+ sleep 0.1 while d.emit_count == prev_emit_count
527
+ prev_emit_count = d.emit_count
528
+ end
529
+ }
530
+
531
+ d.run(default_tag: 'test', timeout: 300_000) do
532
+ 3600.times do
533
+ d.feed({'message'=> 'a' * 100})
534
+ d.feed({'message'=> 'b' * 100})
535
+ d.feed({'message'=> 'c' * 100})
536
+ end
537
+
538
+ Timecop.freeze(time1)
539
+ sleep 2 # nothing emitted here - time is frozen
540
+ assert_equal 0, d.emit_count
541
+
542
+ 3600.times do
543
+ d.feed({'message'=> 'a' * 100})
544
+ d.feed({'message'=> 'b' * 100})
545
+ d.feed({'message'=> 'c' * 100})
546
+ end
547
+
548
+ Timecop.freeze(time2)
549
+ wait_next_emit.call # emitted from 7200 * 3 events
550
+
551
+ Timecop.freeze(time3)
552
+
553
+ 3600.times do
554
+ d.feed({'message'=> 'a' * 100})
555
+ d.feed({'message'=> 'b' * 100})
556
+ d.feed({'message'=> 'c' * 100})
557
+ end
558
+
559
+ Timecop.freeze(time4)
560
+ sleep 1
561
+ assert_equal prev_emit_count, d.emit_count
562
+
563
+ Timecop.freeze(time5)
564
+ wait_next_emit.call # emitted from 3600 events
565
+ end
566
+
567
+ events = d.events
568
+ assert_equal 2, events.size
569
+ tag, t1, r1 = events[0]
570
+ assert_equal Fluent::EventTime.new(time2.to_i, 0), t1
571
+ assert_equal 3600*3*2, r1["count"]
572
+ tag, t2, r2 = events[1]
573
+ assert_equal Fluent::EventTime.new(time5.to_i, 0), t2
574
+ assert_equal 3600*3, r2["count"]
575
+ end
376
576
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fluent-plugin-flowcounter
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - TAGOMORI Satoshi
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-07-25 00:00:00.000000000 Z
11
+ date: 2017-07-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
@@ -38,6 +38,20 @@ dependencies:
38
38
  - - ">="
39
39
  - !ruby/object:Gem::Version
40
40
  version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: timecop
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
41
55
  - !ruby/object:Gem::Dependency
42
56
  name: fluentd
43
57
  requirement: !ruby/object:Gem::Requirement