fluentd 0.14.11 → 0.14.12

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 (119) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +1 -5
  3. data/ChangeLog +54 -2
  4. data/example/in_dummy_blocks.conf +17 -0
  5. data/example/in_forward_tls.conf +14 -0
  6. data/example/in_forward_workers.conf +21 -0
  7. data/example/logevents.conf +25 -0
  8. data/example/out_forward_heartbeat_none.conf +16 -0
  9. data/example/out_forward_tls.conf +18 -0
  10. data/example/suppress_config_dump.conf +7 -0
  11. data/lib/fluent/agent.rb +3 -32
  12. data/lib/fluent/clock.rb +62 -0
  13. data/lib/fluent/command/fluentd.rb +12 -0
  14. data/lib/fluent/compat/input.rb +10 -1
  15. data/lib/fluent/compat/output.rb +40 -1
  16. data/lib/fluent/config/configure_proxy.rb +30 -7
  17. data/lib/fluent/config/section.rb +4 -0
  18. data/lib/fluent/config/types.rb +2 -2
  19. data/lib/fluent/configurable.rb +31 -5
  20. data/lib/fluent/engine.rb +61 -12
  21. data/lib/fluent/event_router.rb +6 -0
  22. data/lib/fluent/load.rb +0 -1
  23. data/lib/fluent/log.rb +118 -42
  24. data/lib/fluent/match.rb +37 -0
  25. data/lib/fluent/plugin.rb +25 -3
  26. data/lib/fluent/plugin/base.rb +4 -0
  27. data/lib/fluent/plugin/buf_file.rb +38 -14
  28. data/lib/fluent/plugin/buffer.rb +20 -20
  29. data/lib/fluent/plugin/buffer/file_chunk.rb +2 -2
  30. data/lib/fluent/plugin/compressable.rb +1 -0
  31. data/lib/fluent/plugin/filter_record_transformer.rb +3 -6
  32. data/lib/fluent/plugin/formatter_csv.rb +4 -1
  33. data/lib/fluent/plugin/formatter_hash.rb +5 -1
  34. data/lib/fluent/plugin/formatter_json.rb +10 -0
  35. data/lib/fluent/plugin/formatter_ltsv.rb +2 -1
  36. data/lib/fluent/plugin/in_dummy.rb +4 -0
  37. data/lib/fluent/plugin/in_exec.rb +4 -0
  38. data/lib/fluent/plugin/in_forward.rb +11 -3
  39. data/lib/fluent/plugin/in_gc_stat.rb +4 -0
  40. data/lib/fluent/plugin/in_http.rb +4 -0
  41. data/lib/fluent/plugin/in_monitor_agent.rb +29 -2
  42. data/lib/fluent/plugin/in_object_space.rb +4 -1
  43. data/lib/fluent/plugin/in_syslog.rb +4 -0
  44. data/lib/fluent/plugin/in_tail.rb +193 -116
  45. data/lib/fluent/plugin/in_tcp.rb +5 -1
  46. data/lib/fluent/plugin/in_udp.rb +4 -0
  47. data/lib/fluent/plugin/input.rb +4 -0
  48. data/lib/fluent/plugin/out_copy.rb +4 -0
  49. data/lib/fluent/plugin/out_exec.rb +4 -0
  50. data/lib/fluent/plugin/out_exec_filter.rb +4 -0
  51. data/lib/fluent/plugin/out_file.rb +70 -30
  52. data/lib/fluent/plugin/out_forward.rb +132 -28
  53. data/lib/fluent/plugin/out_null.rb +10 -0
  54. data/lib/fluent/plugin/out_relabel.rb +4 -0
  55. data/lib/fluent/plugin/out_roundrobin.rb +4 -0
  56. data/lib/fluent/plugin/out_secondary_file.rb +5 -0
  57. data/lib/fluent/plugin/out_stdout.rb +5 -0
  58. data/lib/fluent/plugin/output.rb +18 -9
  59. data/lib/fluent/plugin/storage_local.rb +25 -2
  60. data/lib/fluent/plugin_helper/cert_option.rb +159 -0
  61. data/lib/fluent/plugin_helper/child_process.rb +6 -6
  62. data/lib/fluent/plugin_helper/compat_parameters.rb +1 -1
  63. data/lib/fluent/plugin_helper/event_loop.rb +29 -4
  64. data/lib/fluent/plugin_helper/inject.rb +14 -1
  65. data/lib/fluent/plugin_helper/server.rb +275 -31
  66. data/lib/fluent/plugin_helper/socket.rb +144 -4
  67. data/lib/fluent/plugin_helper/socket_option.rb +2 -17
  68. data/lib/fluent/plugin_helper/storage.rb +7 -1
  69. data/lib/fluent/plugin_helper/thread.rb +16 -4
  70. data/lib/fluent/registry.rb +26 -9
  71. data/lib/fluent/root_agent.rb +7 -3
  72. data/lib/fluent/supervisor.rb +37 -15
  73. data/lib/fluent/system_config.rb +37 -10
  74. data/lib/fluent/test.rb +2 -0
  75. data/lib/fluent/test/driver/base.rb +24 -26
  76. data/lib/fluent/test/helpers.rb +21 -0
  77. data/lib/fluent/version.rb +1 -1
  78. data/test/command/test_fluentd.rb +274 -4
  79. data/test/config/test_configurable.rb +154 -0
  80. data/test/config/test_configure_proxy.rb +180 -1
  81. data/test/config/test_system_config.rb +10 -0
  82. data/test/config/test_types.rb +1 -0
  83. data/test/plugin/test_base.rb +4 -0
  84. data/test/plugin/test_buf_file.rb +241 -9
  85. data/test/plugin/test_buffer.rb +11 -11
  86. data/test/plugin/test_buffer_file_chunk.rb +6 -6
  87. data/test/plugin/test_compressable.rb +3 -0
  88. data/test/plugin/test_filter.rb +4 -0
  89. data/test/plugin/test_filter_record_transformer.rb +20 -0
  90. data/test/plugin/test_formatter_csv.rb +9 -0
  91. data/test/plugin/test_formatter_hash.rb +35 -0
  92. data/test/plugin/test_formatter_json.rb +8 -0
  93. data/test/plugin/test_formatter_ltsv.rb +7 -0
  94. data/test/plugin/test_in_dummy.rb +7 -3
  95. data/test/plugin/test_in_monitor_agent.rb +43 -5
  96. data/test/plugin/test_in_tail.rb +97 -4
  97. data/test/plugin/test_input.rb +4 -0
  98. data/test/plugin/test_out_file.rb +46 -7
  99. data/test/plugin/test_out_forward.rb +59 -7
  100. data/test/plugin/test_output.rb +10 -4
  101. data/test/plugin/test_output_as_buffered.rb +37 -25
  102. data/test/plugin/test_output_as_buffered_compress.rb +1 -1
  103. data/test/plugin/test_output_as_buffered_retries.rb +6 -6
  104. data/test/plugin/test_output_as_buffered_secondary.rb +91 -31
  105. data/test/plugin/test_storage_local.rb +40 -1
  106. data/test/plugin_helper/test_child_process.rb +29 -28
  107. data/test/plugin_helper/test_compat_parameters.rb +1 -1
  108. data/test/plugin_helper/test_inject.rb +27 -9
  109. data/test/plugin_helper/test_server.rb +822 -50
  110. data/test/plugin_helper/test_storage.rb +11 -0
  111. data/test/plugin_helper/test_timer.rb +1 -0
  112. data/test/test_clock.rb +164 -0
  113. data/test/test_log.rb +146 -15
  114. data/test/test_plugin.rb +251 -0
  115. data/test/test_supervisor.rb +65 -57
  116. data/test/test_test_drivers.rb +2 -2
  117. metadata +18 -7
  118. data/lib/fluent/process.rb +0 -504
  119. data/test/test_process.rb +0 -48
@@ -104,6 +104,26 @@ class RecordTransformerFilterTest < Test::Unit::TestCase
104
104
  filtered.each_with_index do |(time, _record), i|
105
105
  assert_equal(times[i].to_i, time)
106
106
  assert(time.is_a?(Fluent::EventTime))
107
+ assert_true(_record.has_key?('message'))
108
+ end
109
+ end
110
+
111
+ test 'renew_time_key and remove_keys' do
112
+ config = %[
113
+ renew_time_key event_time_key
114
+ remove_keys event_time_key
115
+ auto_typecast true
116
+ <record>
117
+ event_time_key ${record["message"]}
118
+ </record>
119
+ ]
120
+ times = [Time.local(2, 2, 3, 4, 5, 2010, nil, nil, nil, nil), Time.local(3, 2, 3, 4, 5, 2010, nil, nil, nil, nil)]
121
+ msgs = times.map { |t| t.to_f.to_s }
122
+ filtered = filter(config, msgs)
123
+ filtered.each_with_index do |(time, _record), i|
124
+ assert_equal(times[i].to_i, time)
125
+ assert(time.is_a?(Fluent::EventTime))
126
+ assert_false(_record.has_key?('event_time_key'))
107
127
  end
108
128
  end
109
129
 
@@ -54,6 +54,15 @@ class CsvFormatterTest < ::Test::Unit::TestCase
54
54
  assert_equal("\"awesome\",\"awesome2\"\n", formatted)
55
55
  end
56
56
 
57
+ def test_format_without_newline
58
+ d = create_driver("fields" => "message,message2", "add_newline" => false)
59
+ formatted = d.instance.format(tag, @time, {
60
+ 'message' => 'awesome',
61
+ 'message2' => 'awesome2'
62
+ })
63
+ assert_equal("\"awesome\",\"awesome2\"", formatted)
64
+ end
65
+
57
66
  def test_format_with_customized_delimiters
58
67
  d = create_driver("fields" => "message,message2",
59
68
  "delimiter" => "\t")
@@ -0,0 +1,35 @@
1
+ require_relative '../helper'
2
+ require 'fluent/test/driver/formatter'
3
+ require 'fluent/plugin/formatter_hash'
4
+
5
+ class HashFormatterTest < ::Test::Unit::TestCase
6
+ def setup
7
+ @time = event_time
8
+ end
9
+
10
+ def create_driver(conf = "")
11
+ Fluent::Test::Driver::Formatter.new(Fluent::Plugin::HashFormatter).configure(conf)
12
+ end
13
+
14
+ def tag
15
+ "tag"
16
+ end
17
+
18
+ def record
19
+ {'message' => 'awesome', 'greeting' => 'hello'}
20
+ end
21
+
22
+ def test_format
23
+ d = create_driver({})
24
+ formatted = d.instance.format(tag, @time, record)
25
+
26
+ assert_equal(%Q!{"message"=>"awesome", "greeting"=>"hello"}\n!, formatted.encode(Encoding::UTF_8))
27
+ end
28
+
29
+ def test_format_without_newline
30
+ d = create_driver('add_newline' => false)
31
+ formatted = d.instance.format(tag, @time, record)
32
+
33
+ assert_equal(%Q!{"message"=>"awesome", "greeting"=>"hello"}!, formatted.encode(Encoding::UTF_8))
34
+ end
35
+ end
@@ -33,6 +33,14 @@ class JsonFormatterTest < ::Test::Unit::TestCase
33
33
  assert_equal("#{JSON.generate(record)}\n", formatted)
34
34
  end
35
35
 
36
+ data('oj' => 'oj', 'yajl' => 'yajl')
37
+ def test_format_without_nl(data)
38
+ d = create_driver('json_parser' => data, 'add_newline' => false)
39
+ formatted = d.instance.format(tag, @time, record)
40
+
41
+ assert_equal(JSON.generate(record), formatted)
42
+ end
43
+
36
44
  data('oj' => 'oj', 'yajl' => 'yajl')
37
45
  def test_format_with_symbolic_record(data)
38
46
  d = create_driver('json_parser' => data)
@@ -40,6 +40,13 @@ class LabeledTSVFormatterTest < ::Test::Unit::TestCase
40
40
  assert_equal("message:awesome\tgreeting:hello\n", formatted)
41
41
  end
42
42
 
43
+ def test_format_without_newline
44
+ d = create_driver('add_newline' => false)
45
+ formatted = d.instance.format(tag, @time, record)
46
+
47
+ assert_equal("message:awesome\tgreeting:hello", formatted)
48
+ end
49
+
43
50
  def test_format_with_customized_delimiters
44
51
  d = create_driver(
45
52
  'delimiter' => ',',
@@ -112,10 +112,12 @@ class DummyTest < Test::Unit::TestCase
112
112
  d1.instance.emit(4)
113
113
  end
114
114
 
115
- first_id1 = d1.events.first[2]['id']
115
+ events = d1.events.sort{|a,b| a[2]['id'] <=> b[2]['id'] }
116
+
117
+ first_id1 = events.first[2]['id']
116
118
  assert_equal 0, first_id1
117
119
 
118
- last_id1 = d1.events.last[2]['id']
120
+ last_id1 = events.last[2]['id']
119
121
  assert { last_id1 > 0 }
120
122
 
121
123
  assert !File.exist?(File.join(TEST_PLUGIN_STORAGE_PATH, 'json', 'test-01.json'))
@@ -125,7 +127,9 @@ class DummyTest < Test::Unit::TestCase
125
127
  d2.instance.emit(4)
126
128
  end
127
129
 
128
- first_id2 = d2.events.first[2]['id']
130
+ events = d2.events.sort{|a,b| a[2]['id'] <=> b[2]['id'] }
131
+
132
+ first_id2 = events.first[2]['id']
129
133
  assert_equal 0, first_id2
130
134
 
131
135
  assert !File.exist?(File.join(TEST_PLUGIN_STORAGE_PATH, 'json', 'test-01.json'))
@@ -350,6 +350,39 @@ plugin_id:test_filter\tplugin_category:filter\ttype:test_filter\toutput_plugin:f
350
350
  assert_equal(expected_null_response, null_response)
351
351
  end
352
352
 
353
+ test "/api/plugins.json with 'with_ivars'. response contains specified instance variables of each plugin" do
354
+ d = create_driver("
355
+ @type monitor_agent
356
+ bind '127.0.0.1'
357
+ port #{@port}
358
+ tag monitor
359
+ ")
360
+ d.instance.start
361
+ expected_test_in_response = {
362
+ "output_plugin" => false,
363
+ "plugin_category" => "input",
364
+ "plugin_id" => "test_in",
365
+ "retry_count" => nil,
366
+ "type" => "test_in",
367
+ "instance_variables" => {"id" => "test_in"}
368
+ }
369
+ expected_null_response = {
370
+ "buffer_queue_length" => 0,
371
+ "buffer_total_queued_size" => 0,
372
+ "output_plugin" => true,
373
+ "plugin_category" => "output",
374
+ "plugin_id" => "null",
375
+ "retry_count" => 0,
376
+ "type" => "null",
377
+ "instance_variables" => {"id" => "null", "num_errors" => 0}
378
+ }
379
+ response = JSON.parse(get("http://127.0.0.1:#{@port}/api/plugins.json?with_config=no&with_retry=no&with_ivars=id,num_errors"))
380
+ test_in_response = response["plugins"][0]
381
+ null_response = response["plugins"][5]
382
+ assert_equal(expected_test_in_response, test_in_response)
383
+ assert_equal(expected_null_response, null_response)
384
+ end
385
+
353
386
  test "/api/config" do
354
387
  d = create_driver("
355
388
  @type monitor_agent
@@ -427,11 +460,7 @@ plugin_id:test_filter\tplugin_category:filter\ttype:test_filter\toutput_plugin:f
427
460
  "output_plugin" => true,
428
461
  "plugin_category" => "output",
429
462
  "plugin_id" => "test_out_fail_write",
430
- "retry_count" => 2,
431
463
  "type" => "test_out_fail_write",
432
- "retry" => {
433
- "steps" => 1
434
- }
435
464
  }
436
465
  output = @ra.outputs[0]
437
466
  output.start
@@ -440,12 +469,21 @@ plugin_id:test_filter\tplugin_category:filter\ttype:test_filter\toutput_plugin:f
440
469
  # flush few times to check steps
441
470
  2.times do
442
471
  output.force_flush
472
+ # output.force_flush calls #submit_flush_all, but #submit_flush_all skips to call #submit_flush_once when @retry exists.
473
+ # So that forced flush in retry state should be done by calling #submit_flush_once directly.
474
+ output.submit_flush_once
475
+ sleep 0.1 until output.buffer.queued?
443
476
  end
444
477
  response = JSON.parse(get("http://127.0.0.1:#{@port}/api/plugins.json"))
445
478
  test_out_fail_write_response = response["plugins"][1]
446
479
  # remove dynamic keys
447
- ["start", "next_time"].each { |key| test_out_fail_write_response["retry"].delete(key) }
480
+ response_retry_count = test_out_fail_write_response.delete("retry_count")
481
+ response_retry = test_out_fail_write_response.delete("retry")
448
482
  assert_equal(expected_test_out_fail_write_response, test_out_fail_write_response)
483
+ assert{ response_retry.has_key?("steps") }
484
+ # it's very hard to check exact retry count (because retries are called by output flush thread scheduling)
485
+ assert{ response_retry_count >= 1 && response_retry["steps"] >= 0 }
486
+ assert{ response_retry_count == response_retry["steps"] + 1 }
449
487
  end
450
488
  end
451
489
  end
@@ -37,6 +37,7 @@ class TailInputTest < Test::Unit::TestCase
37
37
  COMMON_CONFIG = CONFIG + config_element("", "", { "pos_file" => "#{TMP_DIR}/tail.pos" })
38
38
  CONFIG_READ_FROM_HEAD = config_element("", "", { "read_from_head" => true })
39
39
  CONFIG_ENABLE_WATCH_TIMER = config_element("", "", { "enable_watch_timer" => false })
40
+ CONFIG_OPEN_ON_EVERY_UPDATE = config_element("", "", { "open_on_every_update" => true })
40
41
  SINGLE_LINE_CONFIG = config_element("", "", { "format" => "/(?<message>.*)/" })
41
42
  PARSE_SINGLE_LINE_CONFIG = config_element("", "", {}, [config_element("parse", "", { "@type" => "/(?<message>.*)/" })])
42
43
  MULTILINE_CONFIG = config_element(
@@ -135,8 +136,7 @@ class TailInputTest < Test::Unit::TestCase
135
136
 
136
137
  d.run(expect_emits: 1) do
137
138
  File.open("#{TMP_DIR}/tail.txt", "ab") {|f|
138
- f.puts "test3"
139
- f.puts "test4"
139
+ f.puts "test3\ntest4"
140
140
  }
141
141
  end
142
142
 
@@ -149,6 +149,28 @@ class TailInputTest < Test::Unit::TestCase
149
149
  assert_equal(1, d.emit_count)
150
150
  end
151
151
 
152
+ def test_emit_with_emit_unmatched_lines_true
153
+ config = config_element("", "", { "format" => "/^(?<message>test.*)/", "emit_unmatched_lines" => true })
154
+ File.open("#{TMP_DIR}/tail.txt", "wb") { |f| }
155
+
156
+ d = create_driver(config)
157
+ d.run(expect_emits: 1) do
158
+ File.open("#{TMP_DIR}/tail.txt", "ab") {|f|
159
+ f.puts "test line 1"
160
+ f.puts "test line 2"
161
+ f.puts "bad line 1"
162
+ f.puts "test line 3"
163
+ }
164
+ end
165
+
166
+ events = d.events
167
+ assert_equal(4, events.length)
168
+ assert_equal({"message" => "test line 1"}, events[0][2])
169
+ assert_equal({"message" => "test line 2"}, events[1][2])
170
+ assert_equal({"unmatched_line" => "bad line 1"}, events[2][2])
171
+ assert_equal({"message" => "test line 3"}, events[3][2])
172
+ end
173
+
152
174
  data('flat 1' => [:flat, 1, 2],
153
175
  'flat 10' => [:flat, 10, 1],
154
176
  'parse 1' => [:parse, 1, 2],
@@ -315,6 +337,20 @@ class TailInputTest < Test::Unit::TestCase
315
337
  assert_equal({"message" => "test6"}, events[5][2])
316
338
  end
317
339
 
340
+ data(flat: CONFIG_OPEN_ON_EVERY_UPDATE + CONFIG_READ_FROM_HEAD + SINGLE_LINE_CONFIG,
341
+ parse: CONFIG_OPEN_ON_EVERY_UPDATE + CONFIG_READ_FROM_HEAD + PARSE_SINGLE_LINE_CONFIG)
342
+ def test_rotate_file_with_open_on_every_update(data)
343
+ config = data
344
+ events = sub_test_rotate_file(config, expect_records: 6)
345
+ assert_equal(6, events.length)
346
+ assert_equal({"message" => "test1"}, events[0][2])
347
+ assert_equal({"message" => "test2"}, events[1][2])
348
+ assert_equal({"message" => "test3"}, events[2][2])
349
+ assert_equal({"message" => "test4"}, events[3][2])
350
+ assert_equal({"message" => "test5"}, events[4][2])
351
+ assert_equal({"message" => "test6"}, events[5][2])
352
+ end
353
+
318
354
  data(flat: SINGLE_LINE_CONFIG,
319
355
  parse: PARSE_SINGLE_LINE_CONFIG)
320
356
  def test_rotate_file_with_write_old(data)
@@ -331,6 +367,8 @@ class TailInputTest < Test::Unit::TestCase
331
367
  f.puts "test6"
332
368
  }
333
369
  }
370
+ # This test sometimes fails and it shows a potential bug of in_tail
371
+ # https://github.com/fluent/fluentd/issues/1434
334
372
  assert_equal(6, events.length)
335
373
  assert_equal({"message" => "test3"}, events[0][2])
336
374
  assert_equal({"message" => "test4"}, events[1][2])
@@ -487,6 +525,30 @@ class TailInputTest < Test::Unit::TestCase
487
525
  assert_equal(Encoding::UTF_8, events[0][2]['message'].encoding)
488
526
  end
489
527
 
528
+ def test_from_encoding_utf16
529
+ conf = config_element(
530
+ "", "", {
531
+ "format" => "/(?<message>.*)/",
532
+ "read_from_head" => "true",
533
+ "from_encoding" => "utf-16le",
534
+ "encoding" => "utf-8"
535
+ })
536
+ d = create_driver(conf)
537
+ utf16_message = "\u306F\u308D\u30FC\u308F\u30FC\u308B\u3069\n".encode(Encoding::UTF_16LE)
538
+ utf8_message = utf16_message.encode(Encoding::UTF_8).strip
539
+
540
+ d.run(expect_emits: 1) do
541
+ File.open("#{TMP_DIR}/tail.txt", "w:utf-16le") { |f|
542
+ f.write utf16_message
543
+ }
544
+ end
545
+
546
+ events = d.events
547
+ assert_equal(utf8_message, events[0][2]['message'])
548
+ assert_equal(Encoding::UTF_8, events[0][2]['message'].encoding)
549
+ end
550
+
551
+
490
552
  sub_test_case "multiline" do
491
553
  data(flat: MULTILINE_CONFIG,
492
554
  parse: PARSE_MULTILINE_CONFIG)
@@ -516,6 +578,35 @@ class TailInputTest < Test::Unit::TestCase
516
578
  assert_equal({"message1" => "test8"}, events[3][2])
517
579
  end
518
580
 
581
+ data(flat: MULTILINE_CONFIG,
582
+ parse: PARSE_MULTILINE_CONFIG)
583
+ def test_multiline_with_emit_unmatched_lines_true(data)
584
+ config = data + config_element("", "", { "emit_unmatched_lines" => true })
585
+ File.open("#{TMP_DIR}/tail.txt", "wb") { |f| }
586
+
587
+ d = create_driver(config)
588
+ d.run(expect_emits: 1) do
589
+ File.open("#{TMP_DIR}/tail.txt", "ab") { |f|
590
+ f.puts "f test1"
591
+ f.puts "s test2"
592
+ f.puts "f test3"
593
+ f.puts "f test4"
594
+ f.puts "s test5"
595
+ f.puts "s test6"
596
+ f.puts "f test7"
597
+ f.puts "s test8"
598
+ }
599
+ end
600
+
601
+ events = d.events
602
+ assert_equal(5, events.length)
603
+ assert_equal({"unmatched_line" => "f test1"}, events[0][2])
604
+ assert_equal({"message1" => "test2", "message2" => "test3", "message3" => "test4"}, events[1][2])
605
+ assert_equal({"message1" => "test5"}, events[2][2])
606
+ assert_equal({"message1" => "test6", "message2" => "test7"}, events[3][2])
607
+ assert_equal({"message1" => "test8"}, events[4][2])
608
+ end
609
+
519
610
  data(flat: MULTILINE_CONFIG,
520
611
  parse: PARSE_MULTILINE_CONFIG)
521
612
  def test_multiline_with_flush_interval(data)
@@ -769,7 +860,7 @@ class TailInputTest < Test::Unit::TestCase
769
860
  Timecop.freeze(2010, 1, 2, 3, 4, 5) do
770
861
  flexstub(Fluent::Plugin::TailInput::TailWatcher) do |watcherclass|
771
862
  EX_PATHS.each do |path|
772
- watcherclass.should_receive(:new).with(path, EX_RORATE_WAIT, Fluent::Plugin::TailInput::FilePositionEntry, any, true, true, 1000, any, any, any).once.and_return do
863
+ watcherclass.should_receive(:new).with(path, EX_RORATE_WAIT, Fluent::Plugin::TailInput::FilePositionEntry, any, true, true, 1000, any, any, any, any, any, any).once.and_return do
773
864
  flexmock('TailWatcher') { |watcher|
774
865
  watcher.should_receive(:attach).once
775
866
  watcher.should_receive(:unwatched=).zero_or_more_times
@@ -787,7 +878,7 @@ class TailInputTest < Test::Unit::TestCase
787
878
 
788
879
  Timecop.freeze(2010, 1, 2, 3, 4, 6) do
789
880
  flexstub(Fluent::Plugin::TailInput::TailWatcher) do |watcherclass|
790
- watcherclass.should_receive(:new).with('test/plugin/data/2010/01/20100102-030406.log', EX_RORATE_WAIT, Fluent::Plugin::TailInput::FilePositionEntry, any, true, true, 1000, any, any, any).once.and_return do
881
+ watcherclass.should_receive(:new).with('test/plugin/data/2010/01/20100102-030406.log', EX_RORATE_WAIT, Fluent::Plugin::TailInput::FilePositionEntry, any, true, true, 1000, any, any, any, any, any, any).once.and_return do
791
882
  flexmock('TailWatcher') do |watcher|
792
883
  watcher.should_receive(:attach).once
793
884
  watcher.should_receive(:unwatched=).zero_or_more_times
@@ -898,6 +989,8 @@ class TailInputTest < Test::Unit::TestCase
898
989
  f.puts "test4"
899
990
  }
900
991
  end
992
+ # This test sometimes fails and it shows a potential bug of in_tail
993
+ # https://github.com/fluent/fluentd/issues/1434
901
994
  events = d.events
902
995
  assert_equal(2, events.length)
903
996
  assert_equal({"message" => "test3"}, events[0][2])
@@ -85,6 +85,10 @@ class InputTest < Test::Unit::TestCase
85
85
  end
86
86
  end
87
87
 
88
+ test 'are not available with multi workers configuration in default' do
89
+ assert_false @p.multi_workers_ready?
90
+ end
91
+
88
92
  test 'has router and can emit into it' do
89
93
  assert @p.has_router?
90
94
 
@@ -22,8 +22,8 @@ class FileOutputTest < Test::Unit::TestCase
22
22
  utc
23
23
  ]
24
24
 
25
- def create_driver(conf = CONFIG)
26
- Fluent::Test::Driver::Output.new(Fluent::Plugin::FileOutput).configure(conf)
25
+ def create_driver(conf = CONFIG, opts = {})
26
+ Fluent::Test::Driver::Output.new(Fluent::Plugin::FileOutput, opts: opts).configure(conf)
27
27
  end
28
28
 
29
29
  sub_test_case 'configuration' do
@@ -37,7 +37,29 @@ class FileOutputTest < Test::Unit::TestCase
37
37
  assert_equal :gzip, d.instance.instance_eval{ @compress_method }
38
38
  end
39
39
 
40
+ test 'using root_dir for buffer path' do
41
+ system_conf_opts = {'root_dir' => File.join(TMP_DIR, 'testrootdir')}
42
+ buf_conf = config_element('buffer', '', {'flush_interval' => '1s'})
43
+ conf = config_element('match', '**', {'@id' => 'myout', 'path' => 'test_path', 'append' => 'true'}, [buf_conf])
44
+ d = create_driver(conf, system_conf_opts)
45
+
46
+ assert_equal 'test_path', d.instance.path
47
+ assert d.instance.append
48
+
49
+ assert d.instance.buffer.respond_to?(:path) # file buffer
50
+ assert_equal 1, d.instance.buffer_config.flush_interval
51
+
52
+ assert_equal File.join(TMP_DIR, 'testrootdir', 'worker0', 'myout'), d.instance.plugin_root_dir
53
+
54
+ buffer_path_under_root_dir = File.join(TMP_DIR, 'testrootdir', 'worker0', 'myout', 'buffer', 'buffer.*.log')
55
+ assert_equal buffer_path_under_root_dir, d.instance.buffer.path
56
+ end
57
+
40
58
  test 'path should be writable' do
59
+ assert_raise(Fluent::ConfigError.new("'path' parameter is required")) do
60
+ create_driver ""
61
+ end
62
+
41
63
  assert_nothing_raised do
42
64
  create_driver %[path #{TMP_DIR}/test_path]
43
65
  end
@@ -718,7 +740,7 @@ class FileOutputTest < Test::Unit::TestCase
718
740
  end
719
741
 
720
742
  test 'raise error to show it is a bug when path including * specified without timekey' do
721
- assert_raise "BUG: configuration error must be raised for path including '*' without timekey" do
743
+ assert_raise RuntimeError.new("BUG: configuration error must be raised for path including '*' without timekey") do
722
744
  @i.generate_path_template('/path/to/file.*.log', nil, false, nil)
723
745
  end
724
746
  end
@@ -808,8 +830,10 @@ class FileOutputTest < Test::Unit::TestCase
808
830
  end
809
831
 
810
832
  test 'raise error if argument path does not include index placeholder' do
811
- assert_raise "BUG: index placeholder not found in path: #{@tmp}/myfile" do
812
- @i.find_filepath_available("#{@tmp}/myfile")
833
+ assert_raise RuntimeError.new("BUG: index placeholder not found in path: #{@tmp}/myfile") do
834
+ @i.find_filepath_available("#{@tmp}/myfile") do |path|
835
+ # ...
836
+ end
813
837
  end
814
838
  end
815
839
 
@@ -821,14 +845,29 @@ class FileOutputTest < Test::Unit::TestCase
821
845
  )
822
846
  test 'returns filepath with _0 at first' do |data|
823
847
  expected, argument = data
824
- assert_equal File.join(@tmp, expected), @i.find_filepath_available(File.join(@tmp, argument))
848
+ @i.find_filepath_available(File.join(@tmp, argument)) do |path|
849
+ assert_equal File.join(@tmp, expected), path
850
+ end
825
851
  end
826
852
 
827
853
  test 'returns filepath with index which does not exist yet' do
854
+ 5.times do |i|
855
+ File.open(File.join(@tmp, "exist_#{i}.log"), 'a'){|f| } # open(create) and close
856
+ end
857
+ @i.find_filepath_available(File.join(@tmp, "exist_**.log")) do |path|
858
+ assert_equal File.join(@tmp, "exist_5.log"), path
859
+ end
860
+ end
861
+
862
+ test 'creates lock directory when with_lock is true to exclude operations of other worker process' do
828
863
  5.times do |i|
829
864
  File.open(File.join(@tmp, "exist_#{i}.log"), 'a')
830
865
  end
831
- assert_equal File.join(@tmp, "exist_5.log"), @i.find_filepath_available(File.join(@tmp, "exist_**.log"))
866
+ Dir.mkdir(File.join(@tmp, "exist_5.log.lock"))
867
+ @i.find_filepath_available(File.join(@tmp, "exist_**.log"), with_lock: true) do |path|
868
+ assert Dir.exist?(File.join(@tmp, "exist_6.log.lock"))
869
+ assert_equal File.join(@tmp, "exist_6.log"), path
870
+ end
832
871
  end
833
872
  end
834
873
  end