fluentd 1.19.2 → 1.19.4

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.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +81 -0
  3. data/Rakefile +7 -0
  4. data/lib/fluent/command/cat.rb +1 -1
  5. data/lib/fluent/config/basic_parser.rb +0 -1
  6. data/lib/fluent/config/literal_parser.rb +18 -7
  7. data/lib/fluent/config/types.rb +10 -2
  8. data/lib/fluent/configurable.rb +1 -0
  9. data/lib/fluent/daemon.rb +2 -1
  10. data/lib/fluent/engine.rb +1 -1
  11. data/lib/fluent/env.rb +1 -0
  12. data/lib/fluent/event.rb +2 -1
  13. data/lib/fluent/plugin/base.rb +7 -2
  14. data/lib/fluent/plugin/buf_file.rb +4 -4
  15. data/lib/fluent/plugin/buf_file_single.rb +2 -2
  16. data/lib/fluent/plugin/buf_memory.rb +1 -1
  17. data/lib/fluent/plugin/buffer/chunk.rb +16 -7
  18. data/lib/fluent/plugin/buffer/file_chunk.rb +7 -5
  19. data/lib/fluent/plugin/buffer/file_single_chunk.rb +7 -5
  20. data/lib/fluent/plugin/buffer/memory_chunk.rb +1 -1
  21. data/lib/fluent/plugin/buffer.rb +26 -5
  22. data/lib/fluent/plugin/compressable.rb +7 -62
  23. data/lib/fluent/plugin/extractor.rb +132 -0
  24. data/lib/fluent/plugin/filter_record_transformer.rb +2 -1
  25. data/lib/fluent/plugin/in_debug_agent.rb +1 -1
  26. data/lib/fluent/plugin/in_forward.rb +3 -1
  27. data/lib/fluent/plugin/in_http.rb +91 -6
  28. data/lib/fluent/plugin/in_monitor_agent.rb +21 -23
  29. data/lib/fluent/plugin/in_sample.rb +2 -1
  30. data/lib/fluent/plugin/in_syslog.rb +70 -4
  31. data/lib/fluent/plugin/out_file.rb +10 -0
  32. data/lib/fluent/plugin/out_forward/connection_manager.rb +18 -0
  33. data/lib/fluent/plugin/out_forward/socket_cache.rb +45 -10
  34. data/lib/fluent/plugin/out_forward.rb +13 -2
  35. data/lib/fluent/plugin/out_http.rb +31 -1
  36. data/lib/fluent/plugin/output.rb +69 -5
  37. data/lib/fluent/plugin/parser_csv.rb +5 -0
  38. data/lib/fluent/plugin/parser_json.rb +6 -1
  39. data/lib/fluent/plugin/parser_syslog.rb +3 -3
  40. data/lib/fluent/plugin/sd_file.rb +2 -1
  41. data/lib/fluent/plugin/storage_local.rb +4 -4
  42. data/lib/fluent/supervisor.rb +22 -1
  43. data/lib/fluent/test/base.rb +5 -0
  44. data/lib/fluent/version.rb +1 -1
  45. metadata +3 -3
  46. data/.deepsource.toml +0 -13
@@ -31,20 +31,26 @@ module Fluent::Plugin
31
31
  end
32
32
 
33
33
  def checkout_or(key)
34
+ obsolete_sockets = []
35
+
34
36
  @mutex.synchronize do
35
- tsock = pick_socket(key)
37
+ tsock, obsolete_sockets = pick_socket(key)
36
38
 
37
39
  if tsock
38
- tsock.sock
40
+ return tsock.sock
39
41
  else
40
42
  sock = yield
41
43
  new_tsock = TimedSocket.new(timeout, key, sock)
42
44
  @log.debug("connect new socket #{new_tsock}")
43
45
 
44
46
  @inflight_sockets[sock] = new_tsock
45
- new_tsock.sock
47
+ return new_tsock.sock
46
48
  end
47
49
  end
50
+ ensure
51
+ obsolete_sockets.each do |sock|
52
+ sock.sock.close rescue nil
53
+ end
48
54
  end
49
55
 
50
56
  def checkin(sock)
@@ -117,17 +123,32 @@ module Fluent::Plugin
117
123
  # this method is not thread safe
118
124
  def pick_socket(key)
119
125
  if @available_sockets[key].empty?
120
- return nil
126
+ return nil, []
121
127
  end
122
128
 
123
129
  t = Time.now
124
- if (s = @available_sockets[key].find { |sock| !expired_socket?(sock, time: t) })
125
- @inflight_sockets[s.sock] = @available_sockets[key].delete(s)
126
- s.timeout = timeout
127
- s
128
- else
129
- nil
130
+ selected = nil
131
+ remaining = []
132
+ obsolete_sockets = []
133
+
134
+ @available_sockets[key].each do |sock|
135
+ if expired_socket?(sock, time: t) || unavailable_socket?(sock.sock)
136
+ obsolete_sockets << sock
137
+ elsif selected.nil?
138
+ selected = sock
139
+ else
140
+ remaining << sock
141
+ end
142
+ end
143
+
144
+ @available_sockets[key] = remaining
145
+
146
+ if selected
147
+ @inflight_sockets[selected.sock] = selected
148
+ selected.timeout = timeout
130
149
  end
150
+
151
+ [selected, obsolete_sockets]
131
152
  end
132
153
 
133
154
  def timeout
@@ -137,6 +158,20 @@ module Fluent::Plugin
137
158
  def expired_socket?(sock, time: Time.now)
138
159
  sock.timeout ? sock.timeout < time : false
139
160
  end
161
+
162
+ def unavailable_socket?(sock)
163
+ return sock.closed? if sock.respond_to?(:closed?) && sock.closed?
164
+
165
+ io = if sock.respond_to?(:to_io)
166
+ sock.to_io
167
+ elsif sock.is_a?(IO)
168
+ sock
169
+ end
170
+
171
+ io ? !!IO.select([io], nil, nil, 0) : false
172
+ rescue IOError, SystemCallError
173
+ true
174
+ end
140
175
  end
141
176
  end
142
177
  end
@@ -525,17 +525,28 @@ module Fluent::Plugin
525
525
 
526
526
  def ack_check(select_interval)
527
527
  @ack_handler.collect_response(select_interval) do |chunk_id, node, sock, result|
528
- @connection_manager.close(sock)
529
-
530
528
  case result
531
529
  when AckHandler::Result::SUCCESS
530
+ @connection_manager.close(sock)
532
531
  commit_write(chunk_id)
533
532
  when AckHandler::Result::FAILED
533
+ # The ack timed out or the peer closed the connection. A keepalive
534
+ # socket may still have an in-flight ack for this chunk, so drop it
535
+ # instead of returning it to the reuse pool; otherwise a late ack
536
+ # could later be read against a chunk sent on the reused socket (#4057).
537
+ @connection_manager.discard(sock)
534
538
  node&.disable!
535
539
  rollback_write(chunk_id, update_retry: false) if chunk_id
536
540
  when AckHandler::Result::CHUNKID_UNMATCHED
541
+ # The ack read from this socket belongs to a different chunk than the
542
+ # one tracked for it: the keepalive connection's ack stream is out of
543
+ # sync (e.g. a stale ack left over from before an aggregator restart).
544
+ # Discard the socket so the following acks are not each offset by one,
545
+ # which would otherwise flood the log with mismatch warnings (#4057).
546
+ @connection_manager.discard(sock)
537
547
  rollback_write(chunk_id, update_retry: false)
538
548
  else
549
+ @connection_manager.close(sock)
539
550
  log.warn("BUG: invalid status #{result} #{chunk_id}")
540
551
 
541
552
  if chunk_id
@@ -17,6 +17,7 @@
17
17
  require 'net/http'
18
18
  require 'uri'
19
19
  require 'openssl'
20
+ require 'securerandom'
20
21
  require 'fluent/tls'
21
22
  require 'fluent/plugin/output'
22
23
  require 'fluent/plugin_helper/socket'
@@ -58,6 +59,9 @@ module Fluent::Plugin
58
59
  desc 'Compress HTTP request body'
59
60
  config_param :compress, :enum, list: [:text, :gzip], default: :text
60
61
 
62
+ desc 'Allowed hosts list for dynamic endpoints'
63
+ config_param :allowed_hosts, :array, default: []
64
+
61
65
  desc 'The connection open timeout in seconds'
62
66
  config_param :open_timeout, :integer, default: nil
63
67
  desc 'The read timeout in seconds'
@@ -106,6 +110,11 @@ module Fluent::Plugin
106
110
  config_param :aws_role_arn, :string, default: nil
107
111
  end
108
112
 
113
+ # To prevent URI::InvalidURIError, we replace Fluentd placeholders with a dummy string.
114
+ # We use the ".invalid" TLD (RFC 2606) to ensure it is RFC-compliant for URI parsing,
115
+ # while guaranteeing it will never conflict with a real-world hostname.
116
+ REPLACED_ENDPOINT_PLACEHOLDER = "#{SecureRandom.uuid}.invalid".freeze
117
+
109
118
  def connection_cache_id_thread_key
110
119
  "#{plugin_id}_connection_cache_id"
111
120
  end
@@ -146,6 +155,15 @@ module Fluent::Plugin
146
155
  @retryable_response_codes = [503]
147
156
  end
148
157
 
158
+ begin
159
+ # Replace all Fluentd placeholder syntaxes (${...} or %{...})
160
+ endpoint = @endpoint.gsub(%r([$%]{[^}]+}), REPLACED_ENDPOINT_PLACEHOLDER)
161
+ # If @endpoint has placeholder as host name, then, @endpoint_host == REPLACED_ENDPOINT_PLACEHOLDER
162
+ @endpoint_host = URI.parse(endpoint).host
163
+ rescue URI::InvalidURIError => e
164
+ raise Fluent::ConfigError, "Invalid endpoint URI: #{@endpoint} (#{e.message})"
165
+ end
166
+
149
167
  @http_opt = setup_http_option
150
168
  @proxy_uri = URI.parse(@proxy) if @proxy
151
169
  @formatter = formatter_create
@@ -278,7 +296,19 @@ module Fluent::Plugin
278
296
 
279
297
  def parse_endpoint(chunk)
280
298
  endpoint = extract_placeholders(@endpoint, chunk)
281
- URI.parse(endpoint)
299
+ uri = URI.parse(endpoint)
300
+
301
+ if @endpoint_host != uri.host
302
+ if @allowed_hosts.empty?
303
+ raise Fluent::UnrecoverableError, "allowed_hosts is strictly required when using placeholders in the endpoint host"
304
+ end
305
+
306
+ unless @allowed_hosts.include?(uri.host)
307
+ raise Fluent::UnrecoverableError, "Not allowed host: #{uri.host}"
308
+ end
309
+ end
310
+
311
+ uri
282
312
  end
283
313
 
284
314
  def set_headers(req, uri, chunk)
@@ -28,6 +28,7 @@ require 'fluent/unique_id'
28
28
  require 'fluent/clock'
29
29
  require 'fluent/ext_monitor_require'
30
30
 
31
+ require 'json'
31
32
  require 'time'
32
33
 
33
34
  module Fluent
@@ -45,6 +46,21 @@ module Fluent
45
46
  CHUNK_TAG_PLACEHOLDER_PATTERN = /\$\{(tag(?:\[-?\d+\])?)\}/
46
47
  CHUNK_ID_PLACEHOLDER_PATTERN = /\$\{chunk_id\}/
47
48
 
49
+ # Matches absolute paths, including the POSIX root, Windows drive roots, and UNC paths (e.g., \\host\share).
50
+ # It also accounts for forward slashes used as directory separators in Windows.
51
+ ABSOLUTE_PATH_PATTERN = %r{\A[/\\]}
52
+ # Matches Windows drive specifications.
53
+ # A trailing separator after the colon is not required, because a drive-relative path like C:Windows can still escape a relative path template.
54
+ WINDOWS_DRIVE_PATTERN = %r{\A[a-zA-Z]:}
55
+ # Matches .. only when it acts as an independent path component (e.g., ../etc or foo/..),
56
+ # ignoring dots embedded within strings like app..web or a..b/c.
57
+ # It uses zero-width negative lookarounds to avoid consuming characters.
58
+ # This ensures that adjacent components like /a/../../b are accurately detected and counted without missing any.
59
+ PARENT_DIRECTORY_PATTERN = %r{(?<![^/\\])\.\.(?![^/\\])}
60
+
61
+ INVALID_PATH_COMPONENT_PATTERN =
62
+ Regexp.union(ABSOLUTE_PATH_PATTERN, WINDOWS_DRIVE_PATTERN, PARENT_DIRECTORY_PATTERN)
63
+
48
64
  CHUNKING_FIELD_WARN_NUM = 4
49
65
 
50
66
  config_param :time_as_integer, :bool, default: false
@@ -372,6 +388,13 @@ module Fluent
372
388
  buf_type = Plugin.lookup_type_from_class(@buffer.class)
373
389
  log.warn "'flush_at_shutdown' is false, and buffer plugin '#{buf_type}' is not persistent buffer."
374
390
  log.warn "your configuration will lose buffered data at shutdown. please confirm your configuration again."
391
+ else
392
+ if Fluent.windows? && @buffer.persistent?
393
+ service_timeout = read_wait_to_kill_service_timeout
394
+ if service_timeout && service_timeout <= 5000 # default value might varies on windows client/server
395
+ log.warn "your WaitToKillServiceTimeout=#{service_timeout} registry configuration seems too short. Recommend to extend the value of 'HKLM\\SYSTEM\\CurrentControlSet\\Control\\WaitToKillServiceTimeout' to prevent buffer corruption from a forced shutdown"
396
+ end
397
+ end
375
398
  end
376
399
 
377
400
  if (@flush_mode != :interval) && buffer_conf.has_key?('flush_interval')
@@ -418,6 +441,22 @@ module Fluent
418
441
  self
419
442
  end
420
443
 
444
+ def read_wait_to_kill_service_timeout
445
+ if Fluent.windows?
446
+ begin
447
+ require "win32/registry"
448
+ Win32::Registry::HKEY_LOCAL_MACHINE.open("SYSTEM\\CurrentControlSet\\Control",
449
+ Win32::Registry::KEY_READ) do |reg|
450
+ reg["WaitToKillServiceTimeout"].to_i
451
+ end
452
+ rescue => e
453
+ log.warn "'flush_at_shutdown' is true, but can't check WaitToKillServiceTimeout registry configuration", error: e
454
+ end
455
+ else
456
+ nil # not supported
457
+ end
458
+ end
459
+
421
460
  def keep_buffer_config_compat
422
461
  # Need this to call `@buffer_config.disable_chunk_backup` just as before,
423
462
  # since some plugins may use this option in this way.
@@ -802,9 +841,17 @@ module Fluent
802
841
  # ${tag}, ${tag[0]}, ${tag[1]}, ... , ${tag[-2]}, ${tag[-1]}
803
842
  if @chunk_key_tag
804
843
  if str.include?('${tag}')
844
+ if metadata.tag.match?(INVALID_PATH_COMPONENT_PATTERN)
845
+ raise Fluent::UnrecoverableError, "Invalid path component detected in tag: #{metadata.tag}"
846
+ end
847
+
805
848
  rvalue = rvalue.gsub('${tag}', metadata.tag)
806
849
  end
807
850
  if CHUNK_TAG_PLACEHOLDER_PATTERN.match?(str)
851
+ if metadata.tag.match?(INVALID_PATH_COMPONENT_PATTERN)
852
+ raise Fluent::UnrecoverableError, "Invalid path component detected in tag: #{metadata.tag}"
853
+ end
854
+
808
855
  hash = {}
809
856
  tag_parts = metadata.tag.split('.')
810
857
  tag_parts.each_with_index do |part, i|
@@ -835,13 +882,25 @@ module Fluent
835
882
  end
836
883
 
837
884
  rvalue = rvalue.gsub(CHUNK_KEY_PLACEHOLDER_PATTERN) do |matched|
838
- hash.fetch(matched) do
885
+ replace = hash.fetch(matched) do
839
886
  log.warn "chunk key placeholder '#{matched[2..-2]}' not replaced. template:#{str}"
840
887
  ''
841
888
  end
889
+ if replace.to_s.match?(INVALID_PATH_COMPONENT_PATTERN)
890
+ raise Fluent::UnrecoverableError, "Invalid path component detected in #{matched}: #{replace}"
891
+ end
892
+
893
+ replace
842
894
  end
843
895
  end
844
896
 
897
+ # Verifies that the number of parent directory traversals (..) has not increased after variable substitution.
898
+ # While each replaced value is validated individually, this step prevents harmless values from concatenating into a dangerous path (e.g., ${tag}${key}/ becoming ../ if both tag and key are .).
899
+ if rvalue.match?(PARENT_DIRECTORY_PATTERN) &&
900
+ rvalue.scan(PARENT_DIRECTORY_PATTERN).size > str.scan(PARENT_DIRECTORY_PATTERN).size
901
+ raise Fluent::UnrecoverableError, "Invalid path component detected, replaced to: #{rvalue}"
902
+ end
903
+
845
904
  if rvalue =~ CHUNK_KEY_PLACEHOLDER_PATTERN
846
905
  log.warn "chunk key placeholder '#{$1}' not replaced. template:#{str}"
847
906
  end
@@ -1168,7 +1227,7 @@ module Fluent
1168
1227
  end
1169
1228
  end
1170
1229
 
1171
- UNRECOVERABLE_ERRORS = [Fluent::UnrecoverableError, TypeError, ArgumentError, NoMethodError, MessagePack::UnpackError, EncodingError]
1230
+ UNRECOVERABLE_ERRORS = [Fluent::UnrecoverableError, TypeError, ArgumentError, NoMethodError, MessagePack::UnpackError, EncodingError, JSON::GeneratorError]
1172
1231
 
1173
1232
  def try_flush
1174
1233
  chunk = @buffer.dequeue_chunk
@@ -1275,9 +1334,14 @@ module Fluent
1275
1334
  if @buffer.disable_chunk_backup
1276
1335
  log.warn "disable_chunk_backup is true. #{dump_unique_id_hex(chunk.unique_id)} chunk is thrown away"
1277
1336
  else
1278
- @buffer.backup(chunk.unique_id) { |f|
1279
- chunk.write_to(f)
1280
- }
1337
+ begin
1338
+ @buffer.backup(chunk.unique_id) { |f|
1339
+ chunk.write_to(f)
1340
+ }
1341
+ rescue => e
1342
+ log.warn "failed to back up #{dump_unique_id_hex(chunk.unique_id)} chunk. it is thrown away", error: e
1343
+ log.warn_backtrace
1344
+ end
1281
1345
  end
1282
1346
  commit_write(chunk.unique_id, secondary: using_secondary, delayed: delayed_commit)
1283
1347
  end
@@ -47,6 +47,11 @@ module Fluent
47
47
 
48
48
  def parse(text, &block)
49
49
  values = CSV.parse_line(text, col_sep: @delimiter)
50
+ unless values
51
+ yield nil, nil
52
+ return
53
+ end
54
+
50
55
  r = Hash[@keys.zip(values)]
51
56
  time, record = convert_values(parse_time(r), r)
52
57
  yield 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/parser'
18
19
  require 'fluent/time'
19
20
  require 'fluent/oj_options'
@@ -38,6 +39,10 @@ module Fluent
38
39
 
39
40
  config_set_default :time_type, :float
40
41
 
42
+ # Use a shared proc rather than a per-call lambda so that
43
+ # configure_json_parser returns the same object every time.
44
+ JSON_PARSE_PROC = ->(text) { JSON.parse(text, **Fluent::DEFAULT_JSON_PARSE_OPTIONS) }
45
+
41
46
  def configure(conf)
42
47
  if conf.has_key?('time_format')
43
48
  conf['time_type'] ||= 'string'
@@ -54,7 +59,7 @@ module Fluent
54
59
 
55
60
  log&.info "Oj is not installed, and failing back to JSON for json parser"
56
61
  configure_json_parser(:json)
57
- when :json then [JSON.method(:parse), JSON::ParserError]
62
+ when :json then [JSON_PARSE_PROC, JSON::ParserError]
58
63
  when :yajl then [Yajl.method(:load), Yajl::ParseError]
59
64
  else
60
65
  raise "BUG: unknown json parser specified: #{name}"
@@ -44,7 +44,7 @@ module Fluent
44
44
  RFC3164_CAPTURES = RFC3164_WITHOUT_TIME_AND_PRI_REGEXP.names.freeze
45
45
  RFC3164_PRI_REGEXP = /^<(?<pri>[0-9]{1,3})>/
46
46
 
47
- RFC5424_WITHOUT_TIME_AND_PRI_REGEXP = /(?<host>[!-~]{1,255}) (?<ident>[!-~]{1,48}) (?<pid>[!-~]{1,128}) (?<msgid>[!-~]{1,32}) (?<extradata>(?:\-|(?:\[.*?(?<!\\)\])+))(?: (?<message>.+))?\z/m
47
+ RFC5424_WITHOUT_TIME_AND_PRI_REGEXP = /(?<host>[!-~]{1,255}) (?<ident>[!-~]{1,48}) (?<pid>[!-~]{1,128}) (?<msgid>[!-~]{1,32}) (?<extradata>(?:\-|(?:\[.*?(?<!\\)\])))(?: (?<message>.+))?\z/m
48
48
  RFC5424_CAPTURES = RFC5424_WITHOUT_TIME_AND_PRI_REGEXP.names.freeze
49
49
  RFC5424_PRI_REGEXP = /^<(?<pri>\d{1,3})>\d\d{0,2}\s/
50
50
 
@@ -314,7 +314,7 @@ module Fluent
314
314
  i = text.index(SPLIT_CHAR, i + 1)
315
315
  end
316
316
 
317
- time_str = sq ? text.slice(idx, i - cursor).squeeze(SPLIT_CHAR) : text.slice(cursor, i - cursor)
317
+ time_str = sq ? text.slice(cursor, i - cursor).squeeze(SPLIT_CHAR) : text.slice(cursor, i - cursor)
318
318
  cursor = i + 1
319
319
  end
320
320
 
@@ -406,7 +406,7 @@ module Fluent
406
406
  i = text.index(SPLIT_CHAR, i + 1)
407
407
  end
408
408
 
409
- time_str = sq ? text.slice(idx, i - cursor).squeeze(SPLIT_CHAR) : text.slice(cursor, i - cursor)
409
+ time_str = sq ? text.slice(cursor, i - cursor).squeeze(SPLIT_CHAR) : text.slice(cursor, i - cursor)
410
410
  cursor = i + 1
411
411
  end
412
412
 
@@ -16,6 +16,7 @@
16
16
 
17
17
  require 'cool.io'
18
18
 
19
+ require 'fluent/env'
19
20
  require 'fluent/plugin_helper'
20
21
  require 'fluent/plugin/service_discovery'
21
22
 
@@ -75,7 +76,7 @@ module Fluent
75
76
  -> (v) { YAML.safe_load(v).map }
76
77
  when :json
77
78
  require 'json'
78
- -> (v) { JSON.parse(v) }
79
+ -> (v) { JSON.parse(v, **Fluent::DEFAULT_JSON_PARSE_OPTIONS) }
79
80
  end
80
81
  end
81
82
 
@@ -85,12 +85,12 @@ module Fluent
85
85
  if File.exist?(@path)
86
86
  raise Fluent::ConfigError, "Plugin storage path '#{@path}' is not readable/writable" unless File.readable?(@path) && File.writable?(@path)
87
87
  begin
88
- data = File.open(@path, 'r:utf-8') { |io| io.read }
88
+ data = File.open(@path, 'r:utf-8:utf-8') { |io| io.read }
89
89
  if data.empty?
90
90
  log.warn "detect empty plugin storage file during startup. Ignored: #{@path}"
91
91
  return
92
92
  end
93
- data = JSON.parse(data)
93
+ data = JSON.parse(data, **Fluent::DEFAULT_JSON_PARSE_OPTIONS)
94
94
  raise Fluent::ConfigError, "Invalid contents (not object) in plugin storage file: '#{@path}'" unless data.is_a?(Hash)
95
95
  rescue => e
96
96
  log.error "failed to read data from plugin storage file", path: @path, error: e
@@ -113,8 +113,8 @@ module Fluent
113
113
  return if @on_memory
114
114
  return unless File.exist?(@path)
115
115
  begin
116
- json_string = File.open(@path, 'r:utf-8'){ |io| io.read }
117
- json = JSON.parse(json_string)
116
+ json_string = File.open(@path, 'r:utf-8:utf-8'){ |io| io.read }
117
+ json = JSON.parse(json_string, **Fluent::DEFAULT_JSON_PARSE_OPTIONS)
118
118
  unless json.is_a?(Hash)
119
119
  log.error "broken content for plugin storage (Hash required: ignored)", type: json.class
120
120
  log.debug "broken content", content: json_string
@@ -100,7 +100,14 @@ module Fluent
100
100
  end
101
101
 
102
102
  def cleanup_lock_dir
103
- FileUtils.rm(Dir.glob(File.join(@fluentd_lock_dir, "fluentd-*.lock")))
103
+ begin
104
+ Dir.each_child(@fluentd_lock_dir) do |name|
105
+ FileUtils.rm_f(File.join(@fluentd_lock_dir, name)) if File.fnmatch?("fluentd-*.lock", name)
106
+ end
107
+ rescue Errno::ENOENT
108
+ # Directory is already missing. Fall through and let rmdir below
109
+ # fail in the same way as the current implementation.
110
+ end
104
111
  FileUtils.rmdir(@fluentd_lock_dir)
105
112
  end
106
113
 
@@ -814,6 +821,20 @@ module Fluent
814
821
  @conf += additional_conf
815
822
  end
816
823
 
824
+ if Fluent.windows?
825
+ @conf.elements.each do |element|
826
+ next unless element.name == 'source'
827
+ if element['@type'] == 'tail' && element['pos_file']
828
+ $log.warn("Recommend adding #{File.dirname(element['pos_file'])} to the exclusion path of your antivirus software on Windows")
829
+ else
830
+ storage = element.elements.find { |v| v.name == 'storage' and v['@type'] == 'local' }
831
+ if storage
832
+ $log.warn("Recommend adding #{File.dirname(storage['path'])} to the exclusion path of your antivirus software on Windows")
833
+ end
834
+ end
835
+ end
836
+ end
837
+
817
838
  @libs.each do |lib|
818
839
  require lib
819
840
  end
@@ -70,7 +70,12 @@ module Fluent
70
70
  num_waits.times { sleep 0.05 }
71
71
  return yield
72
72
  ensure
73
+ @instance.stop
74
+ @instance.before_shutdown
73
75
  @instance.shutdown
76
+ @instance.after_shutdown
77
+ @instance.close
78
+ @instance.terminate
74
79
  end
75
80
  end
76
81
  end
@@ -16,6 +16,6 @@
16
16
 
17
17
  module Fluent
18
18
 
19
- VERSION = '1.19.2'
19
+ VERSION = '1.19.4'
20
20
 
21
21
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fluentd
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.19.2
4
+ version: 1.19.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sadayuki Furuhashi
@@ -531,7 +531,6 @@ executables:
531
531
  extensions: []
532
532
  extra_rdoc_files: []
533
533
  files:
534
- - ".deepsource.toml"
535
534
  - ".rubocop.yml"
536
535
  - ADOPTERS.md
537
536
  - AUTHORS
@@ -691,6 +690,7 @@ files:
691
690
  - lib/fluent/plugin/buffer/memory_chunk.rb
692
691
  - lib/fluent/plugin/compressable.rb
693
692
  - lib/fluent/plugin/exec_util.rb
693
+ - lib/fluent/plugin/extractor.rb
694
694
  - lib/fluent/plugin/file_util.rb
695
695
  - lib/fluent/plugin/filter.rb
696
696
  - lib/fluent/plugin/filter_grep.rb
@@ -883,7 +883,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
883
883
  - !ruby/object:Gem::Version
884
884
  version: '0'
885
885
  requirements: []
886
- rubygems_version: 3.6.9
886
+ rubygems_version: 4.0.20
887
887
  specification_version: 4
888
888
  summary: Fluentd event collector
889
889
  test_files: []
data/.deepsource.toml DELETED
@@ -1,13 +0,0 @@
1
- version = 1
2
-
3
- test_patterns = ["test/**/test_*.rb"]
4
-
5
- exclude_patterns = [
6
- "bin/**",
7
- "docs/**",
8
- "example/**"
9
- ]
10
-
11
- [[analyzers]]
12
- name = "ruby"
13
- enabled = true