fluentd 1.16.0 → 1.16.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/ISSUE_TEMPLATE/{bug_report.yaml → bug_report.yml} +1 -0
- data/.github/ISSUE_TEMPLATE/{feature_request.yaml → feature_request.yml} +1 -0
- data/.github/workflows/stale-actions.yml +24 -0
- data/.github/workflows/test.yml +39 -0
- data/CHANGELOG.md +161 -0
- data/CONTRIBUTING.md +1 -1
- data/MAINTAINERS.md +3 -3
- data/README.md +0 -2
- data/SECURITY.md +5 -9
- data/fluentd.gemspec +10 -2
- data/lib/fluent/command/ctl.rb +2 -2
- data/lib/fluent/command/fluentd.rb +1 -1
- data/lib/fluent/command/plugin_config_formatter.rb +1 -1
- data/lib/fluent/config/dsl.rb +1 -1
- data/lib/fluent/config/v1_parser.rb +2 -2
- data/lib/fluent/config/yaml_parser/parser.rb +4 -0
- data/lib/fluent/counter/server.rb +1 -1
- data/lib/fluent/counter/validator.rb +3 -3
- data/lib/fluent/engine.rb +1 -1
- data/lib/fluent/event.rb +8 -4
- data/lib/fluent/log.rb +9 -0
- data/lib/fluent/match.rb +1 -1
- data/lib/fluent/msgpack_factory.rb +6 -1
- data/lib/fluent/plugin/base.rb +1 -1
- data/lib/fluent/plugin/buffer.rb +96 -87
- data/lib/fluent/plugin/filter_record_transformer.rb +1 -1
- data/lib/fluent/plugin/in_forward.rb +1 -1
- data/lib/fluent/plugin/in_http.rb +8 -8
- data/lib/fluent/plugin/in_sample.rb +1 -1
- data/lib/fluent/plugin/in_tail/position_file.rb +32 -18
- data/lib/fluent/plugin/in_tail.rb +99 -31
- data/lib/fluent/plugin/in_tcp.rb +43 -0
- data/lib/fluent/plugin/out_exec_filter.rb +2 -2
- data/lib/fluent/plugin/out_file.rb +8 -0
- data/lib/fluent/plugin/output.rb +2 -2
- data/lib/fluent/plugin/parser_json.rb +4 -12
- data/lib/fluent/plugin_helper/event_loop.rb +2 -2
- data/lib/fluent/plugin_helper/record_accessor.rb +1 -1
- data/lib/fluent/plugin_helper/server.rb +8 -0
- data/lib/fluent/plugin_helper/thread.rb +3 -3
- data/lib/fluent/plugin_id.rb +1 -1
- data/lib/fluent/supervisor.rb +2 -2
- data/lib/fluent/system_config.rb +1 -1
- data/lib/fluent/version.rb +1 -1
- data/lib/fluent/winsvc.rb +28 -3
- data/templates/new_gem/test/helper.rb.erb +0 -1
- data/test/command/test_cat.rb +2 -2
- data/test/command/test_fluentd.rb +65 -12
- data/test/config/test_system_config.rb +2 -2
- data/test/helper.rb +27 -7
- data/test/plugin/in_tail/test_io_handler.rb +13 -14
- data/test/plugin/in_tail/test_position_file.rb +37 -8
- data/test/plugin/out_forward/test_ack_handler.rb +3 -3
- data/test/plugin/out_forward/test_socket_cache.rb +3 -3
- data/test/plugin/test_base.rb +1 -1
- data/test/plugin/test_buffer.rb +110 -0
- data/test/plugin/test_buffer_chunk.rb +11 -0
- data/test/plugin/test_in_forward.rb +11 -10
- data/test/plugin/test_in_http.rb +1 -1
- data/test/plugin/test_in_monitor_agent.rb +6 -6
- data/test/plugin/test_in_syslog.rb +25 -18
- data/test/plugin/test_in_tail.rb +639 -0
- data/test/plugin/test_in_tcp.rb +75 -5
- data/test/plugin/test_in_udp.rb +35 -1
- data/test/plugin/test_in_unix.rb +2 -2
- data/test/plugin/test_multi_output.rb +1 -1
- data/test/plugin/test_out_exec_filter.rb +14 -9
- data/test/plugin/test_out_file.rb +24 -4
- data/test/plugin/test_out_forward.rb +36 -42
- data/test/plugin/test_out_stream.rb +1 -1
- data/test/plugin/test_output.rb +12 -12
- data/test/plugin/test_output_as_buffered.rb +44 -44
- data/test/plugin/test_output_as_buffered_compress.rb +32 -18
- data/test/plugin/test_output_as_buffered_retries.rb +1 -1
- data/test/plugin/test_output_as_buffered_secondary.rb +2 -2
- data/test/plugin/test_parser_json.rb +31 -0
- data/test/plugin_helper/test_child_process.rb +15 -5
- data/test/plugin_helper/test_http_server_helper.rb +1 -1
- data/test/plugin_helper/test_server.rb +110 -38
- data/test/plugin_helper/test_socket.rb +1 -1
- data/test/test_config.rb +6 -0
- data/test/test_event_router.rb +2 -2
- data/test/test_log.rb +38 -1
- data/test/test_msgpack_factory.rb +32 -0
- data/test/test_supervisor.rb +13 -0
- metadata +44 -17
- data/.github/workflows/linux-test.yaml +0 -36
- data/.github/workflows/macos-test.yaml +0 -34
- data/.github/workflows/windows-test.yaml +0 -49
|
@@ -52,6 +52,7 @@ module Fluent::Plugin
|
|
|
52
52
|
super
|
|
53
53
|
@paths = []
|
|
54
54
|
@tails = {}
|
|
55
|
+
@tails_rotate_wait = {}
|
|
55
56
|
@pf_file = nil
|
|
56
57
|
@pf = nil
|
|
57
58
|
@ignore_list = []
|
|
@@ -267,6 +268,9 @@ module Fluent::Plugin
|
|
|
267
268
|
@shutdown_start_time = Fluent::Clock.now
|
|
268
269
|
# during shutdown phase, don't close io. It should be done in close after all threads are stopped. See close.
|
|
269
270
|
stop_watchers(existence_path, immediate: true, remove_watcher: false)
|
|
271
|
+
@tails_rotate_wait.keys.each do |tw|
|
|
272
|
+
detach_watcher(tw, @tails_rotate_wait[tw][:ino], false)
|
|
273
|
+
end
|
|
270
274
|
@pf_file.close if @pf_file
|
|
271
275
|
|
|
272
276
|
super
|
|
@@ -275,6 +279,7 @@ module Fluent::Plugin
|
|
|
275
279
|
def close
|
|
276
280
|
super
|
|
277
281
|
# close file handles after all threads stopped (in #close of thread plugin helper)
|
|
282
|
+
# It may be because we need to wait IOHanlder.ready_to_shutdown()
|
|
278
283
|
close_watcher_handles
|
|
279
284
|
end
|
|
280
285
|
|
|
@@ -370,17 +375,52 @@ module Fluent::Plugin
|
|
|
370
375
|
def refresh_watchers
|
|
371
376
|
target_paths_hash = expand_paths
|
|
372
377
|
existence_paths_hash = existence_path
|
|
373
|
-
|
|
378
|
+
|
|
374
379
|
log.debug {
|
|
375
380
|
target_paths_str = target_paths_hash.collect { |key, target_info| target_info.path }.join(",")
|
|
376
381
|
existence_paths_str = existence_paths_hash.collect { |key, target_info| target_info.path }.join(",")
|
|
377
382
|
"tailing paths: target = #{target_paths_str} | existing = #{existence_paths_str}"
|
|
378
383
|
}
|
|
379
384
|
|
|
380
|
-
|
|
385
|
+
if !@follow_inodes
|
|
386
|
+
need_unwatch_in_stop_watchers = true
|
|
387
|
+
else
|
|
388
|
+
# When using @follow_inodes, need this to unwatch the rotated old inode when it disappears.
|
|
389
|
+
# After `update_watcher` detaches an old TailWatcher, the inode is lost from the `@tails`.
|
|
390
|
+
# So that inode can't be contained in `removed_hash`, and can't be unwatched by `stop_watchers`.
|
|
391
|
+
#
|
|
392
|
+
# This logic may work for `@follow_inodes false` too.
|
|
393
|
+
# Just limiting the case to suppress the impact to existing logics.
|
|
394
|
+
@pf&.unwatch_removed_targets(target_paths_hash)
|
|
395
|
+
need_unwatch_in_stop_watchers = false
|
|
396
|
+
end
|
|
397
|
+
|
|
398
|
+
removed_hash = existence_paths_hash.reject {|key, value| target_paths_hash.key?(key)}
|
|
381
399
|
added_hash = target_paths_hash.reject {|key, value| existence_paths_hash.key?(key)}
|
|
382
400
|
|
|
383
|
-
|
|
401
|
+
# If an exisiting TailWatcher already follows a target path with the different inode,
|
|
402
|
+
# it means that the TailWatcher following the rotated file still exists. In this case,
|
|
403
|
+
# `refresh_watcher` can't start the new TailWatcher for the new current file. So, we
|
|
404
|
+
# should output a warning log in order to prevent silent collection stops.
|
|
405
|
+
# (Such as https://github.com/fluent/fluentd/pull/4327)
|
|
406
|
+
# (Usually, such a TailWatcher should be removed from `@tails` in `update_watcher`.)
|
|
407
|
+
# (The similar warning may work for `@follow_inodes true` too. Just limiting the case
|
|
408
|
+
# to suppress the impact to existing logics.)
|
|
409
|
+
unless @follow_inodes
|
|
410
|
+
target_paths_hash.each do |path, target|
|
|
411
|
+
next unless @tails.key?(path)
|
|
412
|
+
# We can't use `existence_paths_hash[path].ino` because it is from `TailWatcher.ino`,
|
|
413
|
+
# which is very unstable parameter. (It can be `nil` or old).
|
|
414
|
+
# So, we need to use `TailWatcher.pe.read_inode`.
|
|
415
|
+
existing_watcher_inode = @tails[path].pe.read_inode
|
|
416
|
+
if existing_watcher_inode != target.ino
|
|
417
|
+
log.warn "Could not follow a file (inode: #{target.ino}) because an existing watcher for that filepath follows a different inode: #{existing_watcher_inode} (e.g. keeps watching a already rotated file). If you keep getting this message, please restart Fluentd.",
|
|
418
|
+
filepath: target.path
|
|
419
|
+
end
|
|
420
|
+
end
|
|
421
|
+
end
|
|
422
|
+
|
|
423
|
+
stop_watchers(removed_hash, unwatched: need_unwatch_in_stop_watchers) unless removed_hash.empty?
|
|
384
424
|
start_watchers(added_hash) unless added_hash.empty?
|
|
385
425
|
@startup = false if @startup
|
|
386
426
|
end
|
|
@@ -481,11 +521,32 @@ module Fluent::Plugin
|
|
|
481
521
|
tw.close
|
|
482
522
|
end
|
|
483
523
|
end
|
|
524
|
+
@tails_rotate_wait.keys.each do |tw|
|
|
525
|
+
tw.close
|
|
526
|
+
end
|
|
484
527
|
end
|
|
485
528
|
|
|
486
529
|
# refresh_watchers calls @tails.keys so we don't use stop_watcher -> start_watcher sequence for safety.
|
|
487
|
-
def update_watcher(
|
|
488
|
-
|
|
530
|
+
def update_watcher(tail_watcher, pe, new_inode)
|
|
531
|
+
# TODO we should use another callback for this.
|
|
532
|
+
# To supress impact to existing logics, limit the case to `@follow_inodes`.
|
|
533
|
+
# We may not need `@follow_inodes` condition.
|
|
534
|
+
if @follow_inodes && new_inode.nil?
|
|
535
|
+
# nil inode means the file disappeared, so we only need to stop it.
|
|
536
|
+
@tails.delete(tail_watcher.path)
|
|
537
|
+
# https://github.com/fluent/fluentd/pull/4237#issuecomment-1633358632
|
|
538
|
+
# Because of this problem, log duplication can occur during `rotate_wait`.
|
|
539
|
+
# Need to set `rotate_wait 0` for a workaround.
|
|
540
|
+
# Duplication will occur if `refresh_watcher` is called during the `rotate_wait`.
|
|
541
|
+
# In that case, `refresh_watcher` will add the new TailWatcher to tail the same target,
|
|
542
|
+
# and it causes the log duplication.
|
|
543
|
+
# (Other `detach_watcher_after_rotate_wait` may have the same problem.
|
|
544
|
+
# We need the mechanism not to add duplicated TailWathcer with detaching TailWatcher.)
|
|
545
|
+
detach_watcher_after_rotate_wait(tail_watcher, pe.read_inode)
|
|
546
|
+
return
|
|
547
|
+
end
|
|
548
|
+
|
|
549
|
+
path = tail_watcher.path
|
|
489
550
|
|
|
490
551
|
log.info("detected rotation of #{path}; waiting #{@rotate_wait} seconds")
|
|
491
552
|
|
|
@@ -499,30 +560,29 @@ module Fluent::Plugin
|
|
|
499
560
|
end
|
|
500
561
|
end
|
|
501
562
|
|
|
502
|
-
|
|
563
|
+
new_target_info = TargetInfo.new(path, new_inode)
|
|
503
564
|
|
|
504
565
|
if @follow_inodes
|
|
505
|
-
new_position_entry = @pf[
|
|
506
|
-
|
|
566
|
+
new_position_entry = @pf[new_target_info]
|
|
567
|
+
# If `refresh_watcher` find the new file before, this will not be zero.
|
|
568
|
+
# In this case, only we have to do is detaching the current tail_watcher.
|
|
507
569
|
if new_position_entry.read_inode == 0
|
|
508
|
-
|
|
509
|
-
# So it should be unwatched here explicitly.
|
|
510
|
-
rotated_tw.unwatched = true if rotated_tw
|
|
511
|
-
@tails[path] = setup_watcher(target_info, new_position_entry)
|
|
570
|
+
@tails[path] = setup_watcher(new_target_info, new_position_entry)
|
|
512
571
|
@tails[path].on_notify
|
|
513
572
|
end
|
|
514
573
|
else
|
|
515
|
-
@tails[path] = setup_watcher(
|
|
574
|
+
@tails[path] = setup_watcher(new_target_info, pe)
|
|
516
575
|
@tails[path].on_notify
|
|
517
576
|
end
|
|
518
|
-
|
|
577
|
+
|
|
578
|
+
detach_watcher_after_rotate_wait(tail_watcher, pe.read_inode)
|
|
519
579
|
end
|
|
520
580
|
|
|
521
|
-
# TailWatcher#close is called by another thread at shutdown phase.
|
|
522
|
-
# It causes 'can't modify string; temporarily locked' error in IOHandler
|
|
523
|
-
# so adding close_io argument to avoid this problem.
|
|
524
|
-
# At shutdown, IOHandler's io will be released automatically after detached the event loop
|
|
525
581
|
def detach_watcher(tw, ino, close_io = true)
|
|
582
|
+
if @follow_inodes && tw.ino != ino
|
|
583
|
+
log.warn("detach_watcher could be detaching an unexpected tail_watcher with a different ino.",
|
|
584
|
+
path: tw.path, actual_ino_in_tw: tw.ino, expect_ino_to_close: ino)
|
|
585
|
+
end
|
|
526
586
|
tw.watchers.each do |watcher|
|
|
527
587
|
event_loop_detach(watcher)
|
|
528
588
|
end
|
|
@@ -530,7 +590,7 @@ module Fluent::Plugin
|
|
|
530
590
|
|
|
531
591
|
tw.close if close_io
|
|
532
592
|
|
|
533
|
-
if tw.unwatched && @
|
|
593
|
+
if @pf && tw.unwatched && (@follow_inode || !@tails[tw.path])
|
|
534
594
|
target_info = TargetInfo.new(tw.path, ino)
|
|
535
595
|
@pf.unwatch(target_info)
|
|
536
596
|
end
|
|
@@ -548,7 +608,11 @@ module Fluent::Plugin
|
|
|
548
608
|
if @open_on_every_update
|
|
549
609
|
# Detach now because it's already closed, waiting it doesn't make sense.
|
|
550
610
|
detach_watcher(tw, ino)
|
|
551
|
-
|
|
611
|
+
end
|
|
612
|
+
|
|
613
|
+
return if @tails_rotate_wait[tw]
|
|
614
|
+
|
|
615
|
+
if throttling_is_enabled?(tw)
|
|
552
616
|
# When the throttling feature is enabled, it might not reach EOF yet.
|
|
553
617
|
# Should ensure to read all contents before closing it, with keeping throttling.
|
|
554
618
|
start_time_to_wait = Fluent::Clock.now
|
|
@@ -556,14 +620,18 @@ module Fluent::Plugin
|
|
|
556
620
|
elapsed = Fluent::Clock.now - start_time_to_wait
|
|
557
621
|
if tw.eof? && elapsed >= @rotate_wait
|
|
558
622
|
timer.detach
|
|
623
|
+
@tails_rotate_wait.delete(tw)
|
|
559
624
|
detach_watcher(tw, ino)
|
|
560
625
|
end
|
|
561
626
|
end
|
|
627
|
+
@tails_rotate_wait[tw] = { ino: ino, timer: timer }
|
|
562
628
|
else
|
|
563
629
|
# when the throttling feature isn't enabled, just wait @rotate_wait
|
|
564
|
-
timer_execute(:in_tail_close_watcher, @rotate_wait, repeat: false) do
|
|
630
|
+
timer = timer_execute(:in_tail_close_watcher, @rotate_wait, repeat: false) do
|
|
631
|
+
@tails_rotate_wait.delete(tw)
|
|
565
632
|
detach_watcher(tw, ino)
|
|
566
633
|
end
|
|
634
|
+
@tails_rotate_wait[tw] = { ino: ino, timer: timer }
|
|
567
635
|
end
|
|
568
636
|
end
|
|
569
637
|
|
|
@@ -778,7 +846,7 @@ module Fluent::Plugin
|
|
|
778
846
|
attr_accessor :group_watcher
|
|
779
847
|
|
|
780
848
|
def tag
|
|
781
|
-
@parsed_tag ||= @path.tr('/', '.').
|
|
849
|
+
@parsed_tag ||= @path.tr('/', '.').squeeze('.').gsub(/^\./, '')
|
|
782
850
|
end
|
|
783
851
|
|
|
784
852
|
def register_watcher(watcher)
|
|
@@ -874,21 +942,21 @@ module Fluent::Plugin
|
|
|
874
942
|
|
|
875
943
|
if watcher_needs_update
|
|
876
944
|
if @follow_inodes
|
|
877
|
-
#
|
|
878
|
-
#
|
|
879
|
-
#
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
945
|
+
# If stat is nil (file not present), NEED to stop and discard this watcher.
|
|
946
|
+
# When the file is disappeared but is resurrected soon, then `#refresh_watcher`
|
|
947
|
+
# can't recognize this TailWatcher needs to be stopped.
|
|
948
|
+
# This can happens when the file is rotated.
|
|
949
|
+
# If a notify comes before the new file for the path is created during rotation,
|
|
950
|
+
# then it appears as if the file was resurrected once it disappeared.
|
|
951
|
+
# Don't want to swap state because we need latest read offset in pos file even after rotate_wait
|
|
952
|
+
@update_watcher.call(self, @pe, stat&.ino)
|
|
884
953
|
else
|
|
885
954
|
# Permit to handle if stat is nil (file not present).
|
|
886
955
|
# If a file is mv-ed and a new file is created during
|
|
887
956
|
# calling `#refresh_watchers`s, and `#refresh_watchers` won't run `#start_watchers`
|
|
888
957
|
# and `#stop_watchers()` for the path because `target_paths_hash`
|
|
889
958
|
# always contains the path.
|
|
890
|
-
|
|
891
|
-
@update_watcher.call(target_info, swap_state(@pe))
|
|
959
|
+
@update_watcher.call(self, swap_state(@pe), stat&.ino)
|
|
892
960
|
end
|
|
893
961
|
else
|
|
894
962
|
@log.info "detected rotation of #{@path}"
|
data/lib/fluent/plugin/in_tcp.rb
CHANGED
|
@@ -36,6 +36,10 @@ module Fluent::Plugin
|
|
|
36
36
|
desc "The field name of the client's address."
|
|
37
37
|
config_param :source_address_key, :string, default: nil
|
|
38
38
|
|
|
39
|
+
# Setting default to nil for backward compatibility
|
|
40
|
+
desc "The max bytes of message."
|
|
41
|
+
config_param :message_length_limit, :size, default: nil
|
|
42
|
+
|
|
39
43
|
config_param :blocking_timeout, :time, default: 0.5
|
|
40
44
|
|
|
41
45
|
desc 'The payload is read up to this character.'
|
|
@@ -102,6 +106,7 @@ module Fluent::Plugin
|
|
|
102
106
|
|
|
103
107
|
log.info "listening tcp socket", bind: @bind, port: @port
|
|
104
108
|
del_size = @delimiter.length
|
|
109
|
+
discard_till_next_delimiter = false
|
|
105
110
|
if @_extract_enabled && @_extract_tag_key
|
|
106
111
|
server_create(:in_tcp_server_single_emit, @port, bind: @bind, resolve_name: !!@source_hostname_key, send_keepalive_packet: @send_keepalive_packet) do |data, conn|
|
|
107
112
|
unless check_client(conn)
|
|
@@ -116,6 +121,16 @@ module Fluent::Plugin
|
|
|
116
121
|
msg = buf[pos...i]
|
|
117
122
|
pos = i + del_size
|
|
118
123
|
|
|
124
|
+
if discard_till_next_delimiter
|
|
125
|
+
discard_till_next_delimiter = false
|
|
126
|
+
next
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
if !@message_length_limit.nil? && @message_length_limit < msg.bytesize
|
|
130
|
+
log.info "The received data is larger than 'message_length_limit', dropped:", limit: @message_length_limit, size: msg.bytesize, head: msg[...32]
|
|
131
|
+
next
|
|
132
|
+
end
|
|
133
|
+
|
|
119
134
|
@parser.parse(msg) do |time, record|
|
|
120
135
|
unless time && record
|
|
121
136
|
log.warn "pattern not matched", message: msg
|
|
@@ -131,6 +146,15 @@ module Fluent::Plugin
|
|
|
131
146
|
end
|
|
132
147
|
end
|
|
133
148
|
buf.slice!(0, pos) if pos > 0
|
|
149
|
+
# If the buffer size exceeds the limit here, it means that the next message will definitely exceed the limit.
|
|
150
|
+
# So we should clear the buffer here. Otherwise, it will keep storing useless data until the next delimiter comes.
|
|
151
|
+
if !@message_length_limit.nil? && @message_length_limit < buf.bytesize
|
|
152
|
+
log.info "The buffer size exceeds 'message_length_limit', cleared:", limit: @message_length_limit, size: buf.bytesize, head: buf[...32]
|
|
153
|
+
buf.clear
|
|
154
|
+
# We should discard the subsequent data until the next delimiter comes.
|
|
155
|
+
discard_till_next_delimiter = true
|
|
156
|
+
next
|
|
157
|
+
end
|
|
134
158
|
end
|
|
135
159
|
else
|
|
136
160
|
server_create(:in_tcp_server_batch_emit, @port, bind: @bind, resolve_name: !!@source_hostname_key, send_keepalive_packet: @send_keepalive_packet) do |data, conn|
|
|
@@ -147,6 +171,16 @@ module Fluent::Plugin
|
|
|
147
171
|
msg = buf[pos...i]
|
|
148
172
|
pos = i + del_size
|
|
149
173
|
|
|
174
|
+
if discard_till_next_delimiter
|
|
175
|
+
discard_till_next_delimiter = false
|
|
176
|
+
next
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
if !@message_length_limit.nil? && @message_length_limit < msg.bytesize
|
|
180
|
+
log.info "The received data is larger than 'message_length_limit', dropped:", limit: @message_length_limit, size: msg.bytesize, head: msg[...32]
|
|
181
|
+
next
|
|
182
|
+
end
|
|
183
|
+
|
|
150
184
|
@parser.parse(msg) do |time, record|
|
|
151
185
|
unless time && record
|
|
152
186
|
log.warn "pattern not matched", message: msg
|
|
@@ -161,6 +195,15 @@ module Fluent::Plugin
|
|
|
161
195
|
end
|
|
162
196
|
router.emit_stream(@tag, es)
|
|
163
197
|
buf.slice!(0, pos) if pos > 0
|
|
198
|
+
# If the buffer size exceeds the limit here, it means that the next message will definitely exceed the limit.
|
|
199
|
+
# So we should clear the buffer here. Otherwise, it will keep storing useless data until the next delimiter comes.
|
|
200
|
+
if !@message_length_limit.nil? && @message_length_limit < buf.bytesize
|
|
201
|
+
log.info "The buffer size exceeds 'message_length_limit', cleared:", limit: @message_length_limit, size: buf.bytesize, head: buf[...32]
|
|
202
|
+
buf.clear
|
|
203
|
+
# We should discard the subsequent data until the next delimiter comes.
|
|
204
|
+
discard_till_next_delimiter = true
|
|
205
|
+
next
|
|
206
|
+
end
|
|
164
207
|
end
|
|
165
208
|
end
|
|
166
209
|
end
|
|
@@ -163,7 +163,7 @@ module Fluent::Plugin
|
|
|
163
163
|
0
|
|
164
164
|
elsif (@child_respawn == 'inf') || (@child_respawn == '-1')
|
|
165
165
|
-1
|
|
166
|
-
elsif @child_respawn
|
|
166
|
+
elsif /^\d+$/.match?(@child_respawn)
|
|
167
167
|
@child_respawn.to_i
|
|
168
168
|
else
|
|
169
169
|
raise ConfigError, "child_respawn option argument invalid: none(or 0), inf(or -1) or positive number"
|
|
@@ -187,7 +187,7 @@ module Fluent::Plugin
|
|
|
187
187
|
@rr = 0
|
|
188
188
|
|
|
189
189
|
exit_callback = ->(status){
|
|
190
|
-
c = @children.
|
|
190
|
+
c = @children.find{|child| child.pid == status.pid }
|
|
191
191
|
if c
|
|
192
192
|
unless self.stopped?
|
|
193
193
|
log.warn "child process exits with error code", code: status.to_i, status: status.exitstatus, signal: status.termsig
|
|
@@ -172,6 +172,14 @@ module Fluent::Plugin
|
|
|
172
172
|
log.warn "symlink_path is unavailable on Windows platform. disabled."
|
|
173
173
|
@symlink_path = nil
|
|
174
174
|
else
|
|
175
|
+
placeholder_validators(:symlink_path, @symlink_path).reject{ |v| v.type == :time }.each do |v|
|
|
176
|
+
begin
|
|
177
|
+
v.validate!
|
|
178
|
+
rescue Fluent::ConfigError => e
|
|
179
|
+
log.warn "#{e}. This means multiple chunks are competing for a single symlink_path, so some logs may not be taken from the symlink."
|
|
180
|
+
end
|
|
181
|
+
end
|
|
182
|
+
|
|
175
183
|
@buffer.extend SymlinkBufferMixin
|
|
176
184
|
@buffer.symlink_path = @symlink_path
|
|
177
185
|
@buffer.output_plugin_for_symlink = self
|
data/lib/fluent/plugin/output.rb
CHANGED
|
@@ -426,7 +426,7 @@ module Fluent
|
|
|
426
426
|
end
|
|
427
427
|
@secondary.acts_as_secondary(self)
|
|
428
428
|
@secondary.configure(secondary_conf)
|
|
429
|
-
if (@secondary.class != SecondaryFileOutput) &&
|
|
429
|
+
if (@secondary.class.to_s != "Fluent::Plugin::SecondaryFileOutput") &&
|
|
430
430
|
(self.class != @secondary.class) &&
|
|
431
431
|
(@custom_format || @secondary.implement?(:custom_format))
|
|
432
432
|
log.warn "Use different plugin for secondary. Check the plugin works with primary like secondary_file", primary: self.class.to_s, secondary: @secondary.class.to_s
|
|
@@ -824,7 +824,7 @@ module Fluent
|
|
|
824
824
|
if str.include?('${tag}')
|
|
825
825
|
rvalue = rvalue.gsub('${tag}', metadata.tag)
|
|
826
826
|
end
|
|
827
|
-
if str
|
|
827
|
+
if CHUNK_TAG_PLACEHOLDER_PATTERN.match?(str)
|
|
828
828
|
hash = {}
|
|
829
829
|
tag_parts = metadata.tag.split('.')
|
|
830
830
|
tag_parts.each_with_index do |part, i|
|
|
@@ -50,23 +50,15 @@ module Fluent
|
|
|
50
50
|
def configure_json_parser(name)
|
|
51
51
|
case name
|
|
52
52
|
when :oj
|
|
53
|
-
|
|
54
|
-
|
|
53
|
+
return [Oj.method(:load), Oj::ParseError] if Fluent::OjOptions.available?
|
|
54
|
+
|
|
55
|
+
log&.info "Oj is not installed, and failing back to Yajl for json parser"
|
|
56
|
+
configure_json_parser(:yajl)
|
|
55
57
|
when :json then [JSON.method(:load), JSON::ParserError]
|
|
56
58
|
when :yajl then [Yajl.method(:load), Yajl::ParseError]
|
|
57
59
|
else
|
|
58
60
|
raise "BUG: unknown json parser specified: #{name}"
|
|
59
61
|
end
|
|
60
|
-
rescue LoadError => ex
|
|
61
|
-
name = :yajl
|
|
62
|
-
if log
|
|
63
|
-
if /\boj\z/ =~ ex.message
|
|
64
|
-
log.info "Oj is not installed, and failing back to Yajl for json parser"
|
|
65
|
-
else
|
|
66
|
-
log.warn ex.message
|
|
67
|
-
end
|
|
68
|
-
end
|
|
69
|
-
retry
|
|
70
62
|
end
|
|
71
63
|
|
|
72
64
|
def parse(text)
|
|
@@ -99,7 +99,7 @@ module Fluent
|
|
|
99
99
|
|
|
100
100
|
def shutdown
|
|
101
101
|
@_event_loop_mutex.synchronize do
|
|
102
|
-
@_event_loop_attached_watchers.
|
|
102
|
+
@_event_loop_attached_watchers.reverse_each do |w|
|
|
103
103
|
if w.attached?
|
|
104
104
|
begin
|
|
105
105
|
w.detach
|
|
@@ -116,7 +116,7 @@ module Fluent
|
|
|
116
116
|
def after_shutdown
|
|
117
117
|
timeout_at = Fluent::Clock.now + EVENT_LOOP_SHUTDOWN_TIMEOUT
|
|
118
118
|
@_event_loop_mutex.synchronize do
|
|
119
|
-
@_event_loop.watchers.
|
|
119
|
+
@_event_loop.watchers.reverse_each do |w|
|
|
120
120
|
begin
|
|
121
121
|
w.detach
|
|
122
122
|
rescue => e
|
|
@@ -119,7 +119,7 @@ module Fluent
|
|
|
119
119
|
def self.validate_dot_keys(keys)
|
|
120
120
|
keys.each { |key|
|
|
121
121
|
next unless key.is_a?(String)
|
|
122
|
-
if /\s+/.match(key)
|
|
122
|
+
if /\s+/.match?(key)
|
|
123
123
|
raise Fluent::ConfigError, "whitespace character is not allowed in dot notation. Use bracket notation: #{key}"
|
|
124
124
|
end
|
|
125
125
|
}
|
|
@@ -545,6 +545,10 @@ module Fluent
|
|
|
545
545
|
data = @sock.recv(@max_bytes, @flags)
|
|
546
546
|
rescue Errno::EAGAIN, Errno::EWOULDBLOCK, Errno::EINTR, Errno::ECONNRESET, IOError, Errno::EBADF
|
|
547
547
|
return
|
|
548
|
+
rescue Errno::EMSGSIZE
|
|
549
|
+
# Windows ONLY: This happens when the data size is larger than `@max_bytes`.
|
|
550
|
+
@log.info "A received data was ignored since it was too large."
|
|
551
|
+
return
|
|
548
552
|
end
|
|
549
553
|
@callback.call(data)
|
|
550
554
|
rescue => e
|
|
@@ -558,6 +562,10 @@ module Fluent
|
|
|
558
562
|
data, addr = @sock.recvfrom(@max_bytes)
|
|
559
563
|
rescue Errno::EAGAIN, Errno::EWOULDBLOCK, Errno::EINTR, Errno::ECONNRESET, IOError, Errno::EBADF
|
|
560
564
|
return
|
|
565
|
+
rescue Errno::EMSGSIZE
|
|
566
|
+
# Windows ONLY: This happens when the data size is larger than `@max_bytes`.
|
|
567
|
+
@log.info "A received data was ignored since it was too large."
|
|
568
|
+
return
|
|
561
569
|
end
|
|
562
570
|
@callback.call(data, UDPCallbackSocket.new(@sock, addr, close_socket: @close_socket))
|
|
563
571
|
rescue => e
|
|
@@ -101,16 +101,16 @@ module Fluent
|
|
|
101
101
|
end
|
|
102
102
|
|
|
103
103
|
def thread_exist?(title)
|
|
104
|
-
@_threads.values.
|
|
104
|
+
@_threads.values.count{|thread| title == thread[:_fluentd_plugin_helper_thread_title] } > 0
|
|
105
105
|
end
|
|
106
106
|
|
|
107
107
|
def thread_started?(title)
|
|
108
|
-
t = @_threads.values.
|
|
108
|
+
t = @_threads.values.find{|thread| title == thread[:_fluentd_plugin_helper_thread_title] }
|
|
109
109
|
t && t[:_fluentd_plugin_helper_thread_started]
|
|
110
110
|
end
|
|
111
111
|
|
|
112
112
|
def thread_running?(title)
|
|
113
|
-
t = @_threads.values.
|
|
113
|
+
t = @_threads.values.find{|thread| title == thread[:_fluentd_plugin_helper_thread_title] }
|
|
114
114
|
t && t[:_fluentd_plugin_helper_thread_running]
|
|
115
115
|
end
|
|
116
116
|
|
data/lib/fluent/plugin_id.rb
CHANGED
|
@@ -49,7 +49,7 @@ module Fluent
|
|
|
49
49
|
# Thread::Backtrace::Location#path returns base filename or absolute path.
|
|
50
50
|
# #absolute_path returns absolute_path always.
|
|
51
51
|
# https://bugs.ruby-lang.org/issues/12159
|
|
52
|
-
if
|
|
52
|
+
if /\/test_[^\/]+\.rb$/.match?(location.absolute_path) # location.path =~ /test_.+\.rb$/
|
|
53
53
|
return true
|
|
54
54
|
end
|
|
55
55
|
end
|
data/lib/fluent/supervisor.rb
CHANGED
|
@@ -440,7 +440,7 @@ module Fluent
|
|
|
440
440
|
stop_immediately_at_unrecoverable_exit: true,
|
|
441
441
|
root_dir: params['root_dir'],
|
|
442
442
|
logger: $log,
|
|
443
|
-
log: $log
|
|
443
|
+
log: $log&.out,
|
|
444
444
|
log_level: params['log_level'],
|
|
445
445
|
chuser: params['chuser'],
|
|
446
446
|
chgroup: params['chgroup'],
|
|
@@ -697,7 +697,7 @@ module Fluent
|
|
|
697
697
|
actual_log_path = @log_path
|
|
698
698
|
|
|
699
699
|
# We need to prepare a unique path for each worker since Windows locks files.
|
|
700
|
-
if Fluent.windows? && rotate
|
|
700
|
+
if Fluent.windows? && rotate && @log_path && @log_path != "-"
|
|
701
701
|
actual_log_path = Fluent::Log.per_process_path(@log_path, process_type, worker_id)
|
|
702
702
|
end
|
|
703
703
|
|
data/lib/fluent/system_config.rb
CHANGED
data/lib/fluent/version.rb
CHANGED
data/lib/fluent/winsvc.rb
CHANGED
|
@@ -63,10 +63,12 @@ begin
|
|
|
63
63
|
end
|
|
64
64
|
|
|
65
65
|
def service_stop
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
66
|
+
if @pid <= 0
|
|
67
|
+
set_event(@service_name)
|
|
68
|
+
return
|
|
69
69
|
end
|
|
70
|
+
|
|
71
|
+
wait_supervisor_finished
|
|
70
72
|
end
|
|
71
73
|
|
|
72
74
|
def service_paramchange
|
|
@@ -91,6 +93,29 @@ begin
|
|
|
91
93
|
ev.set
|
|
92
94
|
ev.close
|
|
93
95
|
end
|
|
96
|
+
|
|
97
|
+
def repeat_set_event_several_times_until_success(event_name)
|
|
98
|
+
retries = 0
|
|
99
|
+
max_retries = 10
|
|
100
|
+
delay_sec = 3
|
|
101
|
+
|
|
102
|
+
begin
|
|
103
|
+
set_event(event_name)
|
|
104
|
+
rescue Errno::ENOENT
|
|
105
|
+
# This error occurs when the supervisor process has not yet created the event.
|
|
106
|
+
# If STOP is immediately executed, this state will occur.
|
|
107
|
+
# Retry `set_event' to wait for the initialization of the supervisor.
|
|
108
|
+
retries += 1
|
|
109
|
+
raise if max_retries < retries
|
|
110
|
+
sleep(delay_sec)
|
|
111
|
+
retry
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def wait_supervisor_finished
|
|
116
|
+
repeat_set_event_several_times_until_success(@service_name)
|
|
117
|
+
Process.waitpid(@pid)
|
|
118
|
+
end
|
|
94
119
|
end
|
|
95
120
|
|
|
96
121
|
FluentdService.new(opts[:service_name]).mainloop
|
data/test/command/test_cat.rb
CHANGED
|
@@ -18,7 +18,7 @@ class TestFluentCat < ::Test::Unit::TestCase
|
|
|
18
18
|
@primary = create_primary
|
|
19
19
|
metadata = @primary.buffer.new_metadata
|
|
20
20
|
@chunk = create_chunk(@primary, metadata, @es)
|
|
21
|
-
@port = unused_port
|
|
21
|
+
@port = unused_port(protocol: :all)
|
|
22
22
|
end
|
|
23
23
|
|
|
24
24
|
def teardown
|
|
@@ -87,7 +87,7 @@ class TestFluentCat < ::Test::Unit::TestCase
|
|
|
87
87
|
d = create_driver
|
|
88
88
|
d.run(expect_records: 1) do
|
|
89
89
|
Open3.pipeline_w("#{ServerEngine.ruby_bin_path} #{FLUENT_CAT_COMMAND} --port #{@port} --format msgpack secondary") do |stdin|
|
|
90
|
-
stdin.write(File.read(path))
|
|
90
|
+
stdin.write(File.read(path, File.size(path)))
|
|
91
91
|
stdin.close
|
|
92
92
|
end
|
|
93
93
|
end
|