fluentd 0.14.11 → 0.14.12
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/.travis.yml +1 -5
- data/ChangeLog +54 -2
- data/example/in_dummy_blocks.conf +17 -0
- data/example/in_forward_tls.conf +14 -0
- data/example/in_forward_workers.conf +21 -0
- data/example/logevents.conf +25 -0
- data/example/out_forward_heartbeat_none.conf +16 -0
- data/example/out_forward_tls.conf +18 -0
- data/example/suppress_config_dump.conf +7 -0
- data/lib/fluent/agent.rb +3 -32
- data/lib/fluent/clock.rb +62 -0
- data/lib/fluent/command/fluentd.rb +12 -0
- data/lib/fluent/compat/input.rb +10 -1
- data/lib/fluent/compat/output.rb +40 -1
- data/lib/fluent/config/configure_proxy.rb +30 -7
- data/lib/fluent/config/section.rb +4 -0
- data/lib/fluent/config/types.rb +2 -2
- data/lib/fluent/configurable.rb +31 -5
- data/lib/fluent/engine.rb +61 -12
- data/lib/fluent/event_router.rb +6 -0
- data/lib/fluent/load.rb +0 -1
- data/lib/fluent/log.rb +118 -42
- data/lib/fluent/match.rb +37 -0
- data/lib/fluent/plugin.rb +25 -3
- data/lib/fluent/plugin/base.rb +4 -0
- data/lib/fluent/plugin/buf_file.rb +38 -14
- data/lib/fluent/plugin/buffer.rb +20 -20
- data/lib/fluent/plugin/buffer/file_chunk.rb +2 -2
- data/lib/fluent/plugin/compressable.rb +1 -0
- data/lib/fluent/plugin/filter_record_transformer.rb +3 -6
- data/lib/fluent/plugin/formatter_csv.rb +4 -1
- data/lib/fluent/plugin/formatter_hash.rb +5 -1
- data/lib/fluent/plugin/formatter_json.rb +10 -0
- data/lib/fluent/plugin/formatter_ltsv.rb +2 -1
- data/lib/fluent/plugin/in_dummy.rb +4 -0
- data/lib/fluent/plugin/in_exec.rb +4 -0
- data/lib/fluent/plugin/in_forward.rb +11 -3
- data/lib/fluent/plugin/in_gc_stat.rb +4 -0
- data/lib/fluent/plugin/in_http.rb +4 -0
- data/lib/fluent/plugin/in_monitor_agent.rb +29 -2
- data/lib/fluent/plugin/in_object_space.rb +4 -1
- data/lib/fluent/plugin/in_syslog.rb +4 -0
- data/lib/fluent/plugin/in_tail.rb +193 -116
- data/lib/fluent/plugin/in_tcp.rb +5 -1
- data/lib/fluent/plugin/in_udp.rb +4 -0
- data/lib/fluent/plugin/input.rb +4 -0
- data/lib/fluent/plugin/out_copy.rb +4 -0
- data/lib/fluent/plugin/out_exec.rb +4 -0
- data/lib/fluent/plugin/out_exec_filter.rb +4 -0
- data/lib/fluent/plugin/out_file.rb +70 -30
- data/lib/fluent/plugin/out_forward.rb +132 -28
- data/lib/fluent/plugin/out_null.rb +10 -0
- data/lib/fluent/plugin/out_relabel.rb +4 -0
- data/lib/fluent/plugin/out_roundrobin.rb +4 -0
- data/lib/fluent/plugin/out_secondary_file.rb +5 -0
- data/lib/fluent/plugin/out_stdout.rb +5 -0
- data/lib/fluent/plugin/output.rb +18 -9
- data/lib/fluent/plugin/storage_local.rb +25 -2
- data/lib/fluent/plugin_helper/cert_option.rb +159 -0
- data/lib/fluent/plugin_helper/child_process.rb +6 -6
- data/lib/fluent/plugin_helper/compat_parameters.rb +1 -1
- data/lib/fluent/plugin_helper/event_loop.rb +29 -4
- data/lib/fluent/plugin_helper/inject.rb +14 -1
- data/lib/fluent/plugin_helper/server.rb +275 -31
- data/lib/fluent/plugin_helper/socket.rb +144 -4
- data/lib/fluent/plugin_helper/socket_option.rb +2 -17
- data/lib/fluent/plugin_helper/storage.rb +7 -1
- data/lib/fluent/plugin_helper/thread.rb +16 -4
- data/lib/fluent/registry.rb +26 -9
- data/lib/fluent/root_agent.rb +7 -3
- data/lib/fluent/supervisor.rb +37 -15
- data/lib/fluent/system_config.rb +37 -10
- data/lib/fluent/test.rb +2 -0
- data/lib/fluent/test/driver/base.rb +24 -26
- data/lib/fluent/test/helpers.rb +21 -0
- data/lib/fluent/version.rb +1 -1
- data/test/command/test_fluentd.rb +274 -4
- data/test/config/test_configurable.rb +154 -0
- data/test/config/test_configure_proxy.rb +180 -1
- data/test/config/test_system_config.rb +10 -0
- data/test/config/test_types.rb +1 -0
- data/test/plugin/test_base.rb +4 -0
- data/test/plugin/test_buf_file.rb +241 -9
- data/test/plugin/test_buffer.rb +11 -11
- data/test/plugin/test_buffer_file_chunk.rb +6 -6
- data/test/plugin/test_compressable.rb +3 -0
- data/test/plugin/test_filter.rb +4 -0
- data/test/plugin/test_filter_record_transformer.rb +20 -0
- data/test/plugin/test_formatter_csv.rb +9 -0
- data/test/plugin/test_formatter_hash.rb +35 -0
- data/test/plugin/test_formatter_json.rb +8 -0
- data/test/plugin/test_formatter_ltsv.rb +7 -0
- data/test/plugin/test_in_dummy.rb +7 -3
- data/test/plugin/test_in_monitor_agent.rb +43 -5
- data/test/plugin/test_in_tail.rb +97 -4
- data/test/plugin/test_input.rb +4 -0
- data/test/plugin/test_out_file.rb +46 -7
- data/test/plugin/test_out_forward.rb +59 -7
- data/test/plugin/test_output.rb +10 -4
- data/test/plugin/test_output_as_buffered.rb +37 -25
- data/test/plugin/test_output_as_buffered_compress.rb +1 -1
- data/test/plugin/test_output_as_buffered_retries.rb +6 -6
- data/test/plugin/test_output_as_buffered_secondary.rb +91 -31
- data/test/plugin/test_storage_local.rb +40 -1
- data/test/plugin_helper/test_child_process.rb +29 -28
- data/test/plugin_helper/test_compat_parameters.rb +1 -1
- data/test/plugin_helper/test_inject.rb +27 -9
- data/test/plugin_helper/test_server.rb +822 -50
- data/test/plugin_helper/test_storage.rb +11 -0
- data/test/plugin_helper/test_timer.rb +1 -0
- data/test/test_clock.rb +164 -0
- data/test/test_log.rb +146 -15
- data/test/test_plugin.rb +251 -0
- data/test/test_supervisor.rb +65 -57
- data/test/test_test_drivers.rb +2 -2
- metadata +18 -7
- data/lib/fluent/process.rb +0 -504
- data/test/test_process.rb +0 -48
@@ -25,6 +25,7 @@ module Fluent
|
|
25
25
|
|
26
26
|
config_param :delimiter, :string, default: "\t"
|
27
27
|
config_param :label_delimiter, :string, default: ":"
|
28
|
+
config_param :add_newline, :bool, default: true
|
28
29
|
|
29
30
|
# TODO: escaping for \t in values
|
30
31
|
def format(tag, time, record)
|
@@ -33,7 +34,7 @@ module Fluent
|
|
33
34
|
formatted << @delimiter if formatted.length.nonzero?
|
34
35
|
formatted << "#{label}#{@label_delimiter}#{value}"
|
35
36
|
end
|
36
|
-
formatted << "\n"
|
37
|
+
formatted << "\n".freeze if @add_newline
|
37
38
|
formatted
|
38
39
|
end
|
39
40
|
end
|
@@ -19,6 +19,7 @@ require 'fluent/plugin/input'
|
|
19
19
|
require 'fluent/msgpack_factory'
|
20
20
|
require 'yajl'
|
21
21
|
require 'digest'
|
22
|
+
require 'securerandom'
|
22
23
|
|
23
24
|
module Fluent::Plugin
|
24
25
|
class ForwardInput < Input
|
@@ -142,22 +143,29 @@ module Fluent::Plugin
|
|
142
143
|
end
|
143
144
|
end
|
144
145
|
|
146
|
+
def multi_workers_ready?
|
147
|
+
true
|
148
|
+
end
|
149
|
+
|
145
150
|
HEARTBEAT_UDP_PAYLOAD = "\0"
|
146
151
|
|
147
152
|
def start
|
148
153
|
super
|
149
154
|
|
155
|
+
shared_socket = system_config.workers > 1
|
156
|
+
|
157
|
+
log.info "listening port", port: @port, bind: @bind
|
150
158
|
server_create_connection(
|
151
159
|
:in_forward_server, @port,
|
152
160
|
bind: @bind,
|
153
|
-
shared:
|
161
|
+
shared: shared_socket,
|
154
162
|
resolve_name: @resolve_hostname,
|
155
163
|
linger_timeout: @linger_timeout,
|
156
164
|
backlog: @backlog,
|
157
165
|
&method(:handle_connection)
|
158
166
|
)
|
159
167
|
|
160
|
-
server_create(:in_forward_server_udp_heartbeat, @port, shared:
|
168
|
+
server_create(:in_forward_server_udp_heartbeat, @port, shared: shared_socket, proto: :udp, bind: @bind, resolve_name: @resolve_hostname, max_bytes: 128) do |data, sock|
|
161
169
|
log.trace "heartbeat udp data arrived", host: sock.remote_host, port: sock.remote_port, data: data
|
162
170
|
begin
|
163
171
|
sock.write HEARTBEAT_UDP_PAYLOAD
|
@@ -387,7 +395,7 @@ module Fluent::Plugin
|
|
387
395
|
end
|
388
396
|
|
389
397
|
def generate_salt
|
390
|
-
|
398
|
+
::SecureRandom.random_bytes(16)
|
391
399
|
end
|
392
400
|
|
393
401
|
def generate_helo(nonce, user_auth_salt)
|
@@ -85,6 +85,10 @@ module Fluent::Plugin
|
|
85
85
|
opts[:pretty_json] = true
|
86
86
|
end
|
87
87
|
|
88
|
+
if ivars = (qs['with_ivars'] || []).first
|
89
|
+
opts[:ivars] = ivars.split(',')
|
90
|
+
end
|
91
|
+
|
88
92
|
if with_config = get_search_parameter(qs, 'with_config'.freeze)
|
89
93
|
opts[:with_config] = Fluent::Config.bool_value(with_config)
|
90
94
|
end
|
@@ -216,6 +220,16 @@ module Fluent::Plugin
|
|
216
220
|
end
|
217
221
|
end
|
218
222
|
|
223
|
+
def initialize
|
224
|
+
super
|
225
|
+
|
226
|
+
@first_warn = false
|
227
|
+
end
|
228
|
+
|
229
|
+
def multi_workers_ready?
|
230
|
+
true
|
231
|
+
end
|
232
|
+
|
219
233
|
def start
|
220
234
|
super
|
221
235
|
|
@@ -259,8 +273,8 @@ module Fluent::Plugin
|
|
259
273
|
|
260
274
|
MONITOR_INFO = {
|
261
275
|
'output_plugin' => ->(){ is_a?(::Fluent::Plugin::Output) },
|
262
|
-
'buffer_queue_length' => ->(){ throw(:skip) unless instance_variable_defined?(:@buffer)
|
263
|
-
'buffer_total_queued_size' => ->(){ throw(:skip) unless instance_variable_defined?(:@buffer)
|
276
|
+
'buffer_queue_length' => ->(){ throw(:skip) unless instance_variable_defined?(:@buffer) && !@buffer.nil?; @buffer.queue.size },
|
277
|
+
'buffer_total_queued_size' => ->(){ throw(:skip) unless instance_variable_defined?(:@buffer) && !@buffer.nil?; @buffer.stage_size },
|
264
278
|
'retry_count' => ->(){ instance_variable_defined?(:@num_errors) ? @num_errors : nil },
|
265
279
|
}
|
266
280
|
|
@@ -346,6 +360,12 @@ module Fluent::Plugin
|
|
346
360
|
catch(:skip) do
|
347
361
|
obj[key] = pe.instance_exec(&code)
|
348
362
|
end
|
363
|
+
rescue NoMethodError => e
|
364
|
+
unless @first_warn
|
365
|
+
log.error "NoMethodError in monitoring plugins", key: key, plugin: pe.class, error: e
|
366
|
+
log.error_backtrace
|
367
|
+
@first_warn = true
|
368
|
+
end
|
349
369
|
rescue => e
|
350
370
|
log.warn "unexpected error in monitoring plugins", key: key, plugin: pe.class, error: e
|
351
371
|
end
|
@@ -364,6 +384,13 @@ module Fluent::Plugin
|
|
364
384
|
}
|
365
385
|
end
|
366
386
|
obj['instance_variables'] = iv
|
387
|
+
elsif ivars = opts[:ivars]
|
388
|
+
iv = {}
|
389
|
+
ivars.each {|name|
|
390
|
+
iname = "@#{name}"
|
391
|
+
iv[name] = pe.instance_variable_get(iname) if pe.instance_variable_defined?(iname)
|
392
|
+
}
|
393
|
+
obj['instance_variables'] = iv
|
367
394
|
end
|
368
395
|
|
369
396
|
obj
|
@@ -14,7 +14,6 @@
|
|
14
14
|
# limitations under the License.
|
15
15
|
#
|
16
16
|
|
17
|
-
require 'cool.io'
|
18
17
|
require 'yajl'
|
19
18
|
|
20
19
|
require 'fluent/plugin/input'
|
@@ -33,6 +32,10 @@ module Fluent::Plugin
|
|
33
32
|
config_param :tag, :string
|
34
33
|
config_param :top, :integer, default: 15
|
35
34
|
|
35
|
+
def multi_workers_ready?
|
36
|
+
true
|
37
|
+
end
|
38
|
+
|
36
39
|
def start
|
37
40
|
super
|
38
41
|
|
@@ -65,6 +65,8 @@ module Fluent::Plugin
|
|
65
65
|
config_param :read_lines_limit, :integer, default: 1000
|
66
66
|
desc 'The interval of flushing the buffer for multiline format'
|
67
67
|
config_param :multiline_flush_interval, :time, default: nil
|
68
|
+
desc 'Enable the option to emit unmatched lines.'
|
69
|
+
config_param :emit_unmatched_lines, :bool, default: false
|
68
70
|
desc 'Enable the additional watch timer.'
|
69
71
|
config_param :enable_watch_timer, :bool, default: true
|
70
72
|
desc 'The encoding after conversion of the input.'
|
@@ -73,6 +75,8 @@ module Fluent::Plugin
|
|
73
75
|
config_param :from_encoding, :string, default: nil
|
74
76
|
desc 'Add the log path being tailed to records. Specify the field name to be used.'
|
75
77
|
config_param :path_key, :string, default: nil
|
78
|
+
desc 'Open and close the file on every update instead of leaving it open until it gets rotated.'
|
79
|
+
config_param :open_on_every_update, :bool, default: false
|
76
80
|
|
77
81
|
attr_reader :paths
|
78
82
|
|
@@ -213,7 +217,7 @@ module Fluent::Plugin
|
|
213
217
|
|
214
218
|
def setup_watcher(path, pe)
|
215
219
|
line_buffer_timer_flusher = (@multiline_mode && @multiline_flush_interval) ? TailWatcher::LineBufferTimerFlusher.new(log, @multiline_flush_interval, &method(:flush_buffer)) : nil
|
216
|
-
tw = TailWatcher.new(path, @rotate_wait, pe, log, @read_from_head, @enable_watch_timer, @read_lines_limit, method(:update_watcher), line_buffer_timer_flusher, &method(:receive_lines))
|
220
|
+
tw = TailWatcher.new(path, @rotate_wait, pe, log, @read_from_head, @enable_watch_timer, @read_lines_limit, method(:update_watcher), line_buffer_timer_flusher, @from_encoding, @encoding, open_on_every_update, &method(:receive_lines))
|
217
221
|
tw.attach do |watcher|
|
218
222
|
watcher.timer_trigger = timer_execute(:in_tail_timer_trigger, 1, &watcher.method(:on_notify)) if watcher.enable_watch_timer
|
219
223
|
event_loop_attach(watcher.stat_trigger)
|
@@ -297,13 +301,6 @@ module Fluent::Plugin
|
|
297
301
|
def flush_buffer(tw)
|
298
302
|
if lb = tw.line_buffer
|
299
303
|
lb.chomp!
|
300
|
-
if @encoding
|
301
|
-
if @from_encoding
|
302
|
-
lb.encode!(@encoding, @from_encoding)
|
303
|
-
else
|
304
|
-
lb.force_encoding(@encoding)
|
305
|
-
end
|
306
|
-
end
|
307
304
|
@parser.parse(lb) { |time, record|
|
308
305
|
if time && record
|
309
306
|
tag = if @tag_prefix || @tag_suffix
|
@@ -345,18 +342,16 @@ module Fluent::Plugin
|
|
345
342
|
def convert_line_to_event(line, es, tail_watcher)
|
346
343
|
begin
|
347
344
|
line.chomp! # remove \n
|
348
|
-
if @encoding
|
349
|
-
if @from_encoding
|
350
|
-
line.encode!(@encoding, @from_encoding)
|
351
|
-
else
|
352
|
-
line.force_encoding(@encoding)
|
353
|
-
end
|
354
|
-
end
|
355
345
|
@parser.parse(line) { |time, record|
|
356
346
|
if time && record
|
357
347
|
record[@path_key] ||= tail_watcher.path unless @path_key.nil?
|
358
348
|
es.add(time, record)
|
359
349
|
else
|
350
|
+
if @emit_unmatched_lines
|
351
|
+
record = {'unmatched_line' => line}
|
352
|
+
record[@path_key] ||= tail_watcher.path unless @path_key.nil?
|
353
|
+
es.add(Fluent::EventTime.now, record)
|
354
|
+
end
|
360
355
|
log.warn "pattern not match: #{line.inspect}"
|
361
356
|
end
|
362
357
|
}
|
@@ -387,6 +382,9 @@ module Fluent::Plugin
|
|
387
382
|
lb = line
|
388
383
|
else
|
389
384
|
if lb.nil?
|
385
|
+
if @emit_unmatched_lines
|
386
|
+
convert_line_to_event(line, es, tail_watcher)
|
387
|
+
end
|
390
388
|
log.warn "got incomplete line before first line from #{tail_watcher.path}: #{line.inspect}"
|
391
389
|
else
|
392
390
|
lb << line
|
@@ -410,7 +408,7 @@ module Fluent::Plugin
|
|
410
408
|
end
|
411
409
|
|
412
410
|
class TailWatcher
|
413
|
-
def initialize(path, rotate_wait, pe, log, read_from_head, enable_watch_timer, read_lines_limit, update_watcher, line_buffer_timer_flusher, &receive_lines)
|
411
|
+
def initialize(path, rotate_wait, pe, log, read_from_head, enable_watch_timer, read_lines_limit, update_watcher, line_buffer_timer_flusher, from_encoding, encoding, open_on_every_update, &receive_lines)
|
414
412
|
@path = path
|
415
413
|
@rotate_wait = rotate_wait
|
416
414
|
@pe = pe || MemoryPositionEntry.new
|
@@ -420,17 +418,22 @@ module Fluent::Plugin
|
|
420
418
|
@receive_lines = receive_lines
|
421
419
|
@update_watcher = update_watcher
|
422
420
|
|
423
|
-
@stat_trigger = StatWatcher.new(
|
421
|
+
@stat_trigger = StatWatcher.new(self, &method(:on_notify))
|
424
422
|
@timer_trigger = nil
|
425
423
|
|
426
|
-
@rotate_handler = RotateHandler.new(
|
424
|
+
@rotate_handler = RotateHandler.new(self, &method(:on_rotate))
|
427
425
|
@io_handler = nil
|
428
426
|
@log = log
|
429
427
|
|
430
428
|
@line_buffer_timer_flusher = line_buffer_timer_flusher
|
429
|
+
@from_encoding = from_encoding
|
430
|
+
@encoding = encoding
|
431
|
+
@open_on_every_update = open_on_every_update
|
431
432
|
end
|
432
433
|
|
433
434
|
attr_reader :path
|
435
|
+
attr_reader :log, :pe, :read_lines_limit, :open_on_every_update
|
436
|
+
attr_reader :from_encoding, :encoding
|
434
437
|
attr_reader :stat_trigger, :enable_watch_timer
|
435
438
|
attr_accessor :timer_trigger
|
436
439
|
attr_accessor :line_buffer, :line_buffer_timer_flusher
|
@@ -458,21 +461,27 @@ module Fluent::Plugin
|
|
458
461
|
def close
|
459
462
|
if @io_handler
|
460
463
|
@io_handler.close
|
464
|
+
@io_handler = nil
|
461
465
|
end
|
462
466
|
end
|
463
467
|
|
464
468
|
def on_notify
|
465
|
-
|
469
|
+
begin
|
470
|
+
stat = Fluent::FileWrapper.stat(@path)
|
471
|
+
rescue Errno::ENOENT
|
472
|
+
# moved or deleted
|
473
|
+
stat = nil
|
474
|
+
end
|
475
|
+
|
476
|
+
@rotate_handler.on_notify(stat) if @rotate_handler
|
466
477
|
@line_buffer_timer_flusher.on_notify(self) if @line_buffer_timer_flusher
|
467
|
-
|
468
|
-
@io_handler.on_notify
|
478
|
+
@io_handler.on_notify if @io_handler
|
469
479
|
end
|
470
480
|
|
471
|
-
def on_rotate(
|
472
|
-
if @io_handler
|
473
|
-
if
|
481
|
+
def on_rotate(stat)
|
482
|
+
if @io_handler.nil?
|
483
|
+
if stat
|
474
484
|
# first time
|
475
|
-
stat = io.stat
|
476
485
|
fsize = stat.size
|
477
486
|
inode = stat.ino
|
478
487
|
|
@@ -483,13 +492,11 @@ module Fluent::Plugin
|
|
483
492
|
# a) file was once renamed and backed, or
|
484
493
|
# b) symlink or hardlink to the same file is recreated
|
485
494
|
# in either case, seek to the saved position
|
486
|
-
pos = @pe.read_pos
|
487
495
|
elsif last_inode != 0
|
488
496
|
# this is FilePositionEntry and fluentd once started.
|
489
497
|
# read data from the head of the rotated file.
|
490
498
|
# logs never duplicate because this file is a rotated new file.
|
491
|
-
|
492
|
-
@pe.update(inode, pos)
|
499
|
+
@pe.update(inode, 0)
|
493
500
|
else
|
494
501
|
# this is MemoryPositionEntry or this is the first time fluentd started.
|
495
502
|
# seek to the end of the any files.
|
@@ -498,36 +505,37 @@ module Fluent::Plugin
|
|
498
505
|
pos = @read_from_head ? 0 : fsize
|
499
506
|
@pe.update(inode, pos)
|
500
507
|
end
|
501
|
-
|
502
|
-
|
503
|
-
@io_handler = IOHandler.new(io, @pe, @log, @read_lines_limit, &method(:wrap_receive_lines))
|
508
|
+
@io_handler = IOHandler.new(self, &method(:wrap_receive_lines))
|
504
509
|
else
|
505
510
|
@io_handler = NullIOHandler.new
|
506
511
|
end
|
507
512
|
else
|
508
|
-
|
509
|
-
log_msg << "; waiting #{@rotate_wait} seconds" if @io_handler.io # wait rotate_time if previous file is exist
|
510
|
-
@log.info log_msg
|
513
|
+
watcher_needs_update = false
|
511
514
|
|
512
|
-
if
|
513
|
-
stat = io.stat
|
515
|
+
if stat
|
514
516
|
inode = stat.ino
|
515
517
|
if inode == @pe.read_inode # truncated
|
516
|
-
@pe.update_pos(
|
517
|
-
io_handler = IOHandler.new(io, @pe, @log, @read_lines_limit, &method(:wrap_receive_lines))
|
518
|
+
@pe.update_pos(0)
|
518
519
|
@io_handler.close
|
519
|
-
|
520
|
-
|
521
|
-
@pe.update(inode, io.pos)
|
522
|
-
io_handler = IOHandler.new(io, @pe, @log, @read_lines_limit, &method(:wrap_receive_lines))
|
523
|
-
@io_handler = io_handler
|
520
|
+
elsif !@io_handler.opened? # There is no previous file. Reuse TailWatcher
|
521
|
+
@pe.update(inode, 0)
|
524
522
|
else # file is rotated and new file found
|
525
|
-
|
523
|
+
watcher_needs_update = true
|
526
524
|
end
|
527
525
|
else # file is rotated and new file not found
|
528
526
|
# Clear RotateHandler to avoid duplicated file watch in same path.
|
529
527
|
@rotate_handler = nil
|
528
|
+
watcher_needs_update = true
|
529
|
+
end
|
530
|
+
|
531
|
+
log_msg = "detected rotation of #{@path}"
|
532
|
+
log_msg << "; waiting #{@rotate_wait} seconds" if watcher_needs_update # wait rotate_time if previous file exists
|
533
|
+
@log.info log_msg
|
534
|
+
|
535
|
+
if watcher_needs_update
|
530
536
|
@update_watcher.call(@path, swap_state(@pe))
|
537
|
+
else
|
538
|
+
@io_handler = IOHandler.new(self, &method(:wrap_receive_lines))
|
531
539
|
end
|
532
540
|
end
|
533
541
|
end
|
@@ -537,86 +545,154 @@ module Fluent::Plugin
|
|
537
545
|
mpe = MemoryPositionEntry.new
|
538
546
|
mpe.update(pe.read_inode, pe.read_pos)
|
539
547
|
@pe = mpe
|
540
|
-
@io_handler.pe = mpe # Don't re-create IOHandler because IOHandler has an internal buffer.
|
541
|
-
|
542
548
|
pe # This pe will be updated in on_rotate after TailWatcher is initialized
|
543
549
|
end
|
544
550
|
|
545
551
|
class StatWatcher < Coolio::StatWatcher
|
546
|
-
def initialize(
|
552
|
+
def initialize(watcher, &callback)
|
553
|
+
@watcher = watcher
|
547
554
|
@callback = callback
|
548
|
-
|
549
|
-
super(path)
|
555
|
+
super(watcher.path)
|
550
556
|
end
|
551
557
|
|
552
558
|
def on_change(prev, cur)
|
553
559
|
@callback.call
|
554
560
|
rescue
|
555
561
|
# TODO log?
|
556
|
-
@log.error $!.to_s
|
557
|
-
@log.error_backtrace
|
562
|
+
@watcher.log.error $!.to_s
|
563
|
+
@watcher.log.error_backtrace
|
564
|
+
end
|
565
|
+
end
|
566
|
+
|
567
|
+
|
568
|
+
class FIFO
|
569
|
+
def initialize(from_encoding, encoding)
|
570
|
+
@from_encoding = from_encoding
|
571
|
+
@encoding = encoding
|
572
|
+
@buffer = ''.force_encoding(from_encoding)
|
573
|
+
@eol = "\n".encode(from_encoding).freeze
|
574
|
+
end
|
575
|
+
|
576
|
+
attr_reader :from_encoding, :encoding, :buffer
|
577
|
+
|
578
|
+
def <<(chunk)
|
579
|
+
# Although "chunk" is most likely transient besides String#force_encoding itself
|
580
|
+
# won't affect the actual content of it, it is also probable that "chunk" is
|
581
|
+
# a reused buffer and changing its encoding causes some problems on the caller side.
|
582
|
+
#
|
583
|
+
# Actually, the caller here is specific and "chunk" comes from IO#partial with
|
584
|
+
# the second argument, which the function always returns as a return value.
|
585
|
+
#
|
586
|
+
# Feeding a string that has its encoding attribute set to any double-byte or
|
587
|
+
# quad-byte encoding to IO#readpartial as the second arguments results in an
|
588
|
+
# assertion failure on Ruby < 2.4.0 for unknown reasons.
|
589
|
+
orig_encoding = chunk.encoding
|
590
|
+
chunk.force_encoding(from_encoding)
|
591
|
+
@buffer << chunk
|
592
|
+
# Thus the encoding needs to be reverted back here
|
593
|
+
chunk.force_encoding(orig_encoding)
|
594
|
+
end
|
595
|
+
|
596
|
+
def convert(s)
|
597
|
+
if @from_encoding == @encoding
|
598
|
+
s
|
599
|
+
else
|
600
|
+
s.encode(@encoding, @from_encoding)
|
601
|
+
end
|
602
|
+
end
|
603
|
+
|
604
|
+
def next_line
|
605
|
+
idx = @buffer.index(@eol)
|
606
|
+
convert(@buffer.slice!(0, idx + 1)) unless idx.nil?
|
607
|
+
end
|
608
|
+
|
609
|
+
def bytesize
|
610
|
+
@buffer.bytesize
|
558
611
|
end
|
559
612
|
end
|
560
613
|
|
561
614
|
class IOHandler
|
562
|
-
def initialize(
|
563
|
-
@
|
564
|
-
@log.info "following tail of #{io.path}" if first
|
565
|
-
@io = io
|
566
|
-
@pe = pe
|
567
|
-
@read_lines_limit = read_lines_limit
|
615
|
+
def initialize(watcher, &receive_lines)
|
616
|
+
@watcher = watcher
|
568
617
|
@receive_lines = receive_lines
|
569
|
-
@
|
618
|
+
@fifo = FIFO.new(@watcher.from_encoding || Encoding::ASCII_8BIT, @watcher.encoding || Encoding::ASCII_8BIT)
|
570
619
|
@iobuf = ''.force_encoding('ASCII-8BIT')
|
571
620
|
@lines = []
|
621
|
+
@io = nil
|
622
|
+
@watcher.log.info "following tail of #{@watcher.path}"
|
572
623
|
end
|
573
624
|
|
574
|
-
attr_reader :io
|
575
|
-
attr_accessor :pe
|
576
|
-
|
577
625
|
def on_notify
|
578
|
-
|
579
|
-
|
580
|
-
|
581
|
-
|
582
|
-
|
583
|
-
|
584
|
-
|
585
|
-
@io.readpartial(2048, @
|
586
|
-
|
587
|
-
|
588
|
-
|
589
|
-
|
590
|
-
|
591
|
-
|
592
|
-
|
593
|
-
|
594
|
-
read_more = true
|
595
|
-
break
|
626
|
+
with_io do |io|
|
627
|
+
begin
|
628
|
+
read_more = false
|
629
|
+
|
630
|
+
if !io.nil? && @lines.empty?
|
631
|
+
begin
|
632
|
+
while true
|
633
|
+
@fifo << io.readpartial(2048, @iobuf)
|
634
|
+
while (line = @fifo.next_line)
|
635
|
+
@lines << line
|
636
|
+
end
|
637
|
+
if @lines.size >= @watcher.read_lines_limit
|
638
|
+
# not to use too much memory in case the file is very large
|
639
|
+
read_more = true
|
640
|
+
break
|
641
|
+
end
|
596
642
|
end
|
643
|
+
rescue EOFError
|
597
644
|
end
|
598
|
-
rescue EOFError
|
599
645
|
end
|
600
|
-
end
|
601
646
|
|
602
|
-
|
603
|
-
|
604
|
-
|
605
|
-
|
606
|
-
|
607
|
-
|
647
|
+
unless @lines.empty?
|
648
|
+
if @receive_lines.call(@lines)
|
649
|
+
@watcher.pe.update_pos(io.pos - @fifo.bytesize)
|
650
|
+
@lines.clear
|
651
|
+
else
|
652
|
+
read_more = false
|
653
|
+
end
|
608
654
|
end
|
609
|
-
end
|
610
|
-
end
|
611
|
-
|
612
|
-
rescue
|
613
|
-
@log.error $!.to_s
|
614
|
-
@log.error_backtrace
|
615
|
-
close
|
655
|
+
end while read_more
|
656
|
+
end
|
616
657
|
end
|
617
658
|
|
618
659
|
def close
|
619
|
-
@io
|
660
|
+
if @io && !@io.closed?
|
661
|
+
@io.close
|
662
|
+
@io = nil
|
663
|
+
end
|
664
|
+
end
|
665
|
+
|
666
|
+
def opened?
|
667
|
+
!!@io
|
668
|
+
end
|
669
|
+
|
670
|
+
def open
|
671
|
+
io = Fluent::FileWrapper.open(@watcher.path)
|
672
|
+
io.seek(@watcher.pe.read_pos + @fifo.bytesize)
|
673
|
+
io
|
674
|
+
rescue Errno::ENOENT
|
675
|
+
nil
|
676
|
+
end
|
677
|
+
|
678
|
+
def with_io
|
679
|
+
begin
|
680
|
+
if @watcher.open_on_every_update
|
681
|
+
io = open
|
682
|
+
begin
|
683
|
+
yield io
|
684
|
+
ensure
|
685
|
+
io.close unless io.nil?
|
686
|
+
end
|
687
|
+
else
|
688
|
+
@io ||= open
|
689
|
+
yield @io
|
690
|
+
end
|
691
|
+
rescue
|
692
|
+
@watcher.log.error $!.to_s
|
693
|
+
@watcher.log.error_backtrace
|
694
|
+
close
|
695
|
+
end
|
620
696
|
end
|
621
697
|
end
|
622
698
|
|
@@ -632,44 +708,40 @@ module Fluent::Plugin
|
|
632
708
|
|
633
709
|
def close
|
634
710
|
end
|
711
|
+
|
712
|
+
def opened?
|
713
|
+
false
|
714
|
+
end
|
635
715
|
end
|
636
716
|
|
637
717
|
class RotateHandler
|
638
|
-
def initialize(
|
639
|
-
@
|
718
|
+
def initialize(watcher, &on_rotate)
|
719
|
+
@watcher = watcher
|
640
720
|
@inode = nil
|
641
721
|
@fsize = -1 # first
|
642
722
|
@on_rotate = on_rotate
|
643
|
-
@log = log
|
644
723
|
end
|
645
724
|
|
646
|
-
def on_notify
|
647
|
-
|
648
|
-
stat = Fluent::FileWrapper.stat(@path)
|
649
|
-
inode = stat.ino
|
650
|
-
fsize = stat.size
|
651
|
-
rescue Errno::ENOENT
|
652
|
-
# moved or deleted
|
725
|
+
def on_notify(stat)
|
726
|
+
if stat.nil?
|
653
727
|
inode = nil
|
654
728
|
fsize = 0
|
729
|
+
else
|
730
|
+
inode = stat.ino
|
731
|
+
fsize = stat.size
|
655
732
|
end
|
656
733
|
|
657
734
|
begin
|
658
735
|
if @inode != inode || fsize < @fsize
|
659
|
-
|
660
|
-
begin
|
661
|
-
io = Fluent::FileWrapper.open(@path)
|
662
|
-
rescue Errno::ENOENT
|
663
|
-
end
|
664
|
-
@on_rotate.call(io)
|
736
|
+
@on_rotate.call(stat)
|
665
737
|
end
|
666
738
|
@inode = inode
|
667
739
|
@fsize = fsize
|
668
740
|
end
|
669
741
|
|
670
742
|
rescue
|
671
|
-
@log.error $!.to_s
|
672
|
-
@log.error_backtrace
|
743
|
+
@watcher.log.error $!.to_s
|
744
|
+
@watcher.log.error_backtrace
|
673
745
|
end
|
674
746
|
end
|
675
747
|
|
@@ -767,16 +839,19 @@ module Fluent::Plugin
|
|
767
839
|
def initialize(file, seek)
|
768
840
|
@file = file
|
769
841
|
@seek = seek
|
842
|
+
@pos = nil
|
770
843
|
end
|
771
844
|
|
772
845
|
def update(ino, pos)
|
773
846
|
@file.pos = @seek
|
774
847
|
@file.write "%016x\t%016x" % [pos, ino]
|
848
|
+
@pos = pos
|
775
849
|
end
|
776
850
|
|
777
851
|
def update_pos(pos)
|
778
852
|
@file.pos = @seek
|
779
853
|
@file.write "%016x" % pos
|
854
|
+
@pos = pos
|
780
855
|
end
|
781
856
|
|
782
857
|
def read_inode
|
@@ -786,9 +861,11 @@ module Fluent::Plugin
|
|
786
861
|
end
|
787
862
|
|
788
863
|
def read_pos
|
789
|
-
@
|
790
|
-
|
791
|
-
|
864
|
+
@pos ||= begin
|
865
|
+
@file.pos = @seek
|
866
|
+
raw = @file.read(16)
|
867
|
+
raw ? raw.to_i(16) : 0
|
868
|
+
end
|
792
869
|
end
|
793
870
|
end
|
794
871
|
|