fluentd 1.12.0.rc2 → 1.12.4
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 +35 -0
- data/.gitlab-ci.yml +41 -19
- data/CHANGELOG.md +157 -0
- data/MAINTAINERS.md +5 -2
- data/README.md +7 -4
- 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 +0 -1
- data/lib/fluent/command/fluentd.rb +4 -0
- 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/plugin/file_wrapper.rb +39 -3
- data/lib/fluent/plugin/formatter_ltsv.rb +2 -2
- data/lib/fluent/plugin/in_http.rb +1 -1
- data/lib/fluent/plugin/in_monitor_agent.rb +1 -1
- data/lib/fluent/plugin/in_tail.rb +35 -15
- data/lib/fluent/plugin/in_tail/position_file.rb +15 -1
- data/lib/fluent/plugin/in_tcp.rb +1 -0
- 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 +61 -28
- 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/inject.rb +4 -2
- data/lib/fluent/plugin_helper/server.rb +4 -2
- data/lib/fluent/plugin_helper/socket_option.rb +2 -2
- data/lib/fluent/supervisor.rb +13 -5
- data/lib/fluent/system_config.rb +2 -1
- data/lib/fluent/time.rb +58 -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_fluentd.rb +38 -0
- data/test/command/test_plugin_config_formatter.rb +67 -0
- data/test/config/test_configurable.rb +1 -1
- data/test/plugin/in_tail/test_position_file.rb +59 -5
- data/test/plugin/test_file_wrapper.rb +105 -0
- data/test/plugin/test_in_exec.rb +1 -1
- data/test/plugin/test_in_tail.rb +87 -26
- data/test/plugin/test_out_copy.rb +87 -0
- data/test/plugin/test_out_forward.rb +94 -6
- 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 +16 -2
- 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_inject.rb +29 -0
- data/test/plugin_helper/test_server.rb +26 -7
- 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 +31 -0
- data/test/test_time_parser.rb +109 -0
- metadata +36 -31
- data/.travis.yml +0 -77
- data/appveyor.yml +0 -31
data/fluentd.gemspec
CHANGED
@@ -28,12 +28,14 @@ Gem::Specification.new do |gem|
|
|
28
28
|
gem.add_runtime_dependency("tzinfo", [">= 1.0", "< 3.0"])
|
29
29
|
gem.add_runtime_dependency("tzinfo-data", ["~> 1.0"])
|
30
30
|
gem.add_runtime_dependency("strptime", [">= 0.2.2", "< 1.0.0"])
|
31
|
+
gem.add_runtime_dependency("webrick", [">= 1.4.2", "< 1.8.0"])
|
31
32
|
|
32
33
|
# build gem for a certain platform. see also Rakefile
|
33
34
|
fake_platform = ENV['GEM_BUILD_FAKE_PLATFORM'].to_s
|
34
35
|
gem.platform = fake_platform unless fake_platform.empty?
|
35
36
|
if /mswin|mingw/ =~ fake_platform || (/mswin|mingw/ =~ RUBY_PLATFORM && fake_platform.empty?)
|
36
|
-
gem.add_runtime_dependency("win32-
|
37
|
+
gem.add_runtime_dependency("win32-api", [">= 1.10", "< 2.0.0"])
|
38
|
+
gem.add_runtime_dependency("win32-service", ["~> 2.2.0"])
|
37
39
|
gem.add_runtime_dependency("win32-ipc", ["~> 0.7.0"])
|
38
40
|
gem.add_runtime_dependency("win32-event", ["~> 0.6.3"])
|
39
41
|
gem.add_runtime_dependency("windows-pr", ["~> 1.2.6"])
|
@@ -44,11 +46,10 @@ Gem::Specification.new do |gem|
|
|
44
46
|
gem.add_development_dependency("flexmock", ["~> 2.0"])
|
45
47
|
gem.add_development_dependency("parallel_tests", ["~> 0.15.3"])
|
46
48
|
gem.add_development_dependency("simplecov", ["~> 0.7"])
|
47
|
-
gem.add_development_dependency("rr", ["~>
|
49
|
+
gem.add_development_dependency("rr", ["~> 3.0"])
|
48
50
|
gem.add_development_dependency("timecop", ["~> 0.9"])
|
49
51
|
gem.add_development_dependency("test-unit", ["~> 3.3"])
|
50
52
|
gem.add_development_dependency("test-unit-rr", ["~> 1.0"])
|
51
53
|
gem.add_development_dependency("oj", [">= 2.14", "< 4"])
|
52
|
-
gem.add_development_dependency("
|
53
|
-
gem.add_development_dependency("async-http")
|
54
|
+
gem.add_development_dependency("async-http", ">= 0.50.0")
|
54
55
|
end
|
@@ -75,6 +75,8 @@ HELP
|
|
75
75
|
|
76
76
|
digest = OpenSSL::Digest::SHA256.new
|
77
77
|
|
78
|
+
factory = OpenSSL::X509::ExtensionFactory.new
|
79
|
+
|
78
80
|
cert = OpenSSL::X509::Certificate.new
|
79
81
|
cert.not_before = Time.at(0)
|
80
82
|
cert.not_after = Time.now + 5 * 365 * 86400 # 5 years after
|
@@ -82,7 +84,7 @@ HELP
|
|
82
84
|
cert.serial = 1
|
83
85
|
cert.issuer = issuer
|
84
86
|
cert.subject = subject
|
85
|
-
cert.add_extension
|
87
|
+
cert.add_extension(factory.create_extension('basicConstraints', 'CA:TRUE'))
|
86
88
|
cert.sign(key, digest)
|
87
89
|
|
88
90
|
return cert, key
|
@@ -111,8 +113,9 @@ HELP
|
|
111
113
|
cert.issuer = issuer
|
112
114
|
cert.subject = subject
|
113
115
|
|
114
|
-
|
115
|
-
|
116
|
+
factory = OpenSSL::X509::ExtensionFactory.new
|
117
|
+
server_cert.add_extension(factory.create_extension('basicConstraints', 'CA:FALSE'))
|
118
|
+
server_cert.add_extension(factory.create_extension('nsCertType', 'server'))
|
116
119
|
|
117
120
|
cert.sign ca_key, digest
|
118
121
|
|
data/lib/fluent/command/cat.rb
CHANGED
@@ -163,6 +163,10 @@ op.on('--conf-encoding ENCODING', "specify configuration file encoding") { |s|
|
|
163
163
|
opts[:conf_encoding] = s
|
164
164
|
}
|
165
165
|
|
166
|
+
op.on('--disable-shared-socket', "Don't open shared socket for multiple workers") { |b|
|
167
|
+
opts[:disable_shared_socket] = b
|
168
|
+
}
|
169
|
+
|
166
170
|
if Fluent.windows?
|
167
171
|
require 'windows/library'
|
168
172
|
include Windows::Library
|
@@ -44,6 +44,7 @@ class FluentPluginConfigFormatter
|
|
44
44
|
@verbose = false
|
45
45
|
@libs = []
|
46
46
|
@plugin_dirs = []
|
47
|
+
@table = false
|
47
48
|
@options = {}
|
48
49
|
|
49
50
|
prepare_option_parser
|
@@ -162,9 +163,20 @@ class FluentPluginConfigFormatter
|
|
162
163
|
else
|
163
164
|
sections, params = base_section.partition {|_name, value| value[:section] }
|
164
165
|
end
|
166
|
+
if @table && (not params.empty?)
|
167
|
+
dumped << "### Configuration\n\n"
|
168
|
+
dumped << "|parameter|type|description|default|\n"
|
169
|
+
dumped << "|---|---|---|---|\n"
|
170
|
+
end
|
165
171
|
params.each do |name, config|
|
166
172
|
next if name == :section
|
167
|
-
template_name = @compact
|
173
|
+
template_name = if @compact
|
174
|
+
"param.md-compact.erb"
|
175
|
+
elsif @table
|
176
|
+
"param.md-table.erb"
|
177
|
+
else
|
178
|
+
"param.md.erb"
|
179
|
+
end
|
168
180
|
template = template_path(template_name).read
|
169
181
|
dumped <<
|
170
182
|
if ERB.instance_method(:initialize).parameters.assoc(:key) # Ruby 2.6+
|
@@ -257,6 +269,9 @@ BANNER
|
|
257
269
|
@parser.on("-p", "--plugin=DIR", "Add plugin directory") do |s|
|
258
270
|
@plugin_dirs << s
|
259
271
|
end
|
272
|
+
@parser.on("-t", "--table", "Use table syntax to dump parameters") do
|
273
|
+
@table = true
|
274
|
+
end
|
260
275
|
end
|
261
276
|
|
262
277
|
def parse_options!
|
@@ -105,7 +105,7 @@ Generate a project skeleton for creating a Fluentd plugin
|
|
105
105
|
|
106
106
|
Arguments:
|
107
107
|
\ttype: #{SUPPORTED_TYPES.join(",")}
|
108
|
-
\tname: Your plugin name
|
108
|
+
\tname: Your plugin name (fluent-plugin- prefix will be added to <name>)
|
109
109
|
|
110
110
|
Options:
|
111
111
|
BANNER
|
@@ -151,6 +151,36 @@ BANNER
|
|
151
151
|
underscore_name
|
152
152
|
end
|
153
153
|
|
154
|
+
def gem_file_path
|
155
|
+
File.expand_path(File.join(File.dirname(__FILE__),
|
156
|
+
"../../../",
|
157
|
+
"Gemfile"))
|
158
|
+
end
|
159
|
+
|
160
|
+
def lock_file_path
|
161
|
+
File.expand_path(File.join(File.dirname(__FILE__),
|
162
|
+
"../../../",
|
163
|
+
"Gemfile.lock"))
|
164
|
+
end
|
165
|
+
|
166
|
+
def locked_gem_version(gem_name)
|
167
|
+
d = Bundler::Definition.build(gem_file_path, lock_file_path, false)
|
168
|
+
d.locked_gems.dependencies[gem_name].requirement.requirements.first.last.version
|
169
|
+
end
|
170
|
+
|
171
|
+
def rake_version
|
172
|
+
locked_gem_version("rake")
|
173
|
+
end
|
174
|
+
|
175
|
+
def test_unit_version
|
176
|
+
locked_gem_version("test-unit")
|
177
|
+
end
|
178
|
+
|
179
|
+
def bundler_version
|
180
|
+
d = Bundler::Definition.build(gem_file_path, lock_file_path, false)
|
181
|
+
d.locked_gems.bundler_version.version
|
182
|
+
end
|
183
|
+
|
154
184
|
def class_name
|
155
185
|
"#{capitalized_name}#{type.capitalize}"
|
156
186
|
end
|
data/lib/fluent/compat/parser.rb
CHANGED
@@ -244,10 +244,10 @@ module Fluent
|
|
244
244
|
end
|
245
245
|
|
246
246
|
def convert_value_to_nil(value)
|
247
|
-
if value
|
247
|
+
if value && @null_empty_string
|
248
248
|
value = (value == '') ? nil : value
|
249
249
|
end
|
250
|
-
if value
|
250
|
+
if value && @null_value_pattern
|
251
251
|
value = ::Fluent::StringUtil.match_regexp(@null_value_pattern, value) ? nil : value
|
252
252
|
end
|
253
253
|
value
|
@@ -179,7 +179,7 @@ module Fluent
|
|
179
179
|
end
|
180
180
|
|
181
181
|
if section_params[varname].nil?
|
182
|
-
unless proxy.defaults.has_key?(varname)
|
182
|
+
unless proxy.defaults.has_key?(varname) && proxy.defaults[varname].nil?
|
183
183
|
logger.error "config error in:\n#{conf}" if logger
|
184
184
|
raise ConfigError, "'#{name}' parameter is required but nil is specified"
|
185
185
|
end
|
@@ -247,7 +247,7 @@ module Fluent
|
|
247
247
|
def self.check_unused_section(proxy, conf, plugin_class)
|
248
248
|
elems = conf.respond_to?(:elements) ? conf.elements : []
|
249
249
|
elems.each { |e|
|
250
|
-
next if plugin_class.nil? && Fluent::Config::V1Parser::ELEM_SYMBOLS.include?(e.name) # skip pre-defined non-plugin elements because it
|
250
|
+
next if plugin_class.nil? && Fluent::Config::V1Parser::ELEM_SYMBOLS.include?(e.name) # skip pre-defined non-plugin elements because it doesn't have proxy section
|
251
251
|
next if e.unused_in && e.unused_in.empty? # the section is used at least once
|
252
252
|
|
253
253
|
if proxy.sections.any? { |name, subproxy| e.name == subproxy.name.to_s || e.name == subproxy.alias.to_s }
|
data/lib/fluent/config/types.rb
CHANGED
@@ -186,7 +186,7 @@ module Fluent
|
|
186
186
|
return nil if val.nil?
|
187
187
|
|
188
188
|
param = if val.is_a?(String)
|
189
|
-
val.start_with?('{') ? JSON.
|
189
|
+
val.start_with?('{') ? JSON.parse(val) : Hash[val.strip.split(/\s*,\s*/).map{|v| v.split(':', 2)}]
|
190
190
|
else
|
191
191
|
val
|
192
192
|
end
|
@@ -213,7 +213,7 @@ module Fluent
|
|
213
213
|
return nil if val.nil?
|
214
214
|
|
215
215
|
param = if val.is_a?(String)
|
216
|
-
val.start_with?('[') ? JSON.
|
216
|
+
val.start_with?('[') ? JSON.parse(val) : val.strip.split(/\s*,\s*/)
|
217
217
|
else
|
218
218
|
val
|
219
219
|
end
|
data/lib/fluent/event.rb
CHANGED
@@ -254,19 +254,9 @@ module Fluent
|
|
254
254
|
end
|
255
255
|
|
256
256
|
def each(unpacker: nil, &block)
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
end
|
261
|
-
else
|
262
|
-
@unpacked_times = []
|
263
|
-
@unpacked_records = []
|
264
|
-
(unpacker || Fluent::MessagePackFactory.msgpack_unpacker).feed_each(@data) do |time, record|
|
265
|
-
@unpacked_times << time
|
266
|
-
@unpacked_records << record
|
267
|
-
block.call(time, record)
|
268
|
-
end
|
269
|
-
@size = @unpacked_times.size
|
257
|
+
ensure_unpacked!(unpacker: unpacker)
|
258
|
+
@unpacked_times.each_with_index do |time, i|
|
259
|
+
block.call(time, @unpacked_records[i])
|
270
260
|
end
|
271
261
|
nil
|
272
262
|
end
|
data/lib/fluent/load.rb
CHANGED
@@ -46,6 +46,42 @@ module Fluent
|
|
46
46
|
end
|
47
47
|
end
|
48
48
|
|
49
|
+
class Win32Error < StandardError
|
50
|
+
require 'windows/error'
|
51
|
+
include Windows::Error
|
52
|
+
|
53
|
+
attr_reader :errcode, :msg
|
54
|
+
|
55
|
+
def initialize(errcode, msg = nil)
|
56
|
+
@errcode = errcode
|
57
|
+
@msg = msg
|
58
|
+
end
|
59
|
+
|
60
|
+
def format_english_message(errcode)
|
61
|
+
buf = 0.chr * 260
|
62
|
+
flags = FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ARGUMENT_ARRAY
|
63
|
+
english_lang_id = 1033 # The result of MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US)
|
64
|
+
FormatMessageA.call(flags, 0, errcode, english_lang_id, buf, buf.size, 0)
|
65
|
+
buf.force_encoding(Encoding.default_external).strip
|
66
|
+
end
|
67
|
+
|
68
|
+
def to_s
|
69
|
+
msg = super
|
70
|
+
msg << ": code: #{@errcode}, #{format_english_message(@errcode)}"
|
71
|
+
msg << " - #{@msg}" if @msg
|
72
|
+
msg
|
73
|
+
end
|
74
|
+
|
75
|
+
def inspect
|
76
|
+
"#<#{to_s}>"
|
77
|
+
end
|
78
|
+
|
79
|
+
def ==(other)
|
80
|
+
return false if other.class != Win32Error
|
81
|
+
@errcode == other.errcode && @msg == other.msg
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
49
85
|
# To open and get stat with setting FILE_SHARE_DELETE
|
50
86
|
class WindowsFile
|
51
87
|
require 'windows/file'
|
@@ -77,11 +113,11 @@ module Fluent
|
|
77
113
|
@file_handle = CreateFile.call(@path, access, sharemode,
|
78
114
|
0, creationdisposition, FILE_ATTRIBUTE_NORMAL, 0)
|
79
115
|
if @file_handle == INVALID_HANDLE_VALUE
|
80
|
-
err =
|
116
|
+
err = Win32::API.last_error
|
81
117
|
if err == ERROR_FILE_NOT_FOUND || err == ERROR_PATH_NOT_FOUND || err == ERROR_ACCESS_DENIED
|
82
|
-
raise
|
118
|
+
raise Errno::ENOENT
|
83
119
|
end
|
84
|
-
raise
|
120
|
+
raise Win32Error.new(err, path)
|
85
121
|
end
|
86
122
|
end
|
87
123
|
|
@@ -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
|
@@ -503,7 +503,7 @@ module Fluent::Plugin
|
|
503
503
|
# For every incoming request, we check if we have some CORS
|
504
504
|
# restrictions and allow listed origins through @cors_allow_origins.
|
505
505
|
unless @cors_allow_origins.nil?
|
506
|
-
unless @cors_allow_origins.include?('*')
|
506
|
+
unless @cors_allow_origins.include?('*') || include_cors_allow_origin
|
507
507
|
send_response_and_close(RES_403_STATUS, {'Connection' => 'close'}, "")
|
508
508
|
return
|
509
509
|
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]
|
@@ -313,11 +313,17 @@ module Fluent::Plugin
|
|
313
313
|
(paths - excluded).select { |path|
|
314
314
|
FileTest.exist?(path)
|
315
315
|
}.each { |path|
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
316
|
+
# Even we just checked for existence, there is a race condition here as
|
317
|
+
# of which stat() might fail with ENOENT. See #3224.
|
318
|
+
begin
|
319
|
+
target_info = TargetInfo.new(path, Fluent::FileWrapper.stat(path).ino)
|
320
|
+
if @follow_inodes
|
321
|
+
hash[target_info.ino] = target_info
|
322
|
+
else
|
323
|
+
hash[target_info.path] = target_info
|
324
|
+
end
|
325
|
+
rescue Errno::ENOENT
|
326
|
+
$log.warn "expand_paths: stat() for #{path} failed with ENOENT. Skip file."
|
321
327
|
end
|
322
328
|
}
|
323
329
|
hash
|
@@ -406,8 +412,16 @@ module Fluent::Plugin
|
|
406
412
|
log.warn "Skip #{target_info.path} because unexpected setup error happens: #{e}"
|
407
413
|
next
|
408
414
|
end
|
409
|
-
|
410
|
-
|
415
|
+
|
416
|
+
begin
|
417
|
+
target_info = TargetInfo.new(target_info.path, Fluent::FileWrapper.stat(target_info.path).ino)
|
418
|
+
@tails[target_info] = tw
|
419
|
+
rescue Errno::ENOENT
|
420
|
+
$log.warn "stat() for #{target_info.path} failed with ENOENT. Drop tail watcher for now."
|
421
|
+
# explicitly detach and unwatch watcher `tw`.
|
422
|
+
tw.unwatched = true
|
423
|
+
detach_watcher(tw, target_info.ino, false)
|
424
|
+
end
|
411
425
|
}
|
412
426
|
end
|
413
427
|
|
@@ -767,16 +781,22 @@ module Fluent::Plugin
|
|
767
781
|
end
|
768
782
|
|
769
783
|
if watcher_needs_update
|
770
|
-
|
771
|
-
|
772
|
-
|
773
|
-
|
774
|
-
if
|
775
|
-
|
784
|
+
if @follow_inodes
|
785
|
+
# No need to update a watcher if stat is nil (file not present), because moving to inodes will create
|
786
|
+
# new watcher, and old watcher will be closed by stop_watcher in refresh_watchers method
|
787
|
+
# don't want to swap state because we need latest read offset in pos file even after rotate_wait
|
788
|
+
if stat
|
789
|
+
target_info = TargetInfo.new(@path, stat)
|
776
790
|
@update_watcher.call(target_info, @pe)
|
777
|
-
else
|
778
|
-
@update_watcher.call(target_info, swap_state(@pe))
|
779
791
|
end
|
792
|
+
else
|
793
|
+
# Permit to handle if stat is nil (file not present).
|
794
|
+
# If a file is mv-ed and a new file is created during
|
795
|
+
# calling `#refresh_watchers`s, and `#refresh_watchers` won't run `#start_watchers`
|
796
|
+
# and `#stop_watchers()` for the path because `target_paths_hash`
|
797
|
+
# always contains the path.
|
798
|
+
target_info = TargetInfo.new(@path, stat ? stat.ino : nil)
|
799
|
+
@update_watcher.call(target_info, swap_state(@pe))
|
780
800
|
end
|
781
801
|
else
|
782
802
|
@log.info "detected rotation of #{@path}"
|
@@ -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
|