fluentd 0.14.17-x86-mingw32 → 1.3.1-x86-mingw32
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of fluentd might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/.travis.yml +16 -5
- data/ADOPTERS.md +5 -0
- data/{ChangeLog → CHANGELOG.md} +495 -6
- data/CONTRIBUTING.md +5 -2
- data/GOVERNANCE.md +55 -0
- data/LICENSE +202 -0
- data/MAINTAINERS.md +7 -5
- data/README.md +17 -10
- data/bin/fluent-ca-generate +6 -0
- data/example/counter.conf +18 -0
- data/example/secondary_file.conf +3 -2
- data/fluentd.gemspec +3 -3
- data/lib/fluent/agent.rb +1 -1
- data/lib/fluent/command/binlog_reader.rb +11 -2
- data/lib/fluent/command/ca_generate.rb +181 -0
- data/lib/fluent/command/cat.rb +28 -15
- data/lib/fluent/command/debug.rb +4 -4
- data/lib/fluent/command/fluentd.rb +2 -2
- data/lib/fluent/command/plugin_config_formatter.rb +24 -2
- data/lib/fluent/command/plugin_generator.rb +26 -8
- data/lib/fluent/config/configure_proxy.rb +7 -1
- data/lib/fluent/config/dsl.rb +8 -5
- data/lib/fluent/config/element.rb +5 -0
- data/lib/fluent/config/literal_parser.rb +7 -1
- data/lib/fluent/config/types.rb +28 -2
- data/lib/fluent/config/v1_parser.rb +1 -2
- data/lib/fluent/configurable.rb +1 -0
- data/lib/fluent/counter.rb +23 -0
- data/lib/fluent/counter/base_socket.rb +46 -0
- data/lib/fluent/counter/client.rb +297 -0
- data/lib/fluent/counter/error.rb +86 -0
- data/lib/fluent/counter/mutex_hash.rb +163 -0
- data/lib/fluent/counter/server.rb +273 -0
- data/lib/fluent/counter/store.rb +205 -0
- data/lib/fluent/counter/validator.rb +145 -0
- data/lib/fluent/env.rb +1 -0
- data/lib/fluent/event_router.rb +1 -1
- data/lib/fluent/log.rb +119 -29
- data/lib/fluent/plugin/base.rb +12 -0
- data/lib/fluent/plugin/buf_file.rb +20 -16
- data/lib/fluent/plugin/buffer.rb +130 -32
- data/lib/fluent/plugin/buffer/file_chunk.rb +23 -4
- data/lib/fluent/plugin/compressable.rb +1 -1
- data/lib/fluent/plugin/filter_grep.rb +135 -21
- data/lib/fluent/plugin/filter_parser.rb +13 -2
- data/lib/fluent/plugin/filter_record_transformer.rb +16 -14
- data/lib/fluent/plugin/formatter_stdout.rb +3 -2
- data/lib/fluent/plugin/formatter_tsv.rb +5 -1
- data/lib/fluent/plugin/in_debug_agent.rb +8 -1
- data/lib/fluent/plugin/in_forward.rb +1 -1
- data/lib/fluent/plugin/in_http.rb +84 -3
- data/lib/fluent/plugin/in_monitor_agent.rb +7 -1
- data/lib/fluent/plugin/in_syslog.rb +31 -10
- data/lib/fluent/plugin/in_tail.rb +142 -53
- data/lib/fluent/plugin/in_tcp.rb +5 -6
- data/lib/fluent/plugin/in_udp.rb +6 -2
- data/lib/fluent/plugin/in_unix.rb +1 -1
- data/lib/fluent/plugin/multi_output.rb +1 -0
- data/lib/fluent/plugin/out_copy.rb +25 -2
- data/lib/fluent/plugin/out_file.rb +26 -7
- data/lib/fluent/plugin/out_forward.rb +81 -42
- data/lib/fluent/plugin/out_secondary_file.rb +2 -2
- data/lib/fluent/plugin/out_stdout.rb +0 -1
- data/lib/fluent/plugin/out_stream.rb +1 -1
- data/lib/fluent/plugin/output.rb +221 -57
- data/lib/fluent/plugin/parser_apache.rb +1 -1
- data/lib/fluent/plugin/parser_apache2.rb +5 -1
- data/lib/fluent/plugin/parser_apache_error.rb +1 -1
- data/lib/fluent/plugin/parser_json.rb +10 -3
- data/lib/fluent/plugin/parser_ltsv.rb +7 -0
- data/lib/fluent/plugin/parser_multiline.rb +2 -1
- data/lib/fluent/plugin/parser_nginx.rb +1 -1
- data/lib/fluent/plugin/parser_none.rb +1 -0
- data/lib/fluent/plugin/parser_regexp.rb +15 -14
- data/lib/fluent/plugin/parser_syslog.rb +9 -5
- data/lib/fluent/plugin_helper.rb +2 -0
- data/lib/fluent/plugin_helper/cert_option.rb +28 -9
- data/lib/fluent/plugin_helper/compat_parameters.rb +3 -1
- data/lib/fluent/plugin_helper/counter.rb +51 -0
- data/lib/fluent/plugin_helper/event_loop.rb +9 -0
- data/lib/fluent/plugin_helper/record_accessor.rb +210 -0
- data/lib/fluent/plugin_helper/retry_state.rb +15 -7
- data/lib/fluent/plugin_helper/server.rb +87 -25
- data/lib/fluent/plugin_helper/socket_option.rb +5 -2
- data/lib/fluent/plugin_helper/timer.rb +8 -7
- data/lib/fluent/root_agent.rb +18 -9
- data/lib/fluent/supervisor.rb +63 -23
- data/lib/fluent/system_config.rb +30 -2
- data/lib/fluent/test/helpers.rb +1 -1
- data/lib/fluent/time.rb +15 -7
- data/lib/fluent/timezone.rb +26 -2
- data/lib/fluent/version.rb +1 -1
- data/templates/new_gem/README.md.erb +2 -2
- data/templates/new_gem/lib/fluent/plugin/filter.rb.erb +1 -1
- data/templates/new_gem/lib/fluent/plugin/input.rb.erb +1 -1
- data/templates/new_gem/lib/fluent/plugin/output.rb.erb +1 -1
- data/templates/new_gem/lib/fluent/plugin/parser.rb.erb +4 -4
- data/test/command/test_ca_generate.rb +70 -0
- data/test/command/test_fluentd.rb +2 -2
- data/test/command/test_plugin_config_formatter.rb +8 -7
- data/test/command/test_plugin_generator.rb +65 -39
- data/test/config/test_config_parser.rb +7 -2
- data/test/config/test_configurable.rb +7 -2
- data/test/config/test_configure_proxy.rb +41 -3
- data/test/config/test_dsl.rb +10 -10
- data/test/config/test_element.rb +10 -0
- data/test/config/test_literal_parser.rb +8 -0
- data/test/config/test_plugin_configuration.rb +56 -0
- data/test/config/test_system_config.rb +19 -1
- data/test/config/test_types.rb +37 -0
- data/test/counter/test_client.rb +559 -0
- data/test/counter/test_error.rb +44 -0
- data/test/counter/test_mutex_hash.rb +179 -0
- data/test/counter/test_server.rb +589 -0
- data/test/counter/test_store.rb +258 -0
- data/test/counter/test_validator.rb +137 -0
- data/test/plugin/test_buf_file.rb +124 -0
- data/test/plugin/test_buffer.rb +3 -2
- data/test/plugin/test_filter_grep.rb +580 -2
- data/test/plugin/test_filter_parser.rb +33 -2
- data/test/plugin/test_filter_record_transformer.rb +22 -1
- data/test/plugin/test_formatter_ltsv.rb +3 -0
- data/test/plugin/test_formatter_tsv.rb +68 -0
- data/test/plugin/test_in_debug_agent.rb +21 -0
- data/test/plugin/test_in_exec.rb +3 -5
- data/test/plugin/test_in_http.rb +178 -0
- data/test/plugin/test_in_monitor_agent.rb +1 -1
- data/test/plugin/test_in_syslog.rb +64 -0
- data/test/plugin/test_in_tail.rb +116 -6
- data/test/plugin/test_in_tcp.rb +21 -0
- data/test/plugin/test_in_udp.rb +78 -0
- data/test/plugin/test_metadata.rb +89 -0
- data/test/plugin/test_out_copy.rb +31 -0
- data/test/plugin/test_out_file.rb +108 -2
- data/test/plugin/test_out_forward.rb +195 -2
- data/test/plugin/test_out_secondary_file.rb +14 -0
- data/test/plugin/test_output.rb +159 -45
- data/test/plugin/test_output_as_buffered.rb +19 -0
- data/test/plugin/test_output_as_buffered_backup.rb +307 -0
- data/test/plugin/test_output_as_buffered_retries.rb +70 -0
- data/test/plugin/test_output_as_buffered_secondary.rb +1 -1
- data/test/plugin/test_parser_apache2.rb +1 -0
- data/test/plugin/test_parser_labeled_tsv.rb +17 -0
- data/test/plugin/test_parser_nginx.rb +40 -0
- data/test/plugin/test_parser_regexp.rb +6 -7
- data/test/plugin/test_parser_syslog.rb +155 -5
- data/test/plugin_helper/test_child_process.rb +4 -4
- data/test/plugin_helper/test_compat_parameters.rb +22 -0
- data/test/plugin_helper/test_record_accessor.rb +197 -0
- data/test/plugin_helper/test_retry_state.rb +20 -0
- data/test/plugin_helper/test_server.rb +30 -2
- data/test/test_config.rb +3 -3
- data/test/test_configdsl.rb +2 -2
- data/test/test_log.rb +51 -1
- data/test/test_root_agent.rb +33 -0
- data/test/test_supervisor.rb +105 -0
- metadata +68 -8
- data/COPYING +0 -14
data/test/plugin/test_output.rb
CHANGED
@@ -125,6 +125,10 @@ class OutputTest < Test::Unit::TestCase
|
|
125
125
|
def create_metadata(timekey: nil, tag: nil, variables: nil)
|
126
126
|
Fluent::Plugin::Buffer::Metadata.new(timekey, tag, variables)
|
127
127
|
end
|
128
|
+
def create_chunk(timekey: nil, tag: nil, variables: nil)
|
129
|
+
m = Fluent::Plugin::Buffer::Metadata.new(timekey, tag, variables)
|
130
|
+
Fluent::Plugin::Buffer::MemoryChunk.new(m)
|
131
|
+
end
|
128
132
|
def waiting(seconds)
|
129
133
|
begin
|
130
134
|
Timeout.timeout(seconds) do
|
@@ -219,7 +223,9 @@ class OutputTest < Test::Unit::TestCase
|
|
219
223
|
assert @i.terminated?
|
220
224
|
end
|
221
225
|
|
222
|
-
|
226
|
+
data(:new_api => :chunk,
|
227
|
+
:old_api => :metadata)
|
228
|
+
test '#extract_placeholders does nothing if chunk key is not specified' do |api|
|
223
229
|
@i.configure(config_element('ROOT', '', {}, [config_element('buffer', '')]))
|
224
230
|
assert !@i.chunk_key_time
|
225
231
|
assert !@i.chunk_key_tag
|
@@ -227,11 +233,17 @@ class OutputTest < Test::Unit::TestCase
|
|
227
233
|
tmpl = "/mypath/%Y/%m/%d/${tag}/${tag[1]}/${tag[2]}/${key1}/${key2}/tail"
|
228
234
|
t = event_time('2016-04-11 20:30:00 +0900')
|
229
235
|
v = {key1: "value1", key2: "value2"}
|
230
|
-
|
231
|
-
|
236
|
+
c = if api == :chunk
|
237
|
+
create_chunk(timekey: t, tag: 'fluentd.test.output', variables: v)
|
238
|
+
else
|
239
|
+
create_metadata(timekey: t, tag: 'fluentd.test.output', variables: v)
|
240
|
+
end
|
241
|
+
assert_equal tmpl, @i.extract_placeholders(tmpl, c)
|
232
242
|
end
|
233
243
|
|
234
|
-
|
244
|
+
data(:new_api => :chunk,
|
245
|
+
:old_api => :metadata)
|
246
|
+
test '#extract_placeholders can extract time if time key and range are configured' do |api|
|
235
247
|
@i.configure(config_element('ROOT', '', {}, [config_element('buffer', 'time', {'timekey' => 60*30, 'timekey_zone' => "+0900"})]))
|
236
248
|
assert @i.chunk_key_time
|
237
249
|
assert !@i.chunk_key_tag
|
@@ -239,11 +251,17 @@ class OutputTest < Test::Unit::TestCase
|
|
239
251
|
tmpl = "/mypath/%Y/%m/%d/%H-%M/${tag}/${tag[1]}/${tag[2]}/${key1}/${key2}/tail"
|
240
252
|
t = event_time('2016-04-11 20:30:00 +0900')
|
241
253
|
v = {key1: "value1", key2: "value2"}
|
242
|
-
|
243
|
-
|
254
|
+
c = if api == :chunk
|
255
|
+
create_chunk(timekey: t, tag: 'fluentd.test.output', variables: v)
|
256
|
+
else
|
257
|
+
create_metadata(timekey: t, tag: 'fluentd.test.output', variables: v)
|
258
|
+
end
|
259
|
+
assert_equal "/mypath/2016/04/11/20-30/${tag}/${tag[1]}/${tag[2]}/${key1}/${key2}/tail", @i.extract_placeholders(tmpl, c)
|
244
260
|
end
|
245
261
|
|
246
|
-
|
262
|
+
data(:new_api => :chunk,
|
263
|
+
:old_api => :metadata)
|
264
|
+
test '#extract_placeholders can extract tag and parts of tag if tag is configured' do |api|
|
247
265
|
@i.configure(config_element('ROOT', '', {}, [config_element('buffer', 'tag', {})]))
|
248
266
|
assert !@i.chunk_key_time
|
249
267
|
assert @i.chunk_key_tag
|
@@ -251,11 +269,17 @@ class OutputTest < Test::Unit::TestCase
|
|
251
269
|
tmpl = "/mypath/%Y/%m/%d/%H-%M/${tag}/${tag[1]}/${tag[2]}/${key1}/${key2}/tail"
|
252
270
|
t = event_time('2016-04-11 20:30:00 +0900')
|
253
271
|
v = {key1: "value1", key2: "value2"}
|
254
|
-
|
255
|
-
|
272
|
+
c = if api == :chunk
|
273
|
+
create_chunk(timekey: t, tag: 'fluentd.test.output', variables: v)
|
274
|
+
else
|
275
|
+
create_metadata(timekey: t, tag: 'fluentd.test.output', variables: v)
|
276
|
+
end
|
277
|
+
assert_equal "/mypath/%Y/%m/%d/%H-%M/fluentd.test.output/test/output/${key1}/${key2}/tail", @i.extract_placeholders(tmpl, c)
|
256
278
|
end
|
257
279
|
|
258
|
-
|
280
|
+
data(:new_api => :chunk,
|
281
|
+
:old_api => :metadata)
|
282
|
+
test '#extract_placeholders can extract variables if variables are configured' do |api|
|
259
283
|
@i.configure(config_element('ROOT', '', {}, [config_element('buffer', 'key1,key2', {})]))
|
260
284
|
assert !@i.chunk_key_time
|
261
285
|
assert !@i.chunk_key_tag
|
@@ -263,32 +287,95 @@ class OutputTest < Test::Unit::TestCase
|
|
263
287
|
tmpl = "/mypath/%Y/%m/%d/%H-%M/${tag}/${tag[1]}/${tag[2]}/${key1}/${key2}/tail"
|
264
288
|
t = event_time('2016-04-11 20:30:00 +0900')
|
265
289
|
v = {key1: "value1", key2: "value2"}
|
266
|
-
|
267
|
-
|
290
|
+
c = if api == :chunk
|
291
|
+
create_chunk(timekey: t, tag: 'fluentd.test.output', variables: v)
|
292
|
+
else
|
293
|
+
create_metadata(timekey: t, tag: 'fluentd.test.output', variables: v)
|
294
|
+
end
|
295
|
+
assert_equal "/mypath/%Y/%m/%d/%H-%M/${tag}/${tag[1]}/${tag[2]}/value1/value2/tail", @i.extract_placeholders(tmpl, c)
|
296
|
+
end
|
297
|
+
|
298
|
+
data(:new_api => :chunk,
|
299
|
+
:old_api => :metadata)
|
300
|
+
test '#extract_placeholders can extract nested variables if variables are configured with dot notation' do |api|
|
301
|
+
@i.configure(config_element('ROOT', '', {}, [config_element('buffer', 'key,$.nest.key', {})]))
|
302
|
+
assert !@i.chunk_key_time
|
303
|
+
assert !@i.chunk_key_tag
|
304
|
+
assert_equal ['key','$.nest.key'], @i.chunk_keys
|
305
|
+
tmpl = "/mypath/%Y/%m/%d/%H-%M/${tag}/${tag[1]}/${tag[2]}/${key}/${$.nest.key}/tail"
|
306
|
+
t = event_time('2016-04-11 20:30:00 +0900')
|
307
|
+
v = {:key => "value1", :"$.nest.key" => "value2"}
|
308
|
+
c = if api == :chunk
|
309
|
+
create_chunk(timekey: t, tag: 'fluentd.test.output', variables: v)
|
310
|
+
else
|
311
|
+
create_metadata(timekey: t, tag: 'fluentd.test.output', variables: v)
|
312
|
+
end
|
313
|
+
assert_equal "/mypath/%Y/%m/%d/%H-%M/${tag}/${tag[1]}/${tag[2]}/value1/value2/tail", @i.extract_placeholders(tmpl, c)
|
314
|
+
end
|
315
|
+
|
316
|
+
data(:new_api => :chunk,
|
317
|
+
:old_api => :metadata)
|
318
|
+
test '#extract_placeholders can extract all chunk keys if configured' do |api|
|
319
|
+
@i.configure(config_element('ROOT', '', {}, [config_element('buffer', 'time,tag,key1,key2', {'timekey' => 60*30, 'timekey_zone' => "+0900"})]))
|
320
|
+
assert @i.chunk_key_time
|
321
|
+
assert @i.chunk_key_tag
|
322
|
+
assert_equal ['key1','key2'], @i.chunk_keys
|
323
|
+
tmpl = "/mypath/%Y/%m/%d/%H-%M/${tag}/${tag[1]}/${tag[2]}/${key1}/${key2}/tail"
|
324
|
+
t = event_time('2016-04-11 20:30:00 +0900')
|
325
|
+
v = {key1: "value1", key2: "value2"}
|
326
|
+
c = if api == :chunk
|
327
|
+
create_chunk(timekey: t, tag: 'fluentd.test.output', variables: v)
|
328
|
+
else
|
329
|
+
create_metadata(timekey: t, tag: 'fluentd.test.output', variables: v)
|
330
|
+
end
|
331
|
+
assert_equal "/mypath/2016/04/11/20-30/fluentd.test.output/test/output/value1/value2/tail", @i.extract_placeholders(tmpl, c)
|
268
332
|
end
|
269
333
|
|
270
|
-
|
334
|
+
data(:new_api => :chunk,
|
335
|
+
:old_api => :metadata)
|
336
|
+
test '#extract_placeholders can extract negative index with tag' do |api|
|
271
337
|
@i.configure(config_element('ROOT', '', {}, [config_element('buffer', 'time,tag,key1,key2', {'timekey' => 60*30, 'timekey_zone' => "+0900"})]))
|
272
338
|
assert @i.chunk_key_time
|
273
339
|
assert @i.chunk_key_tag
|
274
340
|
assert_equal ['key1','key2'], @i.chunk_keys
|
275
|
-
tmpl = "/mypath/%Y/%m/%d/%H-%M/${tag}/${tag[1]}/${tag[2]}/${key1}/${key2}/tail"
|
341
|
+
tmpl = "/mypath/%Y/%m/%d/%H-%M/${tag}/${tag[-1]}/${tag[-2]}/${key1}/${key2}/tail"
|
276
342
|
t = event_time('2016-04-11 20:30:00 +0900')
|
277
343
|
v = {key1: "value1", key2: "value2"}
|
278
|
-
|
279
|
-
|
344
|
+
c = if api == :chunk
|
345
|
+
create_chunk(timekey: t, tag: 'fluentd.test.output', variables: v)
|
346
|
+
else
|
347
|
+
create_metadata(timekey: t, tag: 'fluentd.test.output', variables: v)
|
348
|
+
end
|
349
|
+
assert_equal "/mypath/2016/04/11/20-30/fluentd.test.output/output/test/value1/value2/tail", @i.extract_placeholders(tmpl, c)
|
280
350
|
end
|
281
351
|
|
282
|
-
|
352
|
+
data(:new_api => :chunk,
|
353
|
+
:old_api => :metadata)
|
354
|
+
test '#extract_placeholders removes out-of-range tag part and unknown variable placeholders' do |api|
|
283
355
|
@i.configure(config_element('ROOT', '', {}, [config_element('buffer', 'time,tag,key1,key2', {'timekey' => 60*30, 'timekey_zone' => "+0900"})]))
|
284
356
|
assert @i.chunk_key_time
|
285
357
|
assert @i.chunk_key_tag
|
286
358
|
assert_equal ['key1','key2'], @i.chunk_keys
|
287
|
-
tmpl = "/mypath/%Y/%m/%d/%H-%M/${tag}/${tag[3]}/${tag[4]}/${key3}/${key4}/tail"
|
359
|
+
tmpl = "/mypath/%Y/%m/%d/%H-%M/${tag}/${tag[3]}/${tag[-4]}/${key3}/${key4}/tail"
|
360
|
+
t = event_time('2016-04-11 20:30:00 +0900')
|
361
|
+
v = {key1: "value1", key2: "value2"}
|
362
|
+
c = if api == :chunk
|
363
|
+
create_chunk(timekey: t, tag: 'fluentd.test.output', variables: v)
|
364
|
+
else
|
365
|
+
create_metadata(timekey: t, tag: 'fluentd.test.output', variables: v)
|
366
|
+
end
|
367
|
+
assert_equal "/mypath/2016/04/11/20-30/fluentd.test.output/////tail", @i.extract_placeholders(tmpl, c)
|
368
|
+
end
|
369
|
+
|
370
|
+
test '#extract_placeholders logs warn message if metadata is passed for ${chunk_id} placeholder' do
|
371
|
+
@i.configure(config_element('ROOT', '', {}, [config_element('buffer', '')]))
|
372
|
+
tmpl = "/mypath/${chunk_id}/tail"
|
288
373
|
t = event_time('2016-04-11 20:30:00 +0900')
|
289
374
|
v = {key1: "value1", key2: "value2"}
|
290
375
|
m = create_metadata(timekey: t, tag: 'fluentd.test.output', variables: v)
|
291
|
-
|
376
|
+
@i.extract_placeholders(tmpl, m)
|
377
|
+
logs = @i.log.out.logs
|
378
|
+
assert { logs.any? { |log| log.include?("${chunk_id} is not allowed in this plugin") } }
|
292
379
|
end
|
293
380
|
|
294
381
|
sub_test_case '#placeholder_validators' do
|
@@ -372,7 +459,7 @@ class OutputTest < Test::Unit::TestCase
|
|
372
459
|
end
|
373
460
|
|
374
461
|
sub_test_case '#placeholder_validate!' do
|
375
|
-
test 'raises configuration error for a
|
462
|
+
test 'raises configuration error for a template when timestamp placeholders exist but time key is missing' do
|
376
463
|
@i.configure(config_element('ROOT', '', {}, [config_element('buffer', '')]))
|
377
464
|
assert_raise Fluent::ConfigError.new("Parameter 'path: /path/without/timestamp/file.%Y%m%d-%H%M.log' has timestamp placeholders, but chunk key 'time' is not configured") do
|
378
465
|
@i.placeholder_validate!(:path, "/path/without/timestamp/file.%Y%m%d-%H%M.log")
|
@@ -414,6 +501,9 @@ class OutputTest < Test::Unit::TestCase
|
|
414
501
|
assert_nothing_raised do
|
415
502
|
@i.placeholder_validate!(:path, "/my/path/${tag}/file.${tag[2]}.log")
|
416
503
|
end
|
504
|
+
assert_nothing_raised do
|
505
|
+
@i.placeholder_validate!(:path, "/my/path/${tag}/file.${tag[-1]}.log")
|
506
|
+
end
|
417
507
|
end
|
418
508
|
|
419
509
|
test 'raises configuration error for a template when variable key placeholders exist but chunk keys are missing' do
|
@@ -493,11 +583,21 @@ class OutputTest < Test::Unit::TestCase
|
|
493
583
|
assert_equal ['.hidden', '0001', '@timestamp', 'a_key', 'my-domain'], @i.get_placeholders_keys("http://${my-domain}/${.hidden}/${0001}/${a_key}?timestamp=${@timestamp}")
|
494
584
|
end
|
495
585
|
|
586
|
+
data('include space' => 'ke y',
|
587
|
+
'bracket notation' => "$['key']",
|
588
|
+
'invalid notation' => "$.ke y")
|
589
|
+
test 'configure checks invalid chunk keys' do |chunk_keys|
|
590
|
+
i = create_output(:buffered)
|
591
|
+
assert_raise Fluent::ConfigError do
|
592
|
+
i.configure(config_element('ROOT' , '', {}, [config_element('buffer', chunk_keys)]))
|
593
|
+
end
|
594
|
+
end
|
595
|
+
|
496
596
|
test '#metadata returns object which contains tag/timekey/variables from records as specified in configuration' do
|
497
597
|
tag = 'test.output'
|
498
598
|
time = event_time('2016-04-12 15:31:23 -0700')
|
499
599
|
timekey = event_time('2016-04-12 15:00:00 -0700')
|
500
|
-
record = {"key1" => "value1", "num1" => 1, "message" => "my message"}
|
600
|
+
record = {"key1" => "value1", "num1" => 1, "message" => "my message", "nest" => {"key" => "nested value"}}
|
501
601
|
|
502
602
|
i1 = create_output(:buffered)
|
503
603
|
i1.configure(config_element('ROOT','',{},[config_element('buffer', '')]))
|
@@ -530,6 +630,10 @@ class OutputTest < Test::Unit::TestCase
|
|
530
630
|
i8 = create_output(:buffered)
|
531
631
|
i8.configure(config_element('ROOT','',{},[config_element('buffer', 'time,tag,key1', {"timekey" => 3600, "timekey_zone" => "-0700"})]))
|
532
632
|
assert_equal create_metadata(timekey: timekey, tag: tag, variables: {key1: "value1"}), i8.metadata(tag, time, record)
|
633
|
+
|
634
|
+
i9 = create_output(:buffered)
|
635
|
+
i9.configure(config_element('ROOT','',{},[config_element('buffer', 'key1,$.nest.key', {})]))
|
636
|
+
assert_equal create_metadata(variables: {:key1 => "value1", :"$.nest.key" => 'nested value'}), i9.metadata(tag, time, record)
|
533
637
|
end
|
534
638
|
|
535
639
|
test '#emit calls #process via #emit_sync for non-buffered output' do
|
@@ -724,34 +828,43 @@ class OutputTest < Test::Unit::TestCase
|
|
724
828
|
assert_equal :interval, @i.instance_variable_get(:@flush_mode)
|
725
829
|
end
|
726
830
|
|
727
|
-
|
728
|
-
|
729
|
-
|
730
|
-
|
831
|
+
sub_test_case 'configure secondary' do
|
832
|
+
test "Warn if primary type is different from secondary type and either primary or secondary has custom_format" do
|
833
|
+
o = create_output(:buffered)
|
834
|
+
mock(o.log).warn("secondary type should be same with primary one",
|
835
|
+
{ primary: o.class.to_s, secondary: "Fluent::Plugin::TestOutput" })
|
731
836
|
|
732
|
-
|
733
|
-
|
734
|
-
|
837
|
+
o.configure(config_element('ROOT','',{},[config_element('secondary','',{'@type'=>'test', 'name' => "cool"})]))
|
838
|
+
assert_not_nil o.instance_variable_get(:@secondary)
|
839
|
+
end
|
735
840
|
|
736
|
-
|
737
|
-
|
738
|
-
|
739
|
-
|
841
|
+
test "don't warn if primary type is the same as secondary type" do
|
842
|
+
o = Fluent::Plugin::TestOutput.new
|
843
|
+
mock(o.log).warn("secondary type should be same with primary one",
|
844
|
+
{ primary: o.class.to_s, secondary: "Fluent::Plugin::TestOutput" }).never
|
740
845
|
|
741
|
-
|
742
|
-
|
743
|
-
|
744
|
-
|
745
|
-
|
746
|
-
|
846
|
+
o.configure(config_element('ROOT','',{'name' => "cool2"},
|
847
|
+
[config_element('secondary','',{'@type'=>'test', 'name' => "cool"}),
|
848
|
+
config_element('buffer','',{'@type'=>'memory'})]
|
849
|
+
))
|
850
|
+
assert_not_nil o.instance_variable_get(:@secondary)
|
851
|
+
end
|
747
852
|
|
748
|
-
|
749
|
-
|
750
|
-
|
751
|
-
|
853
|
+
test "don't warn if primary type is different from secondary type and both don't have custom_format" do
|
854
|
+
o = create_output(:standard)
|
855
|
+
mock(o.log).warn("secondary type should be same with primary one",
|
856
|
+
{ primary: o.class.to_s, secondary: "Fluent::Plugin::TestOutput" }).never
|
752
857
|
|
753
|
-
|
754
|
-
|
858
|
+
o.configure(config_element('ROOT','',{},[config_element('secondary','',{'@type'=>'test', 'name' => "cool"})]))
|
859
|
+
assert_not_nil o.instance_variable_get(:@secondary)
|
860
|
+
end
|
861
|
+
|
862
|
+
test "raise configuration error if secondary type specifies non buffered output" do
|
863
|
+
o = create_output(:standard)
|
864
|
+
assert_raise Fluent::ConfigError do
|
865
|
+
o.configure(config_element('ROOT','',{},[config_element('secondary','',{'@type'=>'copy'})]))
|
866
|
+
end
|
867
|
+
end
|
755
868
|
end
|
756
869
|
end
|
757
870
|
|
@@ -824,7 +937,8 @@ class OutputTest < Test::Unit::TestCase
|
|
824
937
|
|
825
938
|
sub_test_case 'slow_flush_log_threshold' do
|
826
939
|
def invoke_slow_flush_log_threshold_test(i)
|
827
|
-
i.configure(config_element('ROOT', '', {'slow_flush_log_threshold' => 0.5},
|
940
|
+
i.configure(config_element('ROOT', '', {'slow_flush_log_threshold' => 0.5},
|
941
|
+
[config_element('buffer', '', {"flush_mode" => "immediate", "flush_thread_interval" => 30})]))
|
828
942
|
i.start
|
829
943
|
i.after_start
|
830
944
|
|
@@ -219,6 +219,24 @@ class BufferedOutputTest < Test::Unit::TestCase
|
|
219
219
|
Timecop.return
|
220
220
|
end
|
221
221
|
|
222
|
+
test 'queued_chunks_limit_size is same as flush_thread_count by default' do
|
223
|
+
hash = {'flush_thread_count' => 4}
|
224
|
+
i = create_output
|
225
|
+
i.register(:prefer_buffered_processing) { true }
|
226
|
+
i.configure(config_element('ROOT', '', {}, [config_element('buffer','tag',hash)]))
|
227
|
+
|
228
|
+
assert_equal 4, i.buffer.queued_chunks_limit_size
|
229
|
+
end
|
230
|
+
|
231
|
+
test 'prefer queued_chunks_limit_size parameter than flush_thread_count' do
|
232
|
+
hash = {'flush_thread_count' => 4, 'queued_chunks_limit_size' => 2}
|
233
|
+
i = create_output
|
234
|
+
i.register(:prefer_buffered_processing) { true }
|
235
|
+
i.configure(config_element('ROOT', '', {}, [config_element('buffer','tag',hash)]))
|
236
|
+
|
237
|
+
assert_equal 2, i.buffer.queued_chunks_limit_size
|
238
|
+
end
|
239
|
+
|
222
240
|
sub_test_case 'chunk feature in #write for output plugins' do
|
223
241
|
setup do
|
224
242
|
@stored_global_logger = $log
|
@@ -1064,6 +1082,7 @@ class BufferedOutputTest < Test::Unit::TestCase
|
|
1064
1082
|
'flush_thread_count' => 1,
|
1065
1083
|
'flush_thread_burst_interval' => 0.1,
|
1066
1084
|
'chunk_limit_size' => 1024,
|
1085
|
+
'queued_chunks_limit_size' => 100
|
1067
1086
|
}
|
1068
1087
|
@i = create_output(:buffered)
|
1069
1088
|
@i.configure(config_element('ROOT','',{},[config_element('buffer',chunk_key,hash)]))
|
@@ -0,0 +1,307 @@
|
|
1
|
+
require_relative '../helper'
|
2
|
+
require 'fluent/plugin/output'
|
3
|
+
require 'fluent/plugin/buffer'
|
4
|
+
require 'fluent/event'
|
5
|
+
require 'fluent/error'
|
6
|
+
|
7
|
+
require 'json'
|
8
|
+
require 'time'
|
9
|
+
require 'timeout'
|
10
|
+
require 'timecop'
|
11
|
+
|
12
|
+
|
13
|
+
class BufferedOutputBackupTest < Test::Unit::TestCase
|
14
|
+
class BareOutput < Fluent::Plugin::Output
|
15
|
+
def register(name, &block)
|
16
|
+
instance_variable_set("@#{name}", block)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
class DummyOutput < BareOutput
|
20
|
+
def initialize
|
21
|
+
super
|
22
|
+
@process = nil
|
23
|
+
@format = nil
|
24
|
+
@write = nil
|
25
|
+
@try_write = nil
|
26
|
+
end
|
27
|
+
def prefer_buffered_processing
|
28
|
+
true
|
29
|
+
end
|
30
|
+
def prefer_delayed_commit
|
31
|
+
false
|
32
|
+
end
|
33
|
+
def process(tag, es)
|
34
|
+
@process ? @process.call(tag, es) : nil
|
35
|
+
end
|
36
|
+
def format(tag, time, record)
|
37
|
+
[tag, time.to_i, record].to_json + "\n"
|
38
|
+
end
|
39
|
+
def write(chunk)
|
40
|
+
@write ? @write.call(chunk) : nil
|
41
|
+
end
|
42
|
+
def try_write(chunk)
|
43
|
+
@try_write ? @try_write.call(chunk) : nil
|
44
|
+
end
|
45
|
+
end
|
46
|
+
class DummyOutputForSecondary < BareOutput
|
47
|
+
def initialize
|
48
|
+
super
|
49
|
+
@process = nil
|
50
|
+
@format = nil
|
51
|
+
@write = nil
|
52
|
+
@try_write = nil
|
53
|
+
end
|
54
|
+
def prefer_buffered_processing
|
55
|
+
true
|
56
|
+
end
|
57
|
+
def prefer_delayed_commit
|
58
|
+
false
|
59
|
+
end
|
60
|
+
def process(tag, es)
|
61
|
+
@process ? @process.call(tag, es) : nil
|
62
|
+
end
|
63
|
+
def format(tag, time, record)
|
64
|
+
[tag, time.to_i, record].to_json + "\n"
|
65
|
+
end
|
66
|
+
def write(chunk)
|
67
|
+
@write ? @write.call(chunk) : nil
|
68
|
+
end
|
69
|
+
def try_write(chunk)
|
70
|
+
@try_write ? @try_write.call(chunk) : nil
|
71
|
+
end
|
72
|
+
end
|
73
|
+
class DummyAsyncOutputForSecondary < BareOutput
|
74
|
+
def initialize
|
75
|
+
super
|
76
|
+
@process = nil
|
77
|
+
@format = nil
|
78
|
+
@write = nil
|
79
|
+
@try_write = nil
|
80
|
+
end
|
81
|
+
def prefer_buffered_processing
|
82
|
+
true
|
83
|
+
end
|
84
|
+
def prefer_delayed_commit
|
85
|
+
true
|
86
|
+
end
|
87
|
+
def process(tag, es)
|
88
|
+
@process ? @process.call(tag, es) : nil
|
89
|
+
end
|
90
|
+
def format(tag, time, record)
|
91
|
+
[tag, time.to_i, record].to_json + "\n"
|
92
|
+
end
|
93
|
+
def write(chunk)
|
94
|
+
@write ? @write.call(chunk) : nil
|
95
|
+
end
|
96
|
+
def try_write(chunk)
|
97
|
+
@try_write ? @try_write.call(chunk) : nil
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
TMP_DIR = File.expand_path(File.dirname(__FILE__) + "/../tmp/bu#{ENV['TEST_ENV_NUMBER']}")
|
102
|
+
|
103
|
+
def create_output
|
104
|
+
DummyOutput.new
|
105
|
+
end
|
106
|
+
def create_metadata(timekey: nil, tag: nil, variables: nil)
|
107
|
+
Fluent::Plugin::Buffer::Metadata.new(timekey, tag, variables)
|
108
|
+
end
|
109
|
+
def waiting(seconds)
|
110
|
+
begin
|
111
|
+
Timeout.timeout(seconds) do
|
112
|
+
yield
|
113
|
+
end
|
114
|
+
rescue Timeout::Error
|
115
|
+
STDERR.print(*@i.log.out.logs)
|
116
|
+
raise
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
def dummy_event_stream
|
121
|
+
Fluent::ArrayEventStream.new([
|
122
|
+
[ event_time('2016-04-13 18:33:00'), {"name" => "moris", "age" => 36, "message" => "data1"} ],
|
123
|
+
[ event_time('2016-04-13 18:33:13'), {"name" => "moris", "age" => 36, "message" => "data2"} ],
|
124
|
+
[ event_time('2016-04-13 18:33:32'), {"name" => "moris", "age" => 36, "message" => "data3"} ],
|
125
|
+
])
|
126
|
+
end
|
127
|
+
|
128
|
+
setup do
|
129
|
+
@i = create_output
|
130
|
+
FileUtils.rm_rf(TMP_DIR)
|
131
|
+
FileUtils.mkdir_p(TMP_DIR)
|
132
|
+
|
133
|
+
Fluent::Plugin.register_output('backup_output', DummyOutput)
|
134
|
+
Fluent::Plugin.register_output('backup_output2', DummyOutputForSecondary)
|
135
|
+
Fluent::Plugin.register_output('backup_async_output', DummyAsyncOutputForSecondary)
|
136
|
+
end
|
137
|
+
|
138
|
+
teardown do
|
139
|
+
if @i
|
140
|
+
@i.stop unless @i.stopped?
|
141
|
+
@i.before_shutdown unless @i.before_shutdown?
|
142
|
+
@i.shutdown unless @i.shutdown?
|
143
|
+
@i.after_shutdown unless @i.after_shutdown?
|
144
|
+
@i.close unless @i.closed?
|
145
|
+
@i.terminate unless @i.terminated?
|
146
|
+
end
|
147
|
+
Timecop.return
|
148
|
+
end
|
149
|
+
|
150
|
+
sub_test_case 'buffered output for broken chunks' do
|
151
|
+
def flush_chunks
|
152
|
+
@i.start
|
153
|
+
@i.after_start
|
154
|
+
|
155
|
+
@i.interrupt_flushes
|
156
|
+
|
157
|
+
now = Time.parse('2016-04-13 18:33:30 -0700')
|
158
|
+
Timecop.freeze(now)
|
159
|
+
@i.emit_events("test.tag.1", dummy_event_stream())
|
160
|
+
now = Time.parse('2016-04-13 18:33:32 -0700')
|
161
|
+
Timecop.freeze(now)
|
162
|
+
|
163
|
+
@i.enqueue_thread_wait
|
164
|
+
@i.flush_thread_wakeup
|
165
|
+
waiting(4) { Thread.pass until @i.write_count > 0 }
|
166
|
+
|
167
|
+
assert { @i.write_count > 0 }
|
168
|
+
Timecop.freeze(now)
|
169
|
+
@i.flush_thread_wakeup
|
170
|
+
end
|
171
|
+
|
172
|
+
def wait_flush(target_file)
|
173
|
+
waiting(5) {
|
174
|
+
target_dir = File.join(File.dirname(target_file), "*")
|
175
|
+
while Dir.glob(target_dir).size.zero?
|
176
|
+
end
|
177
|
+
}
|
178
|
+
end
|
179
|
+
|
180
|
+
test 'backup chunk without secondary' do
|
181
|
+
Fluent::SystemConfig.overwrite_system_config('root_dir' => TMP_DIR) do
|
182
|
+
id = 'backup_test'
|
183
|
+
hash = {
|
184
|
+
'flush_interval' => 1,
|
185
|
+
'flush_thread_burst_interval' => 0.1,
|
186
|
+
}
|
187
|
+
chunk_id = nil
|
188
|
+
@i.configure(config_element('ROOT', '', {'@id' => id}, [config_element('buffer', 'tag', hash)]))
|
189
|
+
@i.register(:write) { |chunk|
|
190
|
+
chunk_id = chunk.unique_id;
|
191
|
+
raise Fluent::UnrecoverableError, "yay, your #write must fail"
|
192
|
+
}
|
193
|
+
|
194
|
+
flush_chunks
|
195
|
+
|
196
|
+
target = "#{TMP_DIR}/backup/worker0/#{id}/#{@i.dump_unique_id_hex(chunk_id)}.log"
|
197
|
+
wait_flush(target)
|
198
|
+
assert_true File.exist?(target)
|
199
|
+
logs = @i.log.out.logs
|
200
|
+
assert { logs.any? { |l| l.include?("got unrecoverable error in primary and no secondary") } }
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
test 'backup chunk with same type secondary' do
|
205
|
+
Fluent::SystemConfig.overwrite_system_config('root_dir' => TMP_DIR) do
|
206
|
+
id = 'backup_test_with_same_secondary'
|
207
|
+
hash = {
|
208
|
+
'flush_interval' => 1,
|
209
|
+
'flush_thread_burst_interval' => 0.1,
|
210
|
+
}
|
211
|
+
chunk_id = nil
|
212
|
+
secconf = config_element('secondary','',{'@type' => 'backup_output'})
|
213
|
+
@i.configure(config_element('ROOT', '', {'@id' => id}, [config_element('buffer', 'tag', hash), secconf]))
|
214
|
+
@i.register(:write) { |chunk|
|
215
|
+
chunk_id = chunk.unique_id;
|
216
|
+
raise Fluent::UnrecoverableError, "yay, your #write must fail"
|
217
|
+
}
|
218
|
+
|
219
|
+
flush_chunks
|
220
|
+
|
221
|
+
target = "#{TMP_DIR}/backup/worker0/#{id}/#{@i.dump_unique_id_hex(chunk_id)}.log"
|
222
|
+
wait_flush(target)
|
223
|
+
assert_true File.exist?(target)
|
224
|
+
logs = @i.log.out.logs
|
225
|
+
assert { logs.any? { |l| l.include?("got unrecoverable error in primary and secondary type is same as primary") } }
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
test 'backup chunk with different type secondary' do
|
230
|
+
Fluent::SystemConfig.overwrite_system_config('root_dir' => TMP_DIR) do
|
231
|
+
id = 'backup_test_with_diff_secondary'
|
232
|
+
hash = {
|
233
|
+
'flush_interval' => 1,
|
234
|
+
'flush_thread_burst_interval' => 0.1,
|
235
|
+
}
|
236
|
+
chunk_id = nil
|
237
|
+
secconf = config_element('secondary','',{'@type' => 'backup_output2'})
|
238
|
+
@i.configure(config_element('ROOT', '', {'@id' => id}, [config_element('buffer', 'tag', hash), secconf]))
|
239
|
+
@i.register(:write) { |chunk|
|
240
|
+
chunk_id = chunk.unique_id;
|
241
|
+
raise Fluent::UnrecoverableError, "yay, your #write must fail"
|
242
|
+
}
|
243
|
+
@i.secondary.register(:write) { |chunk|
|
244
|
+
raise Fluent::UnrecoverableError, "yay, your secondary #write must fail"
|
245
|
+
}
|
246
|
+
|
247
|
+
flush_chunks
|
248
|
+
|
249
|
+
target = "#{TMP_DIR}/backup/worker0/#{id}/#{@i.dump_unique_id_hex(chunk_id)}.log"
|
250
|
+
wait_flush(target)
|
251
|
+
assert_true File.exist?(target)
|
252
|
+
logs = @i.log.out.logs
|
253
|
+
assert { logs.any? { |l| l.include?("got unrecoverable error in primary. Skip retry and flush chunk to secondary") } }
|
254
|
+
assert { logs.any? { |l| l.include?("got an error in secondary for unrecoverable error") } }
|
255
|
+
end
|
256
|
+
end
|
257
|
+
|
258
|
+
test 'backup chunk with async secondary' do
|
259
|
+
Fluent::SystemConfig.overwrite_system_config('root_dir' => TMP_DIR) do
|
260
|
+
id = 'backup_test_with_diff_secondary'
|
261
|
+
hash = {
|
262
|
+
'flush_interval' => 1,
|
263
|
+
'flush_thread_burst_interval' => 0.1,
|
264
|
+
}
|
265
|
+
chunk_id = nil
|
266
|
+
secconf = config_element('secondary','',{'@type' => 'backup_async_output'})
|
267
|
+
@i.configure(config_element('ROOT', '', {'@id' => id}, [config_element('buffer', 'tag', hash), secconf]))
|
268
|
+
@i.register(:write) { |chunk|
|
269
|
+
chunk_id = chunk.unique_id;
|
270
|
+
raise Fluent::UnrecoverableError, "yay, your #write must fail"
|
271
|
+
}
|
272
|
+
|
273
|
+
flush_chunks
|
274
|
+
|
275
|
+
target = "#{TMP_DIR}/backup/worker0/#{id}/#{@i.dump_unique_id_hex(chunk_id)}.log"
|
276
|
+
wait_flush(target)
|
277
|
+
assert_true File.exist?(target)
|
278
|
+
logs = @i.log.out.logs
|
279
|
+
assert { logs.any? { |l| l.include?("got unrecoverable error in primary and secondary is async output") } }
|
280
|
+
end
|
281
|
+
end
|
282
|
+
|
283
|
+
test 'chunk is thrown away when disable_chunk_backup is true' do
|
284
|
+
Fluent::SystemConfig.overwrite_system_config('root_dir' => TMP_DIR) do
|
285
|
+
id = 'backup_test'
|
286
|
+
hash = {
|
287
|
+
'flush_interval' => 1,
|
288
|
+
'flush_thread_burst_interval' => 0.1,
|
289
|
+
'disable_chunk_backup' => true
|
290
|
+
}
|
291
|
+
chunk_id = nil
|
292
|
+
@i.configure(config_element('ROOT', '', {'@id' => id}, [config_element('buffer', 'tag', hash)]))
|
293
|
+
@i.register(:write) { |chunk|
|
294
|
+
chunk_id = chunk.unique_id;
|
295
|
+
raise Fluent::UnrecoverableError, "yay, your #write must fail"
|
296
|
+
}
|
297
|
+
|
298
|
+
flush_chunks
|
299
|
+
|
300
|
+
target = "#{TMP_DIR}/backup/worker0/#{id}/#{@i.dump_unique_id_hex(chunk_id)}.log"
|
301
|
+
assert_false File.exist?(target)
|
302
|
+
logs = @i.log.out.logs
|
303
|
+
assert { logs.any? { |l| l.include?("disable_chunk_backup is true") } }
|
304
|
+
end
|
305
|
+
end
|
306
|
+
end
|
307
|
+
end
|