fluentd 1.19.4 → 1.20.0
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/AGENTS.md +46 -0
- data/CHANGELOG.md +75 -0
- data/GithubWorkflow.md +1 -1
- data/MAINTAINERS.md +2 -2
- data/README.md +3 -2
- data/Rakefile +2 -1
- data/SECURITY.md +26 -1
- data/lib/fluent/command/cat.rb +2 -2
- data/lib/fluent/compat/exec_util.rb +20 -5
- data/lib/fluent/config/literal_parser.rb +4 -3
- data/lib/fluent/config/yaml_parser/loader.rb +12 -1
- data/lib/fluent/config/yaml_parser/parser.rb +14 -13
- data/lib/fluent/event.rb +37 -0
- data/lib/fluent/load.rb +0 -1
- data/lib/fluent/log.rb +2 -2
- data/lib/fluent/plugin/buf_file.rb +2 -2
- data/lib/fluent/plugin/buf_file_single.rb +6 -2
- data/lib/fluent/plugin/buffer/file_chunk.rb +23 -10
- data/lib/fluent/plugin/buffer/file_single_chunk.rb +23 -10
- data/lib/fluent/plugin/buffer.rb +102 -36
- data/lib/fluent/plugin/formatter_out_file.rb +3 -4
- data/lib/fluent/plugin/in_exec.rb +6 -4
- data/lib/fluent/plugin/in_forward.rb +39 -29
- data/lib/fluent/plugin/in_object_space.rb +2 -2
- data/lib/fluent/plugin/in_syslog.rb +2 -2
- data/lib/fluent/plugin/in_tail/compatibility.rb +111 -0
- data/lib/fluent/plugin/in_tail/dir_watcher.rb +713 -0
- data/lib/fluent/plugin/in_tail/group_watch.rb +13 -13
- data/lib/fluent/plugin/in_tail/io_handler.rb +391 -0
- data/lib/fluent/plugin/in_tail/line_feeder.rb +230 -0
- data/lib/fluent/plugin/in_tail/stat_watcher.rb +52 -0
- data/lib/fluent/plugin/in_tail/tail_watcher.rb +276 -0
- data/lib/fluent/plugin/in_tail/worker_pool.rb +298 -0
- data/lib/fluent/plugin/in_tail.rb +324 -815
- data/lib/fluent/plugin/in_unix.rb +5 -3
- data/lib/fluent/plugin/out_exec_filter.rb +2 -2
- data/lib/fluent/plugin/out_file.rb +10 -4
- data/lib/fluent/plugin/out_forward.rb +8 -0
- data/lib/fluent/plugin/out_http.rb +0 -10
- data/lib/fluent/plugin/output.rb +49 -19
- data/lib/fluent/plugin/parser_csv.rb +7 -2
- data/lib/fluent/plugin/parser_json.rb +20 -12
- data/lib/fluent/plugin/parser_ltsv.rb +2 -4
- data/lib/fluent/plugin/parser_msgpack.rb +7 -6
- data/lib/fluent/plugin/parser_syslog.rb +52 -5
- data/lib/fluent/plugin/parser_tsv.rb +6 -1
- data/lib/fluent/plugin_helper/http_server/server.rb +29 -13
- data/lib/fluent/plugin_helper/http_server.rb +2 -1
- data/lib/fluent/root_agent.rb +1 -1
- data/lib/fluent/supervisor.rb +24 -5
- data/lib/fluent/system_config.rb +5 -2
- data/lib/fluent/time.rb +8 -7
- data/lib/fluent/version.rb +1 -1
- data/templates/new_gem/.gitignore +8 -0
- metadata +39 -16
data/lib/fluent/plugin/buffer.rb
CHANGED
|
@@ -72,6 +72,9 @@ module Fluent
|
|
|
72
72
|
desc 'If true, chunks are thrown away when unrecoverable error happens'
|
|
73
73
|
config_param :disable_chunk_backup, :bool, default: false
|
|
74
74
|
|
|
75
|
+
desc 'The approximate size limit of the evacuated chunk files in the evacuation directory. When the retry limit is reached, queued chunks that fit within the remaining capacity are evacuated, and the others are purged without being evacuated. The total size may exceed this limit due to chunk metadata or concurrent evacuation by multiple workers. Set to 0 to disable evacuation. nil (the default) means no limit.'
|
|
76
|
+
config_param :evacuate_limit_size, :size, default: nil
|
|
77
|
+
|
|
75
78
|
Metadata = Struct.new(:timekey, :tag, :variables, :seq) do
|
|
76
79
|
def initialize(timekey, tag, variables)
|
|
77
80
|
super(timekey, tag, variables, 0)
|
|
@@ -227,6 +230,20 @@ module Fluent
|
|
|
227
230
|
unless @queue_limit_length.nil?
|
|
228
231
|
@total_limit_size = @chunk_limit_size * @queue_limit_length
|
|
229
232
|
end
|
|
233
|
+
if @evacuate_limit_size
|
|
234
|
+
raise Fluent::ConfigError, "evacuate_limit_size must be 0 or larger" if @evacuate_limit_size < 0
|
|
235
|
+
if !persistent?
|
|
236
|
+
log.warn "evacuate_limit_size is ignored because this buffer does not evacuate chunks"
|
|
237
|
+
@evacuate_limit_size = nil
|
|
238
|
+
elsif @evacuate_limit_size > 0
|
|
239
|
+
unless owner.plugin_id_configured?
|
|
240
|
+
log.warn "evacuate_limit_size should be used with @id, because the evacuation directory without @id can change when the configuration is changed or reloaded"
|
|
241
|
+
end
|
|
242
|
+
if @evacuate_limit_size < @chunk_limit_size
|
|
243
|
+
log.warn "evacuate_limit_size is smaller than chunk_limit_size, so full chunks may not be evacuated", evacuate_limit_size: @evacuate_limit_size, chunk_limit_size: @chunk_limit_size
|
|
244
|
+
end
|
|
245
|
+
end
|
|
246
|
+
end
|
|
230
247
|
@stage_length_metrics = metrics_create(namespace: "fluentd", subsystem: "buffer", name: "stage_length",
|
|
231
248
|
help_text: 'Length of stage buffers', prefer_gauge: true)
|
|
232
249
|
@stage_length_metrics.set(0)
|
|
@@ -383,6 +400,12 @@ module Fluent
|
|
|
383
400
|
if enqueue || first_chunk.unstaged? || chunk_size_full?(first_chunk)
|
|
384
401
|
chunks_to_enqueue << first_chunk
|
|
385
402
|
end
|
|
403
|
+
if (bytesize = staged_bytesizes_by_chunk[first_chunk])
|
|
404
|
+
# Account while the chunk lock is still held so enqueue_chunk
|
|
405
|
+
# cannot remove the chunk and subtract its bytes first.
|
|
406
|
+
@stage_size_metrics.add(bytesize)
|
|
407
|
+
log.on_trace { log.trace { "chunk #{first_chunk.path} size_added: #{bytesize} new_size: #{first_chunk.bytesize}" } }
|
|
408
|
+
end
|
|
386
409
|
first_chunk.mon_exit
|
|
387
410
|
rescue
|
|
388
411
|
operated_chunks.unshift(first_chunk)
|
|
@@ -397,6 +420,10 @@ module Fluent
|
|
|
397
420
|
if enqueue || chunk.unstaged? || chunk_size_full?(chunk)
|
|
398
421
|
chunks_to_enqueue << chunk
|
|
399
422
|
end
|
|
423
|
+
if (bytesize = staged_bytesizes_by_chunk[chunk])
|
|
424
|
+
@stage_size_metrics.add(bytesize)
|
|
425
|
+
log.on_trace { log.trace { "chunk #{chunk.path} size_added: #{bytesize} new_size: #{chunk.bytesize}" } }
|
|
426
|
+
end
|
|
400
427
|
chunk.mon_exit
|
|
401
428
|
rescue => e
|
|
402
429
|
chunk.rollback
|
|
@@ -405,44 +432,46 @@ module Fluent
|
|
|
405
432
|
end
|
|
406
433
|
end
|
|
407
434
|
|
|
408
|
-
# All locks about chunks are released.
|
|
409
|
-
|
|
410
|
-
#
|
|
411
|
-
#
|
|
412
|
-
# FIX FOR stage_size miscomputation - https://github.com/fluent/fluentd/issues/2712
|
|
413
|
-
#
|
|
414
|
-
staged_bytesizes_by_chunk.each do |chunk, bytesize|
|
|
415
|
-
chunk.synchronize do
|
|
416
|
-
synchronize { @stage_size_metrics.add(bytesize) }
|
|
417
|
-
log.on_trace { log.trace { "chunk #{chunk.path} size_added: #{bytesize} new_size: #{chunk.bytesize}" } }
|
|
418
|
-
end
|
|
419
|
-
end
|
|
435
|
+
# All locks about chunks are released. Stage-size accounting for the
|
|
436
|
+
# staged chunks happened inside each chunk monitor above, so a
|
|
437
|
+
# concurrent enqueue_chunk cannot observe a transiently negative
|
|
438
|
+
# stage_size (issue #2712 race).
|
|
420
439
|
|
|
440
|
+
enqueue_errors = []
|
|
421
441
|
chunks_to_enqueue.each do |c|
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
442
|
+
begin
|
|
443
|
+
if c.staged? && (enqueue || chunk_size_full?(c))
|
|
444
|
+
m = c.metadata
|
|
445
|
+
enqueue_chunk(m)
|
|
446
|
+
if unstaged_chunks[m] && !unstaged_chunks[m].empty?
|
|
447
|
+
u = unstaged_chunks[m].pop
|
|
448
|
+
u.synchronize do
|
|
449
|
+
if u.unstaged? && !chunk_size_full?(u)
|
|
450
|
+
# `u.metadata.seq` and `m.seq` can be different but Buffer#enqueue_chunk expect them to be the same value
|
|
451
|
+
u.metadata.seq = 0
|
|
452
|
+
synchronize {
|
|
453
|
+
@stage[m] = u.staged!
|
|
454
|
+
@stage_size_metrics.add(u.bytesize)
|
|
455
|
+
}
|
|
456
|
+
end
|
|
435
457
|
end
|
|
436
458
|
end
|
|
459
|
+
elsif c.unstaged?
|
|
460
|
+
enqueue_unstaged_chunk(c)
|
|
461
|
+
else
|
|
462
|
+
# previously staged chunk is already enqueued, closed or purged.
|
|
463
|
+
# no problem.
|
|
437
464
|
end
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
else
|
|
441
|
-
# previously staged chunk is already enqueued, closed or purged.
|
|
442
|
-
# no problem.
|
|
465
|
+
rescue => e
|
|
466
|
+
enqueue_errors << e
|
|
443
467
|
end
|
|
444
468
|
end
|
|
445
469
|
|
|
470
|
+
enqueue_errors.each do |e|
|
|
471
|
+
log.warn "error occurs in enqueueing a chunk", error: e
|
|
472
|
+
log.warn_backtrace e.backtrace
|
|
473
|
+
end
|
|
474
|
+
|
|
446
475
|
operated_chunks.clear if errors.empty?
|
|
447
476
|
|
|
448
477
|
if errors.size > 0
|
|
@@ -492,6 +521,9 @@ module Fluent
|
|
|
492
521
|
|
|
493
522
|
chunk.synchronize do
|
|
494
523
|
synchronize do
|
|
524
|
+
bytesize = chunk.bytesize
|
|
525
|
+
@stage_size_metrics.sub(bytesize)
|
|
526
|
+
@queue_size_metrics.add(bytesize)
|
|
495
527
|
if chunk.empty?
|
|
496
528
|
chunk.close
|
|
497
529
|
else
|
|
@@ -500,9 +532,6 @@ module Fluent
|
|
|
500
532
|
@queued_num[metadata] = @queued_num.fetch(metadata, 0) + 1
|
|
501
533
|
chunk.enqueued!
|
|
502
534
|
end
|
|
503
|
-
bytesize = chunk.bytesize
|
|
504
|
-
@stage_size_metrics.sub(bytesize)
|
|
505
|
-
@queue_size_metrics.add(bytesize)
|
|
506
535
|
end
|
|
507
536
|
end
|
|
508
537
|
nil
|
|
@@ -517,9 +546,9 @@ module Fluent
|
|
|
517
546
|
metadata.seq = 0 # metadata.seq should be 0 for counting @queued_num
|
|
518
547
|
@queue << chunk
|
|
519
548
|
@queued_num[metadata] = @queued_num.fetch(metadata, 0) + 1
|
|
549
|
+
@queue_size_metrics.add(chunk.bytesize)
|
|
520
550
|
chunk.enqueued!
|
|
521
551
|
end
|
|
522
|
-
@queue_size_metrics.add(chunk.bytesize)
|
|
523
552
|
end
|
|
524
553
|
end
|
|
525
554
|
|
|
@@ -630,12 +659,27 @@ module Fluent
|
|
|
630
659
|
def clear_queue!
|
|
631
660
|
log.on_trace { log.trace "clearing queue", instance: self.object_id }
|
|
632
661
|
|
|
662
|
+
evacuate_budget = nil
|
|
663
|
+
skipped_num = 0
|
|
664
|
+
skipped_bytesize = 0
|
|
665
|
+
|
|
633
666
|
synchronize do
|
|
634
667
|
until @queue.empty?
|
|
635
668
|
begin
|
|
636
669
|
q = @queue.shift
|
|
637
|
-
|
|
638
|
-
|
|
670
|
+
if @evacuate_limit_size.nil?
|
|
671
|
+
evacuate_chunk(q)
|
|
672
|
+
elsif @evacuate_limit_size > 0
|
|
673
|
+
evacuate_budget ||= remaining_evacuate_size
|
|
674
|
+
if q.bytesize <= evacuate_budget
|
|
675
|
+
evacuate_chunk(q)
|
|
676
|
+
evacuate_budget -= q.bytesize
|
|
677
|
+
else
|
|
678
|
+
skipped_num += 1
|
|
679
|
+
skipped_bytesize += q.bytesize
|
|
680
|
+
end
|
|
681
|
+
end
|
|
682
|
+
log.trace("purging a chunk in queue"){ {id: dump_unique_id_hex(q.unique_id), bytesize: q.bytesize, size: q.size} }
|
|
639
683
|
q.purge
|
|
640
684
|
rescue => e
|
|
641
685
|
log.error "unexpected error while clearing buffer queue", error_class: e.class, error: e
|
|
@@ -644,6 +688,10 @@ module Fluent
|
|
|
644
688
|
end
|
|
645
689
|
@queue_size_metrics.set(0)
|
|
646
690
|
end
|
|
691
|
+
|
|
692
|
+
if skipped_num > 0
|
|
693
|
+
log.warn "some chunks are purged without being evacuated to stay within evacuate_limit_size.", evacuate_limit_size: @evacuate_limit_size, chunks: skipped_num, bytesize: skipped_bytesize
|
|
694
|
+
end
|
|
647
695
|
end
|
|
648
696
|
|
|
649
697
|
def evacuate_chunk(chunk)
|
|
@@ -992,6 +1040,24 @@ module Fluent
|
|
|
992
1040
|
def backup_base_dir
|
|
993
1041
|
system_config.root_dir || DEFAULT_BACKUP_DIR
|
|
994
1042
|
end
|
|
1043
|
+
|
|
1044
|
+
def evacuate_dir
|
|
1045
|
+
File.join(backup_base_dir, 'buffer', safe_owner_id)
|
|
1046
|
+
end
|
|
1047
|
+
|
|
1048
|
+
def evacuated_bytesize
|
|
1049
|
+
dir = evacuate_dir
|
|
1050
|
+
Dir.children(dir, encoding: dir.encoding).sum { |name| File.size?(File.join(dir, name)).to_i }
|
|
1051
|
+
rescue Errno::ENOENT
|
|
1052
|
+
0
|
|
1053
|
+
end
|
|
1054
|
+
|
|
1055
|
+
def remaining_evacuate_size
|
|
1056
|
+
@evacuate_limit_size - evacuated_bytesize
|
|
1057
|
+
rescue => e
|
|
1058
|
+
log.warn "failed to get the size of the evacuated chunk files. queued chunks are purged without being evacuated.", error: e
|
|
1059
|
+
0
|
|
1060
|
+
end
|
|
995
1061
|
end
|
|
996
1062
|
end
|
|
997
1063
|
end
|
|
@@ -44,10 +44,9 @@ module Fluent
|
|
|
44
44
|
|
|
45
45
|
def format(tag, time, record)
|
|
46
46
|
json_str = JSON.generate(record)
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
"#{header}#{json_str}#{@newline}"
|
|
47
|
+
time_str = @output_time ? "#{@timef.format(time)}#{@delimiter}" : ''
|
|
48
|
+
tag_str = @output_tag ? "#{tag}#{@delimiter}" : ''
|
|
49
|
+
"#{time_str}#{tag_str}#{json_str}#{@newline}"
|
|
51
50
|
ensure
|
|
52
51
|
json_str&.clear
|
|
53
52
|
end
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
#
|
|
16
16
|
|
|
17
17
|
require 'fluent/plugin/input'
|
|
18
|
-
require '
|
|
18
|
+
require 'json'
|
|
19
19
|
|
|
20
20
|
module Fluent::Plugin
|
|
21
21
|
class ExecInput < Fluent::Plugin::Input
|
|
@@ -43,6 +43,8 @@ module Fluent::Plugin
|
|
|
43
43
|
config_param :tag, :string, default: nil
|
|
44
44
|
desc 'The interval time between periodic program runs.'
|
|
45
45
|
config_param :run_interval, :time, default: nil
|
|
46
|
+
desc 'Command (program) execution timeout.'
|
|
47
|
+
config_param :command_timeout, :time, default: nil
|
|
46
48
|
desc 'The default block size to read if parser requires partial read.'
|
|
47
49
|
config_param :read_block_size, :size, default: 10240 # 10k
|
|
48
50
|
desc 'The encoding to receive the result of the command, especially for non-ascii characters.'
|
|
@@ -86,9 +88,9 @@ module Fluent::Plugin
|
|
|
86
88
|
options[:external_encoding] = @encoding if @encoding
|
|
87
89
|
|
|
88
90
|
if @run_interval
|
|
89
|
-
child_process_execute(:exec_input, @command, interval: @run_interval, **options, &method(:run))
|
|
91
|
+
child_process_execute(:exec_input, @command, interval: @run_interval, wait_timeout: @command_timeout, **options, &method(:run))
|
|
90
92
|
else
|
|
91
|
-
child_process_execute(:exec_input, @command, immediate: true, **options, &method(:run))
|
|
93
|
+
child_process_execute(:exec_input, @command, immediate: true, wait_timeout: @command_timeout, **options, &method(:run))
|
|
92
94
|
end
|
|
93
95
|
end
|
|
94
96
|
|
|
@@ -115,7 +117,7 @@ module Fluent::Plugin
|
|
|
115
117
|
time ||= extract_time_from_record(record) || Fluent::EventTime.now
|
|
116
118
|
router.emit(tag, time, record)
|
|
117
119
|
rescue => e
|
|
118
|
-
log.error "exec failed to emit", tag: tag, record:
|
|
120
|
+
log.error "exec failed to emit", tag: tag, record: JSON.generate(record), error: e
|
|
119
121
|
router.emit_error_event(tag, time, record, e) if tag && time && record
|
|
120
122
|
end
|
|
121
123
|
end
|
|
@@ -15,9 +15,10 @@
|
|
|
15
15
|
#
|
|
16
16
|
|
|
17
17
|
|
|
18
|
+
require 'fluent/env'
|
|
18
19
|
require 'fluent/plugin/input'
|
|
19
20
|
require 'fluent/msgpack_factory'
|
|
20
|
-
require '
|
|
21
|
+
require 'json'
|
|
21
22
|
require 'digest'
|
|
22
23
|
require 'securerandom'
|
|
23
24
|
|
|
@@ -31,6 +32,12 @@ module Fluent::Plugin
|
|
|
31
32
|
helpers :server
|
|
32
33
|
|
|
33
34
|
LISTEN_PORT = 24224
|
|
35
|
+
OPTION_ACK = 'ack'.freeze
|
|
36
|
+
OPTION_CHUNK = 'chunk'.freeze
|
|
37
|
+
OPTION_COMPRESSED = 'compressed'.freeze
|
|
38
|
+
OPTION_FLUENT_SIGNAL = 'fluent_signal'.freeze
|
|
39
|
+
OPTION_SIZE = 'size'.freeze
|
|
40
|
+
OPTION_TEXT = 'text'.freeze
|
|
34
41
|
|
|
35
42
|
desc 'The port to listen to.'
|
|
36
43
|
config_param :port, :integer, default: LISTEN_PORT
|
|
@@ -250,13 +257,15 @@ module Fluent::Plugin
|
|
|
250
257
|
unless feeder
|
|
251
258
|
first = data[0]
|
|
252
259
|
if first == '{' || first == '[' # json
|
|
253
|
-
parser =
|
|
254
|
-
parser.on_parse_complete = ->(obj){
|
|
255
|
-
block.call(obj, bytes, serializer)
|
|
256
|
-
bytes = 0
|
|
257
|
-
}
|
|
260
|
+
parser = JSON::ResumableParser.new(**Fluent::DEFAULT_JSON_PARSE_OPTIONS)
|
|
258
261
|
serializer = :to_json.to_proc
|
|
259
|
-
feeder = ->(d){
|
|
262
|
+
feeder = ->(d){
|
|
263
|
+
parser << d
|
|
264
|
+
while parser.parse
|
|
265
|
+
block.call(parser.value, bytes, serializer)
|
|
266
|
+
bytes = 0
|
|
267
|
+
end
|
|
268
|
+
}
|
|
260
269
|
else # msgpack
|
|
261
270
|
parser = Fluent::MessagePackFactory.msgpack_unpacker
|
|
262
271
|
serializer = :to_msgpack.to_proc
|
|
@@ -275,8 +284,8 @@ module Fluent::Plugin
|
|
|
275
284
|
end
|
|
276
285
|
|
|
277
286
|
def response(option)
|
|
278
|
-
if option && option[
|
|
279
|
-
return {
|
|
287
|
+
if option && option[OPTION_CHUNK]
|
|
288
|
+
return { OPTION_ACK => option[OPTION_CHUNK] }
|
|
280
289
|
end
|
|
281
290
|
nil
|
|
282
291
|
end
|
|
@@ -310,14 +319,19 @@ module Fluent::Plugin
|
|
|
310
319
|
when String
|
|
311
320
|
# PackedForward
|
|
312
321
|
option = msg[2] || {}
|
|
313
|
-
size = option[
|
|
322
|
+
size = option[OPTION_SIZE] || 0
|
|
323
|
+
compressed = option[OPTION_COMPRESSED]
|
|
314
324
|
|
|
315
|
-
if
|
|
316
|
-
es = Fluent::CompressedMessagePackEventStream.new(entries, nil, size.to_i, compress:
|
|
325
|
+
if compressed && compressed != OPTION_TEXT
|
|
326
|
+
es = Fluent::CompressedMessagePackEventStream.new(entries, nil, size.to_i, compress: compressed.to_sym, decompression_size_limit: @decompression_size_limit)
|
|
317
327
|
else
|
|
318
328
|
es = Fluent::MessagePackEventStream.new(entries, nil, size.to_i)
|
|
319
329
|
end
|
|
320
|
-
|
|
330
|
+
if @skip_invalid_event
|
|
331
|
+
es = normalize_event_stream(tag, es, conn.remote_host)
|
|
332
|
+
elsif option[OPTION_FLUENT_SIGNAL] == 0
|
|
333
|
+
es = Fluent::MetadataTimeEventStream.new(es)
|
|
334
|
+
end
|
|
321
335
|
if @enable_field_injection
|
|
322
336
|
es = add_source_info(es, conn)
|
|
323
337
|
end
|
|
@@ -325,19 +339,7 @@ module Fluent::Plugin
|
|
|
325
339
|
|
|
326
340
|
when Array
|
|
327
341
|
# Forward
|
|
328
|
-
es =
|
|
329
|
-
check_and_skip_invalid_event(tag, entries, conn.remote_host)
|
|
330
|
-
else
|
|
331
|
-
es = Fluent::MultiEventStream.new
|
|
332
|
-
entries.each { |e|
|
|
333
|
-
record = e[1]
|
|
334
|
-
next if record.nil?
|
|
335
|
-
time = e[0]
|
|
336
|
-
time = Fluent::EventTime.now if time.nil? || time.to_i == 0 # `to_i == 0` for empty EventTime
|
|
337
|
-
es.add(time, record)
|
|
338
|
-
}
|
|
339
|
-
es
|
|
340
|
-
end
|
|
342
|
+
es = normalize_event_stream(tag, entries, conn.remote_host)
|
|
341
343
|
if @enable_field_injection
|
|
342
344
|
es = add_source_info(es, conn)
|
|
343
345
|
end
|
|
@@ -346,7 +348,7 @@ module Fluent::Plugin
|
|
|
346
348
|
|
|
347
349
|
else
|
|
348
350
|
# Message
|
|
349
|
-
time = msg[1]
|
|
351
|
+
time = extract_event_time(msg[1])
|
|
350
352
|
record = msg[2]
|
|
351
353
|
if @skip_invalid_event && invalid_event?(tag, time, record)
|
|
352
354
|
log.warn "got invalid event and drop it:", host: conn.remote_host, tag: tag, time: time, record: record
|
|
@@ -367,21 +369,29 @@ module Fluent::Plugin
|
|
|
367
369
|
end
|
|
368
370
|
|
|
369
371
|
def invalid_event?(tag, time, record)
|
|
372
|
+
time = extract_event_time(time)
|
|
370
373
|
!((time.is_a?(Integer) || time.is_a?(::Fluent::EventTime)) && record.is_a?(Hash) && tag.is_a?(String))
|
|
371
374
|
end
|
|
372
375
|
|
|
373
|
-
def
|
|
376
|
+
def normalize_event_stream(tag, es, remote_host)
|
|
374
377
|
new_es = Fluent::MultiEventStream.new
|
|
375
378
|
es.each { |time, record|
|
|
376
|
-
|
|
379
|
+
time = extract_event_time(time)
|
|
380
|
+
if @skip_invalid_event && invalid_event?(tag, time, record)
|
|
377
381
|
log.warn "skip invalid event:", host: remote_host, tag: tag, time: time, record: record
|
|
378
382
|
next
|
|
379
383
|
end
|
|
384
|
+
next if record.nil?
|
|
385
|
+
time = Fluent::EventTime.now if time.nil? || time.to_i == 0 # `to_i == 0` for empty EventTime
|
|
380
386
|
new_es.add(time, record)
|
|
381
387
|
}
|
|
382
388
|
new_es
|
|
383
389
|
end
|
|
384
390
|
|
|
391
|
+
def extract_event_time(time)
|
|
392
|
+
time.is_a?(Array) ? time[0] : time
|
|
393
|
+
end
|
|
394
|
+
|
|
385
395
|
def add_source_info(es, conn)
|
|
386
396
|
new_es = Fluent::MultiEventStream.new
|
|
387
397
|
if @source_address_key && @source_hostname_key
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
# limitations under the License.
|
|
15
15
|
#
|
|
16
16
|
|
|
17
|
-
require '
|
|
17
|
+
require 'json'
|
|
18
18
|
|
|
19
19
|
require 'fluent/plugin/input'
|
|
20
20
|
|
|
@@ -86,7 +86,7 @@ module Fluent::Plugin
|
|
|
86
86
|
|
|
87
87
|
router.emit(@tag, now, record)
|
|
88
88
|
rescue => e
|
|
89
|
-
log.error "object space failed to emit", error: e, tag: @tag, record:
|
|
89
|
+
log.error "object space failed to emit", error: e, tag: @tag, record: JSON.generate(record)
|
|
90
90
|
log.error_backtrace
|
|
91
91
|
end
|
|
92
92
|
end
|
|
@@ -18,7 +18,7 @@ require 'fluent/plugin/input'
|
|
|
18
18
|
require 'fluent/config/error'
|
|
19
19
|
require 'fluent/plugin/parser'
|
|
20
20
|
|
|
21
|
-
require '
|
|
21
|
+
require 'json'
|
|
22
22
|
|
|
23
23
|
module Fluent::Plugin
|
|
24
24
|
class SyslogInput < Input
|
|
@@ -340,7 +340,7 @@ module Fluent::Plugin
|
|
|
340
340
|
def emit(tag, time, record)
|
|
341
341
|
router.emit(tag, time, record)
|
|
342
342
|
rescue => e
|
|
343
|
-
log.error "syslog failed to emit", error: e, tag: tag, record:
|
|
343
|
+
log.error "syslog failed to emit", error: e, tag: tag, record: JSON.generate(record)
|
|
344
344
|
end
|
|
345
345
|
end
|
|
346
346
|
end
|
|
@@ -0,0 +1,111 @@
|
|
|
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
|
+
require 'fluent/clock'
|
|
19
|
+
|
|
20
|
+
module Fluent::Plugin
|
|
21
|
+
class TailInput < Fluent::Plugin::Input
|
|
22
|
+
module Compatibility
|
|
23
|
+
# Keep these methods as compatibility entry points for plugins which
|
|
24
|
+
# inherit TailInput and override line processing methods. TailInput is a
|
|
25
|
+
# built-in plugin, not a public inheritance API, and these methods may be
|
|
26
|
+
# removed in a future release.
|
|
27
|
+
def flush_buffer(tw, buf)
|
|
28
|
+
@line_feeder.flush_buffer(tw, buf)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Feeds lines through the per-file feed, or through LineFeeder for watchers
|
|
32
|
+
# created by plugins using the deprecated LineBufferTimerFlusher.
|
|
33
|
+
#
|
|
34
|
+
# @return true if no error or unrecoverable error happens in emit action. false if got BufferOverflowError
|
|
35
|
+
def receive_lines(lines, tail_watcher)
|
|
36
|
+
return async_receive_lines(lines, tail_watcher) if @worker_pool
|
|
37
|
+
|
|
38
|
+
file_feed = tail_watcher.file_feed
|
|
39
|
+
return file_feed.feed_lines(lines, tail_watcher) if file_feed.respond_to?(:feed_lines)
|
|
40
|
+
|
|
41
|
+
@line_feeder.feed_lines(lines, tail_watcher)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def convert_line_to_event(line, es, tail_watcher)
|
|
45
|
+
@line_feeder.convert_line_to_event(line, es, tail_watcher)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def parse_singleline(lines, tail_watcher)
|
|
49
|
+
@line_feeder.parse_singleline(lines, tail_watcher)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# TailInput's default #parse_multilines path starts the flush timer before
|
|
53
|
+
# parsing. An override that skips super must call
|
|
54
|
+
# tail_watcher.line_buffer_timer_flusher.reset_timer when needed.
|
|
55
|
+
def parse_multilines(lines, tail_watcher)
|
|
56
|
+
@line_feeder.parse_multilines(lines, tail_watcher)
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
class TailWatcher
|
|
61
|
+
# The object which keeps the line buffer of the multiline mode of the file:
|
|
62
|
+
# the FileFeed built by LineFeeder#new_file_feed, or the deprecated
|
|
63
|
+
# LineBufferTimerFlusher which a plugin overriding TailInput#setup_watcher
|
|
64
|
+
# passes to keep working without changes.
|
|
65
|
+
def line_buffer_timer_flusher
|
|
66
|
+
@file_feed
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# Kept for compatibility with the plugins which build it by themselves and
|
|
70
|
+
# pass it to TailWatcher in the overridden TailInput#setup_watcher.
|
|
71
|
+
# LineFeeder#feed_lines feeds the lines of a watcher which keeps it, and
|
|
72
|
+
# LineFeeder::FileFeed built by LineFeeder#new_file_feed replaces it.
|
|
73
|
+
class LineBufferTimerFlusher
|
|
74
|
+
attr_accessor :line_buffer
|
|
75
|
+
|
|
76
|
+
def initialize(log, flush_interval, &flush_method)
|
|
77
|
+
@log = log
|
|
78
|
+
@flush_interval = flush_interval
|
|
79
|
+
@flush_method = flush_method
|
|
80
|
+
@start = nil
|
|
81
|
+
@line_buffer = nil
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def on_notify(tw)
|
|
85
|
+
unless @start && @flush_method
|
|
86
|
+
return
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
if Fluent::Clock.now - @start >= @flush_interval
|
|
90
|
+
@flush_method.call(tw, @line_buffer) if @line_buffer
|
|
91
|
+
@line_buffer = nil
|
|
92
|
+
@start = nil
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def close(tw)
|
|
97
|
+
return unless @line_buffer
|
|
98
|
+
|
|
99
|
+
@flush_method.call(tw, @line_buffer)
|
|
100
|
+
@line_buffer = nil
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def reset_timer
|
|
104
|
+
return unless @flush_interval
|
|
105
|
+
|
|
106
|
+
@start = Fluent::Clock.now
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
end
|