Skip to content

Commit 2f16ac7

Browse files
committed
Rework server log handling
Instead of configuring Tarantool test servers to write logs both to stdout and a file using the tee command, let's write all logs to stderr only and make the output beautifier work as tee. This fixes two issues at once: - Using `grep_log()` for a server started using the `cluster` helper. - Using `grep_log()` for a stopped server. Note that after this change we don't store all stderr output in the output beautifier anymore. It was used for detecting crashes at exit. Now, we use `grep_log()` to search for segmeration faults and memory leaks. Closes #389 Closes #397
1 parent 1e09c13 commit 2f16ac7

File tree

7 files changed

+44
-85
lines changed

7 files changed

+44
-85
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@
2424
(gh-396).
2525
- Remove pretty-printing of `luatest.log` arguments.
2626
- Add `cluster` helper as a tool for managing a Tarantool cluster (gh-368).
27+
- Fix `Server:grep_log()` to work with a server instance started using
28+
the `cluster` helper (gh-389).
29+
- Fix `Server:grep_log()` to work with a stopped server instance (gh-397).
2730

2831
## 1.0.1
2932

luatest/output_beautifier.lua

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
local checks = require('checks')
22
local fiber = require('fiber')
3+
local fio = require('fio')
34
local fun = require('fun')
45

56
local Class = require('luatest.class')
@@ -57,11 +58,13 @@ end
5758
--- Build OutputBeautifier object.
5859
-- @param object
5960
-- @string object.prefix String to prefix each output line with.
61+
-- @string[opt] object.file Path to the file to append all output too.
6062
-- @string[opt] object.color Color name for prefix.
6163
-- @string[opt] object.color_code Color code for prefix.
6264
-- @return input object.
6365
function OutputBeautifier:new(object)
64-
checks('table', {prefix = 'string', color = '?string', color_code = '?string'})
66+
checks('table', {prefix = 'string', file = '?string',
67+
color = '?string', color_code = '?string'})
6568
return self:from(object)
6669
end
6770

@@ -70,7 +73,6 @@ function OutputBeautifier.mt:initialize()
7073
self.class.COLOR_BY_NAME[self.color] or
7174
OutputBeautifier:next_color_code()
7275
self.pipes = {stdout = ffi_io.create_pipe(), stderr = ffi_io.create_pipe()}
73-
self.stderr = ''
7476
end
7577

7678
-- Replace standard output descriptors with pipes.
@@ -87,7 +89,7 @@ function OutputBeautifier.mt:enable(options)
8789
end
8890
self.fibers = {}
8991
for i, pipe in pairs(self.pipes) do
90-
self.fibers[i] = fiber.new(self.run, self, pipe[0], i)
92+
self.fibers[i] = fiber.new(self.run, self, pipe[0])
9193
end
9294
self.fibers.pid_tracker = options and options.track_pid and fiber.new(function()
9395
Process = Process or require('luatest.process')
@@ -99,6 +101,10 @@ function OutputBeautifier.mt:enable(options)
99101
fiber.sleep(self.class.PID_TRACKER_INTERVAL)
100102
end
101103
end)
104+
if self.file then
105+
self.fh = fio.open(self.file, {'O_CREAT', 'O_WRONLY', 'O_APPEND'},
106+
tonumber('640', 8))
107+
end
102108
end
103109

104110
-- Stop fibers.
@@ -111,6 +117,10 @@ function OutputBeautifier.mt:disable()
111117
end
112118
end
113119
self.fibers = nil
120+
if self.fh then
121+
self.fh:close()
122+
end
123+
self.fh = nil
114124
end
115125

116126
-- Process all available data from fd using synchronization with monitor.
@@ -139,22 +149,21 @@ end
139149
--
140150
-- Every line with log level mark (` X> `) changes the color for all the following
141151
-- lines until the next one with the mark.
142-
function OutputBeautifier.mt:run(fd, pipe)
152+
function OutputBeautifier.mt:run(fd)
143153
local prefix = self.prefix .. ' | '
144154
local colored_prefix = self.color_code .. prefix
145155
local line_color_code = self.class.RESET_TERM
146156
local log_file = rawget(_G, 'log_file')
147157
while fiber.testcancel() or true do
148158
self:process_fd_output(fd, function(chunks)
149-
local raw_lines = table.concat(chunks)
150-
if pipe == 'stderr' then
151-
self.stderr = self.stderr .. raw_lines
152-
end
153-
local lines = raw_lines:split('\n')
159+
local lines = table.concat(chunks):split('\n')
154160
if lines[#lines] == '' then
155161
table.remove(lines)
156162
end
157163
for _, line in pairs(lines) do
164+
if self.fh ~= nil then
165+
self.fh:write(table.concat({prefix, line, '\n'}))
166+
end
158167
if log_file ~= nil then
159168
-- Redirect all output to the log file if unified logging
160169
-- is enabled.

luatest/process.lua

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,16 +36,21 @@ function Process:start(path, args, env, options)
3636
chdir = '?string',
3737
ignore_gc = '?boolean',
3838
output_prefix = '?string',
39+
output_file = '?string',
3940
})
4041
args = args and table.copy(args) or {}
4142
env = env or {}
4243
options = options or {}
4344

4445
table.insert(args, 1, path)
4546

46-
local output_beautifier = options.output_prefix and OutputBeautifier:new({
47-
prefix = options.output_prefix,
48-
})
47+
local output_beautifier
48+
if options.output_prefix or options.output_file then
49+
output_beautifier = OutputBeautifier:new({
50+
prefix = options.output_prefix,
51+
file = options.output_file,
52+
})
53+
end
4954

5055
local env_list = fun.iter(env):map(function(k, v) return k .. '=' .. v end):totable()
5156
local pid = ffi.C.fork()

luatest/server.lua

Lines changed: 14 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -327,9 +327,7 @@ function Server:initialize()
327327
local prefix = fio.pathjoin(Server.vardir, 'artifacts', self.rs_id or '')
328328
self.artifacts = fio.pathjoin(prefix, self.id)
329329

330-
if rawget(_G, 'log_file') ~= nil then
331-
self.unified_log_enabled = true
332-
end
330+
self.log_file = fio.pathjoin(self.workdir, self.alias .. '.log')
333331
end
334332

335333
-- Create a table with env variables based on the constructor params.
@@ -341,7 +339,6 @@ end
341339
-- * `TARANTOOL_ALIAS`
342340
-- * `TARANTOOL_HTTP_PORT`
343341
-- * `TARANTOOL_BOX_CFG`
344-
-- * `TARANTOOL_UNIFIED_LOG_ENABLED`
345342
--
346343
-- @return table
347344
function Server:build_env()
@@ -354,9 +351,6 @@ function Server:build_env()
354351
if self.box_cfg ~= nil then
355352
res.TARANTOOL_BOX_CFG = json.encode(self.box_cfg)
356353
end
357-
if self.unified_log_enabled then
358-
res.TARANTOOL_UNIFIED_LOG_ENABLED = tostring(self.unified_log_enabled)
359-
end
360354
return res
361355
end
362356

@@ -442,6 +436,7 @@ function Server:start(opts)
442436
self.process = Process:start(command, args, env, {
443437
chdir = self.chdir,
444438
output_prefix = self.alias,
439+
output_file = self.log_file,
445440
})
446441

447442
local wait_until_ready
@@ -573,14 +568,16 @@ function Server:stop()
573568
end
574569
local workdir = fio.basename(self.workdir)
575570
local pid = self.process.pid
576-
local stderr = self.process.output_beautifier.stderr
577-
if stderr:find('Segmentation fault') then
578-
error(('Segmentation fault during process termination (alias: %s, workdir: %s, pid: %d)\n%s')
579-
:format(self.alias, workdir, pid, stderr))
580-
end
581-
if stderr:find('LeakSanitizer') then
582-
error(('Memory leak during process execution (alias: %s, workdir: %s, pid: %s)\n%s')
583-
:format(self.alias, workdir, pid, stderr))
571+
-- Check the log file for crash and memory leak reports.
572+
if fio.path.exists(self.log_file) then
573+
if self:grep_log('Segmentation fault$', math.huge) then
574+
error(('Segmentation fault during process termination (alias: %s, workdir: %s, pid: %d)')
575+
:format(self.alias, workdir, pid))
576+
end
577+
if self:grep_log('LeakSanitizer: detected memory leaks$', math.huge) then
578+
error(('Memory leak during process execution (alias: %s, workdir: %s, pid: %s)')
579+
:format(self.alias, workdir, pid))
580+
end
584581
end
585582
log.info('Process of server %q (pid: %d) killed', self.alias, self.process.pid)
586583
self.process = nil
@@ -869,7 +866,6 @@ end
869866
--
870867

871868
--- Search a string pattern in the server's log file.
872-
-- If the server has crashed, `opts.filename` is required.
873869
--
874870
-- @string pattern String pattern to search in the server's log file.
875871
-- @number[opt] bytes_num Number of bytes to read from the server's log file.
@@ -878,21 +874,12 @@ end
878874
-- pattern is found, which means that the server was restarted.
879875
-- Defaults to `true`.
880876
-- @string[opt] opts.filename Path to the server's log file.
881-
-- Defaults to `box.cfg.log`.
877+
-- Defaults to `<workdir>/<alias>.log`.
882878
-- @return string|nil
883879
function Server:grep_log(pattern, bytes_num, opts)
884880
local options = opts or {}
885881
local reset = options.reset or true
886-
887-
-- `box.cfg.log` can contain not only the path to the log file.
888-
-- When unified logging mode is on, `box.cfg.log` is as follows:
889-
--
890-
-- | tee ${TARANTOOL_WORKDIR}/${TARANTOOL_ALIAS}.log
891-
--
892-
-- Therefore, we set `_G.box_cfg_log_file` in server_instance.lua which
893-
-- contains the log file path: ${TARANTOOL_WORKDIR}/${TARANTOOL_ALIAS}.log.
894-
local filename = options.filename or self:exec(function()
895-
return rawget(_G, 'box_cfg_log_file') or box.cfg.log end)
882+
local filename = options.filename or self.log_file
896883
local file = fio.open(filename, {'O_RDONLY', 'O_NONBLOCK'})
897884

898885
log.info('Trying to grep %q in server\'s log file %s', pattern, filename)

luatest/server_instance.lua

Lines changed: 0 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,55 +1,12 @@
1-
local fio = require('fio')
21
local fun = require('fun')
32
local json = require('json')
4-
local log = require('log')
53

64
local TIMEOUT_INFINITY = 500 * 365 * 86400
75

8-
local function log_cfg()
9-
-- `log.cfg{}` is available since 2.5.1 version only. See more
10-
-- details at https://github.com/tarantool/tarantool/issues/689.
11-
if log.cfg ~= nil then
12-
-- Logging may be initialized before `box.cfg{}` call:
13-
--
14-
-- server:new({
15-
-- env = {['TARANTOOL_RUN_BEFORE_BOX_CFG'] = [[
16-
-- require('log').cfg{ log = <custom_log_file> }
17-
-- ]]})
18-
--
19-
-- This causes the `Can't set option 'log' dynamically` error,
20-
-- so we need to return the old log file path.
21-
if log.cfg.log ~= nil then
22-
return log.cfg.log
23-
end
24-
end
25-
local log_file = fio.pathjoin(
26-
os.getenv('TARANTOOL_WORKDIR'),
27-
os.getenv('TARANTOOL_ALIAS') .. '.log'
28-
)
29-
-- When `box.cfg.log` is called, we may get a string like
30-
--
31-
-- | tee ${TARANTOOL_WORKDIR}/${TARANTOOL_ALIAS}.log
32-
--
33-
-- Some tests or functions (e.g. Server:grep_log) may request the
34-
-- log file path, so we save it to a global variable. Thus it can
35-
-- be obtained by `rawget(_G, 'box_cfg_log_file')`.
36-
rawset(_G, 'box_cfg_log_file', log_file)
37-
38-
local unified_log_enabled = os.getenv('TARANTOOL_UNIFIED_LOG_ENABLED')
39-
if unified_log_enabled then
40-
-- Redirect the data stream to two sources at once:
41-
-- to the standard stream (stdout) and to the file
42-
-- ${TARANTOOL_WORKDIR}/${TARANTOOL_ALIAS}.log.
43-
return string.format('| tee %s', log_file)
44-
end
45-
return log_file
46-
end
47-
486
local function default_cfg()
497
return {
508
work_dir = os.getenv('TARANTOOL_WORKDIR'),
519
listen = os.getenv('TARANTOOL_LISTEN'),
52-
log = log_cfg()
5310
}
5411
end
5512

test/server_instance.lua

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,10 @@ local json = require('json')
55
local workdir = os.getenv('TARANTOOL_WORKDIR')
66
local listen = os.getenv('TARANTOOL_LISTEN')
77
local http_port = os.getenv('TARANTOOL_HTTP_PORT')
8-
local log = os.getenv('TARANTOOL_LOG')
98

109
local httpd = require('http.server').new('0.0.0.0', http_port)
1110

12-
box.cfg({work_dir = workdir, log = log})
11+
box.cfg({work_dir = workdir})
1312
box.schema.user.grant('guest', 'super', nil, nil, {if_not_exists = true})
1413
box.cfg({listen = listen})
1514

test/server_test.lua

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ local server = Server:new({
2222
LUA_PATH = root .. '/?.lua;' ..
2323
root .. '/?/init.lua;' ..
2424
root .. '/.rocks/share/tarantool/?.lua',
25-
TARANTOOL_LOG = fio.pathjoin(datadir, 'server_test.log'),
2625
custom_env = 'test_value',
2726
},
2827
http_port = 8182,

0 commit comments

Comments
 (0)