fluentd 1.13.1 → 1.14.0

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 (72) hide show
  1. checksums.yaml +4 -4
  2. data/.github/ISSUE_TEMPLATE/bug_report.yaml +69 -0
  3. data/.github/ISSUE_TEMPLATE/feature_request.yaml +38 -0
  4. data/.github/workflows/windows-test.yaml +3 -3
  5. data/CHANGELOG.md +105 -0
  6. data/README.md +2 -2
  7. data/example/v0_12_filter.conf +2 -2
  8. data/fluentd.gemspec +1 -1
  9. data/lib/fluent/command/fluentd.rb +8 -0
  10. data/lib/fluent/command/plugin_generator.rb +15 -5
  11. data/lib/fluent/compat/output.rb +9 -6
  12. data/lib/fluent/config/types.rb +15 -0
  13. data/lib/fluent/config/v1_parser.rb +3 -2
  14. data/lib/fluent/config.rb +1 -1
  15. data/lib/fluent/env.rb +2 -1
  16. data/lib/fluent/event_router.rb +28 -1
  17. data/lib/fluent/oj_options.rb +62 -0
  18. data/lib/fluent/plugin/bare_output.rb +49 -8
  19. data/lib/fluent/plugin/buffer.rb +84 -22
  20. data/lib/fluent/plugin/file_wrapper.rb +22 -0
  21. data/lib/fluent/plugin/filter.rb +35 -1
  22. data/lib/fluent/plugin/formatter.rb +1 -0
  23. data/lib/fluent/plugin/formatter_json.rb +9 -7
  24. data/lib/fluent/plugin/in_http.rb +21 -2
  25. data/lib/fluent/plugin/in_monitor_agent.rb +4 -2
  26. data/lib/fluent/plugin/in_syslog.rb +13 -1
  27. data/lib/fluent/plugin/in_tail/position_file.rb +20 -18
  28. data/lib/fluent/plugin/in_tail.rb +45 -3
  29. data/lib/fluent/plugin/input.rb +39 -1
  30. data/lib/fluent/plugin/metrics.rb +119 -0
  31. data/lib/fluent/plugin/metrics_local.rb +96 -0
  32. data/lib/fluent/plugin/multi_output.rb +43 -6
  33. data/lib/fluent/plugin/output.rb +74 -33
  34. data/lib/fluent/plugin/parser_json.rb +2 -3
  35. data/lib/fluent/plugin.rb +10 -1
  36. data/lib/fluent/plugin_helper/event_emitter.rb +8 -1
  37. data/lib/fluent/plugin_helper/metrics.rb +129 -0
  38. data/lib/fluent/plugin_helper/server.rb +4 -2
  39. data/lib/fluent/plugin_helper.rb +1 -0
  40. data/lib/fluent/root_agent.rb +6 -0
  41. data/lib/fluent/supervisor.rb +2 -0
  42. data/lib/fluent/system_config.rb +9 -1
  43. data/lib/fluent/test/driver/storage.rb +30 -0
  44. data/lib/fluent/version.rb +1 -1
  45. data/templates/new_gem/lib/fluent/plugin/storage.rb.erb +40 -0
  46. data/templates/new_gem/test/plugin/test_storage.rb.erb +18 -0
  47. data/test/command/test_plugin_generator.rb +2 -1
  48. data/test/config/test_system_config.rb +6 -0
  49. data/test/config/test_types.rb +7 -0
  50. data/test/plugin/in_tail/test_position_file.rb +48 -8
  51. data/test/plugin/test_bare_output.rb +13 -0
  52. data/test/plugin/test_buffer.rb +8 -2
  53. data/test/plugin/test_file_wrapper.rb +11 -0
  54. data/test/plugin/test_filter.rb +11 -0
  55. data/test/plugin/test_in_http.rb +40 -0
  56. data/test/plugin/test_in_monitor_agent.rb +214 -8
  57. data/test/plugin/test_in_syslog.rb +35 -0
  58. data/test/plugin/test_in_tail.rb +138 -26
  59. data/test/plugin/test_input.rb +11 -0
  60. data/test/plugin/test_metrics.rb +294 -0
  61. data/test/plugin/test_metrics_local.rb +96 -0
  62. data/test/plugin/test_multi_output.rb +25 -1
  63. data/test/plugin/test_output.rb +16 -0
  64. data/test/plugin_helper/test_event_emitter.rb +29 -0
  65. data/test/plugin_helper/test_metrics.rb +137 -0
  66. data/test/test_event_time.rb +2 -2
  67. data/test/test_oj_options.rb +55 -0
  68. data/test/test_plugin_classes.rb +102 -0
  69. data/test/test_root_agent.rb +30 -1
  70. metadata +21 -6
  71. data/.github/ISSUE_TEMPLATE/bug_report.md +0 -40
  72. data/.github/ISSUE_TEMPLATE/feature_request.md +0 -23
@@ -155,7 +155,29 @@ module Fluent
155
155
  by_handle_file_information.unpack("I11Q1")[11] # fileindex
156
156
  end
157
157
 
158
+ # DeletePending is a Windows-specific file state that roughly means
159
+ # "this file is queued for deletion, so close any open handlers"
160
+ #
161
+ # This flag can be retrieved via GetFileInformationByHandleEx().
162
+ #
163
+ # https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-getfileinformationbyhandleex
164
+ #
165
+ def delete_pending
166
+ file_standard_info = 0x01
167
+ bufsize = 1024
168
+ buf = '\0' * bufsize
169
+
170
+ unless GetFileInformationByHandleEx.call(@file_handle, file_standard_info, buf, bufsize)
171
+ return false
172
+ end
173
+
174
+ return buf.unpack("QQICC")[3] != 0
175
+ end
176
+
177
+ private :delete_pending
178
+
158
179
  def stat
180
+ raise Errno::ENOENT if delete_pending
159
181
  s = File.stat(@path)
160
182
  s.instance_variable_set :@ino, self.ino
161
183
  def s.ino; @ino; end
@@ -28,13 +28,47 @@ module Fluent
28
28
  include PluginLoggerMixin
29
29
  include PluginHelper::Mixin
30
30
 
31
- helpers_internal :event_emitter
31
+ helpers_internal :event_emitter, :metrics
32
32
 
33
33
  attr_reader :has_filter_with_time
34
34
 
35
35
  def initialize
36
36
  super
37
37
  @has_filter_with_time = has_filter_with_time?
38
+ @emit_records_metrics = nil
39
+ @emit_size_metrics = nil
40
+ @counter_mutex = Mutex.new
41
+ @enable_size_metrics = false
42
+ end
43
+
44
+ def emit_records
45
+ @emit_records_metrics.get
46
+ end
47
+
48
+ def emit_size
49
+ @emit_size_metrics.get
50
+ end
51
+
52
+ def configure(conf)
53
+ super
54
+
55
+ @emit_records_metrics = metrics_create(namespace: "fluentd", subsystem: "filter", name: "emit_records", help_text: "Number of count emit records")
56
+ @emit_size_metrics = metrics_create(namespace: "fluentd", subsystem: "filter", name: "emit_size", help_text: "Total size of emit events")
57
+ @enable_size_metrics = !!system_config.enable_size_metrics
58
+ end
59
+
60
+ def statistics
61
+ stats = {
62
+ 'emit_records' => @emit_records_metrics.get,
63
+ 'emit_size' => @emit_size_metrics.get,
64
+ }
65
+
66
+ { 'filter' => stats }
67
+ end
68
+
69
+ def measure_metrics(es)
70
+ @emit_records_metrics.add(es.size)
71
+ @emit_size_metrics.add(es.to_msgpack_stream.bytesize) if @enable_size_metrics
38
72
  end
39
73
 
40
74
  def filter(tag, time, record)
@@ -14,6 +14,7 @@
14
14
  # limitations under the License.
15
15
  #
16
16
 
17
+ require 'fluent/env'
17
18
  require 'fluent/plugin/base'
18
19
  require 'fluent/plugin/owned_by_mixin'
19
20
  require 'fluent/time'
@@ -15,7 +15,7 @@
15
15
  #
16
16
 
17
17
  require 'fluent/plugin/formatter'
18
- require 'fluent/env'
18
+ require 'fluent/oj_options'
19
19
 
20
20
  module Fluent
21
21
  module Plugin
@@ -30,12 +30,14 @@ module Fluent
30
30
  def configure(conf)
31
31
  super
32
32
 
33
- begin
34
- raise LoadError unless @json_parser == 'oj'
35
- require 'oj'
36
- Oj.default_options = Fluent::DEFAULT_OJ_OPTIONS
37
- @dump_proc = Oj.method(:dump)
38
- rescue LoadError
33
+ if @json_parser == 'oj'
34
+ if Fluent::OjOptions.available?
35
+ @dump_proc = Oj.method(:dump)
36
+ else
37
+ log.info "Oj isn't installed, fallback to Yajl as json parser"
38
+ @dump_proc = Yajl.method(:dump)
39
+ end
40
+ else
39
41
  @dump_proc = Yajl.method(:dump)
40
42
  end
41
43
 
@@ -74,6 +74,8 @@ module Fluent::Plugin
74
74
  config_param :blocking_timeout, :time, default: 0.5
75
75
  desc 'Set a allow list of domains that can do CORS (Cross-Origin Resource Sharing)'
76
76
  config_param :cors_allow_origins, :array, default: nil
77
+ desc 'Tells browsers whether to expose the response to frontend when the credentials mode is "include".'
78
+ config_param :cors_allow_credentials, :bool, default: false
77
79
  desc 'Respond with empty gif image of 1x1 pixel.'
78
80
  config_param :respond_with_empty_img, :bool, default: false
79
81
  desc 'Respond status code with 204.'
@@ -112,6 +114,12 @@ module Fluent::Plugin
112
114
 
113
115
  super
114
116
 
117
+ if @cors_allow_credentials
118
+ if @cors_allow_origins.nil? || @cors_allow_origins.include?('*')
119
+ raise Fluent::ConfigError, "Cannot enable cors_allow_credentials without specific origins"
120
+ end
121
+ end
122
+
115
123
  m = if @parser_configs.first['@type'] == 'in_http'
116
124
  @parser_msgpack = parser_create(usage: 'parser_in_http_msgpack', type: 'msgpack')
117
125
  @parser_msgpack.time_key = nil
@@ -279,7 +287,10 @@ module Fluent::Plugin
279
287
  private
280
288
 
281
289
  def on_server_connect(conn)
282
- handler = Handler.new(conn, @km, method(:on_request), @body_size_limit, @format_name, log, @cors_allow_origins, @add_query_params)
290
+ handler = Handler.new(conn, @km, method(:on_request),
291
+ @body_size_limit, @format_name, log,
292
+ @cors_allow_origins, @cors_allow_credentials,
293
+ @add_query_params)
283
294
 
284
295
  conn.on(:data) do |data|
285
296
  handler.on_read(data)
@@ -356,7 +367,8 @@ module Fluent::Plugin
356
367
  class Handler
357
368
  attr_reader :content_type
358
369
 
359
- def initialize(io, km, callback, body_size_limit, format_name, log, cors_allow_origins, add_query_params)
370
+ def initialize(io, km, callback, body_size_limit, format_name, log,
371
+ cors_allow_origins, cors_allow_credentials, add_query_params)
360
372
  @io = io
361
373
  @km = km
362
374
  @callback = callback
@@ -365,6 +377,7 @@ module Fluent::Plugin
365
377
  @format_name = format_name
366
378
  @log = log
367
379
  @cors_allow_origins = cors_allow_origins
380
+ @cors_allow_credentials = cors_allow_credentials
368
381
  @idle = 0
369
382
  @add_query_params = add_query_params
370
383
  @km.add(self)
@@ -491,6 +504,9 @@ module Fluent::Plugin
491
504
  send_response_and_close(RES_200_STATUS, header, "")
492
505
  elsif include_cors_allow_origin
493
506
  header["Access-Control-Allow-Origin"] = @origin
507
+ if @cors_allow_credentials
508
+ header["Access-Control-Allow-Credentials"] = true
509
+ end
494
510
  send_response_and_close(RES_200_STATUS, header, "")
495
511
  else
496
512
  send_response_and_close(RES_403_STATUS, {}, "")
@@ -576,6 +592,9 @@ module Fluent::Plugin
576
592
  header['Access-Control-Allow-Origin'] = '*'
577
593
  elsif include_cors_allow_origin
578
594
  header['Access-Control-Allow-Origin'] = @origin
595
+ if @cors_allow_credentials
596
+ header["Access-Control-Allow-Credentials"] = true
597
+ end
579
598
  end
580
599
  end
581
600
 
@@ -238,7 +238,7 @@ module Fluent::Plugin
238
238
  'buffer_queue_length' => ->(){ throw(:skip) unless instance_variable_defined?(:@buffer) && !@buffer.nil? && @buffer.is_a?(::Fluent::Plugin::Buffer); @buffer.queue.size },
239
239
  'buffer_timekeys' => ->(){ throw(:skip) unless instance_variable_defined?(:@buffer) && !@buffer.nil? && @buffer.is_a?(::Fluent::Plugin::Buffer); @buffer.timekeys },
240
240
  'buffer_total_queued_size' => ->(){ throw(:skip) unless instance_variable_defined?(:@buffer) && !@buffer.nil? && @buffer.is_a?(::Fluent::Plugin::Buffer); @buffer.stage_size + @buffer.queue_size },
241
- 'retry_count' => ->(){ instance_variable_defined?(:@num_errors) ? @num_errors : nil },
241
+ 'retry_count' => ->(){ respond_to?(:num_errors) ? num_errors : nil },
242
242
  }
243
243
 
244
244
  def all_plugins
@@ -335,7 +335,9 @@ module Fluent::Plugin
335
335
  }
336
336
 
337
337
  if pe.respond_to?(:statistics)
338
- obj.merge!(pe.statistics['output'] || {})
338
+ obj.merge!(pe.statistics.dig('output') || {})
339
+ obj.merge!(pe.statistics.dig('filter') || {})
340
+ obj.merge!(pe.statistics.dig('input') || {})
339
341
  end
340
342
 
341
343
  obj['retry'] = get_retry_info(pe.retry) if opts[:with_retry] && pe.instance_variable_defined?(:@retry)
@@ -89,6 +89,8 @@ module Fluent::Plugin
89
89
  config_param :source_hostname_key, :string, default: nil
90
90
  desc 'Try to resolve hostname from IP addresses or not.'
91
91
  config_param :resolve_hostname, :bool, default: nil
92
+ desc 'Check the remote connection is still available by sending a keepalive packet if this value is true.'
93
+ config_param :send_keepalive_packet, :bool, default: false
92
94
  desc 'The field name of source address of sender.'
93
95
  config_param :source_address_key, :string, default: nil
94
96
  desc 'The field name of the severity.'
@@ -143,6 +145,11 @@ module Fluent::Plugin
143
145
  end
144
146
 
145
147
  @_event_loop_run_timeout = @blocking_timeout
148
+
149
+ protocol = @protocol_type || @transport_config.protocol
150
+ if @send_keepalive_packet && protocol == :udp
151
+ raise Fluent::ConfigError, "send_keepalive_packet is available for tcp/tls"
152
+ end
146
153
  end
147
154
 
148
155
  def multi_workers_ready?
@@ -173,7 +180,12 @@ module Fluent::Plugin
173
180
 
174
181
  delimiter = octet_count_frame ? " " : @delimiter
175
182
  delimiter_size = delimiter.size
176
- server_create_connection(tls ? :in_syslog_tls_server : :in_syslog_tcp_server, @port, bind: @bind, resolve_name: @resolve_hostname) do |conn|
183
+ server_create_connection(
184
+ tls ? :in_syslog_tls_server : :in_syslog_tcp_server, @port,
185
+ bind: @bind,
186
+ resolve_name: @resolve_hostname,
187
+ send_keepalive_packet: @send_keepalive_packet
188
+ ) do |conn|
177
189
  conn.data do |data|
178
190
  buffer = conn.buffer
179
191
  buffer << data
@@ -22,19 +22,18 @@ module Fluent::Plugin
22
22
  UNWATCHED_POSITION = 0xffffffffffffffff
23
23
  POSITION_FILE_ENTRY_REGEX = /^([^\t]+)\t([0-9a-fA-F]+)\t([0-9a-fA-F]+)/.freeze
24
24
 
25
- def self.load(file, follow_inodes, existing_paths, logger:)
26
- pf = new(file, follow_inodes, existing_paths, logger: logger)
27
- pf.load
25
+ def self.load(file, follow_inodes, existing_targets, logger:)
26
+ pf = new(file, follow_inodes, logger: logger)
27
+ pf.load(existing_targets)
28
28
  pf
29
29
  end
30
30
 
31
- def initialize(file, follow_inodes, existing_paths, logger: nil)
31
+ def initialize(file, follow_inodes, logger: nil)
32
32
  @file = file
33
33
  @logger = logger
34
34
  @file_mutex = Mutex.new
35
35
  @map = {}
36
36
  @follow_inodes = follow_inodes
37
- @existing_paths = existing_paths
38
37
  end
39
38
 
40
39
  def [](target_info)
@@ -60,8 +59,8 @@ module Fluent::Plugin
60
59
  end
61
60
  end
62
61
 
63
- def load
64
- compact
62
+ def load(existing_targets = nil)
63
+ compact(existing_targets)
65
64
 
66
65
  map = {}
67
66
  @file_mutex.synchronize do
@@ -118,9 +117,9 @@ module Fluent::Plugin
118
117
 
119
118
  private
120
119
 
121
- def compact
120
+ def compact(existing_targets = nil)
122
121
  @file_mutex.synchronize do
123
- entries = fetch_compacted_entries.values.map(&:to_entry_fmt)
122
+ entries = fetch_compacted_entries(existing_targets).values.map(&:to_entry_fmt)
124
123
 
125
124
  @file.pos = 0
126
125
  @file.truncate(0)
@@ -128,7 +127,7 @@ module Fluent::Plugin
128
127
  end
129
128
  end
130
129
 
131
- def fetch_compacted_entries
130
+ def fetch_compacted_entries(existing_targets = nil)
132
131
  entries = {}
133
132
 
134
133
  @file.pos = 0
@@ -151,23 +150,26 @@ module Fluent::Plugin
151
150
  end
152
151
 
153
152
  if @follow_inodes
154
- entries[ino] = Entry.new(path, pos, ino, file_pos + path.size + 1)
153
+ entries[ino] = Entry.new(path, pos, ino, file_pos + path.bytesize + 1)
155
154
  else
156
- entries[path] = Entry.new(path, pos, ino, file_pos + path.size + 1)
155
+ entries[path] = Entry.new(path, pos, ino, file_pos + path.bytesize + 1)
157
156
  end
158
157
  file_pos += line.size
159
158
  end
160
159
  end
161
160
 
162
- entries = remove_deleted_files_entries(entries, @existing_paths) if @follow_inodes
161
+ entries = remove_deleted_files_entries(entries, existing_targets)
163
162
  entries
164
163
  end
165
164
 
166
- def remove_deleted_files_entries(existent_entries, existing_paths)
167
- filtered_entries = existent_entries.select {|file_entry|
168
- existing_paths.key?(file_entry)
169
- }
170
- filtered_entries
165
+ def remove_deleted_files_entries(existent_entries, existing_targets)
166
+ if existing_targets
167
+ existent_entries.select { |path_or_ino|
168
+ existing_targets.key?(path_or_ino)
169
+ }
170
+ else
171
+ existent_entries
172
+ end
171
173
  end
172
174
  end
173
175
 
@@ -426,6 +426,7 @@ module Fluent::Plugin
426
426
 
427
427
  begin
428
428
  target_info = TargetInfo.new(target_info.path, Fluent::FileWrapper.stat(target_info.path).ino)
429
+ @tails.delete(target_info)
429
430
  @tails[target_info] = tw
430
431
  tw.on_notify
431
432
  rescue Errno::ENOENT, Errno::EACCES => e
@@ -491,10 +492,17 @@ module Fluent::Plugin
491
492
  new_position_entry = @pf[target_info]
492
493
 
493
494
  if new_position_entry.read_inode == 0
495
+ # When follow_inodes is true, it's not cleaned up by refresh_watcher.
496
+ # So it should be unwatched here explicitly.
497
+ rotated_tw.unwatched = true
498
+ # Make sure to delete old key, it has a different ino while the hash key is same.
499
+ @tails.delete(rotated_target_info)
494
500
  @tails[new_target_info] = setup_watcher(new_target_info, new_position_entry)
495
501
  @tails[new_target_info].on_notify
496
502
  end
497
503
  else
504
+ # Make sure to delete old key, it has a different ino while the hash key is same.
505
+ @tails.delete(rotated_target_info)
498
506
  @tails[new_target_info] = setup_watcher(new_target_info, pe)
499
507
  @tails[new_target_info].on_notify
500
508
  end
@@ -522,8 +530,25 @@ module Fluent::Plugin
522
530
  def detach_watcher_after_rotate_wait(tw, ino)
523
531
  # Call event_loop_attach/event_loop_detach is high-cost for short-live object.
524
532
  # If this has a problem with large number of files, use @_event_loop directly instead of timer_execute.
525
- timer_execute(:in_tail_close_watcher, @rotate_wait, repeat: false) do
533
+ if @open_on_every_update
534
+ # Detach now because it's already closed, waiting it doesn't make sense.
526
535
  detach_watcher(tw, ino)
536
+ elsif @read_bytes_limit_per_second < 0
537
+ # throttling isn't enabled, just wait @rotate_wait
538
+ timer_execute(:in_tail_close_watcher, @rotate_wait, repeat: false) do
539
+ detach_watcher(tw, ino)
540
+ end
541
+ else
542
+ # When the throttling feature is enabled, it might not reach EOF yet.
543
+ # Should ensure to read all contents before closing it, with keeping throttling.
544
+ start_time_to_wait = Fluent::Clock.now
545
+ timer = timer_execute(:in_tail_close_watcher, 1, repeat: true) do
546
+ elapsed = Fluent::Clock.now - start_time_to_wait
547
+ if tw.eof? && elapsed >= @rotate_wait
548
+ timer.detach
549
+ detach_watcher(tw, ino)
550
+ end
551
+ end
527
552
  end
528
553
  end
529
554
 
@@ -736,6 +761,10 @@ module Fluent::Plugin
736
761
  end
737
762
  end
738
763
 
764
+ def eof?
765
+ @io_handler.eof?
766
+ end
767
+
739
768
  def on_notify
740
769
  begin
741
770
  stat = Fluent::FileWrapper.stat(@path)
@@ -810,7 +839,7 @@ module Fluent::Plugin
810
839
  # new watcher, and old watcher will be closed by stop_watcher in refresh_watchers method
811
840
  # don't want to swap state because we need latest read offset in pos file even after rotate_wait
812
841
  if stat
813
- target_info = TargetInfo.new(@path, stat)
842
+ target_info = TargetInfo.new(@path, stat.ino)
814
843
  @update_watcher.call(target_info, @pe)
815
844
  end
816
845
  else
@@ -923,6 +952,7 @@ module Fluent::Plugin
923
952
  @shutdown_start_time = nil
924
953
  @shutdown_timeout = SHUTDOWN_TIMEOUT
925
954
  @shutdown_mutex = Mutex.new
955
+ @eof = false
926
956
 
927
957
  @log.info "following tail of #{@path}"
928
958
  end
@@ -949,6 +979,10 @@ module Fluent::Plugin
949
979
  !!@io
950
980
  end
951
981
 
982
+ def eof?
983
+ @eof
984
+ end
985
+
952
986
  private
953
987
 
954
988
  def limit_bytes_per_second_reached?
@@ -989,6 +1023,7 @@ module Fluent::Plugin
989
1023
  while true
990
1024
  @start_reading_time ||= Fluent::Clock.now
991
1025
  data = io.readpartial(BYTES_TO_READ, @iobuf)
1026
+ @eof = false
992
1027
  @number_bytes_read += data.bytesize
993
1028
  @fifo << data
994
1029
  @fifo.read_lines(@lines)
@@ -1005,6 +1040,7 @@ module Fluent::Plugin
1005
1040
  end
1006
1041
  end
1007
1042
  rescue EOFError
1043
+ @eof = true
1008
1044
  end
1009
1045
  end
1010
1046
 
@@ -1027,7 +1063,10 @@ module Fluent::Plugin
1027
1063
  rescue RangeError
1028
1064
  io.close if io
1029
1065
  raise WatcherSetupError, "seek error with #{@path}: file position = #{@watcher.pe.read_pos.to_s(16)}, reading bytesize = #{@fifo.bytesize.to_s(16)}"
1030
- rescue Errno::ENOENT, Errno::EACCES
1066
+ rescue Errno::EACCES => e
1067
+ @log.warn "#{e}"
1068
+ nil
1069
+ rescue Errno::ENOENT
1031
1070
  nil
1032
1071
  end
1033
1072
 
@@ -1042,14 +1081,17 @@ module Fluent::Plugin
1042
1081
  else
1043
1082
  @io ||= open
1044
1083
  yield @io
1084
+ @eof = true if @io.nil?
1045
1085
  end
1046
1086
  rescue WatcherSetupError => e
1047
1087
  close
1088
+ @eof = true
1048
1089
  raise e
1049
1090
  rescue
1050
1091
  @log.error $!.to_s
1051
1092
  @log.error_backtrace
1052
1093
  close
1094
+ @eof = true
1053
1095
  end
1054
1096
  end
1055
1097