fluentd 1.19.2 → 1.19.3
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/CHANGELOG.md +36 -0
- data/Rakefile +7 -0
- data/lib/fluent/engine.rb +1 -1
- data/lib/fluent/event.rb +2 -1
- data/lib/fluent/plugin/buf_file.rb +3 -3
- data/lib/fluent/plugin/buf_file_single.rb +2 -2
- data/lib/fluent/plugin/buf_memory.rb +1 -1
- data/lib/fluent/plugin/buffer/chunk.rb +2 -1
- data/lib/fluent/plugin/buffer/file_chunk.rb +2 -2
- data/lib/fluent/plugin/buffer/file_single_chunk.rb +2 -2
- data/lib/fluent/plugin/buffer/memory_chunk.rb +1 -1
- data/lib/fluent/plugin/buffer.rb +3 -0
- data/lib/fluent/plugin/compressable.rb +7 -62
- data/lib/fluent/plugin/extractor.rb +121 -0
- data/lib/fluent/plugin/in_debug_agent.rb +1 -1
- data/lib/fluent/plugin/in_forward.rb +3 -1
- data/lib/fluent/plugin/in_http.rb +8 -4
- data/lib/fluent/plugin/in_monitor_agent.rb +19 -22
- data/lib/fluent/plugin/out_file.rb +10 -0
- data/lib/fluent/plugin/out_forward/socket_cache.rb +45 -10
- data/lib/fluent/plugin/out_http.rb +31 -1
- data/lib/fluent/plugin/output.rb +45 -1
- data/lib/fluent/plugin/parser_csv.rb +5 -0
- data/lib/fluent/plugin/storage_local.rb +2 -2
- data/lib/fluent/supervisor.rb +14 -0
- data/lib/fluent/test/base.rb +5 -0
- data/lib/fluent/version.rb +1 -1
- metadata +7 -4
- data/.deepsource.toml +0 -13
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 9613f1dacdb60cfccb245e988e67cec002daee2f81cbe38c2b80c30d525ad7b8
|
|
4
|
+
data.tar.gz: 4a85aa6c3bdaebd9cd069d508ac969d8792586247bef1d73d25cc906f6cd7c87
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: c2d63e0bd8cf51a61d254cfda559f38ed1bcc79ceb1bf1f0d0c80b21b922e67091d2ee8c291a0dc0fcac0110873d0045874df297e0795c0cd604af64f431f845
|
|
7
|
+
data.tar.gz: 5fe9e2450048bacc978389621b0e3f2c8aed0e9136ffe11192945a754173c1ce13ff50c310cfcdf1633d7dc784e14e9a4fc5a6de519da3bc81decb12f6d5b28d
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,41 @@
|
|
|
1
1
|
# v1.19
|
|
2
2
|
|
|
3
|
+
## Release v1.19.3 - 2026/06/25
|
|
4
|
+
|
|
5
|
+
### Bug Fix
|
|
6
|
+
|
|
7
|
+
* out_http: add strict host validation for dynamic endpoints https://github.com/fluent/fluentd/pull/5394
|
|
8
|
+
* buffer, in_http: enforce size limits on decompressed payloads https://github.com/fluent/fluentd/pull/5393
|
|
9
|
+
* in_monitor_agent: change default visibility of config, retry, and debug info https://github.com/fluent/fluentd/pull/5392
|
|
10
|
+
* output: enforce strict path boundary validation for tag https://github.com/fluent/fluentd/pull/5391
|
|
11
|
+
* engine: remove duplicated word in unreloadable plugin error message https://github.com/fluent/fluentd/pull/5389
|
|
12
|
+
* storage_local: fix encoding error when reading non-ASCII characters https://github.com/fluent/fluentd/pull/5382
|
|
13
|
+
* parser_csv: skip empty or unparseable lines https://github.com/fluent/fluentd/pull/5359
|
|
14
|
+
* out_forward: avoid reusing closed keepalive sockets after remote disconnects https://github.com/fluent/fluentd/pull/5343
|
|
15
|
+
* buffer: resume buffer correctly even though path contains [] https://github.com/fluent/fluentd/pull/5305
|
|
16
|
+
* in_debug_agent: accept only from local machine by default https://github.com/fluent/fluentd/pull/5279
|
|
17
|
+
|
|
18
|
+
### Misc
|
|
19
|
+
|
|
20
|
+
* gem: add win32-registry as runtime dependency for Ruby 4.1 https://github.com/fluent/fluentd/pull/5317
|
|
21
|
+
* output windows: check shorter service timeout on shutdown https://github.com/fluent/fluentd/pull/5306
|
|
22
|
+
* buffer: warn if default timekey (1d) will be used https://github.com/fluent/fluentd/pull/5291
|
|
23
|
+
* warn recommended exclusion path for antivirus https://github.com/fluent/fluentd/pull/5280
|
|
24
|
+
* CI fixes
|
|
25
|
+
* https://github.com/fluent/fluentd/pull/5387
|
|
26
|
+
* https://github.com/fluent/fluentd/pull/5386
|
|
27
|
+
* https://github.com/fluent/fluentd/pull/5366
|
|
28
|
+
* https://github.com/fluent/fluentd/pull/5342
|
|
29
|
+
* https://github.com/fluent/fluentd/pull/5341
|
|
30
|
+
* https://github.com/fluent/fluentd/pull/5340
|
|
31
|
+
* https://github.com/fluent/fluentd/pull/5339
|
|
32
|
+
* https://github.com/fluent/fluentd/pull/5338
|
|
33
|
+
* https://github.com/fluent/fluentd/pull/5337
|
|
34
|
+
* https://github.com/fluent/fluentd/pull/5336
|
|
35
|
+
* https://github.com/fluent/fluentd/pull/5335
|
|
36
|
+
* https://github.com/fluent/fluentd/pull/5334
|
|
37
|
+
* https://github.com/fluent/fluentd/pull/5333
|
|
38
|
+
|
|
3
39
|
## Release v1.19.2 - 2026/02/13
|
|
4
40
|
|
|
5
41
|
### Bug Fix
|
data/Rakefile
CHANGED
|
@@ -78,4 +78,11 @@ task :coverity do
|
|
|
78
78
|
FileUtils.rm_rf(['./cov-int', 'cov-fluentd.tar.gz'])
|
|
79
79
|
end
|
|
80
80
|
|
|
81
|
+
task :check_env do
|
|
82
|
+
unless ENV['GEM_HOST_API_KEY']
|
|
83
|
+
abort "Missing required environment variable: GEM_HOST_API_KEY\nSee https://guides.rubygems.org/api-key-scopes/"
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
Rake::Task["release:rubygem_push"].enhance(["check_env"])
|
|
87
|
+
|
|
81
88
|
task default: [:test, :build]
|
data/lib/fluent/engine.rb
CHANGED
|
@@ -186,7 +186,7 @@ module Fluent
|
|
|
186
186
|
|
|
187
187
|
ret.all_plugins.each do |plugin|
|
|
188
188
|
if plugin.respond_to?(:reloadable_plugin?) && !plugin.reloadable_plugin?
|
|
189
|
-
raise Fluent::ConfigError, "Unreloadable plugin
|
|
189
|
+
raise Fluent::ConfigError, "Unreloadable plugin: #{Fluent::Plugin.lookup_type_from_class(plugin.class)}, plugin_id: #{plugin.plugin_id}, class_name: #{plugin.class})"
|
|
190
190
|
end
|
|
191
191
|
end
|
|
192
192
|
|
data/lib/fluent/event.rb
CHANGED
|
@@ -268,11 +268,12 @@ module Fluent
|
|
|
268
268
|
end
|
|
269
269
|
|
|
270
270
|
class CompressedMessagePackEventStream < MessagePackEventStream
|
|
271
|
-
def initialize(data, cached_unpacker = nil, size = 0, unpacked_times: nil, unpacked_records: nil, compress: :gzip)
|
|
271
|
+
def initialize(data, cached_unpacker = nil, size = 0, unpacked_times: nil, unpacked_records: nil, compress: :gzip, decompression_size_limit: Fluent::Plugin::Compressable::DEFAULT_DECOMPRESSION_SIZE_LIMIT)
|
|
272
272
|
super(data, cached_unpacker, size, unpacked_times: unpacked_times, unpacked_records: unpacked_records)
|
|
273
273
|
@decompressed_data = nil
|
|
274
274
|
@compressed_data = data
|
|
275
275
|
@type = compress
|
|
276
|
+
@decompression_size_limit = decompression_size_limit
|
|
276
277
|
end
|
|
277
278
|
|
|
278
279
|
def empty?
|
|
@@ -205,7 +205,7 @@ module Fluent
|
|
|
205
205
|
def generate_chunk(metadata)
|
|
206
206
|
# FileChunk generates real path with unique_id
|
|
207
207
|
perm = @file_permission || system_config.file_permission
|
|
208
|
-
chunk = Fluent::Plugin::Buffer::FileChunk.new(metadata, @path, :create, perm: perm, compress: @compress)
|
|
208
|
+
chunk = Fluent::Plugin::Buffer::FileChunk.new(metadata, @path, :create, perm: perm, compress: @compress, decompression_size_limit: @decompression_size_limit)
|
|
209
209
|
log.debug "Created new chunk", chunk_id: dump_unique_id_hex(chunk.unique_id), metadata: metadata
|
|
210
210
|
|
|
211
211
|
return chunk
|
|
@@ -247,8 +247,8 @@ module Fluent
|
|
|
247
247
|
|
|
248
248
|
def escaped_patterns(patterns)
|
|
249
249
|
patterns.map { |pattern|
|
|
250
|
-
# '{' '}' are special character in Dir.glob
|
|
251
|
-
pattern.gsub(/[\{\}]/) { |c| "\\#{c}" }
|
|
250
|
+
# '{', '}', '[' and ']' are special character in Dir.glob
|
|
251
|
+
pattern.gsub(/[\{\}\[\]]/) { |c| "\\#{c}" }
|
|
252
252
|
}
|
|
253
253
|
end
|
|
254
254
|
end
|
|
@@ -183,7 +183,7 @@ module Fluent
|
|
|
183
183
|
end
|
|
184
184
|
|
|
185
185
|
begin
|
|
186
|
-
chunk = Fluent::Plugin::Buffer::FileSingleChunk.new(m, path, mode, @key_in_path, compress: @compress)
|
|
186
|
+
chunk = Fluent::Plugin::Buffer::FileSingleChunk.new(m, path, mode, @key_in_path, compress: @compress, decompression_size_limit: @decompression_size_limit)
|
|
187
187
|
chunk.restore_size(@chunk_format) if @calc_num_records
|
|
188
188
|
rescue Fluent::Plugin::Buffer::FileSingleChunk::FileChunkError => e
|
|
189
189
|
exist_broken_file = true
|
|
@@ -216,7 +216,7 @@ module Fluent
|
|
|
216
216
|
def generate_chunk(metadata)
|
|
217
217
|
# FileChunk generates real path with unique_id
|
|
218
218
|
perm = @file_permission || system_config.file_permission
|
|
219
|
-
chunk = Fluent::Plugin::Buffer::FileSingleChunk.new(metadata, @path, :create, @key_in_path, perm: perm, compress: @compress)
|
|
219
|
+
chunk = Fluent::Plugin::Buffer::FileSingleChunk.new(metadata, @path, :create, @key_in_path, perm: perm, compress: @compress, decompression_size_limit: @decompression_size_limit)
|
|
220
220
|
|
|
221
221
|
log.debug "Created new chunk", chunk_id: dump_unique_id_hex(chunk.unique_id), metadata: metadata
|
|
222
222
|
|
|
@@ -27,7 +27,7 @@ module Fluent
|
|
|
27
27
|
end
|
|
28
28
|
|
|
29
29
|
def generate_chunk(metadata)
|
|
30
|
-
Fluent::Plugin::Buffer::MemoryChunk.new(metadata, compress: @compress)
|
|
30
|
+
Fluent::Plugin::Buffer::MemoryChunk.new(metadata, compress: @compress, decompression_size_limit: @decompression_size_limit)
|
|
31
31
|
end
|
|
32
32
|
end
|
|
33
33
|
end
|
|
@@ -48,7 +48,7 @@ module Fluent
|
|
|
48
48
|
|
|
49
49
|
# TODO: CompressedPackedMessage of forward protocol?
|
|
50
50
|
|
|
51
|
-
def initialize(metadata, compress: :text)
|
|
51
|
+
def initialize(metadata, compress: :text, decompression_size_limit: Compressable::DEFAULT_DECOMPRESSION_SIZE_LIMIT)
|
|
52
52
|
super()
|
|
53
53
|
@unique_id = generate_unique_id
|
|
54
54
|
@metadata = metadata
|
|
@@ -64,6 +64,7 @@ module Fluent
|
|
|
64
64
|
elsif compress == :zstd
|
|
65
65
|
extend ZstdDecompressable
|
|
66
66
|
end
|
|
67
|
+
@decompression_size_limit = decompression_size_limit
|
|
67
68
|
end
|
|
68
69
|
|
|
69
70
|
attr_reader :unique_id, :metadata, :state
|
|
@@ -39,8 +39,8 @@ module Fluent
|
|
|
39
39
|
|
|
40
40
|
attr_reader :path, :meta_path, :permission
|
|
41
41
|
|
|
42
|
-
def initialize(metadata, path, mode, perm: nil, compress: :text)
|
|
43
|
-
super(metadata, compress: compress)
|
|
42
|
+
def initialize(metadata, path, mode, perm: nil, compress: :text, decompression_size_limit: Compressable::DEFAULT_DECOMPRESSION_SIZE_LIMIT)
|
|
43
|
+
super(metadata, compress: compress, decompression_size_limit: decompression_size_limit)
|
|
44
44
|
perm ||= Fluent::DEFAULT_FILE_PERMISSION
|
|
45
45
|
@permission = perm.is_a?(String) ? perm.to_i(8) : perm
|
|
46
46
|
@bytesize = @size = @adding_bytes = @adding_size = 0
|
|
@@ -35,8 +35,8 @@ module Fluent
|
|
|
35
35
|
|
|
36
36
|
attr_reader :path, :permission
|
|
37
37
|
|
|
38
|
-
def initialize(metadata, path, mode, key, perm: Fluent::DEFAULT_FILE_PERMISSION, compress: :text)
|
|
39
|
-
super(metadata, compress: compress)
|
|
38
|
+
def initialize(metadata, path, mode, key, perm: Fluent::DEFAULT_FILE_PERMISSION, compress: :text, decompression_size_limit: Compressable::DEFAULT_DECOMPRESSION_SIZE_LIMIT)
|
|
39
|
+
super(metadata, compress: compress, decompression_size_limit: decompression_size_limit)
|
|
40
40
|
@key = key
|
|
41
41
|
perm ||= Fluent::DEFAULT_FILE_PERMISSION
|
|
42
42
|
@permission = perm.is_a?(String) ? perm.to_i(8) : perm
|
|
@@ -20,7 +20,7 @@ module Fluent
|
|
|
20
20
|
module Plugin
|
|
21
21
|
class Buffer
|
|
22
22
|
class MemoryChunk < Chunk
|
|
23
|
-
def initialize(metadata, compress: :text)
|
|
23
|
+
def initialize(metadata, compress: :text, decompression_size_limit: Compressable::DEFAULT_DECOMPRESSION_SIZE_LIMIT)
|
|
24
24
|
super
|
|
25
25
|
@chunk = ''.force_encoding(Encoding::ASCII_8BIT)
|
|
26
26
|
@chunk_bytes = 0
|
data/lib/fluent/plugin/buffer.rb
CHANGED
|
@@ -66,6 +66,9 @@ module Fluent
|
|
|
66
66
|
desc 'Compress buffered data.'
|
|
67
67
|
config_param :compress, :enum, list: [:text, :gzip, :zstd], default: :text
|
|
68
68
|
|
|
69
|
+
desc 'The size limit of the decompressed element.'
|
|
70
|
+
config_param :decompression_size_limit, :size, default: Compressable::DEFAULT_DECOMPRESSION_SIZE_LIMIT
|
|
71
|
+
|
|
69
72
|
desc 'If true, chunks are thrown away when unrecoverable error happens'
|
|
70
73
|
config_param :disable_chunk_backup, :bool, default: false
|
|
71
74
|
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
# limitations under the License.
|
|
15
15
|
#
|
|
16
16
|
|
|
17
|
+
require 'fluent/plugin/extractor'
|
|
17
18
|
require 'stringio'
|
|
18
19
|
require 'zlib'
|
|
19
20
|
require 'zstd-ruby'
|
|
@@ -21,6 +22,8 @@ require 'zstd-ruby'
|
|
|
21
22
|
module Fluent
|
|
22
23
|
module Plugin
|
|
23
24
|
module Compressable
|
|
25
|
+
DEFAULT_DECOMPRESSION_SIZE_LIMIT = 256 * 1024 * 1024
|
|
26
|
+
|
|
24
27
|
def compress(data, type: :gzip, **kwargs)
|
|
25
28
|
output_io = kwargs[:output_io]
|
|
26
29
|
io = output_io || StringIO.new
|
|
@@ -60,79 +63,21 @@ module Fluent
|
|
|
60
63
|
|
|
61
64
|
private
|
|
62
65
|
|
|
63
|
-
def string_decompress_gzip(compressed_data)
|
|
64
|
-
io = StringIO.new(compressed_data)
|
|
65
|
-
out = ''
|
|
66
|
-
loop do
|
|
67
|
-
reader = Zlib::GzipReader.new(io)
|
|
68
|
-
out << reader.read
|
|
69
|
-
unused = reader.unused
|
|
70
|
-
reader.finish
|
|
71
|
-
unless unused.nil?
|
|
72
|
-
adjust = unused.length
|
|
73
|
-
io.pos -= adjust
|
|
74
|
-
end
|
|
75
|
-
break if io.eof?
|
|
76
|
-
end
|
|
77
|
-
out
|
|
78
|
-
end
|
|
79
|
-
|
|
80
|
-
def string_decompress_zstd(compressed_data)
|
|
81
|
-
io = StringIO.new(compressed_data)
|
|
82
|
-
reader = Zstd::StreamReader.new(io)
|
|
83
|
-
out = ''
|
|
84
|
-
loop do
|
|
85
|
-
# Zstd::StreamReader needs to specify the size of the buffer
|
|
86
|
-
out << reader.read(1024)
|
|
87
|
-
# Zstd::StreamReader doesn't provide unused data, so we have to manually adjust the position
|
|
88
|
-
break if io.eof?
|
|
89
|
-
end
|
|
90
|
-
out
|
|
91
|
-
end
|
|
92
|
-
|
|
93
66
|
def string_decompress(compressed_data, type = :gzip)
|
|
94
67
|
if type == :gzip
|
|
95
|
-
|
|
68
|
+
Extractor.decompress_gzip(compressed_data, limit: @decompression_size_limit || DEFAULT_DECOMPRESSION_SIZE_LIMIT)
|
|
96
69
|
elsif type == :zstd
|
|
97
|
-
|
|
70
|
+
Extractor.decompress_zstd(compressed_data, limit: @decompression_size_limit || DEFAULT_DECOMPRESSION_SIZE_LIMIT)
|
|
98
71
|
else
|
|
99
72
|
raise ArgumentError, "Unknown compression type: #{type}"
|
|
100
73
|
end
|
|
101
74
|
end
|
|
102
75
|
|
|
103
|
-
def io_decompress_gzip(input, output)
|
|
104
|
-
loop do
|
|
105
|
-
reader = Zlib::GzipReader.new(input)
|
|
106
|
-
v = reader.read
|
|
107
|
-
output.write(v)
|
|
108
|
-
unused = reader.unused
|
|
109
|
-
reader.finish
|
|
110
|
-
unless unused.nil?
|
|
111
|
-
adjust = unused.length
|
|
112
|
-
input.pos -= adjust
|
|
113
|
-
end
|
|
114
|
-
break if input.eof?
|
|
115
|
-
end
|
|
116
|
-
output
|
|
117
|
-
end
|
|
118
|
-
|
|
119
|
-
def io_decompress_zstd(input, output)
|
|
120
|
-
reader = Zstd::StreamReader.new(input)
|
|
121
|
-
loop do
|
|
122
|
-
# Zstd::StreamReader needs to specify the size of the buffer
|
|
123
|
-
v = reader.read(1024)
|
|
124
|
-
output.write(v)
|
|
125
|
-
# Zstd::StreamReader doesn't provide unused data, so we have to manually adjust the position
|
|
126
|
-
break if input.eof?
|
|
127
|
-
end
|
|
128
|
-
output
|
|
129
|
-
end
|
|
130
|
-
|
|
131
76
|
def io_decompress(input, output, type = :gzip)
|
|
132
77
|
if type == :gzip
|
|
133
|
-
io_decompress_gzip(input, output)
|
|
78
|
+
Extractor.io_decompress_gzip(input, output)
|
|
134
79
|
elsif type == :zstd
|
|
135
|
-
io_decompress_zstd(input, output)
|
|
80
|
+
Extractor.io_decompress_zstd(input, output)
|
|
136
81
|
else
|
|
137
82
|
raise ArgumentError, "Unknown compression type: #{type}"
|
|
138
83
|
end
|
|
@@ -0,0 +1,121 @@
|
|
|
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 'stringio'
|
|
18
|
+
require 'zlib'
|
|
19
|
+
require 'zstd-ruby'
|
|
20
|
+
require 'fluent/error'
|
|
21
|
+
|
|
22
|
+
module Fluent
|
|
23
|
+
module Plugin
|
|
24
|
+
module Extractor
|
|
25
|
+
class SizeLimitError < UnrecoverableError; end
|
|
26
|
+
|
|
27
|
+
BYTES_TO_READ = 64 * 1024
|
|
28
|
+
INFLATE_BYTES_TO_READ = 1024
|
|
29
|
+
|
|
30
|
+
def self.decompress_gzip(compressed_data, limit:)
|
|
31
|
+
io = StringIO.new(compressed_data)
|
|
32
|
+
out = ''
|
|
33
|
+
loop do
|
|
34
|
+
reader = Zlib::GzipReader.new(io)
|
|
35
|
+
while (chunk = reader.read(BYTES_TO_READ))
|
|
36
|
+
out << chunk
|
|
37
|
+
if out.bytesize > limit
|
|
38
|
+
raise SizeLimitError, "Decompressed data exceeds limit of #{limit} bytes"
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
unused = reader.unused
|
|
43
|
+
reader.finish
|
|
44
|
+
unless unused.nil?
|
|
45
|
+
adjust = unused.length
|
|
46
|
+
io.pos -= adjust
|
|
47
|
+
end
|
|
48
|
+
break if io.eof?
|
|
49
|
+
end
|
|
50
|
+
out
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def self.decompress_zstd(compressed_data, limit:)
|
|
54
|
+
io = StringIO.new(compressed_data)
|
|
55
|
+
reader = Zstd::StreamReader.new(io)
|
|
56
|
+
out = ''
|
|
57
|
+
loop do
|
|
58
|
+
# Zstd::StreamReader needs to specify the size of the buffer
|
|
59
|
+
out << reader.read(BYTES_TO_READ)
|
|
60
|
+
if out.bytesize > limit
|
|
61
|
+
raise SizeLimitError, "Decompressed data exceeds limit of #{limit} bytes"
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# Zstd::StreamReader doesn't provide unused data, so we have to manually adjust the position
|
|
65
|
+
break if io.eof?
|
|
66
|
+
end
|
|
67
|
+
out
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def self.decompress_deflate(compressed_data, limit:)
|
|
71
|
+
io = StringIO.new(compressed_data)
|
|
72
|
+
out = ''
|
|
73
|
+
begin
|
|
74
|
+
zstream = Zlib::Inflate.new
|
|
75
|
+
while (chunk = io.read(INFLATE_BYTES_TO_READ))
|
|
76
|
+
out << zstream.inflate(chunk)
|
|
77
|
+
if out.bytesize > limit
|
|
78
|
+
raise SizeLimitError, "Decompressed data exceeds limit of #{limit} bytes"
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
out << zstream.finish
|
|
82
|
+
if out.bytesize > limit
|
|
83
|
+
raise SizeLimitError, "Decompressed data exceeds limit of #{limit} bytes"
|
|
84
|
+
end
|
|
85
|
+
ensure
|
|
86
|
+
zstream&.close
|
|
87
|
+
end
|
|
88
|
+
out
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def self.io_decompress_gzip(input, output)
|
|
92
|
+
loop do
|
|
93
|
+
reader = Zlib::GzipReader.new(input)
|
|
94
|
+
while (chunk = reader.read(BYTES_TO_READ))
|
|
95
|
+
output.write(chunk)
|
|
96
|
+
end
|
|
97
|
+
unused = reader.unused
|
|
98
|
+
reader.finish
|
|
99
|
+
unless unused.nil?
|
|
100
|
+
adjust = unused.length
|
|
101
|
+
input.pos -= adjust
|
|
102
|
+
end
|
|
103
|
+
break if input.eof?
|
|
104
|
+
end
|
|
105
|
+
output
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def self.io_decompress_zstd(input, output)
|
|
109
|
+
reader = Zstd::StreamReader.new(input)
|
|
110
|
+
loop do
|
|
111
|
+
# Zstd::StreamReader needs to specify the size of the buffer
|
|
112
|
+
chunk = reader.read(BYTES_TO_READ)
|
|
113
|
+
output.write(chunk)
|
|
114
|
+
# Zstd::StreamReader doesn't provide unused data, so we have to manually adjust the position
|
|
115
|
+
break if input.eof?
|
|
116
|
+
end
|
|
117
|
+
output
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
end
|
|
@@ -26,7 +26,7 @@ module Fluent::Plugin
|
|
|
26
26
|
super
|
|
27
27
|
end
|
|
28
28
|
|
|
29
|
-
config_param :bind, :string, default: '
|
|
29
|
+
config_param :bind, :string, default: '127.0.0.1'
|
|
30
30
|
config_param :port, :integer, default: 24230
|
|
31
31
|
config_param :unix_path, :string, default: nil
|
|
32
32
|
#config_param :unix_mode # TODO
|
|
@@ -54,6 +54,8 @@ module Fluent::Plugin
|
|
|
54
54
|
config_param :chunk_size_warn_limit, :size, default: nil
|
|
55
55
|
desc 'Received chunk is dropped if it is larger than this value.'
|
|
56
56
|
config_param :chunk_size_limit, :size, default: nil
|
|
57
|
+
desc 'The size limit of the decompressed element.'
|
|
58
|
+
config_param :decompression_size_limit, :size, default: 256*1024*1024
|
|
57
59
|
desc 'Skip an event if incoming event is invalid.'
|
|
58
60
|
config_param :skip_invalid_event, :bool, default: true
|
|
59
61
|
|
|
@@ -311,7 +313,7 @@ module Fluent::Plugin
|
|
|
311
313
|
size = option['size'] || 0
|
|
312
314
|
|
|
313
315
|
if option['compressed'] && option['compressed'] != 'text'
|
|
314
|
-
es = Fluent::CompressedMessagePackEventStream.new(entries, nil, size.to_i, compress: option['compressed'].to_sym)
|
|
316
|
+
es = Fluent::CompressedMessagePackEventStream.new(entries, nil, size.to_i, compress: option['compressed'].to_sym, decompression_size_limit: @decompression_size_limit)
|
|
315
317
|
else
|
|
316
318
|
es = Fluent::MessagePackEventStream.new(entries, nil, size.to_i)
|
|
317
319
|
end
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
# limitations under the License.
|
|
15
15
|
#
|
|
16
16
|
|
|
17
|
+
require 'fluent/plugin/extractor'
|
|
17
18
|
require 'fluent/plugin/input'
|
|
18
19
|
require 'fluent/plugin/parser'
|
|
19
20
|
require 'fluent/event'
|
|
@@ -64,6 +65,8 @@ module Fluent::Plugin
|
|
|
64
65
|
config_param :bind, :string, default: '0.0.0.0'
|
|
65
66
|
desc 'The size limit of the POSTed element. Default is 32MB.'
|
|
66
67
|
config_param :body_size_limit, :size, default: 32*1024*1024 # TODO default
|
|
68
|
+
desc 'The size limit of the decompressed element.'
|
|
69
|
+
config_param :decompression_size_limit, :size, default: 256*1024*1024 # TODO default
|
|
67
70
|
desc 'The timeout limit for keeping the connection alive.'
|
|
68
71
|
config_param :keepalive_timeout, :time, default: 10 # TODO default
|
|
69
72
|
config_param :backlog, :integer, default: nil
|
|
@@ -259,7 +262,7 @@ module Fluent::Plugin
|
|
|
259
262
|
|
|
260
263
|
def on_server_connect(conn)
|
|
261
264
|
handler = Handler.new(conn, @km, method(:on_request),
|
|
262
|
-
@body_size_limit, @format_name, log,
|
|
265
|
+
@body_size_limit, @decompression_size_limit, @format_name, log,
|
|
263
266
|
@cors_allow_origins, @cors_allow_credentials,
|
|
264
267
|
@add_query_params)
|
|
265
268
|
|
|
@@ -343,12 +346,13 @@ module Fluent::Plugin
|
|
|
343
346
|
class Handler
|
|
344
347
|
attr_reader :content_type
|
|
345
348
|
|
|
346
|
-
def initialize(io, km, callback, body_size_limit, format_name, log,
|
|
349
|
+
def initialize(io, km, callback, body_size_limit, decompression_size_limit, format_name, log,
|
|
347
350
|
cors_allow_origins, cors_allow_credentials, add_query_params)
|
|
348
351
|
@io = io
|
|
349
352
|
@km = km
|
|
350
353
|
@callback = callback
|
|
351
354
|
@body_size_limit = body_size_limit
|
|
355
|
+
@decompression_size_limit = decompression_size_limit
|
|
352
356
|
@next_close = false
|
|
353
357
|
@format_name = format_name
|
|
354
358
|
@log = log
|
|
@@ -518,9 +522,9 @@ module Fluent::Plugin
|
|
|
518
522
|
# For now, we only support 'gzip' and 'deflate'.
|
|
519
523
|
begin
|
|
520
524
|
if @content_encoding == 'gzip'.freeze
|
|
521
|
-
@body =
|
|
525
|
+
@body = Extractor.decompress_gzip(@body, limit: @decompression_size_limit)
|
|
522
526
|
elsif @content_encoding == 'deflate'.freeze
|
|
523
|
-
@body =
|
|
527
|
+
@body = Extractor.decompress_deflate(@body, limit: @decompression_size_limit)
|
|
524
528
|
end
|
|
525
529
|
rescue
|
|
526
530
|
@log.warn 'fails to decode payload', error: $!.to_s
|
|
@@ -37,9 +37,11 @@ module Fluent::Plugin
|
|
|
37
37
|
desc 'Determine the rate to emit internal metrics as events.'
|
|
38
38
|
config_param :emit_interval, :time, default: 60
|
|
39
39
|
desc 'Determine whether to include the config information.'
|
|
40
|
-
config_param :include_config, :bool, default:
|
|
40
|
+
config_param :include_config, :bool, default: false
|
|
41
41
|
desc 'Determine whether to include the retry information.'
|
|
42
|
-
config_param :include_retry, :bool, default:
|
|
42
|
+
config_param :include_retry, :bool, default: false
|
|
43
|
+
desc 'Determine whether to include the debug information.'
|
|
44
|
+
config_param :include_debug_info, :bool, default: false
|
|
43
45
|
|
|
44
46
|
class APIHandler
|
|
45
47
|
def initialize(agent)
|
|
@@ -151,28 +153,23 @@ module Fluent::Plugin
|
|
|
151
153
|
# parse ?=query string
|
|
152
154
|
qs.merge!(req.query || {})
|
|
153
155
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
opts[:pretty_json] = true
|
|
160
|
-
end
|
|
161
|
-
|
|
162
|
-
if ivars = qs['with_ivars'.freeze].first
|
|
163
|
-
opts[:ivars] = ivars.split(',')
|
|
164
|
-
end
|
|
156
|
+
opts = {
|
|
157
|
+
query: qs,
|
|
158
|
+
with_config: @agent.include_config,
|
|
159
|
+
with_retry: @agent.include_retry
|
|
160
|
+
}
|
|
165
161
|
|
|
166
|
-
if
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
162
|
+
if @agent.include_debug_info
|
|
163
|
+
# if ?debug=1 is set, set :with_debug_info for get_monitor_info
|
|
164
|
+
# and :pretty_json for render_json_error
|
|
165
|
+
if qs['debug'.freeze].first
|
|
166
|
+
opts[:with_debug_info] = true
|
|
167
|
+
opts[:pretty_json] = true
|
|
168
|
+
end
|
|
171
169
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
opts[:with_retry] = @agent.include_retry
|
|
170
|
+
if ivars = qs['with_ivars'.freeze].first
|
|
171
|
+
opts[:ivars] = ivars.split(',')
|
|
172
|
+
end
|
|
176
173
|
end
|
|
177
174
|
|
|
178
175
|
opts
|
|
@@ -117,7 +117,17 @@ module Fluent::Plugin
|
|
|
117
117
|
configured_time_slice_format = conf['time_slice_format']
|
|
118
118
|
|
|
119
119
|
if conf.elements(name: 'buffer').empty?
|
|
120
|
+
# no <buffer> section, default time chunk key and timekey (1d) will be used.
|
|
121
|
+
log.warn "default timekey interval (1d) will be used because of missing <buffer> section. To change the output frequency, please modify the timekey value"
|
|
122
|
+
|
|
120
123
|
conf.add_element('buffer', 'time')
|
|
124
|
+
else
|
|
125
|
+
unless conf.elements(name: 'buffer').first.has_key?('timekey')
|
|
126
|
+
if conf.elements(name: 'buffer').first.arg != "[]"
|
|
127
|
+
# with <buffer> section (except <buffer []>), and no timekey
|
|
128
|
+
log.warn "default timekey interval (1d) will be used. To change the output frequency, please modify the timekey value"
|
|
129
|
+
end
|
|
130
|
+
end
|
|
121
131
|
end
|
|
122
132
|
buffer_conf = conf.elements(name: 'buffer').first
|
|
123
133
|
# Fluent::PluginId#configure is not called yet, so we can't use #plugin_root_dir here.
|
|
@@ -31,20 +31,26 @@ module Fluent::Plugin
|
|
|
31
31
|
end
|
|
32
32
|
|
|
33
33
|
def checkout_or(key)
|
|
34
|
+
obsolete_sockets = []
|
|
35
|
+
|
|
34
36
|
@mutex.synchronize do
|
|
35
|
-
tsock = pick_socket(key)
|
|
37
|
+
tsock, obsolete_sockets = pick_socket(key)
|
|
36
38
|
|
|
37
39
|
if tsock
|
|
38
|
-
tsock.sock
|
|
40
|
+
return tsock.sock
|
|
39
41
|
else
|
|
40
42
|
sock = yield
|
|
41
43
|
new_tsock = TimedSocket.new(timeout, key, sock)
|
|
42
44
|
@log.debug("connect new socket #{new_tsock}")
|
|
43
45
|
|
|
44
46
|
@inflight_sockets[sock] = new_tsock
|
|
45
|
-
new_tsock.sock
|
|
47
|
+
return new_tsock.sock
|
|
46
48
|
end
|
|
47
49
|
end
|
|
50
|
+
ensure
|
|
51
|
+
obsolete_sockets.each do |sock|
|
|
52
|
+
sock.sock.close rescue nil
|
|
53
|
+
end
|
|
48
54
|
end
|
|
49
55
|
|
|
50
56
|
def checkin(sock)
|
|
@@ -117,17 +123,32 @@ module Fluent::Plugin
|
|
|
117
123
|
# this method is not thread safe
|
|
118
124
|
def pick_socket(key)
|
|
119
125
|
if @available_sockets[key].empty?
|
|
120
|
-
return nil
|
|
126
|
+
return nil, []
|
|
121
127
|
end
|
|
122
128
|
|
|
123
129
|
t = Time.now
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
+
selected = nil
|
|
131
|
+
remaining = []
|
|
132
|
+
obsolete_sockets = []
|
|
133
|
+
|
|
134
|
+
@available_sockets[key].each do |sock|
|
|
135
|
+
if expired_socket?(sock, time: t) || unavailable_socket?(sock.sock)
|
|
136
|
+
obsolete_sockets << sock
|
|
137
|
+
elsif selected.nil?
|
|
138
|
+
selected = sock
|
|
139
|
+
else
|
|
140
|
+
remaining << sock
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
@available_sockets[key] = remaining
|
|
145
|
+
|
|
146
|
+
if selected
|
|
147
|
+
@inflight_sockets[selected.sock] = selected
|
|
148
|
+
selected.timeout = timeout
|
|
130
149
|
end
|
|
150
|
+
|
|
151
|
+
[selected, obsolete_sockets]
|
|
131
152
|
end
|
|
132
153
|
|
|
133
154
|
def timeout
|
|
@@ -137,6 +158,20 @@ module Fluent::Plugin
|
|
|
137
158
|
def expired_socket?(sock, time: Time.now)
|
|
138
159
|
sock.timeout ? sock.timeout < time : false
|
|
139
160
|
end
|
|
161
|
+
|
|
162
|
+
def unavailable_socket?(sock)
|
|
163
|
+
return sock.closed? if sock.respond_to?(:closed?) && sock.closed?
|
|
164
|
+
|
|
165
|
+
io = if sock.respond_to?(:to_io)
|
|
166
|
+
sock.to_io
|
|
167
|
+
elsif sock.is_a?(IO)
|
|
168
|
+
sock
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
io ? !!IO.select([io], nil, nil, 0) : false
|
|
172
|
+
rescue IOError, SystemCallError
|
|
173
|
+
true
|
|
174
|
+
end
|
|
140
175
|
end
|
|
141
176
|
end
|
|
142
177
|
end
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
require 'net/http'
|
|
18
18
|
require 'uri'
|
|
19
19
|
require 'openssl'
|
|
20
|
+
require 'securerandom'
|
|
20
21
|
require 'fluent/tls'
|
|
21
22
|
require 'fluent/plugin/output'
|
|
22
23
|
require 'fluent/plugin_helper/socket'
|
|
@@ -58,6 +59,9 @@ module Fluent::Plugin
|
|
|
58
59
|
desc 'Compress HTTP request body'
|
|
59
60
|
config_param :compress, :enum, list: [:text, :gzip], default: :text
|
|
60
61
|
|
|
62
|
+
desc 'Allowed hosts list for dynamic endpoints'
|
|
63
|
+
config_param :allowed_hosts, :array, default: []
|
|
64
|
+
|
|
61
65
|
desc 'The connection open timeout in seconds'
|
|
62
66
|
config_param :open_timeout, :integer, default: nil
|
|
63
67
|
desc 'The read timeout in seconds'
|
|
@@ -106,6 +110,11 @@ module Fluent::Plugin
|
|
|
106
110
|
config_param :aws_role_arn, :string, default: nil
|
|
107
111
|
end
|
|
108
112
|
|
|
113
|
+
# To prevent URI::InvalidURIError, we replace Fluentd placeholders with a dummy string.
|
|
114
|
+
# We use the ".invalid" TLD (RFC 2606) to ensure it is RFC-compliant for URI parsing,
|
|
115
|
+
# while guaranteeing it will never conflict with a real-world hostname.
|
|
116
|
+
REPLACED_ENDPOINT_PLACEHOLDER = "#{SecureRandom.uuid}.invalid".freeze
|
|
117
|
+
|
|
109
118
|
def connection_cache_id_thread_key
|
|
110
119
|
"#{plugin_id}_connection_cache_id"
|
|
111
120
|
end
|
|
@@ -146,6 +155,15 @@ module Fluent::Plugin
|
|
|
146
155
|
@retryable_response_codes = [503]
|
|
147
156
|
end
|
|
148
157
|
|
|
158
|
+
begin
|
|
159
|
+
# Replace all Fluentd placeholder syntaxes (${...} or %{...})
|
|
160
|
+
endpoint = @endpoint.gsub(%r([$%]{[^}]+}), REPLACED_ENDPOINT_PLACEHOLDER)
|
|
161
|
+
# If @endpoint has placeholder as host name, then, @endpoint_host == REPLACED_ENDPOINT_PLACEHOLDER
|
|
162
|
+
@endpoint_host = URI.parse(endpoint).host
|
|
163
|
+
rescue URI::InvalidURIError => e
|
|
164
|
+
raise Fluent::ConfigError, "Invalid endpoint URI: #{@endpoint} (#{e.message})"
|
|
165
|
+
end
|
|
166
|
+
|
|
149
167
|
@http_opt = setup_http_option
|
|
150
168
|
@proxy_uri = URI.parse(@proxy) if @proxy
|
|
151
169
|
@formatter = formatter_create
|
|
@@ -278,7 +296,19 @@ module Fluent::Plugin
|
|
|
278
296
|
|
|
279
297
|
def parse_endpoint(chunk)
|
|
280
298
|
endpoint = extract_placeholders(@endpoint, chunk)
|
|
281
|
-
URI.parse(endpoint)
|
|
299
|
+
uri = URI.parse(endpoint)
|
|
300
|
+
|
|
301
|
+
if @endpoint_host != uri.host
|
|
302
|
+
if @allowed_hosts.empty?
|
|
303
|
+
raise Fluent::UnrecoverableError, "allowed_hosts is strictly required when using placeholders in the endpoint host"
|
|
304
|
+
end
|
|
305
|
+
|
|
306
|
+
unless @allowed_hosts.include?(uri.host)
|
|
307
|
+
raise Fluent::UnrecoverableError, "Not allowed host: #{uri.host}"
|
|
308
|
+
end
|
|
309
|
+
end
|
|
310
|
+
|
|
311
|
+
uri
|
|
282
312
|
end
|
|
283
313
|
|
|
284
314
|
def set_headers(req, uri, chunk)
|
data/lib/fluent/plugin/output.rb
CHANGED
|
@@ -44,6 +44,8 @@ module Fluent
|
|
|
44
44
|
CHUNK_KEY_PLACEHOLDER_PATTERN = /\$\{([-_.@$a-zA-Z0-9]+)\}/
|
|
45
45
|
CHUNK_TAG_PLACEHOLDER_PATTERN = /\$\{(tag(?:\[-?\d+\])?)\}/
|
|
46
46
|
CHUNK_ID_PLACEHOLDER_PATTERN = /\$\{chunk_id\}/
|
|
47
|
+
INVALID_PATH_COMPONENT_PATTERN = %r{\.\.[/\\]|^[/\\]}
|
|
48
|
+
PARENT_DIRECTORY_PATTERN = %r{\.\.[/\\]}
|
|
47
49
|
|
|
48
50
|
CHUNKING_FIELD_WARN_NUM = 4
|
|
49
51
|
|
|
@@ -372,6 +374,13 @@ module Fluent
|
|
|
372
374
|
buf_type = Plugin.lookup_type_from_class(@buffer.class)
|
|
373
375
|
log.warn "'flush_at_shutdown' is false, and buffer plugin '#{buf_type}' is not persistent buffer."
|
|
374
376
|
log.warn "your configuration will lose buffered data at shutdown. please confirm your configuration again."
|
|
377
|
+
else
|
|
378
|
+
if Fluent.windows? && @buffer.persistent?
|
|
379
|
+
service_timeout = read_wait_to_kill_service_timeout
|
|
380
|
+
if service_timeout && service_timeout <= 5000 # default value might varies on windows client/server
|
|
381
|
+
log.warn "your WaitToKillServiceTimeout=#{service_timeout} registry configuration seems too short. Recommend to extend the value of 'HKLM\\SYSTEM\\CurrentControlSet\\Control\\WaitToKillServiceTimeout' to prevent buffer corruption from a forced shutdown"
|
|
382
|
+
end
|
|
383
|
+
end
|
|
375
384
|
end
|
|
376
385
|
|
|
377
386
|
if (@flush_mode != :interval) && buffer_conf.has_key?('flush_interval')
|
|
@@ -418,6 +427,22 @@ module Fluent
|
|
|
418
427
|
self
|
|
419
428
|
end
|
|
420
429
|
|
|
430
|
+
def read_wait_to_kill_service_timeout
|
|
431
|
+
if Fluent.windows?
|
|
432
|
+
begin
|
|
433
|
+
require "win32/registry"
|
|
434
|
+
Win32::Registry::HKEY_LOCAL_MACHINE.open("SYSTEM\\CurrentControlSet\\Control",
|
|
435
|
+
Win32::Registry::KEY_READ) do |reg|
|
|
436
|
+
reg["WaitToKillServiceTimeout"].to_i
|
|
437
|
+
end
|
|
438
|
+
rescue => e
|
|
439
|
+
log.warn "'flush_at_shutdown' is true, but can't check WaitToKillServiceTimeout registry configuration", error: e
|
|
440
|
+
end
|
|
441
|
+
else
|
|
442
|
+
nil # not supported
|
|
443
|
+
end
|
|
444
|
+
end
|
|
445
|
+
|
|
421
446
|
def keep_buffer_config_compat
|
|
422
447
|
# Need this to call `@buffer_config.disable_chunk_backup` just as before,
|
|
423
448
|
# since some plugins may use this option in this way.
|
|
@@ -802,9 +827,17 @@ module Fluent
|
|
|
802
827
|
# ${tag}, ${tag[0]}, ${tag[1]}, ... , ${tag[-2]}, ${tag[-1]}
|
|
803
828
|
if @chunk_key_tag
|
|
804
829
|
if str.include?('${tag}')
|
|
830
|
+
if metadata.tag.match?(INVALID_PATH_COMPONENT_PATTERN)
|
|
831
|
+
raise Fluent::UnrecoverableError, "Invalid path component detected in tag: #{metadata.tag}"
|
|
832
|
+
end
|
|
833
|
+
|
|
805
834
|
rvalue = rvalue.gsub('${tag}', metadata.tag)
|
|
806
835
|
end
|
|
807
836
|
if CHUNK_TAG_PLACEHOLDER_PATTERN.match?(str)
|
|
837
|
+
if metadata.tag.match?(INVALID_PATH_COMPONENT_PATTERN)
|
|
838
|
+
raise Fluent::UnrecoverableError, "Invalid path component detected in tag: #{metadata.tag}"
|
|
839
|
+
end
|
|
840
|
+
|
|
808
841
|
hash = {}
|
|
809
842
|
tag_parts = metadata.tag.split('.')
|
|
810
843
|
tag_parts.each_with_index do |part, i|
|
|
@@ -835,10 +868,21 @@ module Fluent
|
|
|
835
868
|
end
|
|
836
869
|
|
|
837
870
|
rvalue = rvalue.gsub(CHUNK_KEY_PLACEHOLDER_PATTERN) do |matched|
|
|
838
|
-
hash.fetch(matched) do
|
|
871
|
+
replace = hash.fetch(matched) do
|
|
839
872
|
log.warn "chunk key placeholder '#{matched[2..-2]}' not replaced. template:#{str}"
|
|
840
873
|
''
|
|
841
874
|
end
|
|
875
|
+
if replace.to_s.match?(INVALID_PATH_COMPONENT_PATTERN)
|
|
876
|
+
raise Fluent::UnrecoverableError, "Invalid path component detected in #{matched}: #{replace}"
|
|
877
|
+
end
|
|
878
|
+
|
|
879
|
+
replace
|
|
880
|
+
end
|
|
881
|
+
# Check if the number of parent directory components (../) has increased due to variable substitution
|
|
882
|
+
if rvalue.match?(PARENT_DIRECTORY_PATTERN)
|
|
883
|
+
if rvalue.scan(PARENT_DIRECTORY_PATTERN).size > str.scan(PARENT_DIRECTORY_PATTERN).size
|
|
884
|
+
raise Fluent::UnrecoverableError, "Invalid path component detected, replaced to: #{rvalue}"
|
|
885
|
+
end
|
|
842
886
|
end
|
|
843
887
|
end
|
|
844
888
|
|
|
@@ -47,6 +47,11 @@ module Fluent
|
|
|
47
47
|
|
|
48
48
|
def parse(text, &block)
|
|
49
49
|
values = CSV.parse_line(text, col_sep: @delimiter)
|
|
50
|
+
unless values
|
|
51
|
+
yield nil, nil
|
|
52
|
+
return
|
|
53
|
+
end
|
|
54
|
+
|
|
50
55
|
r = Hash[@keys.zip(values)]
|
|
51
56
|
time, record = convert_values(parse_time(r), r)
|
|
52
57
|
yield time, record
|
|
@@ -85,7 +85,7 @@ module Fluent
|
|
|
85
85
|
if File.exist?(@path)
|
|
86
86
|
raise Fluent::ConfigError, "Plugin storage path '#{@path}' is not readable/writable" unless File.readable?(@path) && File.writable?(@path)
|
|
87
87
|
begin
|
|
88
|
-
data = File.open(@path, 'r:utf-8') { |io| io.read }
|
|
88
|
+
data = File.open(@path, 'r:utf-8:utf-8') { |io| io.read }
|
|
89
89
|
if data.empty?
|
|
90
90
|
log.warn "detect empty plugin storage file during startup. Ignored: #{@path}"
|
|
91
91
|
return
|
|
@@ -113,7 +113,7 @@ module Fluent
|
|
|
113
113
|
return if @on_memory
|
|
114
114
|
return unless File.exist?(@path)
|
|
115
115
|
begin
|
|
116
|
-
json_string = File.open(@path, 'r:utf-8'){ |io| io.read }
|
|
116
|
+
json_string = File.open(@path, 'r:utf-8:utf-8'){ |io| io.read }
|
|
117
117
|
json = JSON.parse(json_string)
|
|
118
118
|
unless json.is_a?(Hash)
|
|
119
119
|
log.error "broken content for plugin storage (Hash required: ignored)", type: json.class
|
data/lib/fluent/supervisor.rb
CHANGED
|
@@ -814,6 +814,20 @@ module Fluent
|
|
|
814
814
|
@conf += additional_conf
|
|
815
815
|
end
|
|
816
816
|
|
|
817
|
+
if Fluent.windows?
|
|
818
|
+
@conf.elements.each do |element|
|
|
819
|
+
next unless element.name == 'source'
|
|
820
|
+
if element['@type'] == 'tail' && element['pos_file']
|
|
821
|
+
$log.warn("Recommend adding #{File.dirname(element['pos_file'])} to the exclusion path of your antivirus software on Windows")
|
|
822
|
+
else
|
|
823
|
+
storage = element.elements.find { |v| v.name == 'storage' and v['@type'] == 'local' }
|
|
824
|
+
if storage
|
|
825
|
+
$log.warn("Recommend adding #{File.dirname(storage['path'])} to the exclusion path of your antivirus software on Windows")
|
|
826
|
+
end
|
|
827
|
+
end
|
|
828
|
+
end
|
|
829
|
+
end
|
|
830
|
+
|
|
817
831
|
@libs.each do |lib|
|
|
818
832
|
require lib
|
|
819
833
|
end
|
data/lib/fluent/test/base.rb
CHANGED
data/lib/fluent/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: fluentd
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.19.
|
|
4
|
+
version: 1.19.3
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Sadayuki Furuhashi
|
|
8
|
+
autorequire:
|
|
8
9
|
bindir: bin
|
|
9
10
|
cert_chain: []
|
|
10
|
-
date:
|
|
11
|
+
date: 2026-06-25 00:00:00.000000000 Z
|
|
11
12
|
dependencies:
|
|
12
13
|
- !ruby/object:Gem::Dependency
|
|
13
14
|
name: bundler
|
|
@@ -531,7 +532,6 @@ executables:
|
|
|
531
532
|
extensions: []
|
|
532
533
|
extra_rdoc_files: []
|
|
533
534
|
files:
|
|
534
|
-
- ".deepsource.toml"
|
|
535
535
|
- ".rubocop.yml"
|
|
536
536
|
- ADOPTERS.md
|
|
537
537
|
- AUTHORS
|
|
@@ -691,6 +691,7 @@ files:
|
|
|
691
691
|
- lib/fluent/plugin/buffer/memory_chunk.rb
|
|
692
692
|
- lib/fluent/plugin/compressable.rb
|
|
693
693
|
- lib/fluent/plugin/exec_util.rb
|
|
694
|
+
- lib/fluent/plugin/extractor.rb
|
|
694
695
|
- lib/fluent/plugin/file_util.rb
|
|
695
696
|
- lib/fluent/plugin/filter.rb
|
|
696
697
|
- lib/fluent/plugin/filter_grep.rb
|
|
@@ -869,6 +870,7 @@ metadata:
|
|
|
869
870
|
source_code_uri: https://github.com/fluent/fluentd
|
|
870
871
|
changelog_uri: https://github.com/fluent/fluentd/blob/master/CHANGELOG.md
|
|
871
872
|
bug_tracker_uri: https://github.com/fluent/fluentd/issues
|
|
873
|
+
post_install_message:
|
|
872
874
|
rdoc_options: []
|
|
873
875
|
require_paths:
|
|
874
876
|
- lib
|
|
@@ -883,7 +885,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
883
885
|
- !ruby/object:Gem::Version
|
|
884
886
|
version: '0'
|
|
885
887
|
requirements: []
|
|
886
|
-
rubygems_version: 3.
|
|
888
|
+
rubygems_version: 3.5.22
|
|
889
|
+
signing_key:
|
|
887
890
|
specification_version: 4
|
|
888
891
|
summary: Fluentd event collector
|
|
889
892
|
test_files: []
|