fluentd 1.12.0-x64-mingw32 → 1.13.0-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.
- checksums.yaml +4 -4
- data/.deepsource.toml +13 -0
- data/.github/ISSUE_TEMPLATE/config.yml +2 -2
- data/.github/workflows/linux-test.yaml +36 -0
- data/.github/workflows/macos-test.yaml +30 -0
- data/.github/workflows/windows-test.yaml +46 -0
- data/.gitlab-ci.yml +41 -19
- data/CHANGELOG.md +164 -2
- data/CONTRIBUTING.md +2 -2
- data/MAINTAINERS.md +5 -2
- data/README.md +7 -4
- data/example/counter.conf +1 -1
- data/fluentd.gemspec +5 -4
- data/lib/fluent/command/bundler_injection.rb +1 -1
- data/lib/fluent/command/ca_generate.rb +6 -3
- data/lib/fluent/command/cat.rb +19 -4
- data/lib/fluent/command/fluentd.rb +5 -2
- data/lib/fluent/command/plugin_config_formatter.rb +16 -1
- data/lib/fluent/command/plugin_generator.rb +31 -1
- data/lib/fluent/compat/parser.rb +2 -2
- data/lib/fluent/config/section.rb +2 -2
- data/lib/fluent/config/types.rb +2 -2
- data/lib/fluent/event.rb +3 -13
- data/lib/fluent/load.rb +0 -1
- data/lib/fluent/log.rb +1 -0
- data/lib/fluent/plugin/file_wrapper.rb +49 -4
- data/lib/fluent/plugin/formatter_ltsv.rb +2 -2
- data/lib/fluent/plugin/in_http.rb +11 -1
- data/lib/fluent/plugin/in_monitor_agent.rb +1 -1
- data/lib/fluent/plugin/in_tail.rb +141 -41
- data/lib/fluent/plugin/in_tail/position_file.rb +15 -1
- data/lib/fluent/plugin/out_copy.rb +18 -5
- data/lib/fluent/plugin/out_exec_filter.rb +3 -3
- data/lib/fluent/plugin/out_forward.rb +74 -58
- data/lib/fluent/plugin/out_http.rb +9 -2
- data/lib/fluent/plugin/output.rb +11 -9
- data/lib/fluent/plugin/parser_csv.rb +2 -2
- data/lib/fluent/plugin/parser_syslog.rb +2 -2
- data/lib/fluent/plugin/storage_local.rb +4 -4
- data/lib/fluent/plugin_helper/server.rb +4 -2
- data/lib/fluent/plugin_helper/service_discovery.rb +39 -1
- data/lib/fluent/plugin_helper/service_discovery/manager.rb +11 -5
- data/lib/fluent/plugin_helper/socket_option.rb +2 -2
- data/lib/fluent/supervisor.rb +28 -5
- data/lib/fluent/system_config.rb +16 -1
- data/lib/fluent/time.rb +57 -1
- data/lib/fluent/version.rb +1 -1
- data/templates/new_gem/fluent-plugin.gemspec.erb +3 -3
- data/templates/plugin_config_formatter/param.md-table.erb +10 -0
- data/test/command/test_cat.rb +96 -0
- data/test/command/test_fluentd.rb +38 -0
- data/test/command/test_plugin_config_formatter.rb +67 -0
- data/test/config/test_configurable.rb +1 -1
- data/test/config/test_system_config.rb +46 -0
- data/test/plugin/in_tail/test_io_handler.rb +4 -4
- data/test/plugin/in_tail/test_position_file.rb +59 -5
- data/test/plugin/test_file_wrapper.rb +115 -0
- data/test/plugin/test_in_exec.rb +1 -1
- data/test/plugin/test_in_http.rb +15 -0
- data/test/plugin/test_in_tail.rb +309 -26
- data/test/plugin/test_out_copy.rb +87 -0
- data/test/plugin/test_out_forward.rb +104 -11
- data/test/plugin/test_out_http.rb +20 -1
- data/test/plugin/test_output.rb +15 -3
- data/test/plugin/test_output_as_buffered_backup.rb +2 -0
- data/test/plugin/test_parser_csv.rb +14 -0
- data/test/plugin/test_parser_syslog.rb +14 -0
- data/test/plugin/test_sd_file.rb +1 -1
- data/test/plugin_helper/service_discovery/test_manager.rb +1 -1
- data/test/plugin_helper/test_child_process.rb +5 -2
- data/test/plugin_helper/test_http_server_helper.rb +4 -2
- data/test/plugin_helper/test_server.rb +26 -7
- data/test/plugin_helper/test_service_discovery.rb +74 -14
- data/test/test_config.rb +2 -1
- data/test/test_event.rb +16 -0
- data/test/test_formatter.rb +30 -0
- data/test/test_output.rb +2 -2
- data/test/test_supervisor.rb +66 -0
- data/test/test_time_parser.rb +109 -0
- metadata +58 -31
- data/.travis.yml +0 -77
- data/appveyor.yml +0 -31
@@ -27,14 +27,14 @@ module Fluent
|
|
27
27
|
|
28
28
|
config_param :delimiter, :string, default: "\t".freeze
|
29
29
|
config_param :label_delimiter, :string, default: ":".freeze
|
30
|
+
config_param :replacement, :string, default: " ".freeze
|
30
31
|
config_param :add_newline, :bool, default: true
|
31
32
|
|
32
|
-
# TODO: escaping for \t in values
|
33
33
|
def format(tag, time, record)
|
34
34
|
formatted = ""
|
35
35
|
record.each do |label, value|
|
36
36
|
formatted << @delimiter if formatted.length.nonzero?
|
37
|
-
formatted << "#{label}#{@label_delimiter}#{value}"
|
37
|
+
formatted << "#{label}#{@label_delimiter}#{value.to_s.gsub(@delimiter, @replacement)}"
|
38
38
|
end
|
39
39
|
formatted << @newline if @add_newline
|
40
40
|
formatted
|
@@ -461,6 +461,12 @@ module Fluent::Plugin
|
|
461
461
|
RES_200_STATUS = "200 OK".freeze
|
462
462
|
RES_403_STATUS = "403 Forbidden".freeze
|
463
463
|
|
464
|
+
# Azure App Service sends GET requests for health checking purpose.
|
465
|
+
# Respond with `200 OK` to accommodate it.
|
466
|
+
def handle_get_request
|
467
|
+
return send_response_and_close(RES_200_STATUS, {}, "")
|
468
|
+
end
|
469
|
+
|
464
470
|
# Web browsers can send an OPTIONS request before performing POST
|
465
471
|
# to check if cross-origin requests are supported.
|
466
472
|
def handle_options_request
|
@@ -494,6 +500,10 @@ module Fluent::Plugin
|
|
494
500
|
def on_message_complete
|
495
501
|
return if closing?
|
496
502
|
|
503
|
+
if @parser.http_method == 'GET'.freeze
|
504
|
+
return handle_get_request()
|
505
|
+
end
|
506
|
+
|
497
507
|
if @parser.http_method == 'OPTIONS'.freeze
|
498
508
|
return handle_options_request()
|
499
509
|
end
|
@@ -503,7 +513,7 @@ module Fluent::Plugin
|
|
503
513
|
# For every incoming request, we check if we have some CORS
|
504
514
|
# restrictions and allow listed origins through @cors_allow_origins.
|
505
515
|
unless @cors_allow_origins.nil?
|
506
|
-
unless @cors_allow_origins.include?('*')
|
516
|
+
unless @cors_allow_origins.include?('*') || include_cors_allow_origin
|
507
517
|
send_response_and_close(RES_403_STATUS, {'Connection' => 'close'}, "")
|
508
518
|
return
|
509
519
|
end
|
@@ -338,7 +338,7 @@ module Fluent::Plugin
|
|
338
338
|
obj.merge!(pe.statistics['output'] || {})
|
339
339
|
end
|
340
340
|
|
341
|
-
obj['retry'] = get_retry_info(pe.retry) if opts[:with_retry]
|
341
|
+
obj['retry'] = get_retry_info(pe.retry) if opts[:with_retry] && pe.instance_variable_defined?(:@retry)
|
342
342
|
|
343
343
|
# include all instance variables if :with_debug_info is set
|
344
344
|
if opts[:with_debug_info]
|
@@ -56,6 +56,7 @@ module Fluent::Plugin
|
|
56
56
|
@pf_file = nil
|
57
57
|
@pf = nil
|
58
58
|
@ignore_list = []
|
59
|
+
@shutdown_start_time = nil
|
59
60
|
end
|
60
61
|
|
61
62
|
desc 'The paths to read. Multiple paths can be specified, separated by comma.'
|
@@ -81,6 +82,8 @@ module Fluent::Plugin
|
|
81
82
|
config_param :refresh_interval, :time, default: 60
|
82
83
|
desc 'The number of reading lines at each IO.'
|
83
84
|
config_param :read_lines_limit, :integer, default: 1000
|
85
|
+
desc 'The number of reading bytes per second'
|
86
|
+
config_param :read_bytes_limit_per_second, :size, default: -1
|
84
87
|
desc 'The interval of flushing the buffer for multiline format'
|
85
88
|
config_param :multiline_flush_interval, :time, default: nil
|
86
89
|
desc 'Enable the option to emit unmatched lines.'
|
@@ -178,6 +181,16 @@ module Fluent::Plugin
|
|
178
181
|
# parser is already created by parser helper
|
179
182
|
@parser = parser_create(usage: parser_config['usage'] || @parser_configs.first.usage)
|
180
183
|
@capability = Fluent::Capability.new(:current_process)
|
184
|
+
if @read_bytes_limit_per_second > 0
|
185
|
+
if !@enable_watch_timer
|
186
|
+
raise Fluent::ConfigError, "Need to enable watch timer when using log throttling feature"
|
187
|
+
end
|
188
|
+
min_bytes = TailWatcher::IOHandler::BYTES_TO_READ
|
189
|
+
if @read_bytes_limit_per_second < min_bytes
|
190
|
+
log.warn "Should specify greater equal than #{min_bytes}. Use #{min_bytes} for read_bytes_limit_per_second"
|
191
|
+
@read_bytes_limit_per_second = min_bytes
|
192
|
+
end
|
193
|
+
end
|
181
194
|
end
|
182
195
|
|
183
196
|
def configure_tag
|
@@ -244,6 +257,7 @@ module Fluent::Plugin
|
|
244
257
|
end
|
245
258
|
|
246
259
|
def shutdown
|
260
|
+
@shutdown_start_time = Fluent::Clock.now
|
247
261
|
# during shutdown phase, don't close io. It should be done in close after all threads are stopped. See close.
|
248
262
|
stop_watchers(existence_path, immediate: true, remove_watcher: false)
|
249
263
|
@pf_file.close if @pf_file
|
@@ -290,7 +304,7 @@ module Fluent::Plugin
|
|
290
304
|
end
|
291
305
|
false
|
292
306
|
end
|
293
|
-
rescue Errno::ENOENT
|
307
|
+
rescue Errno::ENOENT, Errno::EACCES
|
294
308
|
log.debug("#{p} is missing after refresh file list")
|
295
309
|
false
|
296
310
|
end
|
@@ -313,11 +327,17 @@ module Fluent::Plugin
|
|
313
327
|
(paths - excluded).select { |path|
|
314
328
|
FileTest.exist?(path)
|
315
329
|
}.each { |path|
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
330
|
+
# Even we just checked for existence, there is a race condition here as
|
331
|
+
# of which stat() might fail with ENOENT. See #3224.
|
332
|
+
begin
|
333
|
+
target_info = TargetInfo.new(path, Fluent::FileWrapper.stat(path).ino)
|
334
|
+
if @follow_inodes
|
335
|
+
hash[target_info.ino] = target_info
|
336
|
+
else
|
337
|
+
hash[target_info.path] = target_info
|
338
|
+
end
|
339
|
+
rescue Errno::ENOENT, Errno::EACCES => e
|
340
|
+
$log.warn "expand_paths: stat() for #{path} failed with #{e.class.name}. Skip file."
|
321
341
|
end
|
322
342
|
}
|
323
343
|
hash
|
@@ -367,8 +387,6 @@ module Fluent::Plugin
|
|
367
387
|
tw.register_watcher(tt)
|
368
388
|
end
|
369
389
|
|
370
|
-
tw.on_notify
|
371
|
-
|
372
390
|
tw.watchers.each do |watcher|
|
373
391
|
event_loop_attach(watcher)
|
374
392
|
end
|
@@ -380,34 +398,48 @@ module Fluent::Plugin
|
|
380
398
|
event_loop_detach(watcher)
|
381
399
|
end
|
382
400
|
|
383
|
-
tw.detach
|
401
|
+
tw.detach(@shutdown_start_time)
|
384
402
|
tw.close
|
385
403
|
end
|
386
404
|
raise e
|
387
405
|
end
|
388
406
|
|
389
|
-
def
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
$log.warn "#{target_info.path} not found. Continuing without tailing it."
|
399
|
-
end
|
407
|
+
def construct_watcher(target_info)
|
408
|
+
pe = nil
|
409
|
+
if @pf
|
410
|
+
pe = @pf[target_info]
|
411
|
+
if @read_from_head && pe.read_inode.zero?
|
412
|
+
begin
|
413
|
+
pe.update(Fluent::FileWrapper.stat(target_info.path).ino, 0)
|
414
|
+
rescue Errno::ENOENT, Errno::EACCES
|
415
|
+
$log.warn "stat() for #{target_info.path} failed. Continuing without tailing it."
|
400
416
|
end
|
401
417
|
end
|
418
|
+
end
|
402
419
|
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
|
420
|
+
begin
|
421
|
+
tw = setup_watcher(target_info, pe)
|
422
|
+
rescue WatcherSetupError => e
|
423
|
+
log.warn "Skip #{target_info.path} because unexpected setup error happens: #{e}"
|
424
|
+
return
|
425
|
+
end
|
426
|
+
|
427
|
+
begin
|
409
428
|
target_info = TargetInfo.new(target_info.path, Fluent::FileWrapper.stat(target_info.path).ino)
|
410
429
|
@tails[target_info] = tw
|
430
|
+
tw.on_notify
|
431
|
+
rescue Errno::ENOENT, Errno::EACCES => e
|
432
|
+
$log.warn "stat() for #{target_info.path} failed with #{e.class.name}. Drop tail watcher for now."
|
433
|
+
# explicitly detach and unwatch watcher `tw`.
|
434
|
+
tw.unwatched = true
|
435
|
+
detach_watcher(tw, target_info.ino, false)
|
436
|
+
end
|
437
|
+
end
|
438
|
+
|
439
|
+
def start_watchers(targets_info)
|
440
|
+
targets_info.each_value {|target_info|
|
441
|
+
construct_watcher(target_info)
|
442
|
+
break if before_shutdown?
|
411
443
|
}
|
412
444
|
end
|
413
445
|
|
@@ -460,9 +492,11 @@ module Fluent::Plugin
|
|
460
492
|
|
461
493
|
if new_position_entry.read_inode == 0
|
462
494
|
@tails[new_target_info] = setup_watcher(new_target_info, new_position_entry)
|
495
|
+
@tails[new_target_info].on_notify
|
463
496
|
end
|
464
497
|
else
|
465
498
|
@tails[new_target_info] = setup_watcher(new_target_info, pe)
|
499
|
+
@tails[new_target_info].on_notify
|
466
500
|
end
|
467
501
|
detach_watcher_after_rotate_wait(rotated_tw, pe.read_inode) if rotated_tw
|
468
502
|
end
|
@@ -475,7 +509,7 @@ module Fluent::Plugin
|
|
475
509
|
tw.watchers.each do |watcher|
|
476
510
|
event_loop_detach(watcher)
|
477
511
|
end
|
478
|
-
tw.detach
|
512
|
+
tw.detach(@shutdown_start_time)
|
479
513
|
|
480
514
|
tw.close if close_io
|
481
515
|
|
@@ -619,6 +653,7 @@ module Fluent::Plugin
|
|
619
653
|
path: path,
|
620
654
|
log: log,
|
621
655
|
read_lines_limit: @read_lines_limit,
|
656
|
+
read_bytes_limit_per_second: @read_bytes_limit_per_second,
|
622
657
|
open_on_every_update: @open_on_every_update,
|
623
658
|
from_encoding: @from_encoding,
|
624
659
|
encoding: @encoding,
|
@@ -686,8 +721,11 @@ module Fluent::Plugin
|
|
686
721
|
@watchers << watcher
|
687
722
|
end
|
688
723
|
|
689
|
-
def detach
|
690
|
-
|
724
|
+
def detach(shutdown_start_time = nil)
|
725
|
+
if @io_handler
|
726
|
+
@io_handler.ready_to_shutdown(shutdown_start_time)
|
727
|
+
@io_handler.on_notify
|
728
|
+
end
|
691
729
|
@line_buffer_timer_flusher&.close(self)
|
692
730
|
end
|
693
731
|
|
@@ -701,7 +739,7 @@ module Fluent::Plugin
|
|
701
739
|
def on_notify
|
702
740
|
begin
|
703
741
|
stat = Fluent::FileWrapper.stat(@path)
|
704
|
-
rescue Errno::ENOENT
|
742
|
+
rescue Errno::ENOENT, Errno::EACCES
|
705
743
|
# moved or deleted
|
706
744
|
stat = nil
|
707
745
|
end
|
@@ -767,16 +805,22 @@ module Fluent::Plugin
|
|
767
805
|
end
|
768
806
|
|
769
807
|
if watcher_needs_update
|
770
|
-
|
771
|
-
|
772
|
-
|
773
|
-
|
774
|
-
if
|
775
|
-
|
808
|
+
if @follow_inodes
|
809
|
+
# No need to update a watcher if stat is nil (file not present), because moving to inodes will create
|
810
|
+
# new watcher, and old watcher will be closed by stop_watcher in refresh_watchers method
|
811
|
+
# don't want to swap state because we need latest read offset in pos file even after rotate_wait
|
812
|
+
if stat
|
813
|
+
target_info = TargetInfo.new(@path, stat)
|
776
814
|
@update_watcher.call(target_info, @pe)
|
777
|
-
else
|
778
|
-
@update_watcher.call(target_info, swap_state(@pe))
|
779
815
|
end
|
816
|
+
else
|
817
|
+
# Permit to handle if stat is nil (file not present).
|
818
|
+
# If a file is mv-ed and a new file is created during
|
819
|
+
# calling `#refresh_watchers`s, and `#refresh_watchers` won't run `#start_watchers`
|
820
|
+
# and `#stop_watchers()` for the path because `target_paths_hash`
|
821
|
+
# always contains the path.
|
822
|
+
target_info = TargetInfo.new(@path, stat ? stat.ino : nil)
|
823
|
+
@update_watcher.call(target_info, swap_state(@pe))
|
780
824
|
end
|
781
825
|
else
|
782
826
|
@log.info "detected rotation of #{@path}"
|
@@ -856,10 +900,16 @@ module Fluent::Plugin
|
|
856
900
|
end
|
857
901
|
|
858
902
|
class IOHandler
|
859
|
-
|
903
|
+
BYTES_TO_READ = 8192
|
904
|
+
SHUTDOWN_TIMEOUT = 5
|
905
|
+
|
906
|
+
attr_accessor :shutdown_timeout
|
907
|
+
|
908
|
+
def initialize(watcher, path:, read_lines_limit:, read_bytes_limit_per_second:, log:, open_on_every_update:, from_encoding: nil, encoding: nil, &receive_lines)
|
860
909
|
@watcher = watcher
|
861
910
|
@path = path
|
862
911
|
@read_lines_limit = read_lines_limit
|
912
|
+
@read_bytes_limit_per_second = read_bytes_limit_per_second
|
863
913
|
@receive_lines = receive_lines
|
864
914
|
@open_on_every_update = open_on_every_update
|
865
915
|
@fifo = FIFO.new(from_encoding || Encoding::ASCII_8BIT, encoding || Encoding::ASCII_8BIT)
|
@@ -868,6 +918,11 @@ module Fluent::Plugin
|
|
868
918
|
@io = nil
|
869
919
|
@notify_mutex = Mutex.new
|
870
920
|
@log = log
|
921
|
+
@start_reading_time = nil
|
922
|
+
@number_bytes_read = 0
|
923
|
+
@shutdown_start_time = nil
|
924
|
+
@shutdown_timeout = SHUTDOWN_TIMEOUT
|
925
|
+
@shutdown_mutex = Mutex.new
|
871
926
|
|
872
927
|
@log.info "following tail of #{@path}"
|
873
928
|
end
|
@@ -876,6 +931,13 @@ module Fluent::Plugin
|
|
876
931
|
@notify_mutex.synchronize { handle_notify }
|
877
932
|
end
|
878
933
|
|
934
|
+
def ready_to_shutdown(shutdown_start_time = nil)
|
935
|
+
@shutdown_mutex.synchronize {
|
936
|
+
@shutdown_start_time =
|
937
|
+
shutdown_start_time || Fluent::Clock.now
|
938
|
+
}
|
939
|
+
end
|
940
|
+
|
879
941
|
def close
|
880
942
|
if @io && !@io.closed?
|
881
943
|
@io.close
|
@@ -889,7 +951,35 @@ module Fluent::Plugin
|
|
889
951
|
|
890
952
|
private
|
891
953
|
|
954
|
+
def limit_bytes_per_second_reached?
|
955
|
+
return false if @read_bytes_limit_per_second < 0 # not enabled by conf
|
956
|
+
return false if @number_bytes_read < @read_bytes_limit_per_second
|
957
|
+
|
958
|
+
@start_reading_time ||= Fluent::Clock.now
|
959
|
+
time_spent_reading = Fluent::Clock.now - @start_reading_time
|
960
|
+
@log.debug("time_spent_reading: #{time_spent_reading} #{ @watcher.path}")
|
961
|
+
|
962
|
+
if time_spent_reading < 1
|
963
|
+
true
|
964
|
+
else
|
965
|
+
@start_reading_time = nil
|
966
|
+
@number_bytes_read = 0
|
967
|
+
false
|
968
|
+
end
|
969
|
+
end
|
970
|
+
|
971
|
+
def should_shutdown_now?
|
972
|
+
# Ensure to read all remaining lines, but abort immediately if it
|
973
|
+
# seems to take too long time.
|
974
|
+
@shutdown_mutex.synchronize {
|
975
|
+
return false if @shutdown_start_time.nil?
|
976
|
+
return Fluent::Clock.now - @shutdown_start_time > @shutdown_timeout
|
977
|
+
}
|
978
|
+
end
|
979
|
+
|
892
980
|
def handle_notify
|
981
|
+
return if limit_bytes_per_second_reached?
|
982
|
+
|
893
983
|
with_io do |io|
|
894
984
|
begin
|
895
985
|
read_more = false
|
@@ -897,8 +987,18 @@ module Fluent::Plugin
|
|
897
987
|
if !io.nil? && @lines.empty?
|
898
988
|
begin
|
899
989
|
while true
|
900
|
-
@
|
990
|
+
@start_reading_time ||= Fluent::Clock.now
|
991
|
+
data = io.readpartial(BYTES_TO_READ, @iobuf)
|
992
|
+
@number_bytes_read += data.bytesize
|
993
|
+
@fifo << data
|
901
994
|
@fifo.read_lines(@lines)
|
995
|
+
|
996
|
+
@log.debug("reading file: #{@path}")
|
997
|
+
if limit_bytes_per_second_reached? || should_shutdown_now?
|
998
|
+
# Just get out from tailing loop.
|
999
|
+
read_more = false
|
1000
|
+
break
|
1001
|
+
end
|
902
1002
|
if @lines.size >= @read_lines_limit
|
903
1003
|
# not to use too much memory in case the file is very large
|
904
1004
|
read_more = true
|
@@ -928,7 +1028,7 @@ module Fluent::Plugin
|
|
928
1028
|
rescue RangeError
|
929
1029
|
io.close if io
|
930
1030
|
raise WatcherSetupError, "seek error with #{@path}: file position = #{@watcher.pe.read_pos.to_s(16)}, reading bytesize = #{@fifo.bytesize.to_s(16)}"
|
931
|
-
rescue Errno::ENOENT
|
1031
|
+
rescue Errno::ENOENT, Errno::EACCES
|
932
1032
|
nil
|
933
1033
|
end
|
934
1034
|
|
@@ -248,6 +248,20 @@ module Fluent::Plugin
|
|
248
248
|
end
|
249
249
|
end
|
250
250
|
|
251
|
-
TargetInfo = Struct.new(:path, :ino)
|
251
|
+
TargetInfo = Struct.new(:path, :ino) do
|
252
|
+
def ==(other)
|
253
|
+
return false unless other.is_a?(TargetInfo)
|
254
|
+
self.path == other.path
|
255
|
+
end
|
256
|
+
|
257
|
+
def hash
|
258
|
+
self.path.hash
|
259
|
+
end
|
260
|
+
|
261
|
+
def eql?(other)
|
262
|
+
return false unless other.is_a?(TargetInfo)
|
263
|
+
self.path == other.path
|
264
|
+
end
|
265
|
+
end
|
252
266
|
end
|
253
267
|
end
|
@@ -27,20 +27,28 @@ module Fluent::Plugin
|
|
27
27
|
desc 'Pass different record to each `store` plugin by specified method'
|
28
28
|
config_param :copy_mode, :enum, list: [:no_copy, :shallow, :deep, :marshal], default: :no_copy
|
29
29
|
|
30
|
-
attr_reader :ignore_errors
|
30
|
+
attr_reader :ignore_errors, :ignore_if_prev_successes
|
31
31
|
|
32
32
|
def initialize
|
33
33
|
super
|
34
34
|
@ignore_errors = []
|
35
|
+
@ignore_if_prev_successes = []
|
35
36
|
end
|
36
37
|
|
37
38
|
def configure(conf)
|
38
39
|
super
|
39
40
|
|
40
41
|
@copy_proc = gen_copy_proc
|
41
|
-
@stores.
|
42
|
-
|
42
|
+
@stores.each_with_index { |store, i|
|
43
|
+
if i == 0 && store.arg.include?('ignore_if_prev_success')
|
44
|
+
raise Fluent::ConfigError, "ignore_if_prev_success must specify 2nd or later <store> directives"
|
45
|
+
end
|
46
|
+
@ignore_errors << (store.arg.include?('ignore_error'))
|
47
|
+
@ignore_if_prev_successes << (store.arg.include?('ignore_if_prev_success'))
|
43
48
|
}
|
49
|
+
if @ignore_errors.uniq.size == 1 && @ignore_errors.include?(true) && @ignore_if_prev_successes.include?(false)
|
50
|
+
log.warn "ignore_errors are specified in all <store>, but ignore_if_prev_success is not specified. Is this intended?"
|
51
|
+
end
|
44
52
|
end
|
45
53
|
|
46
54
|
def multi_workers_ready?
|
@@ -55,10 +63,15 @@ module Fluent::Plugin
|
|
55
63
|
}
|
56
64
|
es = m
|
57
65
|
end
|
58
|
-
|
66
|
+
success = Array.new(outputs.size)
|
59
67
|
outputs.each_with_index do |output, i|
|
60
68
|
begin
|
61
|
-
|
69
|
+
if i > 0 && success[i - 1] && @ignore_if_prev_successes[i]
|
70
|
+
log.debug "ignore copy because prev_success in #{output.plugin_id}", index: i
|
71
|
+
else
|
72
|
+
output.emit_events(tag, @copy_proc ? @copy_proc.call(es) : es)
|
73
|
+
success[i] = true
|
74
|
+
end
|
62
75
|
rescue => e
|
63
76
|
if @ignore_errors[i]
|
64
77
|
log.error "ignore emit error in #{output.plugin_id}", error: e
|