fluent-plugin-datacounter 0.5.0 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
- - ">="
|