fluentd 1.15.2-x64-mingw32 → 1.16.0-x64-mingw32
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/linux-test.yaml +2 -2
- data/.github/workflows/macos-test.yaml +2 -2
- data/.github/workflows/windows-test.yaml +2 -2
- data/CHANGELOG.md +96 -0
- data/MAINTAINERS.md +2 -0
- data/README.md +0 -1
- data/fluentd.gemspec +2 -2
- data/lib/fluent/command/fluentd.rb +55 -64
- data/lib/fluent/config/yaml_parser/loader.rb +18 -1
- data/lib/fluent/daemon.rb +2 -4
- data/lib/fluent/file_wrapper.rb +137 -0
- data/lib/fluent/log/console_adapter.rb +66 -0
- data/lib/fluent/log.rb +35 -5
- data/lib/fluent/oj_options.rb +1 -2
- data/lib/fluent/plugin/base.rb +5 -7
- data/lib/fluent/plugin/buf_file.rb +32 -3
- data/lib/fluent/plugin/buf_file_single.rb +32 -3
- data/lib/fluent/plugin/buffer/file_chunk.rb +1 -1
- data/lib/fluent/plugin/buffer.rb +21 -0
- data/lib/fluent/plugin/in_tail.rb +1 -6
- data/lib/fluent/plugin/in_tcp.rb +4 -2
- data/lib/fluent/plugin/out_file.rb +0 -4
- data/lib/fluent/plugin/out_forward/ack_handler.rb +19 -4
- data/lib/fluent/plugin/out_forward.rb +2 -2
- data/lib/fluent/plugin/out_secondary_file.rb +39 -22
- data/lib/fluent/plugin/output.rb +49 -12
- data/lib/fluent/plugin_helper/http_server/server.rb +2 -1
- data/lib/fluent/supervisor.rb +157 -232
- data/lib/fluent/test/driver/base.rb +11 -5
- data/lib/fluent/test/driver/filter.rb +4 -0
- data/lib/fluent/test/startup_shutdown.rb +6 -8
- data/lib/fluent/version.rb +1 -1
- data/test/command/test_ctl.rb +1 -1
- data/test/command/test_fluentd.rb +168 -22
- data/test/command/test_plugin_config_formatter.rb +0 -1
- data/test/compat/test_parser.rb +5 -5
- data/test/config/test_system_config.rb +0 -8
- data/test/log/test_console_adapter.rb +110 -0
- data/test/plugin/out_forward/test_ack_handler.rb +39 -0
- data/test/plugin/test_base.rb +98 -0
- data/test/plugin/test_buf_file.rb +62 -23
- data/test/plugin/test_buf_file_single.rb +65 -0
- data/test/plugin/test_in_http.rb +2 -3
- data/test/plugin/test_in_monitor_agent.rb +2 -3
- data/test/plugin/test_in_tail.rb +105 -103
- data/test/plugin/test_in_tcp.rb +15 -0
- data/test/plugin/test_out_file.rb +3 -2
- data/test/plugin/test_out_forward.rb +14 -18
- data/test/plugin/test_out_http.rb +1 -0
- data/test/plugin/test_output.rb +269 -0
- data/test/plugin/test_parser_regexp.rb +1 -6
- data/test/plugin_helper/test_http_server_helper.rb +1 -1
- data/test/plugin_helper/test_server.rb +10 -5
- data/test/test_config.rb +57 -21
- data/test/{plugin/test_file_wrapper.rb → test_file_wrapper.rb} +2 -2
- data/test/test_formatter.rb +23 -20
- data/test/test_log.rb +85 -40
- data/test/test_supervisor.rb +300 -283
- metadata +15 -24
- data/.drone.yml +0 -35
- data/.github/workflows/issue-auto-closer.yml +0 -12
- data/.github/workflows/stale-actions.yml +0 -22
- data/.gitlab-ci.yml +0 -103
- data/lib/fluent/plugin/file_wrapper.rb +0 -131
- data/test/test_logger_initializer.rb +0 -46
data/test/command/test_ctl.rb
CHANGED
@@ -5,20 +5,35 @@ require_relative '../helper'
|
|
5
5
|
|
6
6
|
require 'fileutils'
|
7
7
|
require 'timeout'
|
8
|
+
require 'securerandom'
|
9
|
+
require 'fluent/file_wrapper'
|
8
10
|
|
9
11
|
class TestFluentdCommand < ::Test::Unit::TestCase
|
10
|
-
TMP_DIR = File.expand_path(File.dirname(__FILE__) + "/../tmp/command/fluentd#{ENV['TEST_ENV_NUMBER']}")
|
11
12
|
SUPERVISOR_PID_PATTERN = /starting fluentd-[.0-9]+ pid=(\d+)/
|
12
13
|
WORKER_PID_PATTERN = /starting fluentd worker pid=(\d+) /
|
13
14
|
|
15
|
+
def tmp_dir
|
16
|
+
File.join(File.dirname(__FILE__), "..", "tmp", "command" "fluentd#{ENV['TEST_ENV_NUMBER']}", SecureRandom.hex(10))
|
17
|
+
end
|
18
|
+
|
14
19
|
setup do
|
15
|
-
|
16
|
-
FileUtils.mkdir_p(
|
20
|
+
@tmp_dir = tmp_dir
|
21
|
+
FileUtils.mkdir_p(@tmp_dir)
|
17
22
|
@supervisor_pid = nil
|
18
23
|
@worker_pids = []
|
19
24
|
ENV["TEST_RUBY_PATH"] = nil
|
20
25
|
end
|
21
26
|
|
27
|
+
teardown do
|
28
|
+
begin
|
29
|
+
FileUtils.rm_rf(@tmp_dir)
|
30
|
+
rescue Errno::EACCES
|
31
|
+
# It may occur on Windows because of delete pending state due to delayed GC.
|
32
|
+
# Ruby 3.2 or later doesn't ignore Errno::EACCES:
|
33
|
+
# https://github.com/ruby/ruby/commit/983115cf3c8f75b1afbe3274f02c1529e1ce3a81
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
22
37
|
def process_exist?(pid)
|
23
38
|
begin
|
24
39
|
r = Process.waitpid(pid, Process::WNOHANG)
|
@@ -30,17 +45,17 @@ class TestFluentdCommand < ::Test::Unit::TestCase
|
|
30
45
|
end
|
31
46
|
|
32
47
|
def create_conf_file(name, content, ext_enc = 'utf-8')
|
33
|
-
conf_path = File.join(
|
34
|
-
|
48
|
+
conf_path = File.join(@tmp_dir, name)
|
49
|
+
Fluent::FileWrapper.open(conf_path, "w:#{ext_enc}:utf-8") do |file|
|
35
50
|
file.write content
|
36
51
|
end
|
37
52
|
conf_path
|
38
53
|
end
|
39
54
|
|
40
55
|
def create_plugin_file(name, content)
|
41
|
-
file_path = File.join(
|
56
|
+
file_path = File.join(@tmp_dir, 'plugin', name)
|
42
57
|
FileUtils.mkdir_p(File.dirname(file_path))
|
43
|
-
|
58
|
+
Fluent::FileWrapper.open(file_path, 'w') do |file|
|
44
59
|
file.write content
|
45
60
|
end
|
46
61
|
file_path
|
@@ -56,8 +71,22 @@ class TestFluentdCommand < ::Test::Unit::TestCase
|
|
56
71
|
end
|
57
72
|
end
|
58
73
|
|
59
|
-
def
|
60
|
-
|
74
|
+
def process_kill(pid)
|
75
|
+
if Fluent.windows?
|
76
|
+
Process.kill(:KILL, pid) rescue nil
|
77
|
+
return
|
78
|
+
end
|
79
|
+
|
80
|
+
begin
|
81
|
+
Process.kill(:TERM, pid) rescue nil
|
82
|
+
Timeout.timeout(10){ sleep 0.1 while process_exist?(pid) }
|
83
|
+
rescue Timeout::Error
|
84
|
+
Process.kill(:KILL, pid) rescue nil
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def execute_command(cmdline, chdir=@tmp_dir, env = {})
|
89
|
+
null_stream = Fluent::FileWrapper.open(File::NULL, 'w')
|
61
90
|
gemfile_path = File.expand_path(File.dirname(__FILE__) + "../../../Gemfile")
|
62
91
|
|
63
92
|
env = { "BUNDLE_GEMFILE" => gemfile_path }.merge(env)
|
@@ -70,12 +99,12 @@ class TestFluentdCommand < ::Test::Unit::TestCase
|
|
70
99
|
yield pid, io
|
71
100
|
# p(here: "execute command", pid: pid, worker_pids: @worker_pids)
|
72
101
|
ensure
|
73
|
-
|
102
|
+
process_kill(pid)
|
74
103
|
if @supervisor_pid
|
75
|
-
|
104
|
+
process_kill(@supervisor_pid)
|
76
105
|
end
|
77
106
|
@worker_pids.each do |cpid|
|
78
|
-
|
107
|
+
process_kill(cpid)
|
79
108
|
end
|
80
109
|
# p(here: "execute command", pid: pid, exist: process_exist?(pid), worker_pids: @worker_pids, exists: @worker_pids.map{|i| process_exist?(i) })
|
81
110
|
Timeout.timeout(10){ sleep 0.1 while process_exist?(pid) }
|
@@ -97,16 +126,18 @@ class TestFluentdCommand < ::Test::Unit::TestCase
|
|
97
126
|
end
|
98
127
|
end
|
99
128
|
|
100
|
-
|
129
|
+
# ATTENTION: This stops taking logs when all `pattern_list` match or timeout,
|
130
|
+
# so `patterns_not_match` can test only logs up to that point.
|
131
|
+
def assert_log_matches(cmdline, *pattern_list, patterns_not_match: [], timeout: 20, env: {})
|
101
132
|
matched = false
|
102
133
|
matched_wrongly = false
|
103
134
|
assert_error_msg = ""
|
104
135
|
stdio_buf = ""
|
105
136
|
begin
|
106
|
-
execute_command(cmdline,
|
137
|
+
execute_command(cmdline, @tmp_dir, env) do |pid, stdout|
|
107
138
|
begin
|
108
139
|
waiting(timeout) do
|
109
|
-
while process_exist?(pid)
|
140
|
+
while process_exist?(pid)
|
110
141
|
readables, _, _ = IO.select([stdout], nil, nil, 1)
|
111
142
|
next unless readables
|
112
143
|
break if readables.first.eof?
|
@@ -118,6 +149,18 @@ class TestFluentdCommand < ::Test::Unit::TestCase
|
|
118
149
|
if pattern_list.all?{|ptn| lines.any?{|line| ptn.is_a?(Regexp) ? ptn.match(line) : line.include?(ptn) } }
|
119
150
|
matched = true
|
120
151
|
end
|
152
|
+
|
153
|
+
if Fluent.windows?
|
154
|
+
# https://github.com/fluent/fluentd/issues/4095
|
155
|
+
# On Windows, the initial process is different from the supervisor process,
|
156
|
+
# so we need to wait until `SUPERVISOR_PID_PATTERN` appears in the logs to get the pid.
|
157
|
+
# (Worker processes will be killed by the supervisor process, so we don't need it-)
|
158
|
+
break if matched && SUPERVISOR_PID_PATTERN =~ stdio_buf
|
159
|
+
else
|
160
|
+
# On Non-Windows, the initial process is the supervisor process,
|
161
|
+
# so we don't need to wait `SUPERVISOR_PID_PATTERN`.
|
162
|
+
break if matched
|
163
|
+
end
|
121
164
|
end
|
122
165
|
end
|
123
166
|
ensure
|
@@ -131,6 +174,10 @@ class TestFluentdCommand < ::Test::Unit::TestCase
|
|
131
174
|
end
|
132
175
|
rescue Timeout::Error
|
133
176
|
assert_error_msg = "execution timeout"
|
177
|
+
# https://github.com/fluent/fluentd/issues/4095
|
178
|
+
# On Windows, timeout without `@supervisor_pid` means that the test is invalid,
|
179
|
+
# since the supervisor process will survive without being killed correctly.
|
180
|
+
flunk("Invalid test: The pid of supervisor could not be taken, which is necessary on Windows.") if Fluent.windows? && @supervisor_pid.nil?
|
134
181
|
rescue => e
|
135
182
|
assert_error_msg = "unexpected error in launching fluentd: #{e.inspect}"
|
136
183
|
else
|
@@ -162,7 +209,7 @@ class TestFluentdCommand < ::Test::Unit::TestCase
|
|
162
209
|
assert matched && !matched_wrongly, assert_error_msg
|
163
210
|
end
|
164
211
|
|
165
|
-
def assert_fluentd_fails_to_start(cmdline, *pattern_list, timeout:
|
212
|
+
def assert_fluentd_fails_to_start(cmdline, *pattern_list, timeout: 20)
|
166
213
|
# empty_list.all?{ ... } is always true
|
167
214
|
matched = false
|
168
215
|
running = false
|
@@ -198,6 +245,10 @@ class TestFluentdCommand < ::Test::Unit::TestCase
|
|
198
245
|
end
|
199
246
|
rescue Timeout::Error
|
200
247
|
assert_error_msg = "execution timeout with command out:\n" + stdio_buf
|
248
|
+
# https://github.com/fluent/fluentd/issues/4095
|
249
|
+
# On Windows, timeout without `@supervisor_pid` means that the test is invalid,
|
250
|
+
# since the supervisor process will survive without being killed correctly.
|
251
|
+
flunk("Invalid test: The pid of supervisor could not be taken, which is necessary on Windows.") if Fluent.windows? && @supervisor_pid.nil?
|
201
252
|
rescue => e
|
202
253
|
assert_error_msg = "unexpected error in launching fluentd: #{e.inspect}\n" + stdio_buf
|
203
254
|
assert false, assert_error_msg
|
@@ -269,7 +320,7 @@ CONF
|
|
269
320
|
|
270
321
|
sub_test_case 'with system configuration about root directory' do
|
271
322
|
setup do
|
272
|
-
@root_path = File.join(
|
323
|
+
@root_path = File.join(@tmp_dir, "rootpath")
|
273
324
|
FileUtils.rm_rf(@root_path)
|
274
325
|
@conf = <<CONF
|
275
326
|
<system>
|
@@ -308,7 +359,7 @@ CONF
|
|
308
359
|
end
|
309
360
|
|
310
361
|
test 'fails to launch fluentd if specified root path is invalid path for directory' do
|
311
|
-
|
362
|
+
Fluent::FileWrapper.open(@root_path, 'w') do |_|
|
312
363
|
# create file and close it
|
313
364
|
end
|
314
365
|
conf_path = create_conf_file('existing_root_dir.conf', @conf)
|
@@ -508,7 +559,7 @@ CONF
|
|
508
559
|
|
509
560
|
assert_fluentd_fails_to_start(
|
510
561
|
create_cmdline(conf_path, "-p", File.dirname(plugin_path)),
|
511
|
-
"in_buggy.rb:5: syntax error, unexpected end-of-input
|
562
|
+
"in_buggy.rb:5: syntax error, unexpected end-of-input"
|
512
563
|
)
|
513
564
|
end
|
514
565
|
end
|
@@ -554,7 +605,7 @@ CONF
|
|
554
605
|
|
555
606
|
sub_test_case 'configured to run 2 workers' do
|
556
607
|
setup do
|
557
|
-
@root_path = File.join(
|
608
|
+
@root_path = File.join(@tmp_dir, "rootpath")
|
558
609
|
FileUtils.rm_rf(@root_path)
|
559
610
|
FileUtils.mkdir_p(@root_path)
|
560
611
|
end
|
@@ -961,10 +1012,10 @@ CONF
|
|
961
1012
|
</match>
|
962
1013
|
CONF
|
963
1014
|
ruby_path = ServerEngine.ruby_bin_path
|
964
|
-
tmp_ruby_path = File.join(
|
1015
|
+
tmp_ruby_path = File.join(@tmp_dir, "ruby with spaces")
|
965
1016
|
if Fluent.windows?
|
966
1017
|
tmp_ruby_path << ".bat"
|
967
|
-
|
1018
|
+
Fluent::FileWrapper.open(tmp_ruby_path, "w") do |file|
|
968
1019
|
file.write "#{ruby_path} %*"
|
969
1020
|
end
|
970
1021
|
else
|
@@ -1136,4 +1187,99 @@ CONF
|
|
1136
1187
|
"shared socket for multiple workers is disabled",)
|
1137
1188
|
end
|
1138
1189
|
end
|
1190
|
+
|
1191
|
+
# TODO: `patterns_not_match` can test only logs up to `pattern_list`,
|
1192
|
+
# so we need to fix some meaningless `patterns_not_match` conditions.
|
1193
|
+
sub_test_case 'log_level by command line option' do
|
1194
|
+
test 'info' do
|
1195
|
+
conf = ""
|
1196
|
+
conf_path = create_conf_file('empty.conf', conf)
|
1197
|
+
assert File.exist?(conf_path)
|
1198
|
+
assert_log_matches(create_cmdline(conf_path),
|
1199
|
+
"[info]",
|
1200
|
+
patterns_not_match: ["[debug]"])
|
1201
|
+
end
|
1202
|
+
|
1203
|
+
test 'debug' do
|
1204
|
+
conf = ""
|
1205
|
+
conf_path = create_conf_file('empty.conf', conf)
|
1206
|
+
assert File.exist?(conf_path)
|
1207
|
+
assert_log_matches(create_cmdline(conf_path, "-v"),
|
1208
|
+
"[debug]",
|
1209
|
+
patterns_not_match: ["[trace]"])
|
1210
|
+
end
|
1211
|
+
|
1212
|
+
data("Trace" => "-vv")
|
1213
|
+
data("Invalid low level should be treated as Trace level": "-vvv")
|
1214
|
+
test 'trace' do |option|
|
1215
|
+
conf = <<CONF
|
1216
|
+
<source>
|
1217
|
+
@type sample
|
1218
|
+
tag test
|
1219
|
+
</source>
|
1220
|
+
CONF
|
1221
|
+
conf_path = create_conf_file('sample.conf', conf)
|
1222
|
+
assert File.exist?(conf_path)
|
1223
|
+
assert_log_matches(create_cmdline(conf_path, option),
|
1224
|
+
"[trace]",)
|
1225
|
+
end
|
1226
|
+
|
1227
|
+
test 'warn' do
|
1228
|
+
omit "Can't run on Windows since there is no way to take pid of the supervisor." if Fluent.windows?
|
1229
|
+
conf = <<CONF
|
1230
|
+
<source>
|
1231
|
+
@type sample
|
1232
|
+
tag test
|
1233
|
+
</source>
|
1234
|
+
CONF
|
1235
|
+
conf_path = create_conf_file('sample.conf', conf)
|
1236
|
+
assert File.exist?(conf_path)
|
1237
|
+
assert_log_matches(create_cmdline(conf_path, "-q"),
|
1238
|
+
"[warn]",
|
1239
|
+
patterns_not_match: ["[info]"])
|
1240
|
+
end
|
1241
|
+
|
1242
|
+
data("Error" => "-qq")
|
1243
|
+
data("Fatal should be treated as Error level" => "-qqq")
|
1244
|
+
data("Invalid high level should be treated as Error level": "-qqqq")
|
1245
|
+
test 'error' do |option|
|
1246
|
+
# This test can run on Windows correctly,
|
1247
|
+
# since the process will stop automatically with an error.
|
1248
|
+
conf = <<CONF
|
1249
|
+
<source>
|
1250
|
+
@type plugin_not_found
|
1251
|
+
tag test
|
1252
|
+
</source>
|
1253
|
+
CONF
|
1254
|
+
conf_path = create_conf_file('plugin_not_found.conf', conf)
|
1255
|
+
assert File.exist?(conf_path)
|
1256
|
+
assert_log_matches(create_cmdline(conf_path, option),
|
1257
|
+
"[error]",
|
1258
|
+
patterns_not_match: ["[warn]"])
|
1259
|
+
end
|
1260
|
+
|
1261
|
+
test 'system config one should not be overwritten when cmd line one is not specified' do
|
1262
|
+
conf = <<CONF
|
1263
|
+
<system>
|
1264
|
+
log_level debug
|
1265
|
+
</system>
|
1266
|
+
CONF
|
1267
|
+
conf_path = create_conf_file('debug.conf', conf)
|
1268
|
+
assert File.exist?(conf_path)
|
1269
|
+
assert_log_matches(create_cmdline(conf_path),
|
1270
|
+
"[debug]")
|
1271
|
+
end
|
1272
|
+
end
|
1273
|
+
|
1274
|
+
sub_test_case "inline_config" do
|
1275
|
+
test "can change log_level by --inline-config" do
|
1276
|
+
# Since we can't define multiple `<system>` directives, this use-case is not recommended.
|
1277
|
+
# This is just for this test.
|
1278
|
+
inline_conf = '<system>\nlog_level debug\n</system>'
|
1279
|
+
conf_path = create_conf_file('test.conf', "")
|
1280
|
+
assert File.exist?(conf_path)
|
1281
|
+
assert_log_matches(create_cmdline(conf_path, "--inline-config", inline_conf),
|
1282
|
+
"[debug]")
|
1283
|
+
end
|
1284
|
+
end
|
1139
1285
|
end
|
@@ -188,7 +188,6 @@ slow_flush_log_threshold: float: (20.0)
|
|
188
188
|
retry_exponential_backoff_base: float: (2)
|
189
189
|
retry_max_interval: time: (nil)
|
190
190
|
retry_randomize: bool: (true)
|
191
|
-
disable_chunk_backup: bool: (false)
|
192
191
|
<secondary>: optional, single
|
193
192
|
@type: string: (nil)
|
194
193
|
<buffer>: optional, single
|
data/test/compat/test_parser.rb
CHANGED
@@ -38,14 +38,14 @@ class TextParserTest < ::Test::Unit::TestCase
|
|
38
38
|
|
39
39
|
def test_parse_with_return
|
40
40
|
parser = Fluent::TextParser.new
|
41
|
-
parser.configure('format' => 'none')
|
41
|
+
parser.configure(config_element('test', '', 'format' => 'none'))
|
42
42
|
_time, record = parser.parse('log message!')
|
43
43
|
assert_equal({'message' => 'log message!'}, record)
|
44
44
|
end
|
45
45
|
|
46
46
|
def test_parse_with_block
|
47
47
|
parser = Fluent::TextParser.new
|
48
|
-
parser.configure('format' => 'none')
|
48
|
+
parser.configure(config_element('test', '', 'format' => 'none'))
|
49
49
|
parser.parse('log message!') { |time, record|
|
50
50
|
assert_equal({'message' => 'log message!'}, record)
|
51
51
|
}
|
@@ -53,7 +53,7 @@ class TextParserTest < ::Test::Unit::TestCase
|
|
53
53
|
|
54
54
|
def test_multi_event_parser
|
55
55
|
parser = Fluent::TextParser.new
|
56
|
-
parser.configure('format' => 'multi_event_test')
|
56
|
+
parser.configure(config_element('test', '', 'format' => 'multi_event_test'))
|
57
57
|
i = 0
|
58
58
|
parser.parse('log message!') { |time, record|
|
59
59
|
assert_equal('log message!', record['message'])
|
@@ -67,7 +67,7 @@ class TextParserTest < ::Test::Unit::TestCase
|
|
67
67
|
assert_nil p1.estimate_current_event
|
68
68
|
assert_nil p1.parser
|
69
69
|
|
70
|
-
p1.configure('format' => 'none')
|
70
|
+
p1.configure(config_element('test', '', 'format' => 'none'))
|
71
71
|
assert_equal true, p1.parser.estimate_current_event
|
72
72
|
|
73
73
|
p2 = Fluent::TextParser.new
|
@@ -76,7 +76,7 @@ class TextParserTest < ::Test::Unit::TestCase
|
|
76
76
|
|
77
77
|
p2.estimate_current_event = false
|
78
78
|
|
79
|
-
p2.configure('format' => 'none')
|
79
|
+
p2.configure(config_element('test', '', 'format' => 'none'))
|
80
80
|
assert_equal false, p2.parser.estimate_current_event
|
81
81
|
end
|
82
82
|
|
@@ -5,13 +5,6 @@ require 'fluent/config/section'
|
|
5
5
|
require 'fluent/system_config'
|
6
6
|
|
7
7
|
module Fluent::Config
|
8
|
-
class FakeLoggerInitializer
|
9
|
-
attr_accessor :level
|
10
|
-
def initialize
|
11
|
-
@level = nil
|
12
|
-
end
|
13
|
-
end
|
14
|
-
|
15
8
|
class FakeSupervisor
|
16
9
|
attr_writer :log_level
|
17
10
|
|
@@ -21,7 +14,6 @@ module Fluent::Config
|
|
21
14
|
wokers: nil,
|
22
15
|
restart_worker_interval: nil,
|
23
16
|
root_dir: nil,
|
24
|
-
log: FakeLoggerInitializer.new,
|
25
17
|
log_level: Fluent::Log::LEVEL_INFO,
|
26
18
|
suppress_interval: nil,
|
27
19
|
suppress_config_dump: nil,
|
@@ -0,0 +1,110 @@
|
|
1
|
+
require_relative '../helper'
|
2
|
+
|
3
|
+
require 'fluent/log'
|
4
|
+
require 'fluent/log/console_adapter'
|
5
|
+
|
6
|
+
class ConsoleAdapterTest < Test::Unit::TestCase
|
7
|
+
def setup
|
8
|
+
@timestamp = Time.parse("2023-01-01 15:32:41 +0000")
|
9
|
+
@timestamp_str = @timestamp.strftime("%Y-%m-%d %H:%M:%S %z")
|
10
|
+
Timecop.freeze(@timestamp)
|
11
|
+
|
12
|
+
@logdev = Fluent::Test::DummyLogDevice.new
|
13
|
+
@logger = ServerEngine::DaemonLogger.new(@logdev)
|
14
|
+
@fluent_log = Fluent::Log.new(@logger)
|
15
|
+
@console_logger = Fluent::Log::ConsoleAdapter.wrap(@fluent_log)
|
16
|
+
end
|
17
|
+
|
18
|
+
def teardown
|
19
|
+
Timecop.return
|
20
|
+
end
|
21
|
+
|
22
|
+
def test_expected_log_levels
|
23
|
+
assert_equal({debug: 0, info: 1, warn: 2, error: 3, fatal: 4},
|
24
|
+
Console::Logger::LEVELS)
|
25
|
+
end
|
26
|
+
|
27
|
+
data(trace: [Fluent::Log::LEVEL_TRACE, :debug],
|
28
|
+
debug: [Fluent::Log::LEVEL_DEBUG, :debug],
|
29
|
+
info: [Fluent::Log::LEVEL_INFO, :info],
|
30
|
+
warn: [Fluent::Log::LEVEL_WARN, :warn],
|
31
|
+
error: [Fluent::Log::LEVEL_ERROR, :error],
|
32
|
+
fatal: [Fluent::Log::LEVEL_FATAL, :fatal])
|
33
|
+
def test_reflect_log_level(data)
|
34
|
+
level, expected = data
|
35
|
+
@fluent_log.level = level
|
36
|
+
console_logger = Fluent::Log::ConsoleAdapter.wrap(@fluent_log)
|
37
|
+
assert_equal(Console::Logger::LEVELS[expected],
|
38
|
+
console_logger.level)
|
39
|
+
end
|
40
|
+
|
41
|
+
data(debug: :debug,
|
42
|
+
info: :info,
|
43
|
+
warn: :warn,
|
44
|
+
error: :error,
|
45
|
+
fatal: :fatal)
|
46
|
+
def test_string_subject(level)
|
47
|
+
@console_logger.send(level, "subject")
|
48
|
+
assert_equal(["#{@timestamp_str} [#{level}]: 0.0s: subject\n"],
|
49
|
+
@logdev.logs)
|
50
|
+
end
|
51
|
+
|
52
|
+
data(debug: :debug,
|
53
|
+
info: :info,
|
54
|
+
warn: :warn,
|
55
|
+
error: :error,
|
56
|
+
fatal: :fatal)
|
57
|
+
def test_args(level)
|
58
|
+
@console_logger.send(level, "subject", 1, 2, 3)
|
59
|
+
assert_equal([
|
60
|
+
"#{@timestamp_str} [#{level}]: 0.0s: subject\n" +
|
61
|
+
" | 1\n" +
|
62
|
+
" | 2\n" +
|
63
|
+
" | 3\n"
|
64
|
+
],
|
65
|
+
@logdev.logs)
|
66
|
+
end
|
67
|
+
|
68
|
+
data(debug: :debug,
|
69
|
+
info: :info,
|
70
|
+
warn: :warn,
|
71
|
+
error: :error,
|
72
|
+
fatal: :fatal)
|
73
|
+
def test_options(level)
|
74
|
+
@console_logger.send(level, "subject", kwarg1: "opt1", kwarg2: "opt2")
|
75
|
+
assert_equal([
|
76
|
+
"#{@timestamp_str} [#{level}]: 0.0s: subject\n" +
|
77
|
+
" | {\"kwarg1\":\"opt1\",\"kwarg2\":\"opt2\"}\n"
|
78
|
+
],
|
79
|
+
@logdev.logs)
|
80
|
+
end
|
81
|
+
|
82
|
+
data(debug: :debug,
|
83
|
+
info: :info,
|
84
|
+
warn: :warn,
|
85
|
+
error: :error,
|
86
|
+
fatal: :fatal)
|
87
|
+
def test_block(level)
|
88
|
+
@console_logger.send(level, "subject") { "block message" }
|
89
|
+
assert_equal([
|
90
|
+
"#{@timestamp_str} [#{level}]: 0.0s: subject\n" +
|
91
|
+
" | block message\n"
|
92
|
+
],
|
93
|
+
@logdev.logs)
|
94
|
+
end
|
95
|
+
|
96
|
+
data(debug: :debug,
|
97
|
+
info: :info,
|
98
|
+
warn: :warn,
|
99
|
+
error: :error,
|
100
|
+
fatal: :fatal)
|
101
|
+
def test_multiple_entries(level)
|
102
|
+
@console_logger.send(level, "subject1")
|
103
|
+
@console_logger.send(level, "line2")
|
104
|
+
assert_equal([
|
105
|
+
"#{@timestamp_str} [#{level}]: 0.0s: subject1\n",
|
106
|
+
"#{@timestamp_str} [#{level}]: 0.0s: line2\n"
|
107
|
+
],
|
108
|
+
@logdev.logs)
|
109
|
+
end
|
110
|
+
end
|
@@ -98,4 +98,43 @@ class AckHandlerTest < Test::Unit::TestCase
|
|
98
98
|
w.close rescue nil
|
99
99
|
end
|
100
100
|
end
|
101
|
+
|
102
|
+
# ForwardOutput uses AckHandler in multiple threads, so we need to assume this case.
|
103
|
+
# If exclusive control for this case is implemented, this test may not be necessary.
|
104
|
+
test 'raises no error when another thread closes a socket' do
|
105
|
+
ack_handler = Fluent::Plugin::ForwardOutput::AckHandler.new(timeout: 10, log: $log, read_length: 100)
|
106
|
+
|
107
|
+
node = flexmock('node', host: '127.0.0.1', port: '1000') # for log
|
108
|
+
chunk_id = 'chunk_id 111'
|
109
|
+
ack = ack_handler.create_ack(chunk_id, node)
|
110
|
+
|
111
|
+
r, w = IO.pipe
|
112
|
+
begin
|
113
|
+
w.write(chunk_id)
|
114
|
+
stub(r).recv { |_|
|
115
|
+
sleep(1) # To ensure that multiple threads select the socket before closing.
|
116
|
+
raise IOError, 'stream closed in another thread' if r.closed?
|
117
|
+
MessagePack.pack({ 'ack' => Base64.encode64('chunk_id 111') })
|
118
|
+
}
|
119
|
+
ack.enqueue(r)
|
120
|
+
|
121
|
+
threads = []
|
122
|
+
2.times do
|
123
|
+
threads << Thread.new do
|
124
|
+
ack_handler.collect_response(1) do |cid, n, s, ret|
|
125
|
+
s&.close
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
assert_true threads.map{ |t| t.join(10) }.all?
|
131
|
+
assert_false(
|
132
|
+
$log.out.logs.any? { |log| log.include?('[error]') },
|
133
|
+
$log.out.logs.select{ |log| log.include?('[error]') }.join('\n')
|
134
|
+
)
|
135
|
+
ensure
|
136
|
+
r.close rescue nil
|
137
|
+
w.close rescue nil
|
138
|
+
end
|
139
|
+
end
|
101
140
|
end
|
data/test/plugin/test_base.rb
CHANGED
@@ -146,4 +146,102 @@ class BaseTest < Test::Unit::TestCase
|
|
146
146
|
end
|
147
147
|
end
|
148
148
|
end
|
149
|
+
|
150
|
+
test '`ArgumentError` when `conf` is not `Fluent::Config::Element`' do
|
151
|
+
assert_raise ArgumentError.new('BUG: type of conf must be Fluent::Config::Element, but Hash is passed.') do
|
152
|
+
@p.configure({})
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
sub_test_case 'system_config.workers value after configure' do
|
157
|
+
def assert_system_config_workers_value(data)
|
158
|
+
conf = config_element()
|
159
|
+
conf.set_target_worker_ids(data[:target_worker_ids])
|
160
|
+
@p.configure(conf)
|
161
|
+
assert{ @p.system_config.workers == data[:expected] }
|
162
|
+
end
|
163
|
+
|
164
|
+
def stub_supervisor_mode
|
165
|
+
stub(Fluent::Engine).supervisor_mode { true }
|
166
|
+
stub(Fluent::Engine).worker_id { -1 }
|
167
|
+
end
|
168
|
+
|
169
|
+
sub_test_case 'with <system> workers 3 </system>' do
|
170
|
+
setup do
|
171
|
+
system_config = Fluent::SystemConfig.new
|
172
|
+
system_config.workers = 3
|
173
|
+
stub(Fluent::Engine).system_config { system_config }
|
174
|
+
end
|
175
|
+
|
176
|
+
data(
|
177
|
+
'without <worker> directive',
|
178
|
+
{
|
179
|
+
target_worker_ids: [],
|
180
|
+
expected: 3
|
181
|
+
},
|
182
|
+
keep: true
|
183
|
+
)
|
184
|
+
data(
|
185
|
+
'with <worker 0>',
|
186
|
+
{
|
187
|
+
target_worker_ids: [0],
|
188
|
+
expected: 1
|
189
|
+
},
|
190
|
+
keep: true
|
191
|
+
)
|
192
|
+
data(
|
193
|
+
'with <worker 0-1>',
|
194
|
+
{
|
195
|
+
target_worker_ids: [0, 1],
|
196
|
+
expected: 2
|
197
|
+
},
|
198
|
+
keep: true
|
199
|
+
)
|
200
|
+
data(
|
201
|
+
'with <worker 0-2>',
|
202
|
+
{
|
203
|
+
target_worker_ids: [0, 1, 2],
|
204
|
+
expected: 3
|
205
|
+
},
|
206
|
+
keep: true
|
207
|
+
)
|
208
|
+
|
209
|
+
test 'system_config.workers value after configure' do
|
210
|
+
assert_system_config_workers_value(data)
|
211
|
+
end
|
212
|
+
|
213
|
+
test 'system_config.workers value after configure with supervisor_mode' do
|
214
|
+
stub_supervisor_mode
|
215
|
+
assert_system_config_workers_value(data)
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
219
|
+
sub_test_case 'without <system> directive' do
|
220
|
+
data(
|
221
|
+
'without <worker> directive',
|
222
|
+
{
|
223
|
+
target_worker_ids: [],
|
224
|
+
expected: 1
|
225
|
+
},
|
226
|
+
keep: true
|
227
|
+
)
|
228
|
+
data(
|
229
|
+
'with <worker 0>',
|
230
|
+
{
|
231
|
+
target_worker_ids: [0],
|
232
|
+
expected: 1
|
233
|
+
},
|
234
|
+
keep: true
|
235
|
+
)
|
236
|
+
|
237
|
+
test 'system_config.workers value after configure' do
|
238
|
+
assert_system_config_workers_value(data)
|
239
|
+
end
|
240
|
+
|
241
|
+
test 'system_config.workers value after configure with supervisor_mode' do
|
242
|
+
stub_supervisor_mode
|
243
|
+
assert_system_config_workers_value(data)
|
244
|
+
end
|
245
|
+
end
|
246
|
+
end
|
149
247
|
end
|