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.

Files changed (159) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +16 -5
  3. data/ADOPTERS.md +5 -0
  4. data/{ChangeLog → CHANGELOG.md} +495 -6
  5. data/CONTRIBUTING.md +5 -2
  6. data/GOVERNANCE.md +55 -0
  7. data/LICENSE +202 -0
  8. data/MAINTAINERS.md +7 -5
  9. data/README.md +17 -10
  10. data/bin/fluent-ca-generate +6 -0
  11. data/example/counter.conf +18 -0
  12. data/example/secondary_file.conf +3 -2
  13. data/fluentd.gemspec +3 -3
  14. data/lib/fluent/agent.rb +1 -1
  15. data/lib/fluent/command/binlog_reader.rb +11 -2
  16. data/lib/fluent/command/ca_generate.rb +181 -0
  17. data/lib/fluent/command/cat.rb +28 -15
  18. data/lib/fluent/command/debug.rb +4 -4
  19. data/lib/fluent/command/fluentd.rb +2 -2
  20. data/lib/fluent/command/plugin_config_formatter.rb +24 -2
  21. data/lib/fluent/command/plugin_generator.rb +26 -8
  22. data/lib/fluent/config/configure_proxy.rb +7 -1
  23. data/lib/fluent/config/dsl.rb +8 -5
  24. data/lib/fluent/config/element.rb +5 -0
  25. data/lib/fluent/config/literal_parser.rb +7 -1
  26. data/lib/fluent/config/types.rb +28 -2
  27. data/lib/fluent/config/v1_parser.rb +1 -2
  28. data/lib/fluent/configurable.rb +1 -0
  29. data/lib/fluent/counter.rb +23 -0
  30. data/lib/fluent/counter/base_socket.rb +46 -0
  31. data/lib/fluent/counter/client.rb +297 -0
  32. data/lib/fluent/counter/error.rb +86 -0
  33. data/lib/fluent/counter/mutex_hash.rb +163 -0
  34. data/lib/fluent/counter/server.rb +273 -0
  35. data/lib/fluent/counter/store.rb +205 -0
  36. data/lib/fluent/counter/validator.rb +145 -0
  37. data/lib/fluent/env.rb +1 -0
  38. data/lib/fluent/event_router.rb +1 -1
  39. data/lib/fluent/log.rb +119 -29
  40. data/lib/fluent/plugin/base.rb +12 -0
  41. data/lib/fluent/plugin/buf_file.rb +20 -16
  42. data/lib/fluent/plugin/buffer.rb +130 -32
  43. data/lib/fluent/plugin/buffer/file_chunk.rb +23 -4
  44. data/lib/fluent/plugin/compressable.rb +1 -1
  45. data/lib/fluent/plugin/filter_grep.rb +135 -21
  46. data/lib/fluent/plugin/filter_parser.rb +13 -2
  47. data/lib/fluent/plugin/filter_record_transformer.rb +16 -14
  48. data/lib/fluent/plugin/formatter_stdout.rb +3 -2
  49. data/lib/fluent/plugin/formatter_tsv.rb +5 -1
  50. data/lib/fluent/plugin/in_debug_agent.rb +8 -1
  51. data/lib/fluent/plugin/in_forward.rb +1 -1
  52. data/lib/fluent/plugin/in_http.rb +84 -3
  53. data/lib/fluent/plugin/in_monitor_agent.rb +7 -1
  54. data/lib/fluent/plugin/in_syslog.rb +31 -10
  55. data/lib/fluent/plugin/in_tail.rb +142 -53
  56. data/lib/fluent/plugin/in_tcp.rb +5 -6
  57. data/lib/fluent/plugin/in_udp.rb +6 -2
  58. data/lib/fluent/plugin/in_unix.rb +1 -1
  59. data/lib/fluent/plugin/multi_output.rb +1 -0
  60. data/lib/fluent/plugin/out_copy.rb +25 -2
  61. data/lib/fluent/plugin/out_file.rb +26 -7
  62. data/lib/fluent/plugin/out_forward.rb +81 -42
  63. data/lib/fluent/plugin/out_secondary_file.rb +2 -2
  64. data/lib/fluent/plugin/out_stdout.rb +0 -1
  65. data/lib/fluent/plugin/out_stream.rb +1 -1
  66. data/lib/fluent/plugin/output.rb +221 -57
  67. data/lib/fluent/plugin/parser_apache.rb +1 -1
  68. data/lib/fluent/plugin/parser_apache2.rb +5 -1
  69. data/lib/fluent/plugin/parser_apache_error.rb +1 -1
  70. data/lib/fluent/plugin/parser_json.rb +10 -3
  71. data/lib/fluent/plugin/parser_ltsv.rb +7 -0
  72. data/lib/fluent/plugin/parser_multiline.rb +2 -1
  73. data/lib/fluent/plugin/parser_nginx.rb +1 -1
  74. data/lib/fluent/plugin/parser_none.rb +1 -0
  75. data/lib/fluent/plugin/parser_regexp.rb +15 -14
  76. data/lib/fluent/plugin/parser_syslog.rb +9 -5
  77. data/lib/fluent/plugin_helper.rb +2 -0
  78. data/lib/fluent/plugin_helper/cert_option.rb +28 -9
  79. data/lib/fluent/plugin_helper/compat_parameters.rb +3 -1
  80. data/lib/fluent/plugin_helper/counter.rb +51 -0
  81. data/lib/fluent/plugin_helper/event_loop.rb +9 -0
  82. data/lib/fluent/plugin_helper/record_accessor.rb +210 -0
  83. data/lib/fluent/plugin_helper/retry_state.rb +15 -7
  84. data/lib/fluent/plugin_helper/server.rb +87 -25
  85. data/lib/fluent/plugin_helper/socket_option.rb +5 -2
  86. data/lib/fluent/plugin_helper/timer.rb +8 -7
  87. data/lib/fluent/root_agent.rb +18 -9
  88. data/lib/fluent/supervisor.rb +63 -23
  89. data/lib/fluent/system_config.rb +30 -2
  90. data/lib/fluent/test/helpers.rb +1 -1
  91. data/lib/fluent/time.rb +15 -7
  92. data/lib/fluent/timezone.rb +26 -2
  93. data/lib/fluent/version.rb +1 -1
  94. data/templates/new_gem/README.md.erb +2 -2
  95. data/templates/new_gem/lib/fluent/plugin/filter.rb.erb +1 -1
  96. data/templates/new_gem/lib/fluent/plugin/input.rb.erb +1 -1
  97. data/templates/new_gem/lib/fluent/plugin/output.rb.erb +1 -1
  98. data/templates/new_gem/lib/fluent/plugin/parser.rb.erb +4 -4
  99. data/test/command/test_ca_generate.rb +70 -0
  100. data/test/command/test_fluentd.rb +2 -2
  101. data/test/command/test_plugin_config_formatter.rb +8 -7
  102. data/test/command/test_plugin_generator.rb +65 -39
  103. data/test/config/test_config_parser.rb +7 -2
  104. data/test/config/test_configurable.rb +7 -2
  105. data/test/config/test_configure_proxy.rb +41 -3
  106. data/test/config/test_dsl.rb +10 -10
  107. data/test/config/test_element.rb +10 -0
  108. data/test/config/test_literal_parser.rb +8 -0
  109. data/test/config/test_plugin_configuration.rb +56 -0
  110. data/test/config/test_system_config.rb +19 -1
  111. data/test/config/test_types.rb +37 -0
  112. data/test/counter/test_client.rb +559 -0
  113. data/test/counter/test_error.rb +44 -0
  114. data/test/counter/test_mutex_hash.rb +179 -0
  115. data/test/counter/test_server.rb +589 -0
  116. data/test/counter/test_store.rb +258 -0
  117. data/test/counter/test_validator.rb +137 -0
  118. data/test/plugin/test_buf_file.rb +124 -0
  119. data/test/plugin/test_buffer.rb +3 -2
  120. data/test/plugin/test_filter_grep.rb +580 -2
  121. data/test/plugin/test_filter_parser.rb +33 -2
  122. data/test/plugin/test_filter_record_transformer.rb +22 -1
  123. data/test/plugin/test_formatter_ltsv.rb +3 -0
  124. data/test/plugin/test_formatter_tsv.rb +68 -0
  125. data/test/plugin/test_in_debug_agent.rb +21 -0
  126. data/test/plugin/test_in_exec.rb +3 -5
  127. data/test/plugin/test_in_http.rb +178 -0
  128. data/test/plugin/test_in_monitor_agent.rb +1 -1
  129. data/test/plugin/test_in_syslog.rb +64 -0
  130. data/test/plugin/test_in_tail.rb +116 -6
  131. data/test/plugin/test_in_tcp.rb +21 -0
  132. data/test/plugin/test_in_udp.rb +78 -0
  133. data/test/plugin/test_metadata.rb +89 -0
  134. data/test/plugin/test_out_copy.rb +31 -0
  135. data/test/plugin/test_out_file.rb +108 -2
  136. data/test/plugin/test_out_forward.rb +195 -2
  137. data/test/plugin/test_out_secondary_file.rb +14 -0
  138. data/test/plugin/test_output.rb +159 -45
  139. data/test/plugin/test_output_as_buffered.rb +19 -0
  140. data/test/plugin/test_output_as_buffered_backup.rb +307 -0
  141. data/test/plugin/test_output_as_buffered_retries.rb +70 -0
  142. data/test/plugin/test_output_as_buffered_secondary.rb +1 -1
  143. data/test/plugin/test_parser_apache2.rb +1 -0
  144. data/test/plugin/test_parser_labeled_tsv.rb +17 -0
  145. data/test/plugin/test_parser_nginx.rb +40 -0
  146. data/test/plugin/test_parser_regexp.rb +6 -7
  147. data/test/plugin/test_parser_syslog.rb +155 -5
  148. data/test/plugin_helper/test_child_process.rb +4 -4
  149. data/test/plugin_helper/test_compat_parameters.rb +22 -0
  150. data/test/plugin_helper/test_record_accessor.rb +197 -0
  151. data/test/plugin_helper/test_retry_state.rb +20 -0
  152. data/test/plugin_helper/test_server.rb +30 -2
  153. data/test/test_config.rb +3 -3
  154. data/test/test_configdsl.rb +2 -2
  155. data/test/test_log.rb +51 -1
  156. data/test/test_root_agent.rb +33 -0
  157. data/test/test_supervisor.rb +105 -0
  158. metadata +68 -8
  159. data/COPYING +0 -14
@@ -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
- test '#extract_placeholders does nothing if chunk key is not specified' do
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
- m = create_metadata(timekey: t, tag: 'fluentd.test.output', variables: v)
231
- assert_equal tmpl, @i.extract_placeholders(tmpl, m)
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
- test '#extract_placeholders can extract time if time key and range are configured' do
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
- m = create_metadata(timekey: t, tag: 'fluentd.test.output', variables: v)
243
- assert_equal "/mypath/2016/04/11/20-30/${tag}/${tag[1]}/${tag[2]}/${key1}/${key2}/tail", @i.extract_placeholders(tmpl, m)
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
- test '#extract_placeholders can extract tag and parts of tag if tag is configured' do
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
- m = create_metadata(timekey: t, tag: 'fluentd.test.output', variables: v)
255
- assert_equal "/mypath/%Y/%m/%d/%H-%M/fluentd.test.output/test/output/${key1}/${key2}/tail", @i.extract_placeholders(tmpl, m)
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
- test '#extract_placeholders can extract variables if variables are configured' do
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
- m = create_metadata(timekey: t, tag: 'fluentd.test.output', variables: v)
267
- assert_equal "/mypath/%Y/%m/%d/%H-%M/${tag}/${tag[1]}/${tag[2]}/value1/value2/tail", @i.extract_placeholders(tmpl, m)
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
- test '#extract_placeholders can extract all chunk keys if configured' do
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
- m = create_metadata(timekey: t, tag: 'fluentd.test.output', variables: v)
279
- assert_equal "/mypath/2016/04/11/20-30/fluentd.test.output/test/output/value1/value2/tail", @i.extract_placeholders(tmpl, m)
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
- test '#extract_placeholders removes out-of-range tag part and unknown variable placeholders' do
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
- assert_equal "/mypath/2016/04/11/20-30/fluentd.test.output/////tail", @i.extract_placeholders(tmpl, m)
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 templace when timestamp placeholders exist but time key is missing' do
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
- test "Warn if primary type is different from secondary type and either primary or secondary has custom_format" do
728
- o = create_output(:buffered)
729
- mock(o.log).warn("secondary type should be same with primary one",
730
- { primary: o.class.to_s, secondary: "Fluent::Plugin::TestOutput" })
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
- o.configure(config_element('ROOT','',{},[config_element('secondary','',{'@type'=>'test', 'name' => "cool"})]))
733
- assert_not_nil o.instance_variable_get(:@secondary)
734
- end
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
- test "don't warn if primary type is the same as secondary type" do
737
- o = Fluent::Plugin::TestOutput.new
738
- mock(o.log).warn("secondary type should be same with primary one",
739
- { primary: o.class.to_s, secondary: "Fluent::Plugin::TestOutput" }).never
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
- o.configure(config_element('ROOT','',{'name' => "cool2"},
742
- [config_element('secondary','',{'@type'=>'test', 'name' => "cool"}),
743
- config_element('buffer','',{'@type'=>'memory'})]
744
- ))
745
- assert_not_nil o.instance_variable_get(:@secondary)
746
- end
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
- test "don't warn if primary type is different from secondary type and both don't have custom_format" do
749
- o = create_output(:standard)
750
- mock(o.log).warn("secondary type should be same with primary one",
751
- { primary: o.class.to_s, secondary: "Fluent::Plugin::TestOutput" }).never
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
- o.configure(config_element('ROOT','',{},[config_element('secondary','',{'@type'=>'test', 'name' => "cool"})]))
754
- assert_not_nil o.instance_variable_get(:@secondary)
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}, [config_element('buffer', '', {"flush_mode" => "immediate"})]))
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