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 +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
|