fluentd 1.16.0 → 1.16.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/ISSUE_TEMPLATE/{bug_report.yaml → bug_report.yml} +1 -0
- data/.github/ISSUE_TEMPLATE/{feature_request.yaml → feature_request.yml} +1 -0
- data/.github/workflows/stale-actions.yml +24 -0
- data/.github/workflows/test.yml +39 -0
- data/CHANGELOG.md +161 -0
- data/CONTRIBUTING.md +1 -1
- data/MAINTAINERS.md +3 -3
- data/README.md +0 -2
- data/SECURITY.md +5 -9
- data/fluentd.gemspec +10 -2
- data/lib/fluent/command/ctl.rb +2 -2
- data/lib/fluent/command/fluentd.rb +1 -1
- data/lib/fluent/command/plugin_config_formatter.rb +1 -1
- data/lib/fluent/config/dsl.rb +1 -1
- data/lib/fluent/config/v1_parser.rb +2 -2
- data/lib/fluent/config/yaml_parser/parser.rb +4 -0
- data/lib/fluent/counter/server.rb +1 -1
- data/lib/fluent/counter/validator.rb +3 -3
- data/lib/fluent/engine.rb +1 -1
- data/lib/fluent/event.rb +8 -4
- data/lib/fluent/log.rb +9 -0
- data/lib/fluent/match.rb +1 -1
- data/lib/fluent/msgpack_factory.rb +6 -1
- data/lib/fluent/plugin/base.rb +1 -1
- data/lib/fluent/plugin/buffer.rb +96 -87
- data/lib/fluent/plugin/filter_record_transformer.rb +1 -1
- data/lib/fluent/plugin/in_forward.rb +1 -1
- data/lib/fluent/plugin/in_http.rb +8 -8
- data/lib/fluent/plugin/in_sample.rb +1 -1
- data/lib/fluent/plugin/in_tail/position_file.rb +32 -18
- data/lib/fluent/plugin/in_tail.rb +99 -31
- data/lib/fluent/plugin/in_tcp.rb +43 -0
- data/lib/fluent/plugin/out_exec_filter.rb +2 -2
- data/lib/fluent/plugin/out_file.rb +8 -0
- data/lib/fluent/plugin/output.rb +2 -2
- data/lib/fluent/plugin/parser_json.rb +4 -12
- data/lib/fluent/plugin_helper/event_loop.rb +2 -2
- data/lib/fluent/plugin_helper/record_accessor.rb +1 -1
- data/lib/fluent/plugin_helper/server.rb +8 -0
- data/lib/fluent/plugin_helper/thread.rb +3 -3
- data/lib/fluent/plugin_id.rb +1 -1
- data/lib/fluent/supervisor.rb +2 -2
- data/lib/fluent/system_config.rb +1 -1
- data/lib/fluent/version.rb +1 -1
- data/lib/fluent/winsvc.rb +28 -3
- data/templates/new_gem/test/helper.rb.erb +0 -1
- data/test/command/test_cat.rb +2 -2
- data/test/command/test_fluentd.rb +65 -12
- data/test/config/test_system_config.rb +2 -2
- data/test/helper.rb +27 -7
- data/test/plugin/in_tail/test_io_handler.rb +13 -14
- data/test/plugin/in_tail/test_position_file.rb +37 -8
- data/test/plugin/out_forward/test_ack_handler.rb +3 -3
- data/test/plugin/out_forward/test_socket_cache.rb +3 -3
- data/test/plugin/test_base.rb +1 -1
- data/test/plugin/test_buffer.rb +110 -0
- data/test/plugin/test_buffer_chunk.rb +11 -0
- data/test/plugin/test_in_forward.rb +11 -10
- data/test/plugin/test_in_http.rb +1 -1
- data/test/plugin/test_in_monitor_agent.rb +6 -6
- data/test/plugin/test_in_syslog.rb +25 -18
- data/test/plugin/test_in_tail.rb +639 -0
- data/test/plugin/test_in_tcp.rb +75 -5
- data/test/plugin/test_in_udp.rb +35 -1
- data/test/plugin/test_in_unix.rb +2 -2
- data/test/plugin/test_multi_output.rb +1 -1
- data/test/plugin/test_out_exec_filter.rb +14 -9
- data/test/plugin/test_out_file.rb +24 -4
- data/test/plugin/test_out_forward.rb +36 -42
- data/test/plugin/test_out_stream.rb +1 -1
- data/test/plugin/test_output.rb +12 -12
- data/test/plugin/test_output_as_buffered.rb +44 -44
- data/test/plugin/test_output_as_buffered_compress.rb +32 -18
- data/test/plugin/test_output_as_buffered_retries.rb +1 -1
- data/test/plugin/test_output_as_buffered_secondary.rb +2 -2
- data/test/plugin/test_parser_json.rb +31 -0
- data/test/plugin_helper/test_child_process.rb +15 -5
- data/test/plugin_helper/test_http_server_helper.rb +1 -1
- data/test/plugin_helper/test_server.rb +110 -38
- data/test/plugin_helper/test_socket.rb +1 -1
- data/test/test_config.rb +6 -0
- data/test/test_event_router.rb +2 -2
- data/test/test_log.rb +38 -1
- data/test/test_msgpack_factory.rb +32 -0
- data/test/test_supervisor.rb +13 -0
- metadata +44 -17
- data/.github/workflows/linux-test.yaml +0 -36
- data/.github/workflows/macos-test.yaml +0 -34
- data/.github/workflows/windows-test.yaml +0 -49
|
@@ -128,11 +128,14 @@ class TestFluentdCommand < ::Test::Unit::TestCase
|
|
|
128
128
|
|
|
129
129
|
# ATTENTION: This stops taking logs when all `pattern_list` match or timeout,
|
|
130
130
|
# so `patterns_not_match` can test only logs up to that point.
|
|
131
|
+
# You can pass a block to assert something after log matching.
|
|
131
132
|
def assert_log_matches(cmdline, *pattern_list, patterns_not_match: [], timeout: 20, env: {})
|
|
132
133
|
matched = false
|
|
133
134
|
matched_wrongly = false
|
|
134
|
-
|
|
135
|
+
error_msg_match = ""
|
|
135
136
|
stdio_buf = ""
|
|
137
|
+
succeeded_block = true
|
|
138
|
+
error_msg_block = ""
|
|
136
139
|
begin
|
|
137
140
|
execute_command(cmdline, @tmp_dir, env) do |pid, stdout|
|
|
138
141
|
begin
|
|
@@ -163,6 +166,13 @@ class TestFluentdCommand < ::Test::Unit::TestCase
|
|
|
163
166
|
end
|
|
164
167
|
end
|
|
165
168
|
end
|
|
169
|
+
|
|
170
|
+
begin
|
|
171
|
+
yield if block_given?
|
|
172
|
+
rescue => e
|
|
173
|
+
succeeded_block = false
|
|
174
|
+
error_msg_block = "failed block execution after matching: #{e}"
|
|
175
|
+
end
|
|
166
176
|
ensure
|
|
167
177
|
if SUPERVISOR_PID_PATTERN =~ stdio_buf
|
|
168
178
|
@supervisor_pid = $1.to_i
|
|
@@ -173,19 +183,19 @@ class TestFluentdCommand < ::Test::Unit::TestCase
|
|
|
173
183
|
end
|
|
174
184
|
end
|
|
175
185
|
rescue Timeout::Error
|
|
176
|
-
|
|
186
|
+
error_msg_match = "execution timeout"
|
|
177
187
|
# https://github.com/fluent/fluentd/issues/4095
|
|
178
188
|
# On Windows, timeout without `@supervisor_pid` means that the test is invalid,
|
|
179
189
|
# since the supervisor process will survive without being killed correctly.
|
|
180
190
|
flunk("Invalid test: The pid of supervisor could not be taken, which is necessary on Windows.") if Fluent.windows? && @supervisor_pid.nil?
|
|
181
191
|
rescue => e
|
|
182
|
-
|
|
192
|
+
error_msg_match = "unexpected error in launching fluentd: #{e.inspect}"
|
|
183
193
|
else
|
|
184
|
-
|
|
194
|
+
error_msg_match = "log doesn't match" unless matched
|
|
185
195
|
end
|
|
186
196
|
|
|
187
197
|
if patterns_not_match.empty?
|
|
188
|
-
|
|
198
|
+
error_msg_match = build_message(error_msg_match,
|
|
189
199
|
"<?>\nwas expected to include:\n<?>",
|
|
190
200
|
stdio_buf, pattern_list)
|
|
191
201
|
else
|
|
@@ -197,16 +207,17 @@ class TestFluentdCommand < ::Test::Unit::TestCase
|
|
|
197
207
|
lines.any?{|line| line.include?(ptn) }
|
|
198
208
|
end
|
|
199
209
|
if matched_wrongly
|
|
200
|
-
|
|
201
|
-
|
|
210
|
+
error_msg_match << "\n" unless error_msg_match.empty?
|
|
211
|
+
error_msg_match << "pattern exists in logs wrongly: #{ptn}"
|
|
202
212
|
end
|
|
203
213
|
end
|
|
204
|
-
|
|
214
|
+
error_msg_match = build_message(error_msg_match,
|
|
205
215
|
"<?>\nwas expected to include:\n<?>\nand not include:\n<?>",
|
|
206
216
|
stdio_buf, pattern_list, patterns_not_match)
|
|
207
217
|
end
|
|
208
218
|
|
|
209
|
-
assert matched && !matched_wrongly,
|
|
219
|
+
assert matched && !matched_wrongly, error_msg_match
|
|
220
|
+
assert succeeded_block, error_msg_block if block_given?
|
|
210
221
|
end
|
|
211
222
|
|
|
212
223
|
def assert_fluentd_fails_to_start(cmdline, *pattern_list, timeout: 20)
|
|
@@ -941,7 +952,7 @@ CONF
|
|
|
941
952
|
'-external-encoding' => '--external-encoding=utf-8',
|
|
942
953
|
'-internal-encoding' => '--internal-encoding=utf-8',
|
|
943
954
|
)
|
|
944
|
-
test "-E option is set to RUBYOPT" do |
|
|
955
|
+
test "-E option is set to RUBYOPT" do |base_opt|
|
|
945
956
|
conf = <<CONF
|
|
946
957
|
<source>
|
|
947
958
|
@type dummy
|
|
@@ -952,6 +963,7 @@ CONF
|
|
|
952
963
|
</match>
|
|
953
964
|
CONF
|
|
954
965
|
conf_path = create_conf_file('rubyopt_test.conf', conf)
|
|
966
|
+
opt = base_opt.dup
|
|
955
967
|
opt << " #{ENV['RUBYOPT']}" if ENV['RUBYOPT']
|
|
956
968
|
assert_log_matches(
|
|
957
969
|
create_cmdline(conf_path),
|
|
@@ -991,9 +1003,14 @@ CONF
|
|
|
991
1003
|
</match>
|
|
992
1004
|
CONF
|
|
993
1005
|
conf_path = create_conf_file('rubyopt_invalid_test.conf', conf)
|
|
1006
|
+
if Gem::Version.create(RUBY_VERSION) >= Gem::Version.create('3.3.0')
|
|
1007
|
+
expected_phrase = 'ruby: invalid switch in RUBYOPT'
|
|
1008
|
+
else
|
|
1009
|
+
expected_phrase = 'Invalid option is passed to RUBYOPT'
|
|
1010
|
+
end
|
|
994
1011
|
assert_log_matches(
|
|
995
1012
|
create_cmdline(conf_path),
|
|
996
|
-
|
|
1013
|
+
expected_phrase,
|
|
997
1014
|
env: { 'RUBYOPT' => 'a' },
|
|
998
1015
|
)
|
|
999
1016
|
end
|
|
@@ -1158,7 +1175,7 @@ CONF
|
|
|
1158
1175
|
end
|
|
1159
1176
|
end
|
|
1160
1177
|
|
|
1161
|
-
sub_test_case '
|
|
1178
|
+
sub_test_case 'shared socket options' do
|
|
1162
1179
|
test 'enable shared socket by default' do
|
|
1163
1180
|
conf = ""
|
|
1164
1181
|
conf_path = create_conf_file('empty.conf', conf)
|
|
@@ -1282,4 +1299,40 @@ CONF
|
|
|
1282
1299
|
"[debug]")
|
|
1283
1300
|
end
|
|
1284
1301
|
end
|
|
1302
|
+
|
|
1303
|
+
sub_test_case "plugin option" do
|
|
1304
|
+
test "should be the default value when not specifying" do
|
|
1305
|
+
conf_path = create_conf_file('test.conf', <<~CONF)
|
|
1306
|
+
<source>
|
|
1307
|
+
@type monitor_agent
|
|
1308
|
+
</source>
|
|
1309
|
+
CONF
|
|
1310
|
+
assert File.exist?(conf_path)
|
|
1311
|
+
cmdline = create_cmdline(conf_path)
|
|
1312
|
+
|
|
1313
|
+
assert_log_matches(cmdline, "fluentd worker is now running") do
|
|
1314
|
+
response = Net::HTTP.get(URI.parse("http://localhost:24220/api/config.json"))
|
|
1315
|
+
actual_conf = JSON.parse(response)
|
|
1316
|
+
assert_equal Fluent::Supervisor.default_options[:plugin_dirs], actual_conf["plugin_dirs"]
|
|
1317
|
+
end
|
|
1318
|
+
end
|
|
1319
|
+
|
|
1320
|
+
data(short: "-p")
|
|
1321
|
+
data(long: "--plugin")
|
|
1322
|
+
test "can be added by specifying the option" do |option_name|
|
|
1323
|
+
conf_path = create_conf_file('test.conf', <<~CONF)
|
|
1324
|
+
<source>
|
|
1325
|
+
@type monitor_agent
|
|
1326
|
+
</source>
|
|
1327
|
+
CONF
|
|
1328
|
+
assert File.exist?(conf_path)
|
|
1329
|
+
cmdline = create_cmdline(conf_path, option_name, @tmp_dir, option_name, @tmp_dir)
|
|
1330
|
+
|
|
1331
|
+
assert_log_matches(cmdline, "fluentd worker is now running") do
|
|
1332
|
+
response = Net::HTTP.get(URI.parse("http://localhost:24220/api/config.json"))
|
|
1333
|
+
actual_conf = JSON.parse(response)
|
|
1334
|
+
assert_equal Fluent::Supervisor.default_options[:plugin_dirs] + [@tmp_dir, @tmp_dir], actual_conf["plugin_dirs"]
|
|
1335
|
+
end
|
|
1336
|
+
end
|
|
1337
|
+
end
|
|
1285
1338
|
end
|
|
@@ -151,7 +151,7 @@ module Fluent::Config
|
|
|
151
151
|
data('daily' => "daily",
|
|
152
152
|
'weekly' => 'weekly',
|
|
153
153
|
'monthly' => 'monthly')
|
|
154
|
-
test "
|
|
154
|
+
test "strings for rotate_age" do |age|
|
|
155
155
|
conf = parse_text(<<-EOS)
|
|
156
156
|
<system>
|
|
157
157
|
<log>
|
|
@@ -160,7 +160,7 @@ module Fluent::Config
|
|
|
160
160
|
</system>
|
|
161
161
|
EOS
|
|
162
162
|
sc = Fluent::SystemConfig.new(conf)
|
|
163
|
-
assert_equal(age
|
|
163
|
+
assert_equal(age, sc.log.rotate_age)
|
|
164
164
|
end
|
|
165
165
|
|
|
166
166
|
test "numeric number for rotate age" do
|
data/test/helper.rb
CHANGED
|
@@ -71,17 +71,31 @@ end
|
|
|
71
71
|
|
|
72
72
|
include Fluent::Test::Helpers
|
|
73
73
|
|
|
74
|
-
def unused_port(num = 1, protocol
|
|
74
|
+
def unused_port(num = 1, protocol:, bind: "0.0.0.0")
|
|
75
75
|
case protocol
|
|
76
|
-
when :tcp
|
|
76
|
+
when :tcp, :tls
|
|
77
77
|
unused_port_tcp(num)
|
|
78
78
|
when :udp
|
|
79
79
|
unused_port_udp(num, bind: bind)
|
|
80
|
+
when :all
|
|
81
|
+
unused_port_tcp_udp(num)
|
|
80
82
|
else
|
|
81
83
|
raise ArgumentError, "unknown protocol: #{protocol}"
|
|
82
84
|
end
|
|
83
85
|
end
|
|
84
86
|
|
|
87
|
+
def unused_port_tcp_udp(num = 1)
|
|
88
|
+
raise "not support num > 1" if num > 1
|
|
89
|
+
|
|
90
|
+
# The default maximum number of file descriptors in macOS is 256.
|
|
91
|
+
# It might need to set num to a smaller value than that.
|
|
92
|
+
tcp_ports = unused_port_tcp(200)
|
|
93
|
+
port = unused_port_udp(1, port_list: tcp_ports)
|
|
94
|
+
raise "can't find unused port" unless port
|
|
95
|
+
|
|
96
|
+
port
|
|
97
|
+
end
|
|
98
|
+
|
|
85
99
|
def unused_port_tcp(num = 1)
|
|
86
100
|
ports = []
|
|
87
101
|
sockets = []
|
|
@@ -90,7 +104,7 @@ def unused_port_tcp(num = 1)
|
|
|
90
104
|
sockets << s
|
|
91
105
|
ports << s.addr[1]
|
|
92
106
|
end
|
|
93
|
-
sockets.each
|
|
107
|
+
sockets.each(&:close)
|
|
94
108
|
if num == 1
|
|
95
109
|
return ports.first
|
|
96
110
|
else
|
|
@@ -100,12 +114,15 @@ end
|
|
|
100
114
|
|
|
101
115
|
PORT_RANGE_AVAILABLE = (1024...65535)
|
|
102
116
|
|
|
103
|
-
def unused_port_udp(num = 1, bind: "0.0.0.0")
|
|
117
|
+
def unused_port_udp(num = 1, port_list: [], bind: "0.0.0.0")
|
|
104
118
|
family = IPAddr.new(IPSocket.getaddress(bind)).ipv4? ? ::Socket::AF_INET : ::Socket::AF_INET6
|
|
105
119
|
ports = []
|
|
106
120
|
sockets = []
|
|
107
|
-
|
|
108
|
-
|
|
121
|
+
|
|
122
|
+
use_random_port = port_list.empty?
|
|
123
|
+
i = 0
|
|
124
|
+
loop do
|
|
125
|
+
port = use_random_port ? rand(PORT_RANGE_AVAILABLE) : port_list[i]
|
|
109
126
|
u = UDPSocket.new(family)
|
|
110
127
|
if (u.bind(bind, port) rescue nil)
|
|
111
128
|
ports << port
|
|
@@ -113,8 +130,11 @@ def unused_port_udp(num = 1, bind: "0.0.0.0")
|
|
|
113
130
|
else
|
|
114
131
|
u.close
|
|
115
132
|
end
|
|
133
|
+
i += 1
|
|
134
|
+
break if ports.size >= num
|
|
135
|
+
break if !use_random_port && i >= port_list.size
|
|
116
136
|
end
|
|
117
|
-
sockets.each
|
|
137
|
+
sockets.each(&:close)
|
|
118
138
|
if num == 1
|
|
119
139
|
return ports.first
|
|
120
140
|
else
|
|
@@ -5,20 +5,19 @@ require 'fluent/plugin/metrics_local'
|
|
|
5
5
|
require 'tempfile'
|
|
6
6
|
|
|
7
7
|
class IntailIOHandlerTest < Test::Unit::TestCase
|
|
8
|
-
setup
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
@file.unlink rescue nil
|
|
8
|
+
def setup
|
|
9
|
+
Tempfile.create('intail_io_handler') do |file|
|
|
10
|
+
file.binmode
|
|
11
|
+
@file = file
|
|
12
|
+
opened_file_metrics = Fluent::Plugin::LocalMetrics.new
|
|
13
|
+
opened_file_metrics.configure(config_element('metrics', '', {}))
|
|
14
|
+
closed_file_metrics = Fluent::Plugin::LocalMetrics.new
|
|
15
|
+
closed_file_metrics.configure(config_element('metrics', '', {}))
|
|
16
|
+
rotated_file_metrics = Fluent::Plugin::LocalMetrics.new
|
|
17
|
+
rotated_file_metrics.configure(config_element('metrics', '', {}))
|
|
18
|
+
@metrics = Fluent::Plugin::TailInput::MetricsInfo.new(opened_file_metrics, closed_file_metrics, rotated_file_metrics)
|
|
19
|
+
yield
|
|
20
|
+
end
|
|
22
21
|
end
|
|
23
22
|
|
|
24
23
|
def create_target_info
|
|
@@ -6,13 +6,12 @@ require 'fileutils'
|
|
|
6
6
|
require 'tempfile'
|
|
7
7
|
|
|
8
8
|
class IntailPositionFileTest < Test::Unit::TestCase
|
|
9
|
-
setup
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
@file.unlink rescue nil
|
|
9
|
+
def setup
|
|
10
|
+
Tempfile.create('intail_position_file_test') do |file|
|
|
11
|
+
file.binmode
|
|
12
|
+
@file = file
|
|
13
|
+
yield
|
|
14
|
+
end
|
|
16
15
|
end
|
|
17
16
|
|
|
18
17
|
UNWATCHED_STR = '%016x' % Fluent::Plugin::TailInput::PositionFile::UNWATCHED_POSITION
|
|
@@ -26,6 +25,10 @@ class IntailPositionFileTest < Test::Unit::TestCase
|
|
|
26
25
|
"valid_path" => Fluent::Plugin::TailInput::TargetInfo.new("valid_path", 1),
|
|
27
26
|
"inode23bit" => Fluent::Plugin::TailInput::TargetInfo.new("inode23bit", 0),
|
|
28
27
|
}
|
|
28
|
+
TEST_CONTENT_INODES = {
|
|
29
|
+
1 => Fluent::Plugin::TailInput::TargetInfo.new("valid_path", 1),
|
|
30
|
+
0 => Fluent::Plugin::TailInput::TargetInfo.new("inode23bit", 0),
|
|
31
|
+
}
|
|
29
32
|
|
|
30
33
|
def write_data(f, content)
|
|
31
34
|
f.write(content)
|
|
@@ -221,7 +224,7 @@ class IntailPositionFileTest < Test::Unit::TestCase
|
|
|
221
224
|
end
|
|
222
225
|
|
|
223
226
|
sub_test_case '#unwatch' do
|
|
224
|
-
test '
|
|
227
|
+
test 'unwatch entry by path' do
|
|
225
228
|
write_data(@file, TEST_CONTENT)
|
|
226
229
|
pf = Fluent::Plugin::TailInput::PositionFile.load(@file, false, {}, logger: $log)
|
|
227
230
|
inode1 = File.stat(@file).ino
|
|
@@ -239,6 +242,32 @@ class IntailPositionFileTest < Test::Unit::TestCase
|
|
|
239
242
|
|
|
240
243
|
assert_not_equal p1, p2
|
|
241
244
|
end
|
|
245
|
+
|
|
246
|
+
test 'unwatch entries by inode' do
|
|
247
|
+
write_data(@file, TEST_CONTENT)
|
|
248
|
+
pf = Fluent::Plugin::TailInput::PositionFile.load(@file, true, TEST_CONTENT_INODES, logger: $log)
|
|
249
|
+
|
|
250
|
+
existing_targets = TEST_CONTENT_INODES.select do |inode, target_info|
|
|
251
|
+
inode == 1
|
|
252
|
+
end
|
|
253
|
+
pe_to_unwatch = pf[TEST_CONTENT_INODES[0]]
|
|
254
|
+
|
|
255
|
+
pf.unwatch_removed_targets(existing_targets)
|
|
256
|
+
|
|
257
|
+
assert_equal(
|
|
258
|
+
{
|
|
259
|
+
map_keys: [TEST_CONTENT_INODES[1].ino],
|
|
260
|
+
unwatched_pe_pos: Fluent::Plugin::TailInput::PositionFile::UNWATCHED_POSITION,
|
|
261
|
+
},
|
|
262
|
+
{
|
|
263
|
+
map_keys: pf.instance_variable_get(:@map).keys,
|
|
264
|
+
unwatched_pe_pos: pe_to_unwatch.read_pos,
|
|
265
|
+
}
|
|
266
|
+
)
|
|
267
|
+
|
|
268
|
+
unwatched_pe_retaken = pf[TEST_CONTENT_INODES[0]]
|
|
269
|
+
assert_not_equal pe_to_unwatch, unwatched_pe_retaken
|
|
270
|
+
end
|
|
242
271
|
end
|
|
243
272
|
|
|
244
273
|
sub_test_case 'FilePositionEntry' do
|
|
@@ -111,11 +111,11 @@ class AckHandlerTest < Test::Unit::TestCase
|
|
|
111
111
|
r, w = IO.pipe
|
|
112
112
|
begin
|
|
113
113
|
w.write(chunk_id)
|
|
114
|
-
|
|
114
|
+
def r.recv(arg)
|
|
115
115
|
sleep(1) # To ensure that multiple threads select the socket before closing.
|
|
116
|
-
raise IOError, 'stream closed in another thread' if
|
|
116
|
+
raise IOError, 'stream closed in another thread' if self.closed?
|
|
117
117
|
MessagePack.pack({ 'ack' => Base64.encode64('chunk_id 111') })
|
|
118
|
-
|
|
118
|
+
end
|
|
119
119
|
ack.enqueue(r)
|
|
120
120
|
|
|
121
121
|
threads = []
|
|
@@ -17,7 +17,7 @@ class SocketCacheTest < Test::Unit::TestCase
|
|
|
17
17
|
assert_equal(socket, c.checkout_or('key') { socket })
|
|
18
18
|
c.checkin(socket)
|
|
19
19
|
|
|
20
|
-
sock =
|
|
20
|
+
sock = mock!.open.never.subject
|
|
21
21
|
assert_equal(socket, c.checkout_or('key') { sock.open })
|
|
22
22
|
end
|
|
23
23
|
|
|
@@ -130,7 +130,7 @@ class SocketCacheTest < Test::Unit::TestCase
|
|
|
130
130
|
|
|
131
131
|
c = Fluent::Plugin::ForwardOutput::SocketCache.new(10, $log)
|
|
132
132
|
sock = mock!.close { 'closed' }.subject
|
|
133
|
-
sock2 =
|
|
133
|
+
sock2 = mock!.close.never.subject
|
|
134
134
|
stub(sock).inspect
|
|
135
135
|
stub(sock2).inspect
|
|
136
136
|
|
|
@@ -154,7 +154,7 @@ class SocketCacheTest < Test::Unit::TestCase
|
|
|
154
154
|
Timecop.freeze(Time.parse('2016-04-13 14:00:00 +0900'))
|
|
155
155
|
|
|
156
156
|
c = Fluent::Plugin::ForwardOutput::SocketCache.new(10, $log)
|
|
157
|
-
sock =
|
|
157
|
+
sock = mock!.close.never.subject
|
|
158
158
|
stub(sock).inspect
|
|
159
159
|
c.checkout_or('key') { sock }
|
|
160
160
|
|
data/test/plugin/test_base.rb
CHANGED
|
@@ -108,7 +108,7 @@ class BaseTest < Test::Unit::TestCase
|
|
|
108
108
|
@p.extend m
|
|
109
109
|
assert_equal [], logger.logs
|
|
110
110
|
|
|
111
|
-
ret = @p.string_safe_encoding("abc\xff.\x01f"){|s| s.split(
|
|
111
|
+
ret = @p.string_safe_encoding("abc\xff.\x01f"){|s| s.split(".") }
|
|
112
112
|
assert_equal ['abc?', "\u0001f"], ret
|
|
113
113
|
assert_equal 1, logger.logs.size
|
|
114
114
|
assert{ logger.logs.first.include?("invalid byte sequence is replaced in ") }
|
data/test/plugin/test_buffer.rb
CHANGED
|
@@ -850,6 +850,116 @@ class BufferTest < Test::Unit::TestCase
|
|
|
850
850
|
test '#compress returns :text' do
|
|
851
851
|
assert_equal :text, @p.compress
|
|
852
852
|
end
|
|
853
|
+
|
|
854
|
+
# https://github.com/fluent/fluentd/issues/3089
|
|
855
|
+
test "closed chunk should not be committed" do
|
|
856
|
+
assert_equal 8 * 1024 * 1024, @p.chunk_limit_size
|
|
857
|
+
assert_equal 0.95, @p.chunk_full_threshold
|
|
858
|
+
|
|
859
|
+
purge_count = 0
|
|
860
|
+
|
|
861
|
+
stub.proxy(@p).generate_chunk(anything) do |chunk|
|
|
862
|
+
stub.proxy(chunk).purge do |result|
|
|
863
|
+
purge_count += 1
|
|
864
|
+
result
|
|
865
|
+
end
|
|
866
|
+
stub.proxy(chunk).commit do |result|
|
|
867
|
+
assert_false(chunk.closed?)
|
|
868
|
+
result
|
|
869
|
+
end
|
|
870
|
+
stub.proxy(chunk).rollback do |result|
|
|
871
|
+
assert_false(chunk.closed?)
|
|
872
|
+
result
|
|
873
|
+
end
|
|
874
|
+
chunk
|
|
875
|
+
end
|
|
876
|
+
|
|
877
|
+
m = @p.metadata(timekey: Time.parse('2016-04-11 16:40:00 +0000').to_i)
|
|
878
|
+
small_row = "x" * 1024 * 400
|
|
879
|
+
big_row = "x" * 1024 * 1024 * 8 # just `chunk_size_limit`, it does't cause BufferOverFlowError.
|
|
880
|
+
|
|
881
|
+
# Write 42 events in 1 event stream, last one is for triggering `ShouldRetry`
|
|
882
|
+
@p.write({m => [small_row] * 40 + [big_row] + ["x"]})
|
|
883
|
+
|
|
884
|
+
# Above event strem will be splitted twice by `Buffer#write_step_by_step`
|
|
885
|
+
#
|
|
886
|
+
# 1. `write_once`: 42 [events] * 1 [stream]
|
|
887
|
+
# 2. `write_step_by_step`: 4 [events]* 10 [streams] + 2 [events] * 1 [stream]
|
|
888
|
+
# 3. `write_step_by_step` (by `ShouldRetry`): 1 [event] * 42 [streams]
|
|
889
|
+
#
|
|
890
|
+
# The problematic data is built in the 2nd stage.
|
|
891
|
+
# In the 2nd stage, 5 streams are packed in a chunk.
|
|
892
|
+
# ((1024 * 400) [bytes] * 4 [events] * 5 [streams] = 8192000 [bytes] < `chunk_limit_size` (8MB)).
|
|
893
|
+
# So 3 chunks are used to store all data.
|
|
894
|
+
# The 1st chunk is already staged by `write_once`.
|
|
895
|
+
# The 2nd & 3rd chunks are newly created as unstaged.
|
|
896
|
+
# The 3rd chunk is purged before `ShouldRetry`, it's no problem:
|
|
897
|
+
# https://github.com/fluent/fluentd/blob/7e9eba736ff40ad985341be800ddc46558be75f2/lib/fluent/plugin/buffer.rb#L850
|
|
898
|
+
# The 2nd chunk is purged in `rescue ShouldRetry`:
|
|
899
|
+
# https://github.com/fluent/fluentd/blob/7e9eba736ff40ad985341be800ddc46558be75f2/lib/fluent/plugin/buffer.rb#L862
|
|
900
|
+
# It causes the issue described in https://github.com/fluent/fluentd/issues/3089#issuecomment-1811839198
|
|
901
|
+
|
|
902
|
+
assert_equal 2, purge_count
|
|
903
|
+
end
|
|
904
|
+
|
|
905
|
+
# https://github.com/fluent/fluentd/issues/4446
|
|
906
|
+
test "#write_step_by_step keeps chunks kept in locked in entire #write process" do
|
|
907
|
+
assert_equal 8 * 1024 * 1024, @p.chunk_limit_size
|
|
908
|
+
assert_equal 0.95, @p.chunk_full_threshold
|
|
909
|
+
|
|
910
|
+
mon_enter_counts_by_chunk = {}
|
|
911
|
+
mon_exit_counts_by_chunk = {}
|
|
912
|
+
|
|
913
|
+
stub.proxy(@p).generate_chunk(anything) do |chunk|
|
|
914
|
+
stub(chunk).mon_enter do
|
|
915
|
+
enter_count = 1 + mon_enter_counts_by_chunk.fetch(chunk, 0)
|
|
916
|
+
exit_count = mon_exit_counts_by_chunk.fetch(chunk, 0)
|
|
917
|
+
mon_enter_counts_by_chunk[chunk] = enter_count
|
|
918
|
+
|
|
919
|
+
# Assert that chunk is passed to &block of write_step_by_step before exiting the lock.
|
|
920
|
+
# (i.e. The lock count must be 2 greater than the exit count).
|
|
921
|
+
# Since ShouldRetry occurs once, the staged chunk takes the lock 3 times when calling the block.
|
|
922
|
+
if chunk.staged?
|
|
923
|
+
lock_in_block = enter_count == 3
|
|
924
|
+
assert_equal(enter_count - 2, exit_count) if lock_in_block
|
|
925
|
+
else
|
|
926
|
+
lock_in_block = enter_count == 2
|
|
927
|
+
assert_equal(enter_count - 2, exit_count) if lock_in_block
|
|
928
|
+
end
|
|
929
|
+
end
|
|
930
|
+
stub(chunk).mon_exit do
|
|
931
|
+
exit_count = 1 + mon_exit_counts_by_chunk.fetch(chunk, 0)
|
|
932
|
+
mon_exit_counts_by_chunk[chunk] = exit_count
|
|
933
|
+
end
|
|
934
|
+
chunk
|
|
935
|
+
end
|
|
936
|
+
|
|
937
|
+
m = @p.metadata(timekey: Time.parse('2016-04-11 16:40:00 +0000').to_i)
|
|
938
|
+
small_row = "x" * 1024 * 400
|
|
939
|
+
big_row = "x" * 1024 * 1024 * 8 # just `chunk_size_limit`, it does't cause BufferOverFlowError.
|
|
940
|
+
|
|
941
|
+
# Write 42 events in 1 event stream, last one is for triggering `ShouldRetry`
|
|
942
|
+
@p.write({m => [small_row] * 40 + [big_row] + ["x"]})
|
|
943
|
+
|
|
944
|
+
# Above event strem will be splitted twice by `Buffer#write_step_by_step`
|
|
945
|
+
#
|
|
946
|
+
# 1. `write_once`: 42 [events] * 1 [stream]
|
|
947
|
+
# 2. `write_step_by_step`: 4 [events]* 10 [streams] + 2 [events] * 1 [stream]
|
|
948
|
+
# 3. `write_step_by_step` (by `ShouldRetry`): 1 [event] * 42 [streams]
|
|
949
|
+
#
|
|
950
|
+
# Example of staged chunk lock behavior:
|
|
951
|
+
#
|
|
952
|
+
# 1. mon_enter in write_step_by_step
|
|
953
|
+
# 2. ShouldRetry occurs
|
|
954
|
+
# 3. mon_exit in write_step_by_step
|
|
955
|
+
# 4. mon_enter again in write_step_by_step (retry)
|
|
956
|
+
# 5. passed to &block of write_step_by_step
|
|
957
|
+
# 6. mon_enter in the block (write)
|
|
958
|
+
# 7. mon_exit in write_step_by_step
|
|
959
|
+
# 8. mon_exit in write
|
|
960
|
+
|
|
961
|
+
assert_equal(mon_enter_counts_by_chunk.values, mon_exit_counts_by_chunk.values)
|
|
962
|
+
end
|
|
853
963
|
end
|
|
854
964
|
|
|
855
965
|
sub_test_case 'standard format with configuration for test with lower chunk limit size' do
|
|
@@ -57,6 +57,17 @@ class BufferChunkTest < Test::Unit::TestCase
|
|
|
57
57
|
assert chunk.respond_to?(:msgpack_each)
|
|
58
58
|
end
|
|
59
59
|
|
|
60
|
+
test 'unpacker arg is not implemented for ChunkMessagePackEventStreamer' do
|
|
61
|
+
meta = Object.new
|
|
62
|
+
chunk = Fluent::Plugin::Buffer::Chunk.new(meta)
|
|
63
|
+
chunk.extend Fluent::ChunkMessagePackEventStreamer
|
|
64
|
+
|
|
65
|
+
unpacker = Fluent::MessagePackFactory.thread_local_msgpack_unpacker
|
|
66
|
+
|
|
67
|
+
assert_raise(NotImplementedError){ chunk.each(unpacker: unpacker) }
|
|
68
|
+
assert_raise(NotImplementedError){ chunk.msgpack_each(unpacker: unpacker) }
|
|
69
|
+
end
|
|
70
|
+
|
|
60
71
|
test 'some methods raise ArgumentError with an option of `compressed: :gzip` and without extending Compressble`' do
|
|
61
72
|
meta = Object.new
|
|
62
73
|
chunk = Fluent::Plugin::Buffer::Chunk.new(meta)
|
|
@@ -18,7 +18,8 @@ class ForwardInputTest < Test::Unit::TestCase
|
|
|
18
18
|
Fluent::Test.setup
|
|
19
19
|
@responses = [] # for testing responses after sending data
|
|
20
20
|
@d = nil
|
|
21
|
-
|
|
21
|
+
# forward plugin uses TCP and UDP sockets on the same port number
|
|
22
|
+
@port = unused_port(protocol: :all)
|
|
22
23
|
end
|
|
23
24
|
|
|
24
25
|
def teardown
|
|
@@ -367,7 +368,7 @@ class ForwardInputTest < Test::Unit::TestCase
|
|
|
367
368
|
end
|
|
368
369
|
|
|
369
370
|
logs = d.instance.log.out.logs
|
|
370
|
-
assert{ logs.
|
|
371
|
+
assert{ logs.count{|line| line =~ /skip invalid event/ } == 2 }
|
|
371
372
|
|
|
372
373
|
d.instance_shutdown
|
|
373
374
|
end
|
|
@@ -593,10 +594,10 @@ class ForwardInputTest < Test::Unit::TestCase
|
|
|
593
594
|
|
|
594
595
|
# check log
|
|
595
596
|
logs = d.instance.log.logs
|
|
596
|
-
assert_equal 1, logs.
|
|
597
|
+
assert_equal 1, logs.count{|line|
|
|
597
598
|
line =~ / \[warn\]: Input chunk size is larger than 'chunk_size_warn_limit':/ &&
|
|
598
599
|
line =~ / tag="test.tag" host="#{LOCALHOST_HOSTNAME}" limit=16777216 size=16777501/
|
|
599
|
-
}
|
|
600
|
+
}, "large chunk warning is not logged"
|
|
600
601
|
|
|
601
602
|
d.instance_shutdown
|
|
602
603
|
end
|
|
@@ -619,10 +620,10 @@ class ForwardInputTest < Test::Unit::TestCase
|
|
|
619
620
|
|
|
620
621
|
# check log
|
|
621
622
|
logs = d.instance.log.logs
|
|
622
|
-
assert_equal 1, logs.
|
|
623
|
+
assert_equal 1, logs.count{ |line|
|
|
623
624
|
line =~ / \[warn\]: Input chunk size is larger than 'chunk_size_warn_limit':/ &&
|
|
624
625
|
line =~ / tag="test.tag" host="#{LOCALHOST_HOSTNAME}" limit=16777216 size=16777501/
|
|
625
|
-
}
|
|
626
|
+
}, "large chunk warning is not logged"
|
|
626
627
|
|
|
627
628
|
d.instance_shutdown
|
|
628
629
|
end
|
|
@@ -653,10 +654,10 @@ class ForwardInputTest < Test::Unit::TestCase
|
|
|
653
654
|
|
|
654
655
|
# check log
|
|
655
656
|
logs = d.instance.log.logs
|
|
656
|
-
assert_equal 1, logs.
|
|
657
|
+
assert_equal 1, logs.count{|line|
|
|
657
658
|
line =~ / \[warn\]: Input chunk size is larger than 'chunk_size_limit', dropped:/ &&
|
|
658
659
|
line =~ / tag="test.tag" host="#{LOCALHOST_HOSTNAME}" limit=33554432 size=33554989/
|
|
659
|
-
}
|
|
660
|
+
}, "large chunk warning is not logged"
|
|
660
661
|
|
|
661
662
|
d.instance_shutdown
|
|
662
663
|
end
|
|
@@ -676,9 +677,9 @@ class ForwardInputTest < Test::Unit::TestCase
|
|
|
676
677
|
|
|
677
678
|
# check log
|
|
678
679
|
logs = d.instance.log.logs
|
|
679
|
-
assert_equal 1, logs.
|
|
680
|
+
assert_equal 1, logs.count{|line|
|
|
680
681
|
line =~ / \[warn\]: incoming chunk is broken: host="#{LOCALHOST_HOSTNAME}" msg=#{data.inspect}/
|
|
681
|
-
}
|
|
682
|
+
}, "should not accept broken chunk"
|
|
682
683
|
|
|
683
684
|
d.instance_shutdown
|
|
684
685
|
end
|
data/test/plugin/test_in_http.rb
CHANGED
|
@@ -392,7 +392,7 @@ EOC
|
|
|
392
392
|
end
|
|
393
393
|
|
|
394
394
|
test "emit" do
|
|
395
|
-
port = unused_port
|
|
395
|
+
port = unused_port(protocol: :tcp)
|
|
396
396
|
d = create_driver("
|
|
397
397
|
@type monitor_agent
|
|
398
398
|
bind '127.0.0.1'
|
|
@@ -451,7 +451,7 @@ EOC
|
|
|
451
451
|
|
|
452
452
|
sub_test_case "servlets" do
|
|
453
453
|
setup do
|
|
454
|
-
@port = unused_port
|
|
454
|
+
@port = unused_port(protocol: :tcp)
|
|
455
455
|
# check @type and type in one configuration
|
|
456
456
|
conf = <<-EOC
|
|
457
457
|
<source>
|
|
@@ -759,7 +759,7 @@ plugin_id:test_filter\tplugin_category:filter\ttype:test_filter\toutput_plugin:f
|
|
|
759
759
|
end
|
|
760
760
|
|
|
761
761
|
setup do
|
|
762
|
-
@port = unused_port
|
|
762
|
+
@port = unused_port(protocol: :tcp)
|
|
763
763
|
# check @type and type in one configuration
|
|
764
764
|
conf = <<-EOC
|
|
765
765
|
<source>
|
|
@@ -840,7 +840,7 @@ plugin_id:test_filter\tplugin_category:filter\ttype:test_filter\toutput_plugin:f
|
|
|
840
840
|
|
|
841
841
|
sub_test_case "check the port number of http server" do
|
|
842
842
|
test "on single worker environment" do
|
|
843
|
-
port = unused_port
|
|
843
|
+
port = unused_port(protocol: :tcp)
|
|
844
844
|
d = create_driver("
|
|
845
845
|
@type monitor_agent
|
|
846
846
|
bind '127.0.0.1'
|
|
@@ -851,7 +851,7 @@ plugin_id:test_filter\tplugin_category:filter\ttype:test_filter\toutput_plugin:f
|
|
|
851
851
|
end
|
|
852
852
|
|
|
853
853
|
test "worker_id = 2 on multi worker environment" do
|
|
854
|
-
port = unused_port
|
|
854
|
+
port = unused_port(protocol: :tcp)
|
|
855
855
|
Fluent::SystemConfig.overwrite_system_config('workers' => 4) do
|
|
856
856
|
d = Fluent::Test::Driver::Input.new(Fluent::Plugin::MonitorAgentInput)
|
|
857
857
|
d.instance.instance_eval{ @_fluentd_worker_id = 2 }
|
|
@@ -905,7 +905,7 @@ EOC
|
|
|
905
905
|
end
|
|
906
906
|
|
|
907
907
|
test "plugins have a variable named buffer does not throws NoMethodError" do
|
|
908
|
-
port = unused_port
|
|
908
|
+
port = unused_port(protocol: :tcp)
|
|
909
909
|
d = create_driver("
|
|
910
910
|
@type monitor_agent
|
|
911
911
|
bind '127.0.0.1'
|