fluentd 1.13.3 → 1.16.5
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/ISSUE_TEMPLATE/{bug_report.yaml → bug_report.yml} +2 -0
- data/.github/ISSUE_TEMPLATE/config.yml +2 -2
- data/.github/ISSUE_TEMPLATE/{feature_request.yaml → feature_request.yml} +1 -0
- data/.github/workflows/stale-actions.yml +11 -9
- data/.github/workflows/test.yml +32 -0
- data/CHANGELOG.md +490 -10
- data/CONTRIBUTING.md +2 -2
- data/MAINTAINERS.md +7 -5
- data/README.md +3 -23
- data/Rakefile +1 -1
- data/SECURITY.md +14 -0
- data/fluentd.gemspec +7 -8
- data/lib/fluent/command/cat.rb +13 -3
- data/lib/fluent/command/ctl.rb +6 -3
- data/lib/fluent/command/fluentd.rb +73 -65
- data/lib/fluent/command/plugin_config_formatter.rb +1 -1
- data/lib/fluent/compat/output.rb +9 -6
- data/lib/fluent/config/dsl.rb +1 -1
- data/lib/fluent/config/error.rb +12 -0
- data/lib/fluent/config/literal_parser.rb +2 -2
- data/lib/fluent/config/parser.rb +1 -1
- data/lib/fluent/config/v1_parser.rb +3 -3
- data/lib/fluent/config/yaml_parser/fluent_value.rb +47 -0
- data/lib/fluent/config/yaml_parser/loader.rb +108 -0
- data/lib/fluent/config/yaml_parser/parser.rb +166 -0
- data/lib/fluent/config/yaml_parser/section_builder.rb +107 -0
- data/lib/fluent/config/yaml_parser.rb +56 -0
- data/lib/fluent/config.rb +14 -1
- data/lib/fluent/counter/server.rb +1 -1
- data/lib/fluent/counter/validator.rb +3 -3
- data/lib/fluent/daemon.rb +2 -4
- data/lib/fluent/engine.rb +1 -1
- data/lib/fluent/env.rb +4 -0
- data/lib/fluent/error.rb +3 -0
- data/lib/fluent/event.rb +8 -4
- data/lib/fluent/event_router.rb +47 -2
- data/lib/fluent/file_wrapper.rb +137 -0
- data/lib/fluent/log/console_adapter.rb +66 -0
- data/lib/fluent/log.rb +44 -5
- data/lib/fluent/match.rb +1 -1
- data/lib/fluent/msgpack_factory.rb +6 -1
- data/lib/fluent/oj_options.rb +1 -2
- data/lib/fluent/plugin/bare_output.rb +49 -8
- data/lib/fluent/plugin/base.rb +26 -9
- data/lib/fluent/plugin/buf_file.rb +34 -5
- data/lib/fluent/plugin/buf_file_single.rb +32 -3
- data/lib/fluent/plugin/buffer/file_chunk.rb +1 -1
- data/lib/fluent/plugin/buffer.rb +216 -70
- data/lib/fluent/plugin/filter.rb +35 -1
- data/lib/fluent/plugin/filter_record_transformer.rb +1 -1
- data/lib/fluent/plugin/in_forward.rb +2 -2
- data/lib/fluent/plugin/in_http.rb +39 -10
- data/lib/fluent/plugin/in_monitor_agent.rb +4 -2
- data/lib/fluent/plugin/in_sample.rb +1 -1
- data/lib/fluent/plugin/in_syslog.rb +13 -1
- data/lib/fluent/plugin/in_tail/group_watch.rb +204 -0
- data/lib/fluent/plugin/in_tail/position_file.rb +33 -33
- data/lib/fluent/plugin/in_tail.rb +216 -84
- data/lib/fluent/plugin/in_tcp.rb +47 -2
- data/lib/fluent/plugin/input.rb +39 -1
- data/lib/fluent/plugin/metrics.rb +119 -0
- data/lib/fluent/plugin/metrics_local.rb +96 -0
- data/lib/fluent/plugin/multi_output.rb +43 -6
- data/lib/fluent/plugin/out_copy.rb +1 -1
- data/lib/fluent/plugin/out_exec_filter.rb +2 -2
- data/lib/fluent/plugin/out_file.rb +20 -2
- data/lib/fluent/plugin/out_forward/ack_handler.rb +19 -4
- data/lib/fluent/plugin/out_forward/socket_cache.rb +2 -0
- data/lib/fluent/plugin/out_forward.rb +17 -9
- data/lib/fluent/plugin/out_secondary_file.rb +39 -22
- data/lib/fluent/plugin/output.rb +167 -78
- data/lib/fluent/plugin/parser.rb +3 -4
- data/lib/fluent/plugin/parser_apache2.rb +1 -1
- data/lib/fluent/plugin/parser_json.rb +1 -1
- data/lib/fluent/plugin/parser_syslog.rb +1 -1
- data/lib/fluent/plugin/storage_local.rb +3 -5
- data/lib/fluent/plugin.rb +10 -1
- data/lib/fluent/plugin_helper/child_process.rb +3 -0
- data/lib/fluent/plugin_helper/event_emitter.rb +8 -1
- data/lib/fluent/plugin_helper/event_loop.rb +2 -2
- data/lib/fluent/plugin_helper/http_server/server.rb +2 -1
- data/lib/fluent/plugin_helper/metrics.rb +129 -0
- data/lib/fluent/plugin_helper/record_accessor.rb +1 -1
- data/lib/fluent/plugin_helper/retry_state.rb +14 -4
- data/lib/fluent/plugin_helper/server.rb +35 -6
- data/lib/fluent/plugin_helper/service_discovery.rb +2 -2
- data/lib/fluent/plugin_helper/socket.rb +13 -2
- data/lib/fluent/plugin_helper/thread.rb +3 -3
- data/lib/fluent/plugin_helper.rb +1 -0
- data/lib/fluent/plugin_id.rb +3 -2
- data/lib/fluent/registry.rb +2 -1
- data/lib/fluent/root_agent.rb +6 -0
- data/lib/fluent/rpc.rb +4 -3
- data/lib/fluent/supervisor.rb +283 -259
- data/lib/fluent/system_config.rb +13 -3
- data/lib/fluent/test/driver/base.rb +11 -5
- data/lib/fluent/test/driver/filter.rb +4 -0
- data/lib/fluent/test/startup_shutdown.rb +6 -8
- data/lib/fluent/time.rb +21 -20
- data/lib/fluent/version.rb +1 -1
- data/lib/fluent/win32api.rb +38 -0
- data/lib/fluent/winsvc.rb +5 -8
- data/templates/new_gem/test/helper.rb.erb +0 -1
- data/test/command/test_cat.rb +31 -2
- data/test/command/test_ctl.rb +1 -2
- data/test/command/test_fluentd.rb +209 -24
- data/test/command/test_plugin_config_formatter.rb +0 -1
- data/test/compat/test_parser.rb +6 -6
- data/test/config/test_system_config.rb +13 -11
- data/test/config/test_types.rb +1 -1
- data/test/log/test_console_adapter.rb +110 -0
- data/test/plugin/in_tail/test_io_handler.rb +26 -8
- data/test/plugin/in_tail/test_position_file.rb +48 -59
- data/test/plugin/out_forward/test_ack_handler.rb +39 -0
- data/test/plugin/out_forward/test_socket_cache.rb +26 -1
- data/test/plugin/test_bare_output.rb +14 -1
- data/test/plugin/test_base.rb +133 -1
- data/test/plugin/test_buf_file.rb +62 -23
- data/test/plugin/test_buf_file_single.rb +65 -0
- data/test/plugin/test_buffer.rb +267 -3
- data/test/plugin/test_buffer_chunk.rb +11 -0
- data/test/plugin/test_filter.rb +12 -1
- data/test/plugin/test_filter_parser.rb +1 -1
- data/test/plugin/test_filter_stdout.rb +2 -2
- data/test/plugin/test_in_forward.rb +9 -11
- data/test/plugin/test_in_http.rb +65 -3
- data/test/plugin/test_in_monitor_agent.rb +216 -11
- data/test/plugin/test_in_object_space.rb +9 -3
- data/test/plugin/test_in_syslog.rb +35 -0
- data/test/plugin/test_in_tail.rb +1393 -385
- data/test/plugin/test_in_tcp.rb +87 -2
- data/test/plugin/test_in_udp.rb +28 -0
- data/test/plugin/test_in_unix.rb +2 -2
- data/test/plugin/test_input.rb +12 -1
- data/test/plugin/test_metrics.rb +294 -0
- data/test/plugin/test_metrics_local.rb +96 -0
- data/test/plugin/test_multi_output.rb +25 -1
- data/test/plugin/test_out_exec.rb +6 -4
- data/test/plugin/test_out_exec_filter.rb +6 -2
- data/test/plugin/test_out_file.rb +34 -17
- data/test/plugin/test_out_forward.rb +78 -77
- data/test/plugin/test_out_http.rb +1 -0
- data/test/plugin/test_out_stdout.rb +2 -2
- data/test/plugin/test_output.rb +297 -12
- data/test/plugin/test_output_as_buffered.rb +44 -44
- data/test/plugin/test_output_as_buffered_compress.rb +32 -18
- data/test/plugin/test_output_as_buffered_retries.rb +54 -7
- data/test/plugin/test_output_as_buffered_secondary.rb +4 -4
- data/test/plugin/test_parser_regexp.rb +1 -6
- data/test/plugin/test_parser_syslog.rb +1 -1
- data/test/plugin_helper/test_cert_option.rb +1 -1
- data/test/plugin_helper/test_child_process.rb +38 -16
- data/test/plugin_helper/test_event_emitter.rb +29 -0
- data/test/plugin_helper/test_http_server_helper.rb +1 -1
- data/test/plugin_helper/test_metrics.rb +137 -0
- data/test/plugin_helper/test_retry_state.rb +602 -38
- data/test/plugin_helper/test_server.rb +78 -6
- data/test/plugin_helper/test_timer.rb +2 -2
- data/test/test_config.rb +191 -24
- data/test/test_event_router.rb +17 -0
- data/test/test_file_wrapper.rb +53 -0
- data/test/test_formatter.rb +24 -21
- data/test/test_log.rb +122 -40
- data/test/test_msgpack_factory.rb +32 -0
- data/test/test_plugin_classes.rb +102 -0
- data/test/test_root_agent.rb +30 -1
- data/test/test_supervisor.rb +477 -257
- data/test/test_time_parser.rb +22 -0
- metadata +55 -34
- data/.drone.yml +0 -35
- data/.github/workflows/issue-auto-closer.yml +0 -12
- data/.github/workflows/linux-test.yaml +0 -36
- data/.github/workflows/macos-test.yaml +0 -30
- data/.github/workflows/windows-test.yaml +0 -46
- data/.gitlab-ci.yml +0 -103
- data/lib/fluent/plugin/file_wrapper.rb +0 -187
- data/test/plugin/test_file_wrapper.rb +0 -126
- data/test/test_logger_initializer.rb +0 -46
|
@@ -74,6 +74,8 @@ module Fluent::Plugin
|
|
|
74
74
|
config_param :blocking_timeout, :time, default: 0.5
|
|
75
75
|
desc 'Set a allow list of domains that can do CORS (Cross-Origin Resource Sharing)'
|
|
76
76
|
config_param :cors_allow_origins, :array, default: nil
|
|
77
|
+
desc 'Tells browsers whether to expose the response to frontend when the credentials mode is "include".'
|
|
78
|
+
config_param :cors_allow_credentials, :bool, default: false
|
|
77
79
|
desc 'Respond with empty gif image of 1x1 pixel.'
|
|
78
80
|
config_param :respond_with_empty_img, :bool, default: false
|
|
79
81
|
desc 'Respond status code with 204.'
|
|
@@ -112,6 +114,12 @@ module Fluent::Plugin
|
|
|
112
114
|
|
|
113
115
|
super
|
|
114
116
|
|
|
117
|
+
if @cors_allow_credentials
|
|
118
|
+
if @cors_allow_origins.nil? || @cors_allow_origins.include?('*')
|
|
119
|
+
raise Fluent::ConfigError, "Cannot enable cors_allow_credentials without specific origins"
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
|
|
115
123
|
m = if @parser_configs.first['@type'] == 'in_http'
|
|
116
124
|
@parser_msgpack = parser_create(usage: 'parser_in_http_msgpack', type: 'msgpack')
|
|
117
125
|
@parser_msgpack.time_key = nil
|
|
@@ -279,7 +287,10 @@ module Fluent::Plugin
|
|
|
279
287
|
private
|
|
280
288
|
|
|
281
289
|
def on_server_connect(conn)
|
|
282
|
-
handler = Handler.new(conn, @km, method(:on_request),
|
|
290
|
+
handler = Handler.new(conn, @km, method(:on_request),
|
|
291
|
+
@body_size_limit, @format_name, log,
|
|
292
|
+
@cors_allow_origins, @cors_allow_credentials,
|
|
293
|
+
@add_query_params)
|
|
283
294
|
|
|
284
295
|
conn.on(:data) do |data|
|
|
285
296
|
handler.on_read(data)
|
|
@@ -303,8 +314,16 @@ module Fluent::Plugin
|
|
|
303
314
|
@parser_json.parse(js) do |_time, record|
|
|
304
315
|
return nil, record
|
|
305
316
|
end
|
|
317
|
+
elsif ndjson = params['ndjson']
|
|
318
|
+
events = []
|
|
319
|
+
ndjson.split(/\r?\n/).each do |js|
|
|
320
|
+
@parser_json.parse(js) do |_time, record|
|
|
321
|
+
events.push(record)
|
|
322
|
+
end
|
|
323
|
+
end
|
|
324
|
+
return nil, events
|
|
306
325
|
else
|
|
307
|
-
raise "'json' or 'msgpack' parameter is required"
|
|
326
|
+
raise "'json', 'ndjson' or 'msgpack' parameter is required"
|
|
308
327
|
end
|
|
309
328
|
end
|
|
310
329
|
|
|
@@ -356,7 +375,8 @@ module Fluent::Plugin
|
|
|
356
375
|
class Handler
|
|
357
376
|
attr_reader :content_type
|
|
358
377
|
|
|
359
|
-
def initialize(io, km, callback, body_size_limit, format_name, log,
|
|
378
|
+
def initialize(io, km, callback, body_size_limit, format_name, log,
|
|
379
|
+
cors_allow_origins, cors_allow_credentials, add_query_params)
|
|
360
380
|
@io = io
|
|
361
381
|
@km = km
|
|
362
382
|
@callback = callback
|
|
@@ -365,6 +385,7 @@ module Fluent::Plugin
|
|
|
365
385
|
@format_name = format_name
|
|
366
386
|
@log = log
|
|
367
387
|
@cors_allow_origins = cors_allow_origins
|
|
388
|
+
@cors_allow_credentials = cors_allow_credentials
|
|
368
389
|
@idle = 0
|
|
369
390
|
@add_query_params = add_query_params
|
|
370
391
|
@km.add(self)
|
|
@@ -407,7 +428,7 @@ module Fluent::Plugin
|
|
|
407
428
|
@content_type = ""
|
|
408
429
|
@content_encoding = ""
|
|
409
430
|
headers.each_pair {|k,v|
|
|
410
|
-
@env["HTTP_#{k.
|
|
431
|
+
@env["HTTP_#{k.tr('-','_').upcase}"] = v
|
|
411
432
|
case k
|
|
412
433
|
when /\AExpect\z/i
|
|
413
434
|
expect = v
|
|
@@ -418,9 +439,9 @@ module Fluent::Plugin
|
|
|
418
439
|
when /\AContent-Encoding\Z/i
|
|
419
440
|
@content_encoding = v
|
|
420
441
|
when /\AConnection\Z/i
|
|
421
|
-
if
|
|
442
|
+
if /close/i.match?(v)
|
|
422
443
|
@keep_alive = false
|
|
423
|
-
elsif
|
|
444
|
+
elsif /Keep-alive/i.match?(v)
|
|
424
445
|
@keep_alive = true
|
|
425
446
|
end
|
|
426
447
|
when /\AOrigin\Z/i
|
|
@@ -491,6 +512,9 @@ module Fluent::Plugin
|
|
|
491
512
|
send_response_and_close(RES_200_STATUS, header, "")
|
|
492
513
|
elsif include_cors_allow_origin
|
|
493
514
|
header["Access-Control-Allow-Origin"] = @origin
|
|
515
|
+
if @cors_allow_credentials
|
|
516
|
+
header["Access-Control-Allow-Credentials"] = true
|
|
517
|
+
end
|
|
494
518
|
send_response_and_close(RES_200_STATUS, header, "")
|
|
495
519
|
else
|
|
496
520
|
send_response_and_close(RES_403_STATUS, {}, "")
|
|
@@ -542,15 +566,17 @@ module Fluent::Plugin
|
|
|
542
566
|
|
|
543
567
|
if @format_name != 'default'
|
|
544
568
|
params[EVENT_RECORD_PARAMETER] = @body
|
|
545
|
-
elsif
|
|
569
|
+
elsif /^application\/x-www-form-urlencoded/.match?(@content_type)
|
|
546
570
|
params.update WEBrick::HTTPUtils.parse_query(@body)
|
|
547
571
|
elsif @content_type =~ /^multipart\/form-data; boundary=(.+)/
|
|
548
572
|
boundary = WEBrick::HTTPUtils.dequote($1)
|
|
549
573
|
params.update WEBrick::HTTPUtils.parse_form_data(@body, boundary)
|
|
550
|
-
elsif
|
|
574
|
+
elsif /^application\/json/.match?(@content_type)
|
|
551
575
|
params['json'] = @body
|
|
552
|
-
elsif
|
|
576
|
+
elsif /^application\/msgpack/.match?(@content_type)
|
|
553
577
|
params['msgpack'] = @body
|
|
578
|
+
elsif /^application\/x-ndjson/.match?(@content_type)
|
|
579
|
+
params['ndjson'] = @body
|
|
554
580
|
end
|
|
555
581
|
path_info = uri.path
|
|
556
582
|
|
|
@@ -559,7 +585,7 @@ module Fluent::Plugin
|
|
|
559
585
|
query_params = WEBrick::HTTPUtils.parse_query(uri.query)
|
|
560
586
|
|
|
561
587
|
query_params.each_pair {|k,v|
|
|
562
|
-
params["QUERY_#{k.
|
|
588
|
+
params["QUERY_#{k.tr('-','_').upcase}"] = v
|
|
563
589
|
}
|
|
564
590
|
end
|
|
565
591
|
|
|
@@ -576,6 +602,9 @@ module Fluent::Plugin
|
|
|
576
602
|
header['Access-Control-Allow-Origin'] = '*'
|
|
577
603
|
elsif include_cors_allow_origin
|
|
578
604
|
header['Access-Control-Allow-Origin'] = @origin
|
|
605
|
+
if @cors_allow_credentials
|
|
606
|
+
header["Access-Control-Allow-Credentials"] = true
|
|
607
|
+
end
|
|
579
608
|
end
|
|
580
609
|
end
|
|
581
610
|
|
|
@@ -238,7 +238,7 @@ module Fluent::Plugin
|
|
|
238
238
|
'buffer_queue_length' => ->(){ throw(:skip) unless instance_variable_defined?(:@buffer) && !@buffer.nil? && @buffer.is_a?(::Fluent::Plugin::Buffer); @buffer.queue.size },
|
|
239
239
|
'buffer_timekeys' => ->(){ throw(:skip) unless instance_variable_defined?(:@buffer) && !@buffer.nil? && @buffer.is_a?(::Fluent::Plugin::Buffer); @buffer.timekeys },
|
|
240
240
|
'buffer_total_queued_size' => ->(){ throw(:skip) unless instance_variable_defined?(:@buffer) && !@buffer.nil? && @buffer.is_a?(::Fluent::Plugin::Buffer); @buffer.stage_size + @buffer.queue_size },
|
|
241
|
-
'retry_count' => ->(){
|
|
241
|
+
'retry_count' => ->(){ respond_to?(:num_errors) ? num_errors : nil },
|
|
242
242
|
}
|
|
243
243
|
|
|
244
244
|
def all_plugins
|
|
@@ -335,7 +335,9 @@ module Fluent::Plugin
|
|
|
335
335
|
}
|
|
336
336
|
|
|
337
337
|
if pe.respond_to?(:statistics)
|
|
338
|
-
obj.merge!(pe.statistics
|
|
338
|
+
obj.merge!(pe.statistics.dig('output') || {})
|
|
339
|
+
obj.merge!(pe.statistics.dig('filter') || {})
|
|
340
|
+
obj.merge!(pe.statistics.dig('input') || {})
|
|
339
341
|
end
|
|
340
342
|
|
|
341
343
|
obj['retry'] = get_retry_info(pe.retry) if opts[:with_retry] && pe.instance_variable_defined?(:@retry)
|
|
@@ -64,7 +64,7 @@ module Fluent::Plugin
|
|
|
64
64
|
def configure(conf)
|
|
65
65
|
super
|
|
66
66
|
@sample_index = 0
|
|
67
|
-
config = conf.elements.
|
|
67
|
+
config = conf.elements.find{|e| e.name == 'storage' }
|
|
68
68
|
@storage = storage_create(usage: 'suspend', conf: config, default_type: DEFAULT_STORAGE_TYPE)
|
|
69
69
|
end
|
|
70
70
|
|
|
@@ -89,6 +89,8 @@ module Fluent::Plugin
|
|
|
89
89
|
config_param :source_hostname_key, :string, default: nil
|
|
90
90
|
desc 'Try to resolve hostname from IP addresses or not.'
|
|
91
91
|
config_param :resolve_hostname, :bool, default: nil
|
|
92
|
+
desc 'Check the remote connection is still available by sending a keepalive packet if this value is true.'
|
|
93
|
+
config_param :send_keepalive_packet, :bool, default: false
|
|
92
94
|
desc 'The field name of source address of sender.'
|
|
93
95
|
config_param :source_address_key, :string, default: nil
|
|
94
96
|
desc 'The field name of the severity.'
|
|
@@ -143,6 +145,11 @@ module Fluent::Plugin
|
|
|
143
145
|
end
|
|
144
146
|
|
|
145
147
|
@_event_loop_run_timeout = @blocking_timeout
|
|
148
|
+
|
|
149
|
+
protocol = @protocol_type || @transport_config.protocol
|
|
150
|
+
if @send_keepalive_packet && protocol == :udp
|
|
151
|
+
raise Fluent::ConfigError, "send_keepalive_packet is available for tcp/tls"
|
|
152
|
+
end
|
|
146
153
|
end
|
|
147
154
|
|
|
148
155
|
def multi_workers_ready?
|
|
@@ -173,7 +180,12 @@ module Fluent::Plugin
|
|
|
173
180
|
|
|
174
181
|
delimiter = octet_count_frame ? " " : @delimiter
|
|
175
182
|
delimiter_size = delimiter.size
|
|
176
|
-
server_create_connection(
|
|
183
|
+
server_create_connection(
|
|
184
|
+
tls ? :in_syslog_tls_server : :in_syslog_tcp_server, @port,
|
|
185
|
+
bind: @bind,
|
|
186
|
+
resolve_name: @resolve_hostname,
|
|
187
|
+
send_keepalive_packet: @send_keepalive_packet
|
|
188
|
+
) do |conn|
|
|
177
189
|
conn.data do |data|
|
|
178
190
|
buffer = conn.buffer
|
|
179
191
|
buffer << data
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Fluentd
|
|
3
|
+
#
|
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
# you may not use this file except in compliance with the License.
|
|
6
|
+
# You may obtain a copy of the License at
|
|
7
|
+
#
|
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
#
|
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
# See the License for the specific language governing permissions and
|
|
14
|
+
# limitations under the License.
|
|
15
|
+
#
|
|
16
|
+
|
|
17
|
+
require 'fluent/plugin/input'
|
|
18
|
+
|
|
19
|
+
module Fluent::Plugin
|
|
20
|
+
class TailInput < Fluent::Plugin::Input
|
|
21
|
+
module GroupWatchParams
|
|
22
|
+
include Fluent::Configurable
|
|
23
|
+
|
|
24
|
+
DEFAULT_KEY = /.*/
|
|
25
|
+
DEFAULT_LIMIT = -1
|
|
26
|
+
REGEXP_JOIN = "_"
|
|
27
|
+
|
|
28
|
+
config_section :group, param_name: :group, required: false, multi: false do
|
|
29
|
+
desc 'Regex for extracting group\'s metadata'
|
|
30
|
+
config_param :pattern,
|
|
31
|
+
:regexp,
|
|
32
|
+
default: /^\/var\/log\/containers\/(?<podname>[a-z0-9]([-a-z0-9]*[a-z0-9])?(\/[a-z0-9]([-a-z0-9]*[a-z0-9])?)*)_(?<namespace>[^_]+)_(?<container>.+)-(?<docker_id>[a-z0-9]{64})\.log$/
|
|
33
|
+
|
|
34
|
+
desc 'Period of time in which the group_line_limit is applied'
|
|
35
|
+
config_param :rate_period, :time, default: 5
|
|
36
|
+
|
|
37
|
+
config_section :rule, param_name: :rule, required: true, multi: true do
|
|
38
|
+
desc 'Key-value pairs for grouping'
|
|
39
|
+
config_param :match, :hash, value_type: :regexp, default: { namespace: [DEFAULT_KEY], podname: [DEFAULT_KEY] }
|
|
40
|
+
desc 'Maximum number of log lines allowed per group over a period of rate_period'
|
|
41
|
+
config_param :limit, :integer, default: DEFAULT_LIMIT
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
module GroupWatch
|
|
47
|
+
def self.included(mod)
|
|
48
|
+
mod.include GroupWatchParams
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
attr_reader :group_watchers, :default_group_key
|
|
52
|
+
|
|
53
|
+
def initialize
|
|
54
|
+
super
|
|
55
|
+
@group_watchers = {}
|
|
56
|
+
@group_keys = nil
|
|
57
|
+
@default_group_key = nil
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def configure(conf)
|
|
61
|
+
super
|
|
62
|
+
|
|
63
|
+
unless @group.nil?
|
|
64
|
+
## Ensuring correct time period syntax
|
|
65
|
+
@group.rule.each { |rule|
|
|
66
|
+
raise "Metadata Group Limit >= DEFAULT_LIMIT" unless rule.limit >= GroupWatchParams::DEFAULT_LIMIT
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
@group_keys = Regexp.compile(@group.pattern).named_captures.keys
|
|
70
|
+
@default_group_key = ([GroupWatchParams::DEFAULT_KEY] * @group_keys.length).join(GroupWatchParams::REGEXP_JOIN)
|
|
71
|
+
|
|
72
|
+
## Ensures that "specific" rules (with larger number of `rule.match` keys)
|
|
73
|
+
## have a higher priority against "generic" rules (with less number of `rule.match` keys).
|
|
74
|
+
## This will be helpful when a file satisfies more than one rule.
|
|
75
|
+
@group.rule.sort_by! { |rule| -rule.match.length() }
|
|
76
|
+
construct_groupwatchers
|
|
77
|
+
@group_watchers[@default_group_key] ||= GroupWatcher.new(@group.rate_period, GroupWatchParams::DEFAULT_LIMIT)
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def add_path_to_group_watcher(path)
|
|
82
|
+
return nil if @group.nil?
|
|
83
|
+
group_watcher = find_group_from_metadata(path)
|
|
84
|
+
group_watcher.add(path) unless group_watcher.include?(path)
|
|
85
|
+
group_watcher
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def remove_path_from_group_watcher(path)
|
|
89
|
+
return if @group.nil?
|
|
90
|
+
group_watcher = find_group_from_metadata(path)
|
|
91
|
+
group_watcher.delete(path)
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def construct_group_key(named_captures)
|
|
95
|
+
match_rule = []
|
|
96
|
+
@group_keys.each { |key|
|
|
97
|
+
match_rule.append(named_captures.fetch(key, GroupWatchParams::DEFAULT_KEY))
|
|
98
|
+
}
|
|
99
|
+
match_rule = match_rule.join(GroupWatchParams::REGEXP_JOIN)
|
|
100
|
+
|
|
101
|
+
match_rule
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def construct_groupwatchers
|
|
105
|
+
@group.rule.each { |rule|
|
|
106
|
+
match_rule = construct_group_key(rule.match)
|
|
107
|
+
@group_watchers[match_rule] ||= GroupWatcher.new(@group.rate_period, rule.limit)
|
|
108
|
+
}
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def find_group(metadata)
|
|
112
|
+
metadata_key = construct_group_key(metadata)
|
|
113
|
+
gw_key = @group_watchers.keys.find { |regexp| metadata_key.match?(regexp) && regexp != @default_group_key }
|
|
114
|
+
gw_key ||= @default_group_key
|
|
115
|
+
|
|
116
|
+
@group_watchers[gw_key]
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def find_group_from_metadata(path)
|
|
120
|
+
begin
|
|
121
|
+
metadata = @group.pattern.match(path).named_captures
|
|
122
|
+
group_watcher = find_group(metadata)
|
|
123
|
+
rescue
|
|
124
|
+
log.warn "Cannot find group from metadata, Adding file in the default group"
|
|
125
|
+
group_watcher = @group_watchers[@default_group_key]
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
group_watcher
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
class GroupWatcher
|
|
133
|
+
attr_accessor :current_paths, :limit, :number_lines_read, :start_reading_time, :rate_period
|
|
134
|
+
|
|
135
|
+
FileCounter = Struct.new(
|
|
136
|
+
:number_lines_read,
|
|
137
|
+
:start_reading_time,
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
def initialize(rate_period = 60, limit = -1)
|
|
141
|
+
@current_paths = {}
|
|
142
|
+
@rate_period = rate_period
|
|
143
|
+
@limit = limit
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
def add(path)
|
|
147
|
+
@current_paths[path] = FileCounter.new(0, nil)
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
def include?(path)
|
|
151
|
+
@current_paths.key?(path)
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
def size
|
|
155
|
+
@current_paths.size
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
def delete(path)
|
|
159
|
+
@current_paths.delete(path)
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
def update_reading_time(path)
|
|
163
|
+
@current_paths[path].start_reading_time ||= Fluent::Clock.now
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
def update_lines_read(path, value)
|
|
167
|
+
@current_paths[path].number_lines_read += value
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
def reset_counter(path)
|
|
171
|
+
@current_paths[path].start_reading_time = nil
|
|
172
|
+
@current_paths[path].number_lines_read = 0
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
def time_spent_reading(path)
|
|
176
|
+
Fluent::Clock.now - @current_paths[path].start_reading_time
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
def limit_time_period_reached?(path)
|
|
180
|
+
time_spent_reading(path) < @rate_period
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
def limit_lines_reached?(path)
|
|
184
|
+
return true unless include?(path)
|
|
185
|
+
return true if @limit == 0
|
|
186
|
+
|
|
187
|
+
return false if @limit < 0
|
|
188
|
+
return false if @current_paths[path].number_lines_read < @limit / size
|
|
189
|
+
|
|
190
|
+
# update_reading_time(path)
|
|
191
|
+
if limit_time_period_reached?(path) # Exceeds limit
|
|
192
|
+
true
|
|
193
|
+
else # Does not exceed limit
|
|
194
|
+
reset_counter(path)
|
|
195
|
+
false
|
|
196
|
+
end
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
def to_s
|
|
200
|
+
super + " current_paths: #{@current_paths} rate_period: #{@rate_period} limit: #{@limit}"
|
|
201
|
+
end
|
|
202
|
+
end
|
|
203
|
+
end
|
|
204
|
+
end
|
|
@@ -53,10 +53,16 @@ module Fluent::Plugin
|
|
|
53
53
|
}
|
|
54
54
|
end
|
|
55
55
|
|
|
56
|
+
def unwatch_removed_targets(existing_targets)
|
|
57
|
+
@map.reject { |key, entry|
|
|
58
|
+
existing_targets.key?(key)
|
|
59
|
+
}.each_key { |key|
|
|
60
|
+
unwatch_key(key)
|
|
61
|
+
}
|
|
62
|
+
end
|
|
63
|
+
|
|
56
64
|
def unwatch(target_info)
|
|
57
|
-
|
|
58
|
-
entry.update_pos(UNWATCHED_POSITION)
|
|
59
|
-
end
|
|
65
|
+
unwatch_key(@follow_inodes ? target_info.ino : target_info.path)
|
|
60
66
|
end
|
|
61
67
|
|
|
62
68
|
def load(existing_targets = nil)
|
|
@@ -96,6 +102,7 @@ module Fluent::Plugin
|
|
|
96
102
|
end
|
|
97
103
|
|
|
98
104
|
entries = fetch_compacted_entries
|
|
105
|
+
@logger&.debug "Compacted entries: ", entries.keys
|
|
99
106
|
|
|
100
107
|
@file_mutex.synchronize do
|
|
101
108
|
if last_modified == @file.mtime && size == @file.size
|
|
@@ -117,17 +124,31 @@ module Fluent::Plugin
|
|
|
117
124
|
|
|
118
125
|
private
|
|
119
126
|
|
|
127
|
+
def unwatch_key(key)
|
|
128
|
+
if (entry = @map.delete(key))
|
|
129
|
+
entry.update_pos(UNWATCHED_POSITION)
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
|
|
120
133
|
def compact(existing_targets = nil)
|
|
121
134
|
@file_mutex.synchronize do
|
|
122
|
-
entries = fetch_compacted_entries
|
|
135
|
+
entries = fetch_compacted_entries
|
|
136
|
+
@logger&.debug "Compacted entries: ", entries.keys
|
|
137
|
+
|
|
138
|
+
if existing_targets
|
|
139
|
+
entries = remove_deleted_files_entries(entries, existing_targets)
|
|
140
|
+
@logger&.debug "Remove missing entries.",
|
|
141
|
+
existing_targets: existing_targets.keys,
|
|
142
|
+
entries_after_removing: entries.keys
|
|
143
|
+
end
|
|
123
144
|
|
|
124
145
|
@file.pos = 0
|
|
125
146
|
@file.truncate(0)
|
|
126
|
-
@file.write(entries.join)
|
|
147
|
+
@file.write(entries.values.map(&:to_entry_fmt).join)
|
|
127
148
|
end
|
|
128
149
|
end
|
|
129
150
|
|
|
130
|
-
def fetch_compacted_entries
|
|
151
|
+
def fetch_compacted_entries
|
|
131
152
|
entries = {}
|
|
132
153
|
|
|
133
154
|
@file.pos = 0
|
|
@@ -145,31 +166,24 @@ module Fluent::Plugin
|
|
|
145
166
|
if pos == UNWATCHED_POSITION
|
|
146
167
|
@logger.debug "Remove unwatched line from pos_file: #{line}" if @logger
|
|
147
168
|
else
|
|
148
|
-
if entries.include?(path)
|
|
149
|
-
@logger.warn("#{path} already exists. use latest one: deleted #{entries[path]}") if @logger
|
|
150
|
-
end
|
|
151
|
-
|
|
152
169
|
if @follow_inodes
|
|
170
|
+
@logger&.warn("#{path} (inode: #{ino}) already exists. use latest one: deleted #{entries[ino]}") if entries.include?(ino)
|
|
153
171
|
entries[ino] = Entry.new(path, pos, ino, file_pos + path.bytesize + 1)
|
|
154
172
|
else
|
|
173
|
+
@logger&.warn("#{path} already exists. use latest one: deleted #{entries[path]}") if entries.include?(path)
|
|
155
174
|
entries[path] = Entry.new(path, pos, ino, file_pos + path.bytesize + 1)
|
|
156
175
|
end
|
|
157
176
|
file_pos += line.size
|
|
158
177
|
end
|
|
159
178
|
end
|
|
160
179
|
|
|
161
|
-
entries = remove_deleted_files_entries(entries, existing_targets) if @follow_inodes
|
|
162
180
|
entries
|
|
163
181
|
end
|
|
164
182
|
|
|
165
183
|
def remove_deleted_files_entries(existent_entries, existing_targets)
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
}
|
|
170
|
-
else
|
|
171
|
-
existent_entries
|
|
172
|
-
end
|
|
184
|
+
existent_entries.select { |path_or_ino|
|
|
185
|
+
existing_targets.key?(path_or_ino)
|
|
186
|
+
}
|
|
173
187
|
end
|
|
174
188
|
end
|
|
175
189
|
|
|
@@ -250,20 +264,6 @@ module Fluent::Plugin
|
|
|
250
264
|
end
|
|
251
265
|
end
|
|
252
266
|
|
|
253
|
-
TargetInfo = Struct.new(:path, :ino)
|
|
254
|
-
def ==(other)
|
|
255
|
-
return false unless other.is_a?(TargetInfo)
|
|
256
|
-
self.path == other.path
|
|
257
|
-
end
|
|
258
|
-
|
|
259
|
-
def hash
|
|
260
|
-
self.path.hash
|
|
261
|
-
end
|
|
262
|
-
|
|
263
|
-
def eql?(other)
|
|
264
|
-
return false unless other.is_a?(TargetInfo)
|
|
265
|
-
self.path == other.path
|
|
266
|
-
end
|
|
267
|
-
end
|
|
267
|
+
TargetInfo = Struct.new(:path, :ino)
|
|
268
268
|
end
|
|
269
269
|
end
|