fluent-plugin-flowcounter 1.0.0 → 1.1.0
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 +4 -4
- data/README.md +23 -0
- data/fluent-plugin-flowcounter.gemspec +2 -1
- data/lib/fluent/plugin/out_flowcounter.rb +40 -21
- data/test/helper.rb +10 -0
- data/test/plugin/test_out_flowcounter.rb +203 -3
- metadata +16 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2d5e0837aff2b5b9122daa256b9eac720886e653
|
4
|
+
data.tar.gz: 5af91ae11eb092d76b87dc3669af751b6141cca7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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.
|
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:
|
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
|
-
|
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,
|
153
|
+
router.emit(@tag, now, data)
|
121
154
|
end
|
122
155
|
else
|
123
|
-
router.emit(@tag,
|
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
|
|
data/test/helper.rb
CHANGED
@@ -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.
|
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-
|
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
|