fluentd 1.16.4-x64-mingw32 → 1.17.0-x64-mingw32
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.
- 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
|