fluentd 1.16.6-x64-mingw32 → 1.17.0-x64-mingw32
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/DISCUSSION_TEMPLATE/q-a-japanese.yml +50 -0
- data/.github/DISCUSSION_TEMPLATE/q-a.yml +47 -0
- data/.github/workflows/test-ruby-head.yml +31 -0
- data/.github/workflows/test.yml +2 -8
- data/CHANGELOG.md +36 -12
- data/README.md +1 -1
- data/Rakefile +1 -1
- data/fluentd.gemspec +10 -5
- data/lib/fluent/command/binlog_reader.rb +1 -1
- data/lib/fluent/command/fluentd.rb +1 -1
- data/lib/fluent/config/configure_proxy.rb +2 -2
- data/lib/fluent/config/types.rb +1 -1
- data/lib/fluent/config/yaml_parser/parser.rb +0 -4
- data/lib/fluent/configurable.rb +2 -2
- data/lib/fluent/counter/mutex_hash.rb +1 -1
- data/lib/fluent/fluent_log_event_router.rb +0 -2
- data/lib/fluent/plugin/buf_file.rb +1 -1
- data/lib/fluent/plugin/buffer/file_chunk.rb +1 -1
- data/lib/fluent/plugin/buffer/file_single_chunk.rb +2 -3
- data/lib/fluent/plugin/filter_parser.rb +26 -8
- data/lib/fluent/plugin/in_http.rb +18 -53
- data/lib/fluent/plugin/in_tail.rb +34 -2
- data/lib/fluent/plugin/out_file.rb +0 -8
- data/lib/fluent/plugin/out_http.rb +125 -13
- data/lib/fluent/plugin/owned_by_mixin.rb +0 -1
- data/lib/fluent/plugin/parser_json.rb +34 -9
- data/lib/fluent/plugin/parser_msgpack.rb +24 -3
- data/lib/fluent/plugin_helper/metrics.rb +2 -2
- data/lib/fluent/registry.rb +6 -6
- data/lib/fluent/test/output_test.rb +1 -1
- data/lib/fluent/unique_id.rb +1 -1
- data/lib/fluent/version.rb +1 -1
- data/test/command/test_fluentd.rb +9 -56
- data/test/log/test_console_adapter.rb +10 -3
- data/test/plugin/data/log_numeric/01.log +0 -0
- data/test/plugin/data/log_numeric/02.log +0 -0
- data/test/plugin/data/log_numeric/12.log +0 -0
- data/test/plugin/data/log_numeric/14.log +0 -0
- data/test/plugin/in_tail/test_io_handler.rb +14 -13
- data/test/plugin/in_tail/test_position_file.rb +7 -6
- data/test/plugin/test_in_http.rb +23 -1
- data/test/plugin/test_in_tail.rb +141 -0
- data/test/plugin/test_out_file.rb +1 -21
- data/test/plugin/test_out_http.rb +128 -0
- data/test/plugin/test_owned_by.rb +0 -1
- data/test/plugin/test_parser_json.rb +106 -31
- data/test/plugin/test_parser_msgpack.rb +127 -0
- data/test/plugin/test_storage.rb +0 -1
- data/test/plugin_helper/test_child_process.rb +4 -4
- data/test/test_config.rb +0 -6
- metadata +99 -16
@@ -65,6 +65,8 @@ module Fluent::Plugin
|
|
65
65
|
config_param :path, :string
|
66
66
|
desc 'path delimiter used for spliting path config'
|
67
67
|
config_param :path_delimiter, :string, default: ','
|
68
|
+
desc 'Choose using glob patterns. Adding capabilities to handle [] and ?, and {}.'
|
69
|
+
config_param :glob_policy, :enum, list: [:backward_compatible, :extended, :always], default: :backward_compatible
|
68
70
|
desc 'The tag of the event.'
|
69
71
|
config_param :tag, :string
|
70
72
|
desc 'The paths to exclude the files from watcher list.'
|
@@ -141,6 +143,14 @@ module Fluent::Plugin
|
|
141
143
|
raise Fluent::ConfigError, "either of enable_watch_timer or enable_stat_watcher must be true"
|
142
144
|
end
|
143
145
|
|
146
|
+
if @glob_policy == :always && @path_delimiter == ','
|
147
|
+
raise Fluent::ConfigError, "cannot use glob_policy as always with the default path_delimitor: `,\""
|
148
|
+
end
|
149
|
+
|
150
|
+
if @glob_policy == :extended && /\{.*,.*\}/.match(@path) && extended_glob_pattern(@path)
|
151
|
+
raise Fluent::ConfigError, "cannot include curly braces with glob patterns in `#{@path}\". Use glob_policy always instead."
|
152
|
+
end
|
153
|
+
|
144
154
|
if RESERVED_CHARS.include?(@path_delimiter)
|
145
155
|
rc = RESERVED_CHARS.join(', ')
|
146
156
|
raise Fluent::ConfigError, "#{rc} are reserved words: #{@path_delimiter}"
|
@@ -288,6 +298,28 @@ module Fluent::Plugin
|
|
288
298
|
@capability.have_capability?(:effective, :dac_override)
|
289
299
|
end
|
290
300
|
|
301
|
+
def extended_glob_pattern(path)
|
302
|
+
path.include?('*') || path.include?('?') || /\[.*\]/.match(path)
|
303
|
+
end
|
304
|
+
|
305
|
+
# Curly braces is not supported with default path_delimiter
|
306
|
+
# because the default delimiter of path is ",".
|
307
|
+
# This should be collided for wildcard pattern for curly braces and
|
308
|
+
# be handled as an error on #configure.
|
309
|
+
def use_glob?(path)
|
310
|
+
if @glob_policy == :always
|
311
|
+
# For future extensions, we decided to use `always' term to handle
|
312
|
+
# regular expressions as much as possible.
|
313
|
+
# This is because not using `true' as a returning value
|
314
|
+
# when choosing :always here.
|
315
|
+
extended_glob_pattern(path) || /\{.*,.*\}/.match(path)
|
316
|
+
elsif @glob_policy == :extended
|
317
|
+
extended_glob_pattern(path)
|
318
|
+
elsif @glob_policy == :backward_compatible
|
319
|
+
path.include?('*')
|
320
|
+
end
|
321
|
+
end
|
322
|
+
|
291
323
|
def expand_paths
|
292
324
|
date = Fluent::EventTime.now
|
293
325
|
paths = []
|
@@ -297,7 +329,7 @@ module Fluent::Plugin
|
|
297
329
|
else
|
298
330
|
date.to_time.strftime(path)
|
299
331
|
end
|
300
|
-
if
|
332
|
+
if use_glob?(path)
|
301
333
|
paths += Dir.glob(path).select { |p|
|
302
334
|
begin
|
303
335
|
is_file = !File.directory?(p)
|
@@ -332,7 +364,7 @@ module Fluent::Plugin
|
|
332
364
|
else
|
333
365
|
date.to_time.strftime(path)
|
334
366
|
end
|
335
|
-
|
367
|
+
use_glob?(path) ? Dir.glob(path) : path
|
336
368
|
}.flatten.uniq
|
337
369
|
# filter out non existing files, so in case pattern is without '*' we don't do unnecessary work
|
338
370
|
hash = {}
|
@@ -172,14 +172,6 @@ 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
|
-
|
183
175
|
@buffer.extend SymlinkBufferMixin
|
184
176
|
@buffer.symlink_path = @symlink_path
|
185
177
|
@buffer.output_plugin_for_symlink = self
|
@@ -37,6 +37,8 @@ module Fluent::Plugin
|
|
37
37
|
|
38
38
|
class RetryableResponse < StandardError; end
|
39
39
|
|
40
|
+
ConnectionCache = Struct.new(:uri, :conn)
|
41
|
+
|
40
42
|
helpers :formatter
|
41
43
|
|
42
44
|
desc 'The endpoint for HTTP request, e.g. http://example.com/api'
|
@@ -60,6 +62,8 @@ module Fluent::Plugin
|
|
60
62
|
config_param :read_timeout, :integer, default: nil
|
61
63
|
desc 'The TLS timeout in seconds'
|
62
64
|
config_param :ssl_timeout, :integer, default: nil
|
65
|
+
desc 'Try to reuse connections'
|
66
|
+
config_param :reuse_connections, :bool, default: false
|
63
67
|
|
64
68
|
desc 'The CA certificate path for TLS'
|
65
69
|
config_param :tls_ca_cert_path, :string, default: nil
|
@@ -87,11 +91,29 @@ module Fluent::Plugin
|
|
87
91
|
|
88
92
|
config_section :auth, required: false, multi: false do
|
89
93
|
desc 'The method for HTTP authentication'
|
90
|
-
config_param :method, :enum, list: [:basic], default: :basic
|
94
|
+
config_param :method, :enum, list: [:basic, :aws_sigv4], default: :basic
|
91
95
|
desc 'The username for basic authentication'
|
92
96
|
config_param :username, :string, default: nil
|
93
97
|
desc 'The password for basic authentication'
|
94
98
|
config_param :password, :string, default: nil, secret: true
|
99
|
+
desc 'The AWS service to authenticate against'
|
100
|
+
config_param :aws_service, :string, default: nil
|
101
|
+
desc 'The AWS region to use when authenticating'
|
102
|
+
config_param :aws_region, :string, default: nil
|
103
|
+
desc 'The AWS role ARN to assume when authenticating'
|
104
|
+
config_param :aws_role_arn, :string, default: nil
|
105
|
+
end
|
106
|
+
|
107
|
+
def connection_cache_id_thread_key
|
108
|
+
"#{plugin_id}_connection_cache_id"
|
109
|
+
end
|
110
|
+
|
111
|
+
def connection_cache_id_for_thread
|
112
|
+
Thread.current[connection_cache_id_thread_key]
|
113
|
+
end
|
114
|
+
|
115
|
+
def connection_cache_id_for_thread=(id)
|
116
|
+
Thread.current[connection_cache_id_thread_key] = id
|
95
117
|
end
|
96
118
|
|
97
119
|
def initialize
|
@@ -100,11 +122,23 @@ module Fluent::Plugin
|
|
100
122
|
@uri = nil
|
101
123
|
@proxy_uri = nil
|
102
124
|
@formatter = nil
|
125
|
+
|
126
|
+
@connection_cache = []
|
127
|
+
@connection_cache_id_mutex = Mutex.new
|
128
|
+
@connection_cache_next_id = 0
|
129
|
+
end
|
130
|
+
|
131
|
+
def close
|
132
|
+
super
|
133
|
+
|
134
|
+
@connection_cache.each {|entry| entry.conn.finish if entry.conn&.started? }
|
103
135
|
end
|
104
136
|
|
105
137
|
def configure(conf)
|
106
138
|
super
|
107
139
|
|
140
|
+
@connection_cache = Array.new(actual_flush_thread_count, ConnectionCache.new("", nil)) if @reuse_connections
|
141
|
+
|
108
142
|
if @retryable_response_codes.nil?
|
109
143
|
log.warn('Status code 503 is going to be removed from default `retryable_response_codes` from fluentd v2. Please add it by yourself if you wish')
|
110
144
|
@retryable_response_codes = [503]
|
@@ -121,6 +155,36 @@ module Fluent::Plugin
|
|
121
155
|
end
|
122
156
|
define_singleton_method(:format, method(:format_json_array))
|
123
157
|
end
|
158
|
+
|
159
|
+
if @auth and @auth.method == :aws_sigv4
|
160
|
+
begin
|
161
|
+
require 'aws-sigv4'
|
162
|
+
require 'aws-sdk-core'
|
163
|
+
rescue LoadError
|
164
|
+
raise Fluent::ConfigError, "The aws-sdk-core and aws-sigv4 gems are required for aws_sigv4 auth. Run: gem install aws-sdk-core -v '~> 3.191'"
|
165
|
+
end
|
166
|
+
|
167
|
+
raise Fluent::ConfigError, "aws_service is required for aws_sigv4 auth" unless @auth.aws_service != nil
|
168
|
+
raise Fluent::ConfigError, "aws_region is required for aws_sigv4 auth" unless @auth.aws_region != nil
|
169
|
+
|
170
|
+
if @auth.aws_role_arn == nil
|
171
|
+
aws_credentials = Aws::CredentialProviderChain.new.resolve
|
172
|
+
else
|
173
|
+
aws_credentials = Aws::AssumeRoleCredentials.new(
|
174
|
+
client: Aws::STS::Client.new(
|
175
|
+
region: @auth.aws_region
|
176
|
+
),
|
177
|
+
role_arn: @auth.aws_role_arn,
|
178
|
+
role_session_name: "fluentd"
|
179
|
+
)
|
180
|
+
end
|
181
|
+
|
182
|
+
@aws_signer = Aws::Sigv4::Signer.new(
|
183
|
+
service: @auth.aws_service,
|
184
|
+
region: @auth.aws_region,
|
185
|
+
credentials_provider: aws_credentials
|
186
|
+
)
|
187
|
+
end
|
124
188
|
end
|
125
189
|
|
126
190
|
def multi_workers_ready?
|
@@ -215,7 +279,7 @@ module Fluent::Plugin
|
|
215
279
|
URI.parse(endpoint)
|
216
280
|
end
|
217
281
|
|
218
|
-
def set_headers(req, chunk)
|
282
|
+
def set_headers(req, uri, chunk)
|
219
283
|
if @headers
|
220
284
|
@headers.each do |k, v|
|
221
285
|
req[k] = v
|
@@ -229,6 +293,28 @@ module Fluent::Plugin
|
|
229
293
|
req['Content-Type'] = @content_type
|
230
294
|
end
|
231
295
|
|
296
|
+
def set_auth(req, uri)
|
297
|
+
return unless @auth
|
298
|
+
|
299
|
+
if @auth.method == :basic
|
300
|
+
req.basic_auth(@auth.username, @auth.password)
|
301
|
+
elsif @auth.method == :aws_sigv4
|
302
|
+
signature = @aws_signer.sign_request(
|
303
|
+
http_method: req.method,
|
304
|
+
url: uri.request_uri,
|
305
|
+
headers: {
|
306
|
+
'Content-Type' => @content_type,
|
307
|
+
'Host' => uri.host
|
308
|
+
},
|
309
|
+
body: req.body
|
310
|
+
)
|
311
|
+
req.add_field('x-amz-date', signature.headers['x-amz-date'])
|
312
|
+
req.add_field('x-amz-security-token', signature.headers['x-amz-security-token'])
|
313
|
+
req.add_field('x-amz-content-sha256', signature.headers['x-amz-content-sha256'])
|
314
|
+
req.add_field('authorization', signature.headers['authorization'])
|
315
|
+
end
|
316
|
+
end
|
317
|
+
|
232
318
|
def create_request(chunk, uri)
|
233
319
|
req = case @http_method
|
234
320
|
when :post
|
@@ -236,23 +322,49 @@ module Fluent::Plugin
|
|
236
322
|
when :put
|
237
323
|
Net::HTTP::Put.new(uri.request_uri)
|
238
324
|
end
|
239
|
-
|
240
|
-
req.basic_auth(@auth.username, @auth.password)
|
241
|
-
end
|
242
|
-
set_headers(req, chunk)
|
325
|
+
set_headers(req, uri, chunk)
|
243
326
|
req.body = @json_array ? "[#{chunk.read.chop}]" : chunk.read
|
327
|
+
|
328
|
+
# At least one authentication method requires the body and other headers, so the order of this call matters
|
329
|
+
set_auth(req, uri)
|
244
330
|
req
|
245
331
|
end
|
246
332
|
|
333
|
+
def make_request_cached(uri, req)
|
334
|
+
id = self.connection_cache_id_for_thread
|
335
|
+
if id.nil?
|
336
|
+
@connection_cache_id_mutex.synchronize {
|
337
|
+
id = @connection_cache_next_id
|
338
|
+
@connection_cache_next_id += 1
|
339
|
+
}
|
340
|
+
self.connection_cache_id_for_thread = id
|
341
|
+
end
|
342
|
+
uri_str = uri.to_s
|
343
|
+
if @connection_cache[id].uri != uri_str
|
344
|
+
@connection_cache[id].conn.finish if @connection_cache[id].conn&.started?
|
345
|
+
http = if @proxy_uri
|
346
|
+
Net::HTTP.start(uri.host, uri.port, @proxy_uri.host, @proxy_uri.port, @proxy_uri.user, @proxy_uri.password, @http_opt)
|
347
|
+
else
|
348
|
+
Net::HTTP.start(uri.host, uri.port, @http_opt)
|
349
|
+
end
|
350
|
+
@connection_cache[id] = ConnectionCache.new(uri_str, http)
|
351
|
+
end
|
352
|
+
@connection_cache[id].conn.request(req)
|
353
|
+
end
|
354
|
+
|
355
|
+
def make_request(uri, req, &block)
|
356
|
+
if @proxy_uri
|
357
|
+
Net::HTTP.start(uri.host, uri.port, @proxy_uri.host, @proxy_uri.port, @proxy_uri.user, @proxy_uri.password, @http_opt, &block)
|
358
|
+
else
|
359
|
+
Net::HTTP.start(uri.host, uri.port, @http_opt, &block)
|
360
|
+
end
|
361
|
+
end
|
362
|
+
|
247
363
|
def send_request(uri, req)
|
248
|
-
res = if @
|
249
|
-
|
250
|
-
http.request(req)
|
251
|
-
}
|
364
|
+
res = if @reuse_connections
|
365
|
+
make_request_cached(uri, req)
|
252
366
|
else
|
253
|
-
|
254
|
-
http.request(req)
|
255
|
-
}
|
367
|
+
make_request(uri, req) { |http| http.request(req) }
|
256
368
|
end
|
257
369
|
|
258
370
|
if res.is_a?(Net::HTTPSuccess)
|
@@ -50,28 +50,53 @@ module Fluent
|
|
50
50
|
def configure_json_parser(name)
|
51
51
|
case name
|
52
52
|
when :oj
|
53
|
-
|
54
|
-
|
55
|
-
log&.info "Oj is not installed, and failing back to Yajl for json parser"
|
56
|
-
configure_json_parser(:yajl)
|
53
|
+
raise LoadError unless Fluent::OjOptions.available?
|
54
|
+
[Oj.method(:load), Oj::ParseError]
|
57
55
|
when :json then [JSON.method(:load), JSON::ParserError]
|
58
56
|
when :yajl then [Yajl.method(:load), Yajl::ParseError]
|
59
57
|
else
|
60
58
|
raise "BUG: unknown json parser specified: #{name}"
|
61
59
|
end
|
60
|
+
rescue LoadError => ex
|
61
|
+
name = :yajl
|
62
|
+
if log
|
63
|
+
if /\boj\z/.match?(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
|
62
70
|
end
|
63
71
|
|
64
72
|
def parse(text)
|
65
|
-
|
66
|
-
|
67
|
-
if
|
68
|
-
time, record =
|
73
|
+
parsed_json = @load_proc.call(text)
|
74
|
+
|
75
|
+
if parsed_json.is_a?(Hash)
|
76
|
+
time, record = parse_one_record(parsed_json)
|
77
|
+
yield time, record
|
78
|
+
elsif parsed_json.is_a?(Array)
|
79
|
+
parsed_json.each do |record|
|
80
|
+
unless record.is_a?(Hash)
|
81
|
+
yield nil, nil
|
82
|
+
next
|
83
|
+
end
|
84
|
+
time, parsed_record = parse_one_record(record)
|
85
|
+
yield time, parsed_record
|
86
|
+
end
|
87
|
+
else
|
88
|
+
yield nil, nil
|
69
89
|
end
|
70
|
-
|
90
|
+
|
71
91
|
rescue @error_class, EncodingError # EncodingError is for oj 3.x or later
|
72
92
|
yield nil, nil
|
73
93
|
end
|
74
94
|
|
95
|
+
def parse_one_record(record)
|
96
|
+
time = parse_time(record)
|
97
|
+
convert_values(time, record)
|
98
|
+
end
|
99
|
+
|
75
100
|
def parser_type
|
76
101
|
:text
|
77
102
|
end
|
@@ -31,9 +31,9 @@ module Fluent
|
|
31
31
|
:binary
|
32
32
|
end
|
33
33
|
|
34
|
-
def parse(data)
|
34
|
+
def parse(data, &block)
|
35
35
|
@unpacker.feed_each(data) do |obj|
|
36
|
-
|
36
|
+
parse_unpacked_data(obj, &block)
|
37
37
|
end
|
38
38
|
end
|
39
39
|
alias parse_partial_data parse
|
@@ -41,8 +41,29 @@ module Fluent
|
|
41
41
|
def parse_io(io, &block)
|
42
42
|
u = Fluent::MessagePackFactory.engine_factory.unpacker(io)
|
43
43
|
u.each do |obj|
|
44
|
-
|
44
|
+
parse_unpacked_data(obj, &block)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def parse_unpacked_data(data)
|
49
|
+
if data.is_a?(Hash)
|
50
|
+
time, record = convert_values(parse_time(data), data)
|
45
51
|
yield time, record
|
52
|
+
return
|
53
|
+
end
|
54
|
+
|
55
|
+
unless data.is_a?(Array)
|
56
|
+
yield nil, nil
|
57
|
+
return
|
58
|
+
end
|
59
|
+
|
60
|
+
data.each do |record|
|
61
|
+
unless record.is_a?(Hash)
|
62
|
+
yield nil, nil
|
63
|
+
next
|
64
|
+
end
|
65
|
+
time, converted_record = convert_values(parse_time(record), record)
|
66
|
+
yield time, converted_record
|
46
67
|
end
|
47
68
|
end
|
48
69
|
end
|
@@ -65,9 +65,9 @@ module Fluent
|
|
65
65
|
metrics.configure(config)
|
66
66
|
# For multi workers environment, cmetrics should be distinguish with static labels.
|
67
67
|
if Fluent::Engine.system_config.workers > 1
|
68
|
-
labels
|
68
|
+
labels[:worker_id] = fluentd_worker_id.to_s
|
69
69
|
end
|
70
|
-
labels
|
70
|
+
labels[:plugin] = @plugin_type_or_id
|
71
71
|
metrics.create(namespace: namespace, subsystem: subsystem, name: name, help_text: help_text, labels: labels)
|
72
72
|
|
73
73
|
@_metrics["#{@plugin_type_or_id}_#{namespace}_#{subsystem}_#{name}"] = metrics
|
data/lib/fluent/registry.rb
CHANGED
@@ -60,13 +60,13 @@ module Fluent
|
|
60
60
|
# search from additional plugin directories
|
61
61
|
if @dir_search_prefix
|
62
62
|
path = "#{@dir_search_prefix}#{type}"
|
63
|
-
files = @paths.
|
63
|
+
files = @paths.filter_map { |lp|
|
64
64
|
lpath = File.expand_path(File.join(lp, "#{path}.rb"))
|
65
65
|
File.exist?(lpath) ? lpath : nil
|
66
|
-
}
|
66
|
+
}
|
67
67
|
unless files.empty?
|
68
68
|
# prefer newer version
|
69
|
-
require files.
|
69
|
+
require files.max
|
70
70
|
return
|
71
71
|
end
|
72
72
|
end
|
@@ -74,17 +74,17 @@ module Fluent
|
|
74
74
|
path = "#{@search_prefix}#{type}"
|
75
75
|
|
76
76
|
# prefer LOAD_PATH than gems
|
77
|
-
files = $LOAD_PATH.
|
77
|
+
files = $LOAD_PATH.filter_map { |lp|
|
78
78
|
if lp == FLUENT_LIB_PATH
|
79
79
|
nil
|
80
80
|
else
|
81
81
|
lpath = File.expand_path(File.join(lp, "#{path}.rb"))
|
82
82
|
File.exist?(lpath) ? lpath : nil
|
83
83
|
end
|
84
|
-
}
|
84
|
+
}
|
85
85
|
unless files.empty?
|
86
86
|
# prefer newer version
|
87
|
-
require files.
|
87
|
+
require files.max
|
88
88
|
return
|
89
89
|
end
|
90
90
|
|
data/lib/fluent/unique_id.rb
CHANGED
data/lib/fluent/version.rb
CHANGED
@@ -128,14 +128,11 @@ class TestFluentdCommand < ::Test::Unit::TestCase
|
|
128
128
|
|
129
129
|
# ATTENTION: This stops taking logs when all `pattern_list` match or timeout,
|
130
130
|
# so `patterns_not_match` can test only logs up to that point.
|
131
|
-
# You can pass a block to assert something after log matching.
|
132
131
|
def assert_log_matches(cmdline, *pattern_list, patterns_not_match: [], timeout: 20, env: {})
|
133
132
|
matched = false
|
134
133
|
matched_wrongly = false
|
135
|
-
|
134
|
+
assert_error_msg = ""
|
136
135
|
stdio_buf = ""
|
137
|
-
succeeded_block = true
|
138
|
-
error_msg_block = ""
|
139
136
|
begin
|
140
137
|
execute_command(cmdline, @tmp_dir, env) do |pid, stdout|
|
141
138
|
begin
|
@@ -166,13 +163,6 @@ class TestFluentdCommand < ::Test::Unit::TestCase
|
|
166
163
|
end
|
167
164
|
end
|
168
165
|
end
|
169
|
-
|
170
|
-
begin
|
171
|
-
yield if block_given?
|
172
|
-
rescue => e
|
173
|
-
succeeded_block = false
|
174
|
-
error_msg_block = "failed block execution after matching: #{e}"
|
175
|
-
end
|
176
166
|
ensure
|
177
167
|
if SUPERVISOR_PID_PATTERN =~ stdio_buf
|
178
168
|
@supervisor_pid = $1.to_i
|
@@ -183,19 +173,19 @@ class TestFluentdCommand < ::Test::Unit::TestCase
|
|
183
173
|
end
|
184
174
|
end
|
185
175
|
rescue Timeout::Error
|
186
|
-
|
176
|
+
assert_error_msg = "execution timeout"
|
187
177
|
# https://github.com/fluent/fluentd/issues/4095
|
188
178
|
# On Windows, timeout without `@supervisor_pid` means that the test is invalid,
|
189
179
|
# since the supervisor process will survive without being killed correctly.
|
190
180
|
flunk("Invalid test: The pid of supervisor could not be taken, which is necessary on Windows.") if Fluent.windows? && @supervisor_pid.nil?
|
191
181
|
rescue => e
|
192
|
-
|
182
|
+
assert_error_msg = "unexpected error in launching fluentd: #{e.inspect}"
|
193
183
|
else
|
194
|
-
|
184
|
+
assert_error_msg = "log doesn't match" unless matched
|
195
185
|
end
|
196
186
|
|
197
187
|
if patterns_not_match.empty?
|
198
|
-
|
188
|
+
assert_error_msg = build_message(assert_error_msg,
|
199
189
|
"<?>\nwas expected to include:\n<?>",
|
200
190
|
stdio_buf, pattern_list)
|
201
191
|
else
|
@@ -207,17 +197,16 @@ class TestFluentdCommand < ::Test::Unit::TestCase
|
|
207
197
|
lines.any?{|line| line.include?(ptn) }
|
208
198
|
end
|
209
199
|
if matched_wrongly
|
210
|
-
|
211
|
-
|
200
|
+
assert_error_msg << "\n" unless assert_error_msg.empty?
|
201
|
+
assert_error_msg << "pattern exists in logs wrongly: #{ptn}"
|
212
202
|
end
|
213
203
|
end
|
214
|
-
|
204
|
+
assert_error_msg = build_message(assert_error_msg,
|
215
205
|
"<?>\nwas expected to include:\n<?>\nand not include:\n<?>",
|
216
206
|
stdio_buf, pattern_list, patterns_not_match)
|
217
207
|
end
|
218
208
|
|
219
|
-
assert matched && !matched_wrongly,
|
220
|
-
assert succeeded_block, error_msg_block if block_given?
|
209
|
+
assert matched && !matched_wrongly, assert_error_msg
|
221
210
|
end
|
222
211
|
|
223
212
|
def assert_fluentd_fails_to_start(cmdline, *pattern_list, timeout: 20)
|
@@ -1299,40 +1288,4 @@ CONF
|
|
1299
1288
|
"[debug]")
|
1300
1289
|
end
|
1301
1290
|
end
|
1302
|
-
|
1303
|
-
sub_test_case "plugin option" do
|
1304
|
-
test "should be the default value when not specifying" do
|
1305
|
-
conf_path = create_conf_file('test.conf', <<~CONF)
|
1306
|
-
<source>
|
1307
|
-
@type monitor_agent
|
1308
|
-
</source>
|
1309
|
-
CONF
|
1310
|
-
assert File.exist?(conf_path)
|
1311
|
-
cmdline = create_cmdline(conf_path)
|
1312
|
-
|
1313
|
-
assert_log_matches(cmdline, "fluentd worker is now running") do
|
1314
|
-
response = Net::HTTP.get(URI.parse("http://localhost:24220/api/config.json"))
|
1315
|
-
actual_conf = JSON.parse(response)
|
1316
|
-
assert_equal Fluent::Supervisor.default_options[:plugin_dirs], actual_conf["plugin_dirs"]
|
1317
|
-
end
|
1318
|
-
end
|
1319
|
-
|
1320
|
-
data(short: "-p")
|
1321
|
-
data(long: "--plugin")
|
1322
|
-
test "can be added by specifying the option" do |option_name|
|
1323
|
-
conf_path = create_conf_file('test.conf', <<~CONF)
|
1324
|
-
<source>
|
1325
|
-
@type monitor_agent
|
1326
|
-
</source>
|
1327
|
-
CONF
|
1328
|
-
assert File.exist?(conf_path)
|
1329
|
-
cmdline = create_cmdline(conf_path, option_name, @tmp_dir, option_name, @tmp_dir)
|
1330
|
-
|
1331
|
-
assert_log_matches(cmdline, "fluentd worker is now running") do
|
1332
|
-
response = Net::HTTP.get(URI.parse("http://localhost:24220/api/config.json"))
|
1333
|
-
actual_conf = JSON.parse(response)
|
1334
|
-
assert_equal Fluent::Supervisor.default_options[:plugin_dirs] + [@tmp_dir, @tmp_dir], actual_conf["plugin_dirs"]
|
1335
|
-
end
|
1336
|
-
end
|
1337
|
-
end
|
1338
1291
|
end
|
@@ -72,11 +72,18 @@ class ConsoleAdapterTest < Test::Unit::TestCase
|
|
72
72
|
fatal: :fatal)
|
73
73
|
def test_options(level)
|
74
74
|
@console_logger.send(level, "subject", kwarg1: "opt1", kwarg2: "opt2")
|
75
|
+
lines = @logdev.logs[0].split("\n")
|
76
|
+
args = JSON.load(lines[1..].collect { |str| str.sub(/\s+\|/, "") }.join("\n"));
|
75
77
|
assert_equal([
|
76
|
-
|
77
|
-
"
|
78
|
+
1,
|
79
|
+
"#{@timestamp_str} [#{level}]: 0.0s: subject",
|
80
|
+
{ "kwarg1" => "opt1", "kwarg2" => "opt2" }
|
78
81
|
],
|
79
|
-
|
82
|
+
[
|
83
|
+
@logdev.logs.size,
|
84
|
+
lines[0],
|
85
|
+
args
|
86
|
+
])
|
80
87
|
end
|
81
88
|
|
82
89
|
data(debug: :debug,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
@@ -5,19 +5,20 @@ require 'fluent/plugin/metrics_local'
|
|
5
5
|
require 'tempfile'
|
6
6
|
|
7
7
|
class IntailIOHandlerTest < Test::Unit::TestCase
|
8
|
-
|
9
|
-
Tempfile.
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
8
|
+
setup do
|
9
|
+
@file = Tempfile.new('intail_io_handler').binmode
|
10
|
+
opened_file_metrics = Fluent::Plugin::LocalMetrics.new
|
11
|
+
opened_file_metrics.configure(config_element('metrics', '', {}))
|
12
|
+
closed_file_metrics = Fluent::Plugin::LocalMetrics.new
|
13
|
+
closed_file_metrics.configure(config_element('metrics', '', {}))
|
14
|
+
rotated_file_metrics = Fluent::Plugin::LocalMetrics.new
|
15
|
+
rotated_file_metrics.configure(config_element('metrics', '', {}))
|
16
|
+
@metrics = Fluent::Plugin::TailInput::MetricsInfo.new(opened_file_metrics, closed_file_metrics, rotated_file_metrics)
|
17
|
+
end
|
18
|
+
|
19
|
+
teardown do
|
20
|
+
@file.close rescue nil
|
21
|
+
@file.unlink rescue nil
|
21
22
|
end
|
22
23
|
|
23
24
|
def create_target_info
|