fluent-plugin-datacounter 0.5.0 → 1.0.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/.travis.yml +3 -2
- data/Rakefile +1 -1
- data/fluent-plugin-datacounter.gemspec +3 -3
- data/lib/fluent/plugin/out_datacounter.rb +115 -121
- data/test/helper.rb +0 -1
- data/test/plugin/test_out_datacounter.rb +175 -148
- metadata +10 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 658db36e09471b16f21cba480b36b5168818daf4
|
4
|
+
data.tar.gz: e798289a5f33cd2bf8a85f3e8c0efa941d2b1439
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 77682c560a84a722ec7b2edb6eb831295ffdfca1d0ba228cd79fefc33ae9a3d93f35ffc26d95a49cd66b54f2b6ecb1215b8527852f0b4a4834f75a56731c34b1
|
7
|
+
data.tar.gz: a575acce7199476d09caff16a83a7323134b869e88f534bc11c9e9c83af5090d11ab0508a2494084a51f3717a81061ce49b86ca79cda2d22e99a87eab81f0357
|
data/.travis.yml
CHANGED
data/Rakefile
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# -*- encoding: utf-8 -*-
|
2
2
|
Gem::Specification.new do |gem|
|
3
3
|
gem.name = "fluent-plugin-datacounter"
|
4
|
-
gem.version = "0.
|
4
|
+
gem.version = "1.0.0"
|
5
5
|
gem.authors = ["TAGOMORI Satoshi"]
|
6
6
|
gem.email = ["tagomoris@gmail.com"]
|
7
7
|
gem.homepage = "https://github.com/tagomoris/fluent-plugin-datacounter"
|
@@ -14,10 +14,10 @@ Gem::Specification.new do |gem|
|
|
14
14
|
gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
15
15
|
gem.require_paths = ["lib"]
|
16
16
|
|
17
|
-
gem.add_runtime_dependency "fluentd", "
|
17
|
+
gem.add_runtime_dependency "fluentd", [">= 0.14.8", "< 2"]
|
18
18
|
|
19
19
|
gem.add_development_dependency "bundler"
|
20
20
|
gem.add_development_dependency "rake"
|
21
21
|
gem.add_development_dependency "test-unit"
|
22
|
-
gem.add_development_dependency "
|
22
|
+
gem.add_development_dependency "timecop"
|
23
23
|
end
|
@@ -1,52 +1,51 @@
|
|
1
|
-
|
1
|
+
require 'pathname'
|
2
|
+
require 'fluent/plugin/output'
|
3
|
+
|
4
|
+
class Fluent::Plugin::DataCounterOutput < Fluent::Plugin::Output
|
2
5
|
Fluent::Plugin.register_output('datacounter', self)
|
3
6
|
|
4
|
-
|
5
|
-
unless method_defined?(:log)
|
6
|
-
define_method("log") { $log }
|
7
|
-
end
|
8
|
-
|
9
|
-
# Define `router` method of v0.12 to support v0.10 or earlier
|
10
|
-
unless method_defined?(:router)
|
11
|
-
define_method("router") { Fluent::Engine }
|
12
|
-
end
|
13
|
-
|
14
|
-
def initialize
|
15
|
-
super
|
16
|
-
require 'pathname'
|
17
|
-
end
|
7
|
+
helpers :event_emitter, :storage, :timer
|
18
8
|
|
9
|
+
DEFAULT_STORAGE_TYPE = 'local'
|
19
10
|
PATTERN_MAX_NUM = 20
|
20
11
|
|
21
|
-
config_param :count_interval, :time, :
|
22
|
-
:
|
23
|
-
config_param :unit, :
|
24
|
-
:
|
25
|
-
config_param :output_per_tag, :bool, :
|
26
|
-
:
|
27
|
-
config_param :aggregate, :
|
28
|
-
:
|
29
|
-
config_param :tag, :string, :
|
30
|
-
:
|
31
|
-
config_param :tag_prefix, :string, :
|
32
|
-
:
|
33
|
-
config_param :input_tag_remove_prefix, :string, :
|
34
|
-
:
|
12
|
+
config_param :count_interval, :time, default: nil,
|
13
|
+
desc: 'The interval time to count in seconds.'
|
14
|
+
config_param :unit, :enum, list: [:minute, :hour, :day], default: :minute,
|
15
|
+
desc: 'The interval time to monitor specified an unit (either of minute, hour, or day).'
|
16
|
+
config_param :output_per_tag, :bool, default: false,
|
17
|
+
desc: 'Emit for each input tag. tag_prefix must be specified together.'
|
18
|
+
config_param :aggregate, :enum, list: [:tag, :all], default: :tag,
|
19
|
+
desc: 'Calculate in each input tag separetely, or all records in a mass.'
|
20
|
+
config_param :tag, :string, default: 'datacount',
|
21
|
+
desc: 'The output tag.'
|
22
|
+
config_param :tag_prefix, :string, default: nil,
|
23
|
+
desc: 'The prefix string which will be added to the input tag.'
|
24
|
+
config_param :input_tag_remove_prefix, :string, default: nil,
|
25
|
+
desc: 'The prefix string which will be removed from the input tag.'
|
35
26
|
config_param :count_key, :string,
|
36
|
-
:
|
37
|
-
config_param :outcast_unmatched, :bool, :
|
38
|
-
:
|
39
|
-
config_param :output_messages, :bool, :
|
40
|
-
:
|
41
|
-
config_param :store_file, :string, :
|
42
|
-
:
|
27
|
+
desc: 'The key to count in the event record.'
|
28
|
+
config_param :outcast_unmatched, :bool, default: false,
|
29
|
+
desc: 'Specify yes if you do not want to include \'unmatched\' counts into percentage. '
|
30
|
+
config_param :output_messages, :bool, default: false,
|
31
|
+
desc: 'Specify yes if you want to get tested messages.'
|
32
|
+
config_param :store_file, :string, default: nil,
|
33
|
+
obsoleted: 'Use store_storage parameter instead.',
|
34
|
+
desc: 'Store internal data into a file of the given path on shutdown, and load on starting.'
|
35
|
+
config_param :store_storage, :bool, default: false,
|
36
|
+
desc: 'Store internal data into a storage on shutdown, and load on starting.'
|
43
37
|
|
44
38
|
# pattern0 reserved as unmatched counts
|
45
39
|
config_param :pattern1, :string, # string: NAME REGEXP
|
46
|
-
:
|
40
|
+
desc: 'Specify RegexpName and Regexp. format: NAME REGEXP'
|
47
41
|
(2..PATTERN_MAX_NUM).each do |i|
|
48
|
-
config_param ('pattern' + i.to_s).to_sym, :string, :
|
49
|
-
:
|
42
|
+
config_param ('pattern' + i.to_s).to_sym, :string, default: nil, # NAME REGEXP
|
43
|
+
desc: "Specify RegexpName and Regexp. format: NAME REGEXP"
|
44
|
+
end
|
45
|
+
|
46
|
+
config_section :storage do
|
47
|
+
config_set_default :usage, 'resume'
|
48
|
+
config_set_default :@type, DEFAULT_STORAGE_TYPE
|
50
49
|
end
|
51
50
|
|
52
51
|
attr_accessor :tick
|
@@ -62,28 +61,21 @@ class Fluent::DataCounterOutput < Fluent::Output
|
|
62
61
|
@tick = @count_interval.to_i
|
63
62
|
else
|
64
63
|
@tick = case @unit
|
65
|
-
when
|
66
|
-
when
|
67
|
-
when
|
64
|
+
when :minute then 60
|
65
|
+
when :hour then 3600
|
66
|
+
when :day then 86400
|
68
67
|
else
|
69
|
-
raise RuntimeError, "
|
68
|
+
raise RuntimeError, "BUG: unknown unit: #{@unit}"
|
70
69
|
end
|
71
70
|
end
|
72
71
|
|
73
|
-
@aggregate = case @aggregate
|
74
|
-
when 'tag' then :tag
|
75
|
-
when 'all' then :all
|
76
|
-
else
|
77
|
-
raise Fluent::ConfigError, "datacounter aggregate allows tag/all"
|
78
|
-
end
|
79
|
-
|
80
72
|
@patterns = [[0, 'unmatched', nil]]
|
81
73
|
pattern_names = ['unmatched']
|
82
74
|
|
83
75
|
pattern_keys = conf.keys.select{|k| k =~ /^pattern(\d+)$/}
|
84
76
|
invalids = pattern_keys.select{|arg| arg =~ /^pattern(\d+)/ and not (1..PATTERN_MAX_NUM).include?($1.to_i)}
|
85
77
|
if invalids.size > 0
|
86
|
-
log.warn "invalid number patterns (valid pattern number:1-20)
|
78
|
+
log.warn "invalid number patterns (valid pattern number:1-20)", invalids: invalids
|
87
79
|
end
|
88
80
|
(1..PATTERN_MAX_NUM).each do |i|
|
89
81
|
next unless conf["pattern#{i}"]
|
@@ -109,28 +101,34 @@ class Fluent::DataCounterOutput < Fluent::Output
|
|
109
101
|
@removed_length = @removed_prefix_string.length
|
110
102
|
end
|
111
103
|
|
112
|
-
if @
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
104
|
+
if @store_storage
|
105
|
+
@storage = storage_create(usage: 'resume')
|
106
|
+
end
|
107
|
+
|
108
|
+
if system_config.workers > 1
|
109
|
+
log.warn "Fluentd is now working with multi process workers, and datacounter plugin will produce counter results in each separeted processes."
|
117
110
|
end
|
118
111
|
|
119
112
|
@counts = count_initialized
|
120
113
|
@mutex = Mutex.new
|
121
114
|
end
|
122
115
|
|
116
|
+
def multi_workers_ready?
|
117
|
+
true
|
118
|
+
end
|
119
|
+
|
123
120
|
def start
|
124
121
|
super
|
125
|
-
|
126
|
-
|
122
|
+
|
123
|
+
load_status(@tick) if @store_storage
|
124
|
+
|
125
|
+
@last_checked = Fluent::Engine.now
|
126
|
+
timer_execute(:out_datacounter_timer, @tick, &method(:watch))
|
127
127
|
end
|
128
128
|
|
129
129
|
def shutdown
|
130
|
+
save_status() if @store_storage
|
130
131
|
super
|
131
|
-
@watcher.terminate
|
132
|
-
@watcher.join
|
133
|
-
save_status(@store_file) if @store_file
|
134
132
|
end
|
135
133
|
|
136
134
|
def count_initialized(keys=nil)
|
@@ -235,39 +233,28 @@ class Fluent::DataCounterOutput < Fluent::Output
|
|
235
233
|
end
|
236
234
|
|
237
235
|
def flush_emit(step)
|
236
|
+
time = Fluent::Engine.now
|
238
237
|
if @output_per_tag
|
239
238
|
# tag - message maps
|
240
|
-
time = Fluent::Engine.now
|
241
239
|
flush_per_tags(step).each do |tag,message|
|
242
240
|
router.emit(@tag_prefix_string + tag, time, message)
|
243
241
|
end
|
244
242
|
else
|
245
243
|
message = flush(step)
|
246
244
|
if message.keys.size > 0
|
247
|
-
router.emit(@tag,
|
245
|
+
router.emit(@tag, time, message)
|
248
246
|
end
|
249
247
|
end
|
250
248
|
end
|
251
249
|
|
252
|
-
def start_watch
|
253
|
-
# for internal, or tests only
|
254
|
-
@watcher = Thread.new(&method(:watch))
|
255
|
-
end
|
256
|
-
|
257
250
|
def watch
|
258
251
|
# instance variable, and public accessable, for test
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
if Fluent::Engine.now - @last_checked >= @tick
|
263
|
-
now = Fluent::Engine.now
|
264
|
-
flush_emit(now - @last_checked)
|
265
|
-
@last_checked = now
|
266
|
-
end
|
267
|
-
end
|
252
|
+
now = Fluent::Engine.now
|
253
|
+
flush_emit(now - @last_checked)
|
254
|
+
@last_checked = now
|
268
255
|
end
|
269
256
|
|
270
|
-
def
|
257
|
+
def process(tag, es)
|
271
258
|
c = [0] * @patterns.length
|
272
259
|
|
273
260
|
es.each do |time,record|
|
@@ -285,65 +272,72 @@ class Fluent::DataCounterOutput < Fluent::Output
|
|
285
272
|
c[0] += 1 unless matched
|
286
273
|
end
|
287
274
|
countups(tag, c)
|
288
|
-
|
289
|
-
chain.next
|
290
275
|
end
|
291
276
|
|
292
|
-
# Store internal status into a
|
277
|
+
# Store internal status into a storage
|
293
278
|
#
|
294
|
-
|
295
|
-
def save_status(file_path)
|
279
|
+
def save_status()
|
296
280
|
begin
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
281
|
+
@saved_at = Fluent::Engine.now
|
282
|
+
@saved_duration = @saved_at - @last_checked
|
283
|
+
patterns = @patterns.map{|idx, label, regexp|
|
284
|
+
if regexp
|
285
|
+
[idx, label, regexp.source]
|
286
|
+
else
|
287
|
+
[idx, label, regexp]
|
288
|
+
end
|
289
|
+
}
|
290
|
+
value = {
|
291
|
+
"counts" => @counts,
|
292
|
+
"saved_at" => @saved_at,
|
293
|
+
"saved_duration" => @saved_duration,
|
294
|
+
"aggregate" => @aggregate,
|
295
|
+
"count_key" => @count_key.to_s,
|
296
|
+
"patterns" => patterns,
|
297
|
+
}
|
298
|
+
@storage.put(:stored_value, value)
|
309
299
|
rescue => e
|
310
|
-
log.warn "
|
300
|
+
log.warn "Can't write store_storage", error: e
|
311
301
|
end
|
312
302
|
end
|
313
303
|
|
314
|
-
# Load internal status from a
|
304
|
+
# Load internal status from a storage
|
315
305
|
#
|
316
|
-
# @param [String] file_path
|
317
306
|
# @param [Interger] tick The count interval
|
318
|
-
def load_status(
|
319
|
-
|
307
|
+
def load_status(tick)
|
308
|
+
stored = @storage.get(:stored_value)
|
309
|
+
return unless stored
|
320
310
|
|
321
311
|
begin
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
stored[:count_key] == @count_key and
|
326
|
-
stored[:patterns] == @patterns
|
327
|
-
|
328
|
-
if Fluent::Engine.now <= stored[:saved_at] + tick
|
329
|
-
@mutex.synchronize {
|
330
|
-
@counts = stored[:counts]
|
331
|
-
@saved_at = stored[:saved_at]
|
332
|
-
@saved_duration = stored[:saved_duration]
|
333
|
-
|
334
|
-
# skip the saved duration to continue counting
|
335
|
-
@last_checked = Fluent::Engine.now - @saved_duration
|
336
|
-
}
|
337
|
-
else
|
338
|
-
log.warn "out_datacounter: stored data is outdated. ignore stored data"
|
339
|
-
end
|
312
|
+
patterns = stored["patterns"].map{|idx, label, regexp|
|
313
|
+
if regexp
|
314
|
+
[idx, label, Regexp.compile(regexp)]
|
340
315
|
else
|
341
|
-
|
316
|
+
[idx, label, regexp]
|
342
317
|
end
|
318
|
+
}
|
319
|
+
|
320
|
+
if stored["aggregate"] == @aggregate.to_s and
|
321
|
+
stored["count_key"] == @count_key and
|
322
|
+
patterns == @patterns
|
323
|
+
|
324
|
+
if Fluent::Engine.now <= stored["saved_at"] + tick
|
325
|
+
@mutex.synchronize {
|
326
|
+
@counts = stored["counts"]
|
327
|
+
@saved_at = stored["saved_at"]
|
328
|
+
@saved_duration = stored["saved_duration"]
|
329
|
+
|
330
|
+
# skip the saved duration to continue counting
|
331
|
+
@last_checked = Fluent::Engine.now - @saved_duration
|
332
|
+
}
|
333
|
+
else
|
334
|
+
log.warn "stored data is outdated. ignore stored data"
|
335
|
+
end
|
336
|
+
else
|
337
|
+
log.warn "configuration param was changed. ignore stored data"
|
343
338
|
end
|
344
339
|
rescue => e
|
345
|
-
log.warn "
|
340
|
+
log.warn "Can't load store_storage", error: e
|
346
341
|
end
|
347
342
|
end
|
348
|
-
|
349
343
|
end
|
data/test/helper.rb
CHANGED
@@ -1,10 +1,22 @@
|
|
1
1
|
require 'helper'
|
2
|
+
require 'fluent/test/driver/output'
|
3
|
+
require 'fileutils'
|
4
|
+
require 'timecop'
|
5
|
+
|
2
6
|
|
3
7
|
class DataCounterOutputTest < Test::Unit::TestCase
|
4
8
|
def setup
|
5
9
|
Fluent::Test.setup
|
6
10
|
end
|
7
11
|
|
12
|
+
def teardown
|
13
|
+
Timecop.return
|
14
|
+
end
|
15
|
+
|
16
|
+
def config_element(name = 'test', argument = '', params = {}, elements = [])
|
17
|
+
Fluent::Config::Element.new(name, argument, params, elements)
|
18
|
+
end
|
19
|
+
|
8
20
|
CONFIG = %[
|
9
21
|
unit minute
|
10
22
|
aggregate tag
|
@@ -30,39 +42,39 @@ class DataCounterOutputTest < Test::Unit::TestCase
|
|
30
42
|
output_messages yes
|
31
43
|
]
|
32
44
|
|
33
|
-
def create_driver(conf = CONFIG
|
34
|
-
Fluent::Test::
|
45
|
+
def create_driver(conf = CONFIG)
|
46
|
+
Fluent::Test::Driver::Output.new(Fluent::Plugin::DataCounterOutput).configure(conf)
|
35
47
|
end
|
36
48
|
|
37
49
|
def test_configure
|
38
50
|
assert_raise(Fluent::ConfigError) {
|
39
|
-
|
51
|
+
create_driver('')
|
40
52
|
}
|
41
53
|
assert_raise(Fluent::ConfigError) {
|
42
|
-
|
54
|
+
create_driver %[
|
43
55
|
count_key field
|
44
56
|
]
|
45
57
|
}
|
46
58
|
assert_raise(Fluent::ConfigError) {
|
47
|
-
|
59
|
+
create_driver %[
|
48
60
|
pattern1 hoge ^1\\d\\d$
|
49
61
|
]
|
50
62
|
}
|
51
63
|
assert_raise(Fluent::ConfigError) {
|
52
|
-
|
64
|
+
create_driver %[
|
53
65
|
count_key field
|
54
66
|
pattern2 hoge ^1\\d\\d$
|
55
67
|
]
|
56
68
|
}
|
57
69
|
assert_raise(Fluent::ConfigError) {
|
58
|
-
|
70
|
+
create_driver %[
|
59
71
|
count_key field
|
60
72
|
pattern1 hoge ^1\\d\\d$
|
61
73
|
pattern4 pos ^4\\d\\d$
|
62
74
|
]
|
63
75
|
}
|
64
76
|
assert_raise(Fluent::ConfigError) {
|
65
|
-
|
77
|
+
create_driver %[
|
66
78
|
count_key field
|
67
79
|
pattern1 hoge ^1\\d\\d$
|
68
80
|
pattern2 hoge ^4\\d\\d$
|
@@ -91,7 +103,7 @@ class DataCounterOutputTest < Test::Unit::TestCase
|
|
91
103
|
pattern1 ok ^2\\d\\d$
|
92
104
|
]
|
93
105
|
assert_equal d1.instance.tick, d2.instance.tick
|
94
|
-
|
106
|
+
|
95
107
|
d = create_driver %[
|
96
108
|
count_interval 5m
|
97
109
|
count_key field
|
@@ -296,7 +308,7 @@ class DataCounterOutputTest < Test::Unit::TestCase
|
|
296
308
|
end
|
297
309
|
|
298
310
|
def test_pattern_num
|
299
|
-
assert_equal 20, Fluent::DataCounterOutput::PATTERN_MAX_NUM
|
311
|
+
assert_equal 20, Fluent::Plugin::DataCounterOutput::PATTERN_MAX_NUM
|
300
312
|
|
301
313
|
conf = %[
|
302
314
|
aggregate all
|
@@ -305,10 +317,10 @@ class DataCounterOutputTest < Test::Unit::TestCase
|
|
305
317
|
(1..20).each do |i|
|
306
318
|
conf += "pattern#{i} name#{i} ^#{i}$\n"
|
307
319
|
end
|
308
|
-
d = create_driver(conf
|
309
|
-
d.run do
|
320
|
+
d = create_driver(conf)
|
321
|
+
d.run(default_tag: 'test.max') do
|
310
322
|
(1..20).each do |j|
|
311
|
-
d.
|
323
|
+
d.feed({'field' => j})
|
312
324
|
end
|
313
325
|
end
|
314
326
|
r = d.instance.flush(60)
|
@@ -335,13 +347,13 @@ class DataCounterOutputTest < Test::Unit::TestCase
|
|
335
347
|
end
|
336
348
|
|
337
349
|
def test_emit
|
338
|
-
d1 = create_driver(CONFIG
|
339
|
-
d1.run do
|
350
|
+
d1 = create_driver(CONFIG)
|
351
|
+
d1.run(default_tag: 'test.tag1') do
|
340
352
|
60.times do
|
341
|
-
d1.
|
342
|
-
d1.
|
343
|
-
d1.
|
344
|
-
d1.
|
353
|
+
d1.feed({'target' => '200'})
|
354
|
+
d1.feed({'target' => '100'})
|
355
|
+
d1.feed({'target' => '200'})
|
356
|
+
d1.feed({'target' => '400'})
|
345
357
|
end
|
346
358
|
end
|
347
359
|
r1 = d1.instance.flush(60)
|
@@ -352,11 +364,11 @@ class DataCounterOutputTest < Test::Unit::TestCase
|
|
352
364
|
assert_equal 60, r1['tag1_status4xx_count']
|
353
365
|
assert_equal 1.0, r1['tag1_status4xx_rate']
|
354
366
|
assert_equal 25.0, r1['tag1_status4xx_percentage']
|
355
|
-
|
367
|
+
|
356
368
|
assert_equal 60, r1['tag1_unmatched_count']
|
357
369
|
assert_equal 1.0, r1['tag1_unmatched_rate']
|
358
370
|
assert_equal 25.0, r1['tag1_unmatched_percentage']
|
359
|
-
|
371
|
+
|
360
372
|
assert_equal 0, r1['tag1_status3xx_count']
|
361
373
|
assert_equal 0.0, r1['tag1_status3xx_rate']
|
362
374
|
assert_equal 0.0, r1['tag1_status3xx_percentage']
|
@@ -370,17 +382,17 @@ class DataCounterOutputTest < Test::Unit::TestCase
|
|
370
382
|
pattern1 ok 2\\d\\d
|
371
383
|
pattern2 redirect 3\\d\\d
|
372
384
|
output_messages yes
|
373
|
-
]
|
374
|
-
d2.run do
|
385
|
+
])
|
386
|
+
d2.run(default_tag: 'test.tag2') do
|
375
387
|
60.times do
|
376
|
-
d2.
|
377
|
-
d2.
|
388
|
+
d2.feed({'target' => '200'})
|
389
|
+
d2.feed({'target' => '300 200'})
|
378
390
|
end
|
391
|
+
d2.instance.flush_emit(120)
|
379
392
|
end
|
380
|
-
d2.
|
381
|
-
|
382
|
-
|
383
|
-
data = emits[0]
|
393
|
+
events = d2.events
|
394
|
+
assert_equal 1, events.length
|
395
|
+
data = events[0]
|
384
396
|
assert_equal 'datacount', data[0] # tag
|
385
397
|
assert_equal 120, data[2]['ok_count']
|
386
398
|
assert_equal 1.0, data[2]['ok_rate']
|
@@ -399,18 +411,18 @@ class DataCounterOutputTest < Test::Unit::TestCase
|
|
399
411
|
pattern1 ok 2\\d\\d
|
400
412
|
pattern2 redirect 3\\d\\d
|
401
413
|
outcast_unmatched yes
|
402
|
-
]
|
403
|
-
d3.run do
|
414
|
+
])
|
415
|
+
d3.run(default_tag: 'test.tag2') do
|
404
416
|
60.times do
|
405
|
-
d3.
|
406
|
-
d3.
|
407
|
-
d3.
|
417
|
+
d3.feed({'target' => '200'})
|
418
|
+
d3.feed({'target' => '300'})
|
419
|
+
d3.feed({'target' => '400'})
|
408
420
|
end
|
421
|
+
d3.instance.flush_emit(180)
|
409
422
|
end
|
410
|
-
d3.
|
411
|
-
|
412
|
-
|
413
|
-
data = emits[0]
|
423
|
+
events = d3.events
|
424
|
+
assert_equal 1, events.length
|
425
|
+
data = events[0]
|
414
426
|
assert_equal 'datacount', data[0] # tag
|
415
427
|
assert_equal 60, data[2]['tag2_unmatched_count']
|
416
428
|
assert_nil data[2]['tag2_unmatched_percentage']
|
@@ -426,18 +438,18 @@ class DataCounterOutputTest < Test::Unit::TestCase
|
|
426
438
|
pattern2 redirect 3\\d\\d
|
427
439
|
outcast_unmatched true
|
428
440
|
output_messages true
|
429
|
-
]
|
430
|
-
d3.run do
|
441
|
+
])
|
442
|
+
d3.run(default_tag: 'test.tag2') do
|
431
443
|
60.times do
|
432
|
-
d3.
|
433
|
-
d3.
|
434
|
-
d3.
|
444
|
+
d3.feed({'target' => '200'})
|
445
|
+
d3.feed({'target' => '300'})
|
446
|
+
d3.feed({'target' => '400'})
|
435
447
|
end
|
448
|
+
d3.instance.flush_emit(180)
|
436
449
|
end
|
437
|
-
d3.
|
438
|
-
|
439
|
-
|
440
|
-
data = emits[0]
|
450
|
+
events = d3.events
|
451
|
+
assert_equal 1, events.length
|
452
|
+
data = events[0]
|
441
453
|
assert_equal 'datacount', data[0] # tag
|
442
454
|
assert_equal 60, data[2]['unmatched_count']
|
443
455
|
assert_nil data[2]['unmatched_percentage']
|
@@ -449,13 +461,13 @@ class DataCounterOutputTest < Test::Unit::TestCase
|
|
449
461
|
end
|
450
462
|
|
451
463
|
def test_emit_output_per_tag
|
452
|
-
d1 = create_driver(CONFIG_OUTPUT_PER_TAG
|
453
|
-
d1.run do
|
464
|
+
d1 = create_driver(CONFIG_OUTPUT_PER_TAG)
|
465
|
+
d1.run(default_tag: 'test.tag1') do
|
454
466
|
60.times do
|
455
|
-
d1.
|
456
|
-
d1.
|
457
|
-
d1.
|
458
|
-
d1.
|
467
|
+
d1.feed({'target' => '200'})
|
468
|
+
d1.feed({'target' => '100'})
|
469
|
+
d1.feed({'target' => '200'})
|
470
|
+
d1.feed({'target' => '400'})
|
459
471
|
end
|
460
472
|
end
|
461
473
|
r1 = d1.instance.flush_per_tags(60)
|
@@ -468,11 +480,11 @@ class DataCounterOutputTest < Test::Unit::TestCase
|
|
468
480
|
assert_equal 60, r['status4xx_count']
|
469
481
|
assert_equal 1.0, r['status4xx_rate']
|
470
482
|
assert_equal 25.0, r['status4xx_percentage']
|
471
|
-
|
483
|
+
|
472
484
|
assert_equal 60, r['unmatched_count']
|
473
485
|
assert_equal 1.0, r['unmatched_rate']
|
474
486
|
assert_equal 25.0, r['unmatched_percentage']
|
475
|
-
|
487
|
+
|
476
488
|
assert_equal 0, r['status3xx_count']
|
477
489
|
assert_equal 0.0, r['status3xx_rate']
|
478
490
|
assert_equal 0.0, r['status3xx_percentage']
|
@@ -489,17 +501,17 @@ class DataCounterOutputTest < Test::Unit::TestCase
|
|
489
501
|
pattern2 redirect 3\\d\\d
|
490
502
|
output_per_tag yes
|
491
503
|
tag_prefix d
|
492
|
-
]
|
493
|
-
d2.run do
|
504
|
+
])
|
505
|
+
d2.run(default_tag: 'test.tag2') do
|
494
506
|
60.times do
|
495
|
-
d2.
|
496
|
-
d2.
|
507
|
+
d2.feed({'target' => '200'})
|
508
|
+
d2.feed({'target' => '300 200'})
|
497
509
|
end
|
510
|
+
d2.instance.flush_emit(120)
|
498
511
|
end
|
499
|
-
d2.
|
500
|
-
|
501
|
-
|
502
|
-
data = emits[0]
|
512
|
+
events = d2.events
|
513
|
+
assert_equal 1, events.length
|
514
|
+
data = events[0]
|
503
515
|
assert_equal 'd.all', data[0] # tag
|
504
516
|
assert_equal 120, data[2]['ok_count']
|
505
517
|
assert_equal 1.0, data[2]['ok_rate']
|
@@ -519,18 +531,18 @@ class DataCounterOutputTest < Test::Unit::TestCase
|
|
519
531
|
outcast_unmatched yes
|
520
532
|
output_per_tag yes
|
521
533
|
tag_prefix d
|
522
|
-
]
|
523
|
-
d3.run do
|
534
|
+
])
|
535
|
+
d3.run(default_tag: 'test.tag2') do
|
524
536
|
60.times do
|
525
|
-
d3.
|
526
|
-
d3.
|
527
|
-
d3.
|
537
|
+
d3.feed({'target' => '200'})
|
538
|
+
d3.feed({'target' => '300'})
|
539
|
+
d3.feed({'target' => '400'})
|
528
540
|
end
|
541
|
+
d3.instance.flush_emit(180)
|
529
542
|
end
|
530
|
-
d3.
|
531
|
-
|
532
|
-
|
533
|
-
data = emits[0]
|
543
|
+
events = d3.events
|
544
|
+
assert_equal 1, events.length
|
545
|
+
data = events[0]
|
534
546
|
assert_equal 'd.tag2', data[0] # tag
|
535
547
|
assert_equal 60, data[2]['unmatched_count']
|
536
548
|
assert_nil data[2]['unmatched_percentage']
|
@@ -547,18 +559,18 @@ class DataCounterOutputTest < Test::Unit::TestCase
|
|
547
559
|
outcast_unmatched true
|
548
560
|
output_per_tag yes
|
549
561
|
tag_prefix ddd
|
550
|
-
]
|
551
|
-
d3.run do
|
562
|
+
])
|
563
|
+
d3.run(default_tag: 'test.tag2') do
|
552
564
|
60.times do
|
553
|
-
d3.
|
554
|
-
d3.
|
555
|
-
d3.
|
565
|
+
d3.feed({'target' => '200'})
|
566
|
+
d3.feed({'target' => '300'})
|
567
|
+
d3.feed({'target' => '400'})
|
556
568
|
end
|
569
|
+
d3.instance.flush_emit(180)
|
557
570
|
end
|
558
|
-
d3.
|
559
|
-
|
560
|
-
|
561
|
-
data = emits[0]
|
571
|
+
events = d3.events
|
572
|
+
assert_equal 1, events.length
|
573
|
+
data = events[0]
|
562
574
|
assert_equal 'ddd.all', data[0] # tag
|
563
575
|
assert_equal 60, data[2]['unmatched_count']
|
564
576
|
assert_nil data[2]['unmatched_percentage']
|
@@ -576,7 +588,7 @@ class DataCounterOutputTest < Test::Unit::TestCase
|
|
576
588
|
['count', 'rate'].map{|a| p + '_' + a}
|
577
589
|
}.flatten
|
578
590
|
|
579
|
-
d = create_driver(CONFIG
|
591
|
+
d = create_driver(CONFIG)
|
580
592
|
# CONFIG = %[
|
581
593
|
# unit minute
|
582
594
|
# aggregate tag
|
@@ -587,27 +599,27 @@ class DataCounterOutputTest < Test::Unit::TestCase
|
|
587
599
|
# pattern3 status4xx ^4\\d\\d$
|
588
600
|
# pattern4 status5xx ^5\\d\\d$
|
589
601
|
# ]
|
590
|
-
d.run do
|
602
|
+
d.run(default_tag: 'test.tag1') do
|
591
603
|
60.times do
|
592
|
-
d.
|
593
|
-
d.
|
594
|
-
d.
|
595
|
-
d.
|
604
|
+
d.feed({'target' => '200'})
|
605
|
+
d.feed({'target' => '100'})
|
606
|
+
d.feed({'target' => '200'})
|
607
|
+
d.feed({'target' => '400'})
|
596
608
|
end
|
609
|
+
d.instance.flush_emit(60)
|
610
|
+
assert_equal 1, d.events.size
|
611
|
+
r1 = d.events[0][2]
|
612
|
+
assert_equal fields, r1.keys
|
613
|
+
|
614
|
+
d.instance.flush_emit(60)
|
615
|
+
assert_equal 2, d.events.size # +1
|
616
|
+
r2 = d.events[1][2]
|
617
|
+
assert_equal fields_without_percentage, r2.keys
|
618
|
+
assert_equal [0]*10, r2.values
|
619
|
+
|
620
|
+
d.instance.flush_emit(60)
|
621
|
+
assert_equal 2, d.events.size # +0
|
597
622
|
end
|
598
|
-
d.instance.flush_emit(60)
|
599
|
-
assert_equal 1, d.emits.size
|
600
|
-
r1 = d.emits[0][2]
|
601
|
-
assert_equal fields, r1.keys
|
602
|
-
|
603
|
-
d.instance.flush_emit(60)
|
604
|
-
assert_equal 2, d.emits.size # +1
|
605
|
-
r2 = d.emits[1][2]
|
606
|
-
assert_equal fields_without_percentage, r2.keys
|
607
|
-
assert_equal [0]*10, r2.values
|
608
|
-
|
609
|
-
d.instance.flush_emit(60)
|
610
|
-
assert_equal 2, d.emits.size # +0
|
611
623
|
end
|
612
624
|
def test_zer_tags_per_tag
|
613
625
|
fields = (['unmatched','status2xx','status3xx','status4xx','status5xx'].map{|p|
|
@@ -617,7 +629,7 @@ class DataCounterOutputTest < Test::Unit::TestCase
|
|
617
629
|
['count', 'rate'].map{|a| p + '_' + a}
|
618
630
|
}.flatten + ['messages']).sort
|
619
631
|
|
620
|
-
d = create_driver(CONFIG_OUTPUT_PER_TAG
|
632
|
+
d = create_driver(CONFIG_OUTPUT_PER_TAG)
|
621
633
|
# CONFIG_OUTPUT_PER_TAG = %[
|
622
634
|
# unit minute
|
623
635
|
# aggregate tag
|
@@ -631,84 +643,99 @@ class DataCounterOutputTest < Test::Unit::TestCase
|
|
631
643
|
# pattern4 status5xx ^5\\d\\d$
|
632
644
|
# output_messages yes
|
633
645
|
# ]
|
634
|
-
d.run do
|
646
|
+
d.run(default_tag: 'test.tag1') do
|
635
647
|
60.times do
|
636
|
-
d.
|
637
|
-
d.
|
638
|
-
d.
|
639
|
-
d.
|
648
|
+
d.feed({'target' => '200'})
|
649
|
+
d.feed({'target' => '100'})
|
650
|
+
d.feed({'target' => '200'})
|
651
|
+
d.feed({'target' => '400'})
|
640
652
|
end
|
653
|
+
d.instance.flush_emit(60)
|
654
|
+
assert_equal 1, d.events.size
|
655
|
+
r1 = d.events[0][2]
|
656
|
+
assert_equal fields, r1.keys.sort
|
657
|
+
|
658
|
+
d.instance.flush_emit(60)
|
659
|
+
assert_equal 2, d.events.size # +1
|
660
|
+
r2 = d.events[1][2]
|
661
|
+
assert_equal fields_without_percentage, r2.keys.sort
|
662
|
+
assert_equal [0]*11, r2.values # (_count, _rate)x5 + messages
|
663
|
+
|
664
|
+
d.instance.flush_emit(60)
|
665
|
+
assert_equal 2, d.events.size # +0
|
641
666
|
end
|
642
|
-
d.instance.flush_emit(60)
|
643
|
-
assert_equal 1, d.emits.size
|
644
|
-
r1 = d.emits[0][2]
|
645
|
-
assert_equal fields, r1.keys.sort
|
646
|
-
|
647
|
-
d.instance.flush_emit(60)
|
648
|
-
assert_equal 2, d.emits.size # +1
|
649
|
-
r2 = d.emits[1][2]
|
650
|
-
assert_equal fields_without_percentage, r2.keys.sort
|
651
|
-
assert_equal [0]*11, r2.values # (_count, _rate)x5 + messages
|
652
|
-
|
653
|
-
d.instance.flush_emit(60)
|
654
|
-
assert_equal 2, d.emits.size # +0
|
655
667
|
end
|
656
668
|
|
657
|
-
def
|
669
|
+
def test_store_storage
|
658
670
|
dir = "test/tmp"
|
659
671
|
Dir.mkdir dir unless Dir.exist? dir
|
660
|
-
|
661
|
-
|
662
|
-
|
672
|
+
storage_path = "#{dir}/test.dat"
|
673
|
+
FileUtils.rm_rf(storage_path)
|
674
|
+
|
675
|
+
config = {
|
676
|
+
"unit" => "minute",
|
677
|
+
"aggregate" => "tag",
|
678
|
+
"input_tag_remove_prefix" => "test",
|
679
|
+
"count_key" => " target",
|
680
|
+
"pattern1" => "status2xx ^2\\d\\d$",
|
681
|
+
"pattern2" => "status3xx ^3\\d\\d$",
|
682
|
+
"pattern3" => "status4xx ^4\\d\\d$",
|
683
|
+
"pattern4" => "status5xx ^5\\d\\d$",
|
684
|
+
"store_storage" => true
|
685
|
+
}
|
686
|
+
storage_conf = config_element('storage', 'resume', {'@type' => 'local', '@id' => 'test-01', 'path' => storage_path, 'persistent' => 'true'})
|
687
|
+
conf = config_element('ROOT', '', config, [storage_conf])
|
663
688
|
# test store
|
664
|
-
d = create_driver(
|
665
|
-
|
689
|
+
d = create_driver(conf)
|
690
|
+
time = Fluent::Engine.now
|
691
|
+
d.run(default_tag: 'test.input') do
|
666
692
|
d.instance.flush_emit(60)
|
667
|
-
d.
|
668
|
-
d.
|
669
|
-
d.
|
670
|
-
d.instance.shutdown
|
693
|
+
d.feed(time, {'target' => 1})
|
694
|
+
d.feed(time, {'target' => 1})
|
695
|
+
d.feed(time, {'target' => 1})
|
671
696
|
end
|
672
697
|
stored_counts = d.instance.counts
|
673
698
|
stored_saved_at = d.instance.saved_at
|
674
699
|
stored_saved_duration = d.instance.saved_duration
|
675
|
-
assert File.exist?
|
700
|
+
assert File.exist?(storage_path)
|
676
701
|
|
677
702
|
# test load
|
678
|
-
d = create_driver(
|
679
|
-
|
703
|
+
d = create_driver(conf)
|
704
|
+
loaded_counts = {}
|
705
|
+
loaded_saved_at = nil
|
706
|
+
loaded_saved_duration = nil
|
707
|
+
d.run(default_tag: 'test.input') do
|
680
708
|
loaded_counts = d.instance.counts
|
681
709
|
loaded_saved_at = d.instance.saved_at
|
682
710
|
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
711
|
end
|
712
|
+
assert_equal stored_counts, loaded_counts
|
713
|
+
assert_equal stored_saved_at, loaded_saved_at
|
714
|
+
assert_equal stored_saved_duration, loaded_saved_duration
|
687
715
|
|
688
716
|
# test not to load if config is changed
|
689
|
-
d = create_driver(
|
690
|
-
d.run do
|
717
|
+
d = create_driver(conf.merge("count_key" => "foobar", "store_storage" => true))
|
718
|
+
d.run(default_tag: 'test.input') do
|
691
719
|
loaded_counts = d.instance.counts
|
692
720
|
loaded_saved_at = d.instance.saved_at
|
693
721
|
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
722
|
end
|
723
|
+
assert_equal({}, loaded_counts)
|
724
|
+
assert_equal(nil, loaded_saved_at)
|
725
|
+
assert_equal(nil, loaded_saved_duration)
|
698
726
|
|
699
727
|
# test not to load if stored data is outdated.
|
700
|
-
|
701
|
-
d = create_driver(
|
702
|
-
d.run do
|
728
|
+
Timecop.freeze(Time.now + 61) # jump more than count_interval
|
729
|
+
d = create_driver(conf.merge("store_storage" => true))
|
730
|
+
d.run(default_tag: 'test.input') do
|
703
731
|
loaded_counts = d.instance.counts
|
704
732
|
loaded_saved_at = d.instance.saved_at
|
705
733
|
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
734
|
end
|
710
|
-
|
735
|
+
assert_equal({}, loaded_counts)
|
736
|
+
assert_equal(nil, loaded_saved_at)
|
737
|
+
assert_equal(nil, loaded_saved_duration)
|
711
738
|
|
712
|
-
|
739
|
+
FileUtils.rm_rf(storage_path)
|
713
740
|
end
|
714
741
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fluent-plugin-datacounter
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- TAGOMORI Satoshi
|
@@ -14,16 +14,22 @@ dependencies:
|
|
14
14
|
name: fluentd
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 0.14.8
|
17
20
|
- - "<"
|
18
21
|
- !ruby/object:Gem::Version
|
19
|
-
version:
|
22
|
+
version: '2'
|
20
23
|
type: :runtime
|
21
24
|
prerelease: false
|
22
25
|
version_requirements: !ruby/object:Gem::Requirement
|
23
26
|
requirements:
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: 0.14.8
|
24
30
|
- - "<"
|
25
31
|
- !ruby/object:Gem::Version
|
26
|
-
version:
|
32
|
+
version: '2'
|
27
33
|
- !ruby/object:Gem::Dependency
|
28
34
|
name: bundler
|
29
35
|
requirement: !ruby/object:Gem::Requirement
|
@@ -67,7 +73,7 @@ dependencies:
|
|
67
73
|
- !ruby/object:Gem::Version
|
68
74
|
version: '0'
|
69
75
|
- !ruby/object:Gem::Dependency
|
70
|
-
name:
|
76
|
+
name: timecop
|
71
77
|
requirement: !ruby/object:Gem::Requirement
|
72
78
|
requirements:
|
73
79
|
- - ">="
|