fluentd 0.14.10-x64-mingw32 → 0.14.11-x64-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.

Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +14 -6
  3. data/ChangeLog +28 -2
  4. data/appveyor.yml +1 -0
  5. data/lib/fluent/engine.rb +4 -7
  6. data/lib/fluent/error.rb +30 -0
  7. data/lib/fluent/log.rb +0 -7
  8. data/lib/fluent/plugin/base.rb +11 -0
  9. data/lib/fluent/plugin/buf_file.rb +9 -7
  10. data/lib/fluent/plugin/formatter_csv.rb +4 -2
  11. data/lib/fluent/plugin/in_forward.rb +46 -17
  12. data/lib/fluent/plugin/in_http.rb +2 -0
  13. data/lib/fluent/plugin/in_monitor_agent.rb +27 -2
  14. data/lib/fluent/plugin/in_syslog.rb +52 -36
  15. data/lib/fluent/plugin/in_tail.rb +1 -0
  16. data/lib/fluent/plugin/out_forward.rb +39 -29
  17. data/lib/fluent/plugin/output.rb +17 -0
  18. data/lib/fluent/plugin/storage_local.rb +16 -13
  19. data/lib/fluent/plugin_helper/storage.rb +21 -9
  20. data/lib/fluent/plugin_id.rb +17 -0
  21. data/lib/fluent/supervisor.rb +73 -45
  22. data/lib/fluent/system_config.rb +24 -21
  23. data/lib/fluent/version.rb +1 -1
  24. data/test/command/test_fluentd.rb +348 -0
  25. data/test/config/test_system_config.rb +39 -31
  26. data/test/plugin/test_base.rb +20 -0
  27. data/test/plugin/test_buf_file.rb +40 -0
  28. data/test/plugin/test_formatter_csv.rb +8 -0
  29. data/test/plugin/test_in_forward.rb +56 -21
  30. data/test/plugin/test_in_monitor_agent.rb +80 -8
  31. data/test/plugin/test_in_syslog.rb +75 -45
  32. data/test/plugin/test_out_file.rb +0 -1
  33. data/test/plugin/test_out_forward.rb +19 -11
  34. data/test/plugin/test_output.rb +44 -0
  35. data/test/plugin/test_storage_local.rb +290 -2
  36. data/test/plugin_helper/test_child_process.rb +40 -39
  37. data/test/plugin_helper/test_storage.rb +4 -3
  38. data/test/test_log.rb +1 -1
  39. data/test/test_output.rb +3 -0
  40. data/test/test_plugin_id.rb +101 -0
  41. data/test/test_supervisor.rb +3 -0
  42. metadata +7 -2
@@ -14,6 +14,7 @@ module Fluent::Config
14
14
 
15
15
  class FakeSupervisor
16
16
  def initialize
17
+ @root_dir = nil
17
18
  @log = FakeLoggerInitializer.new
18
19
  @log_level = nil
19
20
  @suppress_interval = nil
@@ -27,6 +28,7 @@ module Fluent::Config
27
28
  end
28
29
 
29
30
  class TestSystemConfig < ::Test::Unit::TestCase
31
+ TMP_DIR = File.expand_path(File.dirname(__FILE__) + "/tmp/system_config/#{ENV['TEST_ENV_NUMBER']}")
30
32
 
31
33
  def parse_text(text)
32
34
  basepath = File.expand_path(File.dirname(__FILE__) + '/../../')
@@ -41,11 +43,13 @@ module Fluent::Config
41
43
  s = FakeSupervisor.new
42
44
  sc = Fluent::SystemConfig.new(conf)
43
45
  sc.apply(s)
46
+ assert_nil(sc.root_dir)
44
47
  assert_nil(sc.log_level)
45
48
  assert_nil(sc.suppress_repeated_stacktrace)
46
49
  assert_nil(sc.emit_error_log_interval)
47
50
  assert_nil(sc.suppress_config_dump)
48
51
  assert_nil(sc.without_source)
52
+ assert_nil(s.instance_variable_get(:@root_dir))
49
53
  assert_nil(s.instance_variable_get(:@log_level))
50
54
  assert_nil(s.instance_variable_get(:@suppress_repeated_stacktrace))
51
55
  assert_nil(s.instance_variable_get(:@emit_error_log_interval))
@@ -55,41 +59,45 @@ module Fluent::Config
55
59
  assert_nil(s.instance_variable_get(:@dir_permission))
56
60
  end
57
61
 
58
- {'log_level' => 'error',
59
- 'suppress_repeated_stacktrace' => true,
60
- 'emit_error_log_interval' => 60,
61
- 'suppress_config_dump' => true,
62
- 'without_source' => true,
63
- }.each { |k, v|
64
- test "accepts #{k} parameter" do
65
- conf = parse_text(<<-EOS)
62
+ data(
63
+ 'root_dir' => ['root_dir', File.join(TMP_DIR, 'root')],
64
+ 'log_level' => ['log_level', 'error'],
65
+ 'suppress_repeated_stacktrace' => ['suppress_repeated_stacktrace', true],
66
+ 'emit_error_log_interval' => ['emit_error_log_interval', 60],
67
+ 'suppress_config_dump' => ['suppress_config_dump', true],
68
+ 'without_source' => ['without_source', true],
69
+ )
70
+ test "accepts parameters" do |(k, v)|
71
+ conf = parse_text(<<-EOS)
66
72
  <system>
67
73
  #{k} #{v}
68
74
  </system>
69
- EOS
70
- s = FakeSupervisor.new
71
- sc = Fluent::SystemConfig.new(conf)
72
- sc.apply(s)
73
- assert_not_nil(sc.instance_variable_get("@#{k}"))
74
- key = (k == 'emit_error_log_interval' ? 'suppress_interval' : k)
75
- assert_not_nil(s.instance_variable_get("@#{key}"))
76
- end
77
- }
75
+ EOS
76
+ s = FakeSupervisor.new
77
+ sc = Fluent::SystemConfig.new(conf)
78
+ sc.apply(s)
79
+ assert_not_nil(sc.instance_variable_get("@#{k}"))
80
+ key = (k == 'emit_error_log_interval' ? 'suppress_interval' : k)
81
+ assert_not_nil(s.instance_variable_get("@#{key}"))
82
+ end
78
83
 
79
- {'foo' => 'bar', 'hoge' => 'fuga'}.each { |k, v|
80
- test "should not affect settable parameters with unknown #{k} parameter" do
81
- s = FakeSupervisor.new
82
- sc = Fluent::SystemConfig.new({k => v})
83
- sc.apply(s)
84
- assert_nil(s.instance_variable_get(:@log_level))
85
- assert_nil(s.instance_variable_get(:@suppress_repeated_stacktrace))
86
- assert_nil(s.instance_variable_get(:@emit_error_log_interval))
87
- assert_nil(s.instance_variable_get(:@suppress_config_dump))
88
- assert_nil(s.instance_variable_get(:@without_source))
89
- assert_nil(s.instance_variable_get(:@file_permission))
90
- assert_nil(s.instance_variable_get(:@dir_permission))
91
- end
92
- }
84
+ data(
85
+ 'foo' => ['foo', 'bar'],
86
+ 'hoge' => ['hoge', 'fuga'],
87
+ )
88
+ test "should not affect settable parameters with unknown parameters" do |(k, v)|
89
+ s = FakeSupervisor.new
90
+ sc = Fluent::SystemConfig.new({k => v})
91
+ sc.apply(s)
92
+ assert_nil(s.instance_variable_get(:@root_dir))
93
+ assert_nil(s.instance_variable_get(:@log_level))
94
+ assert_nil(s.instance_variable_get(:@suppress_repeated_stacktrace))
95
+ assert_nil(s.instance_variable_get(:@emit_error_log_interval))
96
+ assert_nil(s.instance_variable_get(:@suppress_config_dump))
97
+ assert_nil(s.instance_variable_get(:@without_source))
98
+ assert_nil(s.instance_variable_get(:@file_permission))
99
+ assert_nil(s.instance_variable_get(:@dir_permission))
100
+ end
93
101
 
94
102
  test 'log_level' do
95
103
  conf = parse_text(<<-EOS)
@@ -56,6 +56,26 @@ class BaseTest < Test::Unit::TestCase
56
56
  assert !@p.has_router?
57
57
  end
58
58
 
59
+ sub_test_case '#fluentd_worker_id' do
60
+ test 'returns 0 in default' do
61
+ assert_equal 0, @p.fluentd_worker_id
62
+ end
63
+
64
+ test 'returns the value specified via SERVERENGINE_WORKER_ID env variable' do
65
+ pre_value = ENV['SERVERENGINE_WORKER_ID']
66
+ begin
67
+ ENV['SERVERENGINE_WORKER_ID'] = 7.to_s
68
+ assert_equal 7, @p.fluentd_worker_id
69
+ ensure
70
+ ENV['SERVERENGINE_WORKER_ID'] = pre_value
71
+ end
72
+ end
73
+ end
74
+
75
+ test 'does not have root dir in default' do
76
+ assert_nil @p.plugin_root_dir
77
+ end
78
+
59
79
  test 'is configurable by config_param and config_section' do
60
80
  assert_nothing_raised do
61
81
  class FluentPluginBaseTest::DummyPlugin2 < Fluent::Plugin::TestBase
@@ -10,6 +10,9 @@ require 'msgpack'
10
10
  module FluentPluginFileBufferTest
11
11
  class DummyOutputPlugin < Fluent::Plugin::Output
12
12
  Fluent::Plugin.register_output('buffer_file_test_output', self)
13
+ def write(chunk)
14
+ # drop
15
+ end
13
16
  end
14
17
  end
15
18
 
@@ -233,6 +236,43 @@ class FileBufferTest < Test::Unit::TestCase
233
236
  end
234
237
  end
235
238
 
239
+ sub_test_case 'configured with system root directory and plugin @id' do
240
+ setup do
241
+ @root_dir = File.expand_path('../../tmp/buffer_file_root', __FILE__)
242
+ FileUtils.rm_rf @root_dir
243
+
244
+ Fluent::Test.setup
245
+ @d = FluentPluginFileBufferTest::DummyOutputPlugin.new
246
+ @p = Fluent::Plugin::FileBuffer.new
247
+ @p.owner = @d
248
+ Fluent::SystemConfig.overwrite_system_config('root_dir' => @root_dir) do
249
+ @d.configure(config_element('ROOT', '', {'@id' => 'dummy_output_with_buf'}))
250
+ @p.configure(config_element('buffer', ''))
251
+ end
252
+ end
253
+
254
+ teardown do
255
+ if @p
256
+ @p.stop unless @p.stopped?
257
+ @p.before_shutdown unless @p.before_shutdown?
258
+ @p.shutdown unless @p.shutdown?
259
+ @p.after_shutdown unless @p.after_shutdown?
260
+ @p.close unless @p.closed?
261
+ @p.terminate unless @p.terminated?
262
+ end
263
+ end
264
+
265
+ test '#start creates directory for buffer chunks' do
266
+ expected_buffer_path = File.join(@root_dir, 'worker0', 'dummy_output_with_buf', 'buffer', 'buffer.*.log')
267
+ expected_buffer_dir = File.dirname(expected_buffer_path)
268
+ assert_equal expected_buffer_path, @p.path
269
+ assert_false Dir.exist?(expected_buffer_dir)
270
+
271
+ @p.start
272
+
273
+ assert Dir.exist?(expected_buffer_dir)
274
+ end
275
+ end
236
276
 
237
277
  sub_test_case 'there are no existing file chunks' do
238
278
  setup do
@@ -27,6 +27,14 @@ class CsvFormatterTest < ::Test::Unit::TestCase
27
27
  assert_equal(['a', 'b', 'c'], d.instance.fields)
28
28
  end
29
29
 
30
+ data('empty array' => [],
31
+ 'array including empty string' => ['', ''])
32
+ def test_empty_fields(param)
33
+ assert_raise Fluent::ConfigError do
34
+ create_driver('fields' => param)
35
+ end
36
+ end
37
+
30
38
  data(
31
39
  'tab_char' => ["\t", '\t'],
32
40
  'tab_string' => ["\t", 'TAB'],
@@ -34,7 +34,9 @@ class ForwardInputTest < Test::Unit::TestCase
34
34
  port #{PORT}
35
35
  bind 127.0.0.1
36
36
  ]
37
- PEERADDR = ['?', '0000', '127.0.0.1', '127.0.0.1']
37
+ LOCALHOST_HOSTNAME_GETTER = ->(){sock = UDPSocket.new(::Socket::AF_INET); sock.do_not_reverse_lookup = false; sock.connect("127.0.0.1", 2048); sock.peeraddr[2] }
38
+ LOCALHOST_HOSTNAME = LOCALHOST_HOSTNAME_GETTER.call
39
+ DUMMY_SOCK = Struct.new(:remote_host, :remote_addr, :remote_port).new(LOCALHOST_HOSTNAME, "127.0.0.1", 0)
38
40
  CONFIG_AUTH = %[
39
41
  port #{PORT}
40
42
  bind 127.0.0.1
@@ -452,7 +454,7 @@ class ForwardInputTest < Test::Unit::TestCase
452
454
 
453
455
  d.run do
454
456
  Fluent::Engine.msgpack_factory.unpacker.feed_each(chunk) do |obj|
455
- option = d.instance.send(:on_message, obj, chunk.size, PEERADDR)
457
+ option = d.instance.send(:on_message, obj, chunk.size, DUMMY_SOCK)
456
458
  assert_equal 'gzip', option['compressed']
457
459
  end
458
460
  end
@@ -482,7 +484,7 @@ class ForwardInputTest < Test::Unit::TestCase
482
484
 
483
485
  d.run do
484
486
  Fluent::Engine.msgpack_factory.unpacker.feed_each(chunk) do |obj|
485
- option = d.instance.send(:on_message, obj, chunk.size, PEERADDR)
487
+ option = d.instance.send(:on_message, obj, chunk.size, DUMMY_SOCK)
486
488
  assert_equal 'gzip', option['compressed']
487
489
  end
488
490
  end
@@ -506,7 +508,7 @@ class ForwardInputTest < Test::Unit::TestCase
506
508
 
507
509
  d.run(shutdown: false) do
508
510
  Fluent::Engine.msgpack_factory.unpacker.feed_each(chunk) do |obj|
509
- d.instance.send(:on_message, obj, chunk.size, '127.0.0.1')
511
+ d.instance.send(:on_message, obj, chunk.size, DUMMY_SOCK)
510
512
  end
511
513
  end
512
514
 
@@ -520,7 +522,7 @@ class ForwardInputTest < Test::Unit::TestCase
520
522
  logs = d.instance.log.logs
521
523
  assert_equal 1, logs.select{|line|
522
524
  line =~ / \[warn\]: Input chunk size is larger than 'chunk_size_warn_limit':/ &&
523
- line =~ / tag="test.tag" host="127.0.0.1" limit=16777216 size=16777501/
525
+ line =~ / tag="test.tag" host="#{LOCALHOST_HOSTNAME}" limit=16777216 size=16777501/
524
526
  }.size, "large chunk warning is not logged"
525
527
 
526
528
  d.instance_shutdown
@@ -538,7 +540,7 @@ class ForwardInputTest < Test::Unit::TestCase
538
540
 
539
541
  d.run(shutdown: false) do
540
542
  Fluent::Engine.msgpack_factory.unpacker.feed_each(chunk) do |obj|
541
- d.instance.send(:on_message, obj, chunk.size, '127.0.0.1')
543
+ d.instance.send(:on_message, obj, chunk.size, DUMMY_SOCK)
542
544
  end
543
545
  end
544
546
 
@@ -546,7 +548,7 @@ class ForwardInputTest < Test::Unit::TestCase
546
548
  logs = d.instance.log.logs
547
549
  assert_equal 1, logs.select{ |line|
548
550
  line =~ / \[warn\]: Input chunk size is larger than 'chunk_size_warn_limit':/ &&
549
- line =~ / tag="test.tag" host="127.0.0.1" limit=16777216 size=16777501/
551
+ line =~ / tag="test.tag" host="#{LOCALHOST_HOSTNAME}" limit=16777216 size=16777501/
550
552
  }.size, "large chunk warning is not logged"
551
553
 
552
554
  d.instance_shutdown
@@ -568,7 +570,7 @@ class ForwardInputTest < Test::Unit::TestCase
568
570
  # d.run => send_data
569
571
  d.run(shutdown: false) do
570
572
  Fluent::Engine.msgpack_factory.unpacker.feed_each(chunk) do |obj|
571
- d.instance.send(:on_message, obj, chunk.size, '127.0.0.1')
573
+ d.instance.send(:on_message, obj, chunk.size, DUMMY_SOCK)
572
574
  end
573
575
  end
574
576
 
@@ -580,7 +582,7 @@ class ForwardInputTest < Test::Unit::TestCase
580
582
  logs = d.instance.log.logs
581
583
  assert_equal 1, logs.select{|line|
582
584
  line =~ / \[warn\]: Input chunk size is larger than 'chunk_size_limit', dropped:/ &&
583
- line =~ / tag="test.tag" host="127.0.0.1" limit=33554432 size=33554989/
585
+ line =~ / tag="test.tag" host="#{LOCALHOST_HOSTNAME}" limit=33554432 size=33554989/
584
586
  }.size, "large chunk warning is not logged"
585
587
 
586
588
  d.instance_shutdown
@@ -593,7 +595,7 @@ class ForwardInputTest < Test::Unit::TestCase
593
595
 
594
596
  # d.run => send_data
595
597
  d.run(shutdown: false) do
596
- d.instance.send(:on_message, data, 1000000000, '127.0.0.1')
598
+ d.instance.send(:on_message, data, 1000000000, DUMMY_SOCK)
597
599
  end
598
600
 
599
601
  # check emitted data
@@ -602,7 +604,7 @@ class ForwardInputTest < Test::Unit::TestCase
602
604
  # check log
603
605
  logs = d.instance.log.logs
604
606
  assert_equal 1, logs.select{|line|
605
- line =~ / \[warn\]: incoming chunk is broken: host="127.0.0.1" msg=#{data.inspect}/
607
+ line =~ / \[warn\]: incoming chunk is broken: host="#{LOCALHOST_HOSTNAME}" msg=#{data.inspect}/
606
608
  }.size, "should not accept broken chunk"
607
609
 
608
610
  d.instance_shutdown
@@ -1033,17 +1035,27 @@ class ForwardInputTest < Test::Unit::TestCase
1033
1035
  io.close rescue nil # SSL socket requires any writes to close sockets
1034
1036
  end
1035
1037
 
1036
- sub_test_case 'source_hostname_key feature' do
1037
- test 'message protocol with source_hostname_key' do
1038
- execute_test { |events|
1038
+ sub_test_case 'source_hostname_key and source_address_key features' do
1039
+ data(
1040
+ both: [:hostname, :address],
1041
+ hostname: [:hostname],
1042
+ address: [:address],
1043
+ )
1044
+ test 'message protocol' do |keys|
1045
+ execute_test_with_source_hostname_key(*keys) { |events|
1039
1046
  events.each { |tag, time, record|
1040
1047
  send_data [tag, time, record].to_msgpack
1041
1048
  }
1042
1049
  }
1043
1050
  end
1044
1051
 
1045
- test 'forward protocol with source_hostname_key' do
1046
- execute_test { |events|
1052
+ data(
1053
+ both: [:hostname, :address],
1054
+ hostname: [:hostname],
1055
+ address: [:address],
1056
+ )
1057
+ test 'forward protocol' do |keys|
1058
+ execute_test_with_source_hostname_key(*keys) { |events|
1047
1059
  entries = []
1048
1060
  events.each {|tag,time,record|
1049
1061
  entries << [time, record]
@@ -1052,8 +1064,13 @@ class ForwardInputTest < Test::Unit::TestCase
1052
1064
  }
1053
1065
  end
1054
1066
 
1055
- test 'packed forward protocol with source_hostname_key' do
1056
- execute_test { |events|
1067
+ data(
1068
+ both: [:hostname, :address],
1069
+ hostname: [:hostname],
1070
+ address: [:address],
1071
+ )
1072
+ test 'packed forward protocol' do |keys|
1073
+ execute_test_with_source_hostname_key(*keys) { |events|
1057
1074
  entries = ''
1058
1075
  events.each { |tag, time, record|
1059
1076
  Fluent::Engine.msgpack_factory.packer(entries).write([time, record]).flush
@@ -1063,8 +1080,19 @@ class ForwardInputTest < Test::Unit::TestCase
1063
1080
  end
1064
1081
  end
1065
1082
 
1066
- def execute_test(&block)
1067
- @d = d = create_driver(CONFIG + 'source_hostname_key source')
1083
+ def execute_test_with_source_hostname_key(*keys, &block)
1084
+ conf = CONFIG.dup
1085
+ if keys.include?(:hostname)
1086
+ conf << <<EOL
1087
+ source_hostname_key source_hostname
1088
+ EOL
1089
+ end
1090
+ if keys.include?(:address)
1091
+ conf << <<EOL
1092
+ source_address_key source_address
1093
+ EOL
1094
+ end
1095
+ @d = d = create_driver(conf)
1068
1096
 
1069
1097
  time = event_time("2011-01-02 13:14:15 UTC")
1070
1098
  events = [
@@ -1077,7 +1105,14 @@ class ForwardInputTest < Test::Unit::TestCase
1077
1105
  end
1078
1106
 
1079
1107
  d.events.each { |tag, _time, record|
1080
- assert_true record.has_key?('source')
1108
+ if keys.include?(:hostname)
1109
+ assert_true record.has_key?('source_hostname')
1110
+ assert_equal DUMMY_SOCK.remote_host, record['source_hostname']
1111
+ end
1112
+ if keys.include?(:address)
1113
+ assert_true record.has_key?('source_address')
1114
+ assert_equal DUMMY_SOCK.remote_addr, record['source_address']
1115
+ end
1081
1116
  }
1082
1117
  end
1083
1118
 
@@ -146,7 +146,8 @@ EOC
146
146
  "config_path" => "/etc/fluent/fluent.conf",
147
147
  "pid_file" => nil,
148
148
  "plugin_dirs" => ["/etc/fluent/plugin"],
149
- "log_path" => nil
149
+ "log_path" => nil,
150
+ "root_dir" => nil,
150
151
  }
151
152
  assert_equal(expected_opts, d.instance.fluentd_opts)
152
153
  end
@@ -274,16 +275,16 @@ plugin_id:test_filter\tplugin_category:filter\ttype:test_filter\toutput_plugin:f
274
275
  assert_equal(expected_test_filter_response, test_filter)
275
276
  end
276
277
 
277
- data(:include_config_yes => [true, "include_config yes"],
278
- :include_config_no => [false, "include_config no"])
279
- test "/api/plugins.json" do |(with_config, include_conf)|
280
-
278
+ data(:include_config_and_retry_yes => [true, true, "include_config yes", "include_retry yes"],
279
+ :include_config_and_retry_no => [false, false, "include_config no", "include_retry no"],)
280
+ test "/api/plugins.json" do |(with_config, with_retry, include_conf, retry_conf)|
281
281
  d = create_driver("
282
282
  @type monitor_agent
283
283
  bind '127.0.0.1'
284
284
  port #{@port}
285
285
  tag monitor
286
286
  #{include_conf}
287
+ #{retry_conf}
287
288
  ")
288
289
  d.instance.start
289
290
  expected_test_in_response = {
@@ -304,6 +305,7 @@ plugin_id:test_filter\tplugin_category:filter\ttype:test_filter\toutput_plugin:f
304
305
  "type" => "null"
305
306
  }
306
307
  expected_null_response.merge!("config" => {"@id" => "null", "@type" => "null"}) if with_config
308
+ expected_null_response.merge!("retry" => {}) if with_retry
307
309
  response = JSON.parse(get("http://127.0.0.1:#{@port}/api/plugins.json"))
308
310
  test_in_response = response["plugins"][0]
309
311
  null_response = response["plugins"][5]
@@ -311,9 +313,9 @@ plugin_id:test_filter\tplugin_category:filter\ttype:test_filter\toutput_plugin:f
311
313
  assert_equal(expected_null_response, null_response)
312
314
  end
313
315
 
314
- data(:with_config_yes => [true, "?with_config=yes"],
315
- :with_config_no => [false, "?with_config=no"])
316
- test "/api/plugins.json with query parameter. query parameter is preferred than include_config" do |(with_config, query_param)|
316
+ data(:with_config_and_retry_yes => [true, true, "?with_config=yes&with_retry"],
317
+ :with_config_and_retry_no => [false, false, "?with_config=no&with_retry=no"])
318
+ test "/api/plugins.json with query parameter. query parameter is preferred than include_config" do |(with_config, with_retry, query_param)|
317
319
 
318
320
  d = create_driver("
319
321
  @type monitor_agent
@@ -340,6 +342,7 @@ plugin_id:test_filter\tplugin_category:filter\ttype:test_filter\toutput_plugin:f
340
342
  "type" => "null"
341
343
  }
342
344
  expected_null_response.merge!("config" => {"@id" => "null", "@type" => "null"}) if with_config
345
+ expected_null_response.merge!("retry" => {}) if with_retry
343
346
  response = JSON.parse(get("http://127.0.0.1:#{@port}/api/plugins.json#{query_param}"))
344
347
  test_in_response = response["plugins"][0]
345
348
  null_response = response["plugins"][5]
@@ -376,4 +379,73 @@ plugin_id:test_filter\tplugin_category:filter\ttype:test_filter\toutput_plugin:f
376
379
  assert_nil(res["log_path"])
377
380
  end
378
381
  end
382
+
383
+ sub_test_case "check retry of buffered plugins" do
384
+ class FluentTestFailWriteOutput < ::Fluent::Plugin::Output
385
+ ::Fluent::Plugin.register_output('test_out_fail_write', self)
386
+
387
+ def write(chunk)
388
+ raise "chunk error!"
389
+ end
390
+ end
391
+
392
+ setup do
393
+ @port = unused_port
394
+ # check @type and type in one configuration
395
+ conf = <<-EOC
396
+ <source>
397
+ @type monitor_agent
398
+ @id monitor_agent
399
+ bind "127.0.0.1"
400
+ port #{@port}
401
+ </source>
402
+ <match **>
403
+ @type test_out_fail_write
404
+ @id test_out_fail_write
405
+ <buffer>
406
+ flush_mode immediate
407
+ </buffer>
408
+ </match>
409
+ EOC
410
+ @ra = Fluent::RootAgent.new(log: $log)
411
+ stub(Fluent::Engine).root_agent { @ra }
412
+ @ra = configure_ra(@ra, conf)
413
+ end
414
+
415
+ test "/api/plugins.json retry object should be filled if flush was failed" do
416
+
417
+ d = create_driver("
418
+ @type monitor_agent
419
+ bind '127.0.0.1'
420
+ port #{@port}
421
+ include_config no
422
+ ")
423
+ d.instance.start
424
+ expected_test_out_fail_write_response = {
425
+ "buffer_queue_length" => 1,
426
+ "buffer_total_queued_size" => 0,
427
+ "output_plugin" => true,
428
+ "plugin_category" => "output",
429
+ "plugin_id" => "test_out_fail_write",
430
+ "retry_count" => 2,
431
+ "type" => "test_out_fail_write",
432
+ "retry" => {
433
+ "steps" => 1
434
+ }
435
+ }
436
+ output = @ra.outputs[0]
437
+ output.start
438
+ output.after_start
439
+ output.emit_events('test.tag', Fluent::ArrayEventStream.new([[event_time, {"message" => "test failed flush 1"}]]))
440
+ # flush few times to check steps
441
+ 2.times do
442
+ output.force_flush
443
+ end
444
+ response = JSON.parse(get("http://127.0.0.1:#{@port}/api/plugins.json"))
445
+ test_out_fail_write_response = response["plugins"][1]
446
+ # remove dynamic keys
447
+ ["start", "next_time"].each { |key| test_out_fail_write_response["retry"].delete(key) }
448
+ assert_equal(expected_test_out_fail_write_response, test_out_fail_write_response)
449
+ end
450
+ end
379
451
  end