fluentd 1.16.4-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 +3 -3
- data/CHANGELOG.md +50 -0
- data/README.md +1 -1
- data/Rakefile +1 -1
- data/fluentd.gemspec +9 -1
- data/lib/fluent/command/binlog_reader.rb +1 -1
- data/lib/fluent/config/configure_proxy.rb +2 -2
- data/lib/fluent/config/types.rb +1 -1
- 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/buffer.rb +75 -68
- 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_http.rb +125 -13
- data/lib/fluent/plugin/owned_by_mixin.rb +0 -1
- data/lib/fluent/plugin/parser_json.rb +22 -5
- 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/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/test_buffer.rb +59 -0
- data/test/plugin/test_in_http.rb +23 -1
- data/test/plugin/test_in_tail.rb +141 -0
- 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 -0
- 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
- metadata +101 -4
@@ -203,54 +203,24 @@ module Fluent::Plugin
|
|
203
203
|
begin
|
204
204
|
path = path_info[1..-1] # remove /
|
205
205
|
tag = path.split('/').join('.')
|
206
|
-
record_time, record = parse_params(params)
|
207
206
|
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
else
|
214
|
-
if @use_204_response
|
215
|
-
return RESPONSE_204
|
216
|
-
else
|
217
|
-
return RESPONSE_200
|
218
|
-
end
|
207
|
+
mes = Fluent::MultiEventStream.new
|
208
|
+
parse_params(params) do |record_time, record|
|
209
|
+
if record.nil?
|
210
|
+
log.debug { "incoming event is invalid: path=#{path_info} params=#{params.to_json}" }
|
211
|
+
next
|
219
212
|
end
|
220
|
-
end
|
221
213
|
|
222
|
-
mes = nil
|
223
|
-
# Support batched requests
|
224
|
-
if record.is_a?(Array)
|
225
|
-
mes = Fluent::MultiEventStream.new
|
226
|
-
record.each do |single_record|
|
227
|
-
add_params_to_record(single_record, params)
|
228
|
-
|
229
|
-
if param_time = params['time']
|
230
|
-
param_time = param_time.to_f
|
231
|
-
single_time = param_time.zero? ? Fluent::EventTime.now : @float_time_parser.parse(param_time)
|
232
|
-
elsif @custom_parser
|
233
|
-
single_time = @custom_parser.parse_time(single_record)
|
234
|
-
single_time, single_record = @custom_parser.convert_values(single_time, single_record)
|
235
|
-
else
|
236
|
-
single_time = convert_time_field(single_record)
|
237
|
-
end
|
238
|
-
|
239
|
-
mes.add(single_time, single_record)
|
240
|
-
end
|
241
|
-
else
|
242
214
|
add_params_to_record(record, params)
|
243
215
|
|
244
216
|
time = if param_time = params['time']
|
245
217
|
param_time = param_time.to_f
|
246
218
|
param_time.zero? ? Fluent::EventTime.now : @float_time_parser.parse(param_time)
|
247
219
|
else
|
248
|
-
|
249
|
-
convert_time_field(record)
|
250
|
-
else
|
251
|
-
record_time
|
252
|
-
end
|
220
|
+
record_time.nil? ? convert_time_field(record) : record_time
|
253
221
|
end
|
222
|
+
|
223
|
+
mes.add(time, record)
|
254
224
|
end
|
255
225
|
rescue => e
|
256
226
|
if @dump_error_log
|
@@ -261,11 +231,7 @@ module Fluent::Plugin
|
|
261
231
|
|
262
232
|
# TODO server error
|
263
233
|
begin
|
264
|
-
|
265
|
-
router.emit_stream(tag, mes)
|
266
|
-
else
|
267
|
-
router.emit(tag, time, record)
|
268
|
-
end
|
234
|
+
router.emit_stream(tag, mes) unless mes.empty?
|
269
235
|
rescue => e
|
270
236
|
if @dump_error_log
|
271
237
|
log.error "failed to emit data", error: e
|
@@ -308,20 +274,18 @@ module Fluent::Plugin
|
|
308
274
|
def parse_params_default(params)
|
309
275
|
if msgpack = params['msgpack']
|
310
276
|
@parser_msgpack.parse(msgpack) do |_time, record|
|
311
|
-
|
277
|
+
yield nil, record
|
312
278
|
end
|
313
279
|
elsif js = params['json']
|
314
280
|
@parser_json.parse(js) do |_time, record|
|
315
|
-
|
281
|
+
yield nil, record
|
316
282
|
end
|
317
283
|
elsif ndjson = params['ndjson']
|
318
|
-
events = []
|
319
284
|
ndjson.split(/\r?\n/).each do |js|
|
320
285
|
@parser_json.parse(js) do |_time, record|
|
321
|
-
|
286
|
+
yield nil, record
|
322
287
|
end
|
323
288
|
end
|
324
|
-
return nil, events
|
325
289
|
else
|
326
290
|
raise "'json', 'ndjson' or 'msgpack' parameter is required"
|
327
291
|
end
|
@@ -329,10 +293,9 @@ module Fluent::Plugin
|
|
329
293
|
|
330
294
|
def parse_params_with_parser(params)
|
331
295
|
if content = params[EVENT_RECORD_PARAMETER]
|
332
|
-
@custom_parser.parse(content)
|
333
|
-
|
334
|
-
|
335
|
-
}
|
296
|
+
@custom_parser.parse(content) do |time, record|
|
297
|
+
yield time, record
|
298
|
+
end
|
336
299
|
else
|
337
300
|
raise "'#{EVENT_RECORD_PARAMETER}' parameter is required"
|
338
301
|
end
|
@@ -573,6 +536,8 @@ module Fluent::Plugin
|
|
573
536
|
params.update WEBrick::HTTPUtils.parse_form_data(@body, boundary)
|
574
537
|
elsif /^application\/json/.match?(@content_type)
|
575
538
|
params['json'] = @body
|
539
|
+
elsif /^application\/csp-report/.match?(@content_type)
|
540
|
+
params['json'] = @body
|
576
541
|
elsif /^application\/msgpack/.match?(@content_type)
|
577
542
|
params['msgpack'] = @body
|
578
543
|
elsif /^application\/x-ndjson/.match?(@content_type)
|
@@ -580,7 +545,7 @@ module Fluent::Plugin
|
|
580
545
|
end
|
581
546
|
path_info = uri.path
|
582
547
|
|
583
|
-
if (@add_query_params)
|
548
|
+
if (@add_query_params)
|
584
549
|
|
585
550
|
query_params = WEBrick::HTTPUtils.parse_query(uri.query)
|
586
551
|
|
@@ -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 = {}
|
@@ -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)
|
@@ -70,16 +70,33 @@ module Fluent
|
|
70
70
|
end
|
71
71
|
|
72
72
|
def parse(text)
|
73
|
-
|
74
|
-
|
75
|
-
if
|
76
|
-
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
|
77
89
|
end
|
78
|
-
|
90
|
+
|
79
91
|
rescue @error_class, EncodingError # EncodingError is for oj 3.x or later
|
80
92
|
yield nil, nil
|
81
93
|
end
|
82
94
|
|
95
|
+
def parse_one_record(record)
|
96
|
+
time = parse_time(record)
|
97
|
+
convert_values(time, record)
|
98
|
+
end
|
99
|
+
|
83
100
|
def parser_type
|
84
101
|
:text
|
85
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
@@ -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
|
data/test/plugin/test_buffer.rb
CHANGED
@@ -901,6 +901,65 @@ class BufferTest < Test::Unit::TestCase
|
|
901
901
|
|
902
902
|
assert_equal 2, purge_count
|
903
903
|
end
|
904
|
+
|
905
|
+
# https://github.com/fluent/fluentd/issues/4446
|
906
|
+
test "#write_step_by_step keeps chunks kept in locked in entire #write process" do
|
907
|
+
assert_equal 8 * 1024 * 1024, @p.chunk_limit_size
|
908
|
+
assert_equal 0.95, @p.chunk_full_threshold
|
909
|
+
|
910
|
+
mon_enter_counts_by_chunk = {}
|
911
|
+
mon_exit_counts_by_chunk = {}
|
912
|
+
|
913
|
+
stub.proxy(@p).generate_chunk(anything) do |chunk|
|
914
|
+
stub(chunk).mon_enter do
|
915
|
+
enter_count = 1 + mon_enter_counts_by_chunk.fetch(chunk, 0)
|
916
|
+
exit_count = mon_exit_counts_by_chunk.fetch(chunk, 0)
|
917
|
+
mon_enter_counts_by_chunk[chunk] = enter_count
|
918
|
+
|
919
|
+
# Assert that chunk is passed to &block of write_step_by_step before exiting the lock.
|
920
|
+
# (i.e. The lock count must be 2 greater than the exit count).
|
921
|
+
# Since ShouldRetry occurs once, the staged chunk takes the lock 3 times when calling the block.
|
922
|
+
if chunk.staged?
|
923
|
+
lock_in_block = enter_count == 3
|
924
|
+
assert_equal(enter_count - 2, exit_count) if lock_in_block
|
925
|
+
else
|
926
|
+
lock_in_block = enter_count == 2
|
927
|
+
assert_equal(enter_count - 2, exit_count) if lock_in_block
|
928
|
+
end
|
929
|
+
end
|
930
|
+
stub(chunk).mon_exit do
|
931
|
+
exit_count = 1 + mon_exit_counts_by_chunk.fetch(chunk, 0)
|
932
|
+
mon_exit_counts_by_chunk[chunk] = exit_count
|
933
|
+
end
|
934
|
+
chunk
|
935
|
+
end
|
936
|
+
|
937
|
+
m = @p.metadata(timekey: Time.parse('2016-04-11 16:40:00 +0000').to_i)
|
938
|
+
small_row = "x" * 1024 * 400
|
939
|
+
big_row = "x" * 1024 * 1024 * 8 # just `chunk_size_limit`, it does't cause BufferOverFlowError.
|
940
|
+
|
941
|
+
# Write 42 events in 1 event stream, last one is for triggering `ShouldRetry`
|
942
|
+
@p.write({m => [small_row] * 40 + [big_row] + ["x"]})
|
943
|
+
|
944
|
+
# Above event strem will be splitted twice by `Buffer#write_step_by_step`
|
945
|
+
#
|
946
|
+
# 1. `write_once`: 42 [events] * 1 [stream]
|
947
|
+
# 2. `write_step_by_step`: 4 [events]* 10 [streams] + 2 [events] * 1 [stream]
|
948
|
+
# 3. `write_step_by_step` (by `ShouldRetry`): 1 [event] * 42 [streams]
|
949
|
+
#
|
950
|
+
# Example of staged chunk lock behavior:
|
951
|
+
#
|
952
|
+
# 1. mon_enter in write_step_by_step
|
953
|
+
# 2. ShouldRetry occurs
|
954
|
+
# 3. mon_exit in write_step_by_step
|
955
|
+
# 4. mon_enter again in write_step_by_step (retry)
|
956
|
+
# 5. passed to &block of write_step_by_step
|
957
|
+
# 6. mon_enter in the block (write)
|
958
|
+
# 7. mon_exit in write_step_by_step
|
959
|
+
# 8. mon_exit in write
|
960
|
+
|
961
|
+
assert_equal(mon_enter_counts_by_chunk.values, mon_exit_counts_by_chunk.values)
|
962
|
+
end
|
904
963
|
end
|
905
964
|
|
906
965
|
sub_test_case 'standard format with configuration for test with lower chunk limit size' do
|