fluentd 1.13.2 → 1.14.1
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of fluentd might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/.github/workflows/windows-test.yaml +3 -3
- data/CHANGELOG.md +99 -0
- data/example/v0_12_filter.conf +2 -2
- data/lib/fluent/command/fluentd.rb +8 -0
- data/lib/fluent/compat/output.rb +9 -6
- data/lib/fluent/config/parser.rb +1 -1
- data/lib/fluent/config/v1_parser.rb +1 -1
- data/lib/fluent/event_router.rb +28 -1
- data/lib/fluent/plugin/bare_output.rb +49 -8
- data/lib/fluent/plugin/buffer.rb +84 -22
- data/lib/fluent/plugin/file_wrapper.rb +22 -0
- data/lib/fluent/plugin/filter.rb +35 -1
- data/lib/fluent/plugin/in_http.rb +21 -2
- data/lib/fluent/plugin/in_monitor_agent.rb +4 -2
- data/lib/fluent/plugin/in_syslog.rb +13 -1
- data/lib/fluent/plugin/in_tail/position_file.rb +20 -18
- data/lib/fluent/plugin/in_tail.rb +46 -6
- 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_forward.rb +15 -7
- data/lib/fluent/plugin/output.rb +77 -36
- data/lib/fluent/plugin.rb +10 -1
- data/lib/fluent/plugin_helper/event_emitter.rb +8 -1
- data/lib/fluent/plugin_helper/metrics.rb +129 -0
- data/lib/fluent/plugin_helper/server.rb +4 -2
- data/lib/fluent/plugin_helper.rb +1 -0
- data/lib/fluent/root_agent.rb +6 -0
- data/lib/fluent/supervisor.rb +2 -0
- data/lib/fluent/system_config.rb +9 -1
- data/lib/fluent/version.rb +1 -1
- data/test/config/test_system_config.rb +6 -0
- data/test/plugin/in_tail/test_io_handler.rb +12 -4
- data/test/plugin/in_tail/test_position_file.rb +48 -8
- data/test/plugin/test_bare_output.rb +13 -0
- data/test/plugin/test_buffer.rb +8 -2
- data/test/plugin/test_file_wrapper.rb +11 -0
- data/test/plugin/test_filter.rb +11 -0
- data/test/plugin/test_in_http.rb +40 -0
- data/test/plugin/test_in_monitor_agent.rb +214 -8
- data/test/plugin/test_in_syslog.rb +35 -0
- data/test/plugin/test_in_tail.rb +72 -29
- data/test/plugin/test_input.rb +11 -0
- 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_output.rb +16 -0
- data/test/plugin_helper/test_event_emitter.rb +29 -0
- data/test/plugin_helper/test_metrics.rb +137 -0
- data/test/test_plugin_classes.rb +102 -0
- data/test/test_root_agent.rb +30 -1
- metadata +11 -2
data/lib/fluent/plugin/filter.rb
CHANGED
@@ -28,13 +28,47 @@ module Fluent
|
|
28
28
|
include PluginLoggerMixin
|
29
29
|
include PluginHelper::Mixin
|
30
30
|
|
31
|
-
helpers_internal :event_emitter
|
31
|
+
helpers_internal :event_emitter, :metrics
|
32
32
|
|
33
33
|
attr_reader :has_filter_with_time
|
34
34
|
|
35
35
|
def initialize
|
36
36
|
super
|
37
37
|
@has_filter_with_time = has_filter_with_time?
|
38
|
+
@emit_records_metrics = nil
|
39
|
+
@emit_size_metrics = nil
|
40
|
+
@counter_mutex = Mutex.new
|
41
|
+
@enable_size_metrics = false
|
42
|
+
end
|
43
|
+
|
44
|
+
def emit_records
|
45
|
+
@emit_records_metrics.get
|
46
|
+
end
|
47
|
+
|
48
|
+
def emit_size
|
49
|
+
@emit_size_metrics.get
|
50
|
+
end
|
51
|
+
|
52
|
+
def configure(conf)
|
53
|
+
super
|
54
|
+
|
55
|
+
@emit_records_metrics = metrics_create(namespace: "fluentd", subsystem: "filter", name: "emit_records", help_text: "Number of count emit records")
|
56
|
+
@emit_size_metrics = metrics_create(namespace: "fluentd", subsystem: "filter", name: "emit_size", help_text: "Total size of emit events")
|
57
|
+
@enable_size_metrics = !!system_config.enable_size_metrics
|
58
|
+
end
|
59
|
+
|
60
|
+
def statistics
|
61
|
+
stats = {
|
62
|
+
'emit_records' => @emit_records_metrics.get,
|
63
|
+
'emit_size' => @emit_size_metrics.get,
|
64
|
+
}
|
65
|
+
|
66
|
+
{ 'filter' => stats }
|
67
|
+
end
|
68
|
+
|
69
|
+
def measure_metrics(es)
|
70
|
+
@emit_records_metrics.add(es.size)
|
71
|
+
@emit_size_metrics.add(es.to_msgpack_stream.bytesize) if @enable_size_metrics
|
38
72
|
end
|
39
73
|
|
40
74
|
def filter(tag, time, record)
|
@@ -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)
|
@@ -356,7 +367,8 @@ module Fluent::Plugin
|
|
356
367
|
class Handler
|
357
368
|
attr_reader :content_type
|
358
369
|
|
359
|
-
def initialize(io, km, callback, body_size_limit, format_name, log,
|
370
|
+
def initialize(io, km, callback, body_size_limit, format_name, log,
|
371
|
+
cors_allow_origins, cors_allow_credentials, add_query_params)
|
360
372
|
@io = io
|
361
373
|
@km = km
|
362
374
|
@callback = callback
|
@@ -365,6 +377,7 @@ module Fluent::Plugin
|
|
365
377
|
@format_name = format_name
|
366
378
|
@log = log
|
367
379
|
@cors_allow_origins = cors_allow_origins
|
380
|
+
@cors_allow_credentials = cors_allow_credentials
|
368
381
|
@idle = 0
|
369
382
|
@add_query_params = add_query_params
|
370
383
|
@km.add(self)
|
@@ -491,6 +504,9 @@ module Fluent::Plugin
|
|
491
504
|
send_response_and_close(RES_200_STATUS, header, "")
|
492
505
|
elsif include_cors_allow_origin
|
493
506
|
header["Access-Control-Allow-Origin"] = @origin
|
507
|
+
if @cors_allow_credentials
|
508
|
+
header["Access-Control-Allow-Credentials"] = true
|
509
|
+
end
|
494
510
|
send_response_and_close(RES_200_STATUS, header, "")
|
495
511
|
else
|
496
512
|
send_response_and_close(RES_403_STATUS, {}, "")
|
@@ -576,6 +592,9 @@ module Fluent::Plugin
|
|
576
592
|
header['Access-Control-Allow-Origin'] = '*'
|
577
593
|
elsif include_cors_allow_origin
|
578
594
|
header['Access-Control-Allow-Origin'] = @origin
|
595
|
+
if @cors_allow_credentials
|
596
|
+
header["Access-Control-Allow-Credentials"] = true
|
597
|
+
end
|
579
598
|
end
|
580
599
|
end
|
581
600
|
|
@@ -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)
|
@@ -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
|
@@ -22,19 +22,18 @@ module Fluent::Plugin
|
|
22
22
|
UNWATCHED_POSITION = 0xffffffffffffffff
|
23
23
|
POSITION_FILE_ENTRY_REGEX = /^([^\t]+)\t([0-9a-fA-F]+)\t([0-9a-fA-F]+)/.freeze
|
24
24
|
|
25
|
-
def self.load(file, follow_inodes,
|
26
|
-
pf = new(file, follow_inodes,
|
27
|
-
pf.load
|
25
|
+
def self.load(file, follow_inodes, existing_targets, logger:)
|
26
|
+
pf = new(file, follow_inodes, logger: logger)
|
27
|
+
pf.load(existing_targets)
|
28
28
|
pf
|
29
29
|
end
|
30
30
|
|
31
|
-
def initialize(file, follow_inodes,
|
31
|
+
def initialize(file, follow_inodes, logger: nil)
|
32
32
|
@file = file
|
33
33
|
@logger = logger
|
34
34
|
@file_mutex = Mutex.new
|
35
35
|
@map = {}
|
36
36
|
@follow_inodes = follow_inodes
|
37
|
-
@existing_paths = existing_paths
|
38
37
|
end
|
39
38
|
|
40
39
|
def [](target_info)
|
@@ -60,8 +59,8 @@ module Fluent::Plugin
|
|
60
59
|
end
|
61
60
|
end
|
62
61
|
|
63
|
-
def load
|
64
|
-
compact
|
62
|
+
def load(existing_targets = nil)
|
63
|
+
compact(existing_targets)
|
65
64
|
|
66
65
|
map = {}
|
67
66
|
@file_mutex.synchronize do
|
@@ -118,9 +117,9 @@ module Fluent::Plugin
|
|
118
117
|
|
119
118
|
private
|
120
119
|
|
121
|
-
def compact
|
120
|
+
def compact(existing_targets = nil)
|
122
121
|
@file_mutex.synchronize do
|
123
|
-
entries = fetch_compacted_entries.values.map(&:to_entry_fmt)
|
122
|
+
entries = fetch_compacted_entries(existing_targets).values.map(&:to_entry_fmt)
|
124
123
|
|
125
124
|
@file.pos = 0
|
126
125
|
@file.truncate(0)
|
@@ -128,7 +127,7 @@ module Fluent::Plugin
|
|
128
127
|
end
|
129
128
|
end
|
130
129
|
|
131
|
-
def fetch_compacted_entries
|
130
|
+
def fetch_compacted_entries(existing_targets = nil)
|
132
131
|
entries = {}
|
133
132
|
|
134
133
|
@file.pos = 0
|
@@ -151,23 +150,26 @@ module Fluent::Plugin
|
|
151
150
|
end
|
152
151
|
|
153
152
|
if @follow_inodes
|
154
|
-
entries[ino] = Entry.new(path, pos, ino, file_pos + path.
|
153
|
+
entries[ino] = Entry.new(path, pos, ino, file_pos + path.bytesize + 1)
|
155
154
|
else
|
156
|
-
entries[path] = Entry.new(path, pos, ino, file_pos + path.
|
155
|
+
entries[path] = Entry.new(path, pos, ino, file_pos + path.bytesize + 1)
|
157
156
|
end
|
158
157
|
file_pos += line.size
|
159
158
|
end
|
160
159
|
end
|
161
160
|
|
162
|
-
entries = remove_deleted_files_entries(entries,
|
161
|
+
entries = remove_deleted_files_entries(entries, existing_targets)
|
163
162
|
entries
|
164
163
|
end
|
165
164
|
|
166
|
-
def remove_deleted_files_entries(existent_entries,
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
165
|
+
def remove_deleted_files_entries(existent_entries, existing_targets)
|
166
|
+
if existing_targets
|
167
|
+
existent_entries.select { |path_or_ino|
|
168
|
+
existing_targets.key?(path_or_ino)
|
169
|
+
}
|
170
|
+
else
|
171
|
+
existent_entries
|
172
|
+
end
|
171
173
|
end
|
172
174
|
end
|
173
175
|
|
@@ -38,6 +38,7 @@ module Fluent::Plugin
|
|
38
38
|
helpers :timer, :event_loop, :parser, :compat_parameters
|
39
39
|
|
40
40
|
RESERVED_CHARS = ['/', '*', '%'].freeze
|
41
|
+
MetricsInfo = Struct.new(:opened, :closed, :rotated)
|
41
42
|
|
42
43
|
class WatcherSetupError < StandardError
|
43
44
|
def initialize(msg)
|
@@ -57,6 +58,7 @@ module Fluent::Plugin
|
|
57
58
|
@pf = nil
|
58
59
|
@ignore_list = []
|
59
60
|
@shutdown_start_time = nil
|
61
|
+
@metrics = nil
|
60
62
|
end
|
61
63
|
|
62
64
|
desc 'The paths to read. Multiple paths can be specified, separated by comma.'
|
@@ -191,6 +193,10 @@ module Fluent::Plugin
|
|
191
193
|
@read_bytes_limit_per_second = min_bytes
|
192
194
|
end
|
193
195
|
end
|
196
|
+
opened_file_metrics = metrics_create(namespace: "fluentd", subsystem: "input", name: "files_opened_total", help_text: "Total number of opened files")
|
197
|
+
closed_file_metrics = metrics_create(namespace: "fluentd", subsystem: "input", name: "files_closed_total", help_text: "Total number of closed files")
|
198
|
+
rotated_file_metrics = metrics_create(namespace: "fluentd", subsystem: "input", name: "files_rotated_total", help_text: "Total number of rotated files")
|
199
|
+
@metrics = MetricsInfo.new(opened_file_metrics, closed_file_metrics, rotated_file_metrics)
|
194
200
|
end
|
195
201
|
|
196
202
|
def configure_tag
|
@@ -375,7 +381,7 @@ module Fluent::Plugin
|
|
375
381
|
|
376
382
|
def setup_watcher(target_info, pe)
|
377
383
|
line_buffer_timer_flusher = @multiline_mode ? TailWatcher::LineBufferTimerFlusher.new(log, @multiline_flush_interval, &method(:flush_buffer)) : nil
|
378
|
-
tw = TailWatcher.new(target_info, pe, log, @read_from_head, @follow_inodes, method(:update_watcher), line_buffer_timer_flusher, method(:io_handler))
|
384
|
+
tw = TailWatcher.new(target_info, pe, log, @read_from_head, @follow_inodes, method(:update_watcher), line_buffer_timer_flusher, method(:io_handler), @metrics)
|
379
385
|
|
380
386
|
if @enable_watch_timer
|
381
387
|
tt = TimerTrigger.new(1, log) { tw.on_notify }
|
@@ -426,6 +432,7 @@ module Fluent::Plugin
|
|
426
432
|
|
427
433
|
begin
|
428
434
|
target_info = TargetInfo.new(target_info.path, Fluent::FileWrapper.stat(target_info.path).ino)
|
435
|
+
@tails.delete(target_info)
|
429
436
|
@tails[target_info] = tw
|
430
437
|
tw.on_notify
|
431
438
|
rescue Errno::ENOENT, Errno::EACCES => e
|
@@ -491,10 +498,17 @@ module Fluent::Plugin
|
|
491
498
|
new_position_entry = @pf[target_info]
|
492
499
|
|
493
500
|
if new_position_entry.read_inode == 0
|
501
|
+
# When follow_inodes is true, it's not cleaned up by refresh_watcher.
|
502
|
+
# So it should be unwatched here explicitly.
|
503
|
+
rotated_tw.unwatched = true
|
504
|
+
# Make sure to delete old key, it has a different ino while the hash key is same.
|
505
|
+
@tails.delete(rotated_target_info)
|
494
506
|
@tails[new_target_info] = setup_watcher(new_target_info, new_position_entry)
|
495
507
|
@tails[new_target_info].on_notify
|
496
508
|
end
|
497
509
|
else
|
510
|
+
# Make sure to delete old key, it has a different ino while the hash key is same.
|
511
|
+
@tails.delete(rotated_target_info)
|
498
512
|
@tails[new_target_info] = setup_watcher(new_target_info, pe)
|
499
513
|
@tails[new_target_info].on_notify
|
500
514
|
end
|
@@ -662,6 +676,19 @@ module Fluent::Plugin
|
|
662
676
|
es
|
663
677
|
end
|
664
678
|
|
679
|
+
def statistics
|
680
|
+
stats = super
|
681
|
+
|
682
|
+
stats = {
|
683
|
+
'input' => stats["input"].merge({
|
684
|
+
'opened_file_count' => @metrics.opened.get,
|
685
|
+
'closed_file_count' => @metrics.closed.get,
|
686
|
+
'rotated_file_count' => @metrics.rotated.get,
|
687
|
+
})
|
688
|
+
}
|
689
|
+
stats
|
690
|
+
end
|
691
|
+
|
665
692
|
private
|
666
693
|
|
667
694
|
def io_handler(watcher, path)
|
@@ -674,6 +701,7 @@ module Fluent::Plugin
|
|
674
701
|
open_on_every_update: @open_on_every_update,
|
675
702
|
from_encoding: @from_encoding,
|
676
703
|
encoding: @encoding,
|
704
|
+
metrics: @metrics,
|
677
705
|
&method(:receive_lines)
|
678
706
|
)
|
679
707
|
end
|
@@ -709,7 +737,7 @@ module Fluent::Plugin
|
|
709
737
|
end
|
710
738
|
|
711
739
|
class TailWatcher
|
712
|
-
def initialize(target_info, pe, log, read_from_head, follow_inodes, update_watcher, line_buffer_timer_flusher, io_handler_build)
|
740
|
+
def initialize(target_info, pe, log, read_from_head, follow_inodes, update_watcher, line_buffer_timer_flusher, io_handler_build, metrics)
|
713
741
|
@path = target_info.path
|
714
742
|
@ino = target_info.ino
|
715
743
|
@pe = pe || MemoryPositionEntry.new
|
@@ -721,6 +749,7 @@ module Fluent::Plugin
|
|
721
749
|
@line_buffer_timer_flusher = line_buffer_timer_flusher
|
722
750
|
@io_handler = nil
|
723
751
|
@io_handler_build = io_handler_build
|
752
|
+
@metrics = metrics
|
724
753
|
@watchers = []
|
725
754
|
end
|
726
755
|
|
@@ -754,7 +783,7 @@ module Fluent::Plugin
|
|
754
783
|
end
|
755
784
|
|
756
785
|
def eof?
|
757
|
-
@io_handler.eof?
|
786
|
+
@io_handler.nil? || @io_handler.eof?
|
758
787
|
end
|
759
788
|
|
760
789
|
def on_notify
|
@@ -831,7 +860,7 @@ module Fluent::Plugin
|
|
831
860
|
# new watcher, and old watcher will be closed by stop_watcher in refresh_watchers method
|
832
861
|
# don't want to swap state because we need latest read offset in pos file even after rotate_wait
|
833
862
|
if stat
|
834
|
-
target_info = TargetInfo.new(@path, stat)
|
863
|
+
target_info = TargetInfo.new(@path, stat.ino)
|
835
864
|
@update_watcher.call(target_info, @pe)
|
836
865
|
end
|
837
866
|
else
|
@@ -847,6 +876,7 @@ module Fluent::Plugin
|
|
847
876
|
@log.info "detected rotation of #{@path}"
|
848
877
|
@io_handler = io_handler
|
849
878
|
end
|
879
|
+
@metrics.rotated.inc
|
850
880
|
end
|
851
881
|
end
|
852
882
|
|
@@ -926,7 +956,7 @@ module Fluent::Plugin
|
|
926
956
|
|
927
957
|
attr_accessor :shutdown_timeout
|
928
958
|
|
929
|
-
def initialize(watcher, path:, read_lines_limit:, read_bytes_limit_per_second:, log:, open_on_every_update:, from_encoding: nil, encoding: nil, &receive_lines)
|
959
|
+
def initialize(watcher, path:, read_lines_limit:, read_bytes_limit_per_second:, log:, open_on_every_update:, from_encoding: nil, encoding: nil, metrics:, &receive_lines)
|
930
960
|
@watcher = watcher
|
931
961
|
@path = path
|
932
962
|
@read_lines_limit = read_lines_limit
|
@@ -945,6 +975,7 @@ module Fluent::Plugin
|
|
945
975
|
@shutdown_timeout = SHUTDOWN_TIMEOUT
|
946
976
|
@shutdown_mutex = Mutex.new
|
947
977
|
@eof = false
|
978
|
+
@metrics = metrics
|
948
979
|
|
949
980
|
@log.info "following tail of #{@path}"
|
950
981
|
end
|
@@ -964,6 +995,7 @@ module Fluent::Plugin
|
|
964
995
|
if @io && !@io.closed?
|
965
996
|
@io.close
|
966
997
|
@io = nil
|
998
|
+
@metrics.closed.inc
|
967
999
|
end
|
968
1000
|
end
|
969
1001
|
|
@@ -1051,11 +1083,15 @@ module Fluent::Plugin
|
|
1051
1083
|
def open
|
1052
1084
|
io = Fluent::FileWrapper.open(@path)
|
1053
1085
|
io.seek(@watcher.pe.read_pos + @fifo.bytesize)
|
1086
|
+
@metrics.opened.inc
|
1054
1087
|
io
|
1055
1088
|
rescue RangeError
|
1056
1089
|
io.close if io
|
1057
1090
|
raise WatcherSetupError, "seek error with #{@path}: file position = #{@watcher.pe.read_pos.to_s(16)}, reading bytesize = #{@fifo.bytesize.to_s(16)}"
|
1058
|
-
rescue Errno::
|
1091
|
+
rescue Errno::EACCES => e
|
1092
|
+
@log.warn "#{e}"
|
1093
|
+
nil
|
1094
|
+
rescue Errno::ENOENT
|
1059
1095
|
nil
|
1060
1096
|
end
|
1061
1097
|
|
@@ -1100,6 +1136,10 @@ module Fluent::Plugin
|
|
1100
1136
|
def opened?
|
1101
1137
|
false
|
1102
1138
|
end
|
1139
|
+
|
1140
|
+
def eof?
|
1141
|
+
true
|
1142
|
+
end
|
1103
1143
|
end
|
1104
1144
|
|
1105
1145
|
class RotateHandler
|
data/lib/fluent/plugin/input.rb
CHANGED
@@ -27,7 +27,45 @@ module Fluent
|
|
27
27
|
include PluginLoggerMixin
|
28
28
|
include PluginHelper::Mixin
|
29
29
|
|
30
|
-
helpers_internal :event_emitter
|
30
|
+
helpers_internal :event_emitter, :metrics
|
31
|
+
|
32
|
+
def initialize
|
33
|
+
super
|
34
|
+
@emit_records_metrics = nil
|
35
|
+
@emit_size_metrics = nil
|
36
|
+
@counter_mutex = Mutex.new
|
37
|
+
@enable_size_metrics = false
|
38
|
+
end
|
39
|
+
|
40
|
+
def emit_records
|
41
|
+
@emit_records_metrics.get
|
42
|
+
end
|
43
|
+
|
44
|
+
def emit_size
|
45
|
+
@emit_size_metrics.get
|
46
|
+
end
|
47
|
+
|
48
|
+
def configure(conf)
|
49
|
+
super
|
50
|
+
|
51
|
+
@emit_records_metrics = metrics_create(namespace: "fluentd", subsystem: "input", name: "emit_records", help_text: "Number of count emit records")
|
52
|
+
@emit_size_metrics = metrics_create(namespace: "fluentd", subsystem: "input", name: "emit_size", help_text: "Total size of emit events")
|
53
|
+
@enable_size_metrics = !!system_config.enable_size_metrics
|
54
|
+
end
|
55
|
+
|
56
|
+
def statistics
|
57
|
+
stats = {
|
58
|
+
'emit_records' => @emit_records_metrics.get,
|
59
|
+
'emit_size' => @emit_size_metrics.get,
|
60
|
+
}
|
61
|
+
|
62
|
+
{ 'input' => stats }
|
63
|
+
end
|
64
|
+
|
65
|
+
def metric_callback(es)
|
66
|
+
@emit_records_metrics.add(es.size)
|
67
|
+
@emit_size_metrics.add(es.to_msgpack_stream.bytesize) if @enable_size_metrics
|
68
|
+
end
|
31
69
|
|
32
70
|
def multi_workers_ready?
|
33
71
|
false
|
@@ -0,0 +1,119 @@
|
|
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 'socket'
|
18
|
+
|
19
|
+
require 'fluent/plugin/base'
|
20
|
+
|
21
|
+
require 'fluent/log'
|
22
|
+
require 'fluent/unique_id'
|
23
|
+
require 'fluent/plugin_id'
|
24
|
+
|
25
|
+
module Fluent
|
26
|
+
module Plugin
|
27
|
+
class Metrics < Base
|
28
|
+
include PluginId
|
29
|
+
include PluginLoggerMixin
|
30
|
+
include UniqueId::Mixin
|
31
|
+
|
32
|
+
DEFAULT_TYPE = 'local'
|
33
|
+
|
34
|
+
configured_in :metrics
|
35
|
+
|
36
|
+
config_param :default_labels, :hash, default: {agent: "Fluentd", hostname: "#{Socket.gethostname}"}
|
37
|
+
config_param :labels, :hash, default: {}
|
38
|
+
|
39
|
+
attr_reader :use_gauge_metric
|
40
|
+
attr_reader :has_methods_for_gauge, :has_methods_for_counter
|
41
|
+
|
42
|
+
def initialize
|
43
|
+
super
|
44
|
+
|
45
|
+
@has_methods_for_counter = false
|
46
|
+
@has_methods_for_gauge = false
|
47
|
+
@use_gauge_metric = false
|
48
|
+
end
|
49
|
+
|
50
|
+
def configure(conf)
|
51
|
+
super
|
52
|
+
|
53
|
+
if use_gauge_metric
|
54
|
+
@has_methods_for_gauge = has_methods_for_gauge?
|
55
|
+
else
|
56
|
+
@has_methods_for_counter = has_methods_for_counter?
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
# Some metrics should be counted by gauge.
|
61
|
+
# ref: https://prometheus.io/docs/concepts/metric_types/#gauge
|
62
|
+
def use_gauge_metric=(use_gauge_metric=false)
|
63
|
+
@use_gauge_metric = use_gauge_metric
|
64
|
+
end
|
65
|
+
|
66
|
+
def create(namespace:, subsystem:,name:,help_text:,labels: {})
|
67
|
+
# This API is for cmetrics type.
|
68
|
+
end
|
69
|
+
|
70
|
+
def get
|
71
|
+
raise NotImplementedError, "Implement this method in child class"
|
72
|
+
end
|
73
|
+
|
74
|
+
def inc
|
75
|
+
raise NotImplementedError, "Implement this method in child class"
|
76
|
+
end
|
77
|
+
|
78
|
+
def dec
|
79
|
+
raise NotImplementedError, "Implement this method in child class"
|
80
|
+
end
|
81
|
+
|
82
|
+
def add(value)
|
83
|
+
raise NotImplementedError, "Implement this method in child class"
|
84
|
+
end
|
85
|
+
|
86
|
+
def sub(value)
|
87
|
+
raise NotImplementedError, "Implement this method in child class"
|
88
|
+
end
|
89
|
+
|
90
|
+
def set(value)
|
91
|
+
raise NotImplementedError, "Implement this method in child class"
|
92
|
+
end
|
93
|
+
|
94
|
+
private
|
95
|
+
|
96
|
+
def has_methods_for_counter?
|
97
|
+
implemented_methods = self.class.instance_methods(false)
|
98
|
+
|
99
|
+
if [:get, :inc, :add].all? {|e| implemented_methods.include?(e)} &&
|
100
|
+
[:set].all?{|e| self.class.method_defined?(e)}
|
101
|
+
true
|
102
|
+
else
|
103
|
+
raise "BUG: metrics plugin on counter mode MUST implement `get`, `inc`, `add` methods. And aliased `set` methods should be aliased from another method"
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
def has_methods_for_gauge?
|
108
|
+
implemented_methods = self.class.instance_methods(false)
|
109
|
+
|
110
|
+
if [:get, :inc, :add].all? {|e| implemented_methods.include?(e)} &&
|
111
|
+
[:set, :dec, :sub].all?{|e| self.class.method_defined?(e)}
|
112
|
+
true
|
113
|
+
else
|
114
|
+
raise "BUG: metrics plugin on gauge mode MUST implement `get`, `inc`, and `add` methods. And `dec`, `sub`, and `set` methods should be aliased from other methods"
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|