165 lines
3.7 KiB
Lua
165 lines
3.7 KiB
Lua
local fs = require 'bee.filesystem'
|
|
local time = require 'bee.time'
|
|
|
|
local monotonic = time.monotonic
|
|
local osDate = os.date
|
|
local ioOpen = io.open
|
|
local tablePack = table.pack
|
|
local tableConcat = table.concat
|
|
local tostring = tostring
|
|
local debugTraceBack = debug.traceback
|
|
local mathModf = math.modf
|
|
local debugGetInfo = debug.getinfo
|
|
local ioStdErr = io.stderr
|
|
|
|
local m = {}
|
|
|
|
m.file = nil
|
|
m.startTime = time.time() - monotonic()
|
|
m.size = 0
|
|
m.maxSize = 100 * 1024 * 1024
|
|
m.level = 'info'
|
|
m.levelMap = {
|
|
['trace'] = 1,
|
|
['debug'] = 2,
|
|
['info'] = 3,
|
|
['warn'] = 4,
|
|
['error'] = 5,
|
|
}
|
|
|
|
local function trimSrc(src)
|
|
if src:sub(1, 1) == '@' then
|
|
src = src:sub(2)
|
|
end
|
|
return src
|
|
end
|
|
|
|
local function init_log_file()
|
|
if not m.file then
|
|
m.file = ioOpen(m.path, 'w')
|
|
if not m.file then
|
|
return
|
|
end
|
|
m.file:write('')
|
|
m.file:close()
|
|
m.file = ioOpen(m.path, 'ab')
|
|
if not m.file then
|
|
return
|
|
end
|
|
m.file:setvbuf 'no'
|
|
end
|
|
end
|
|
|
|
local function pushLog(level, ...)
|
|
if not m.path then
|
|
return
|
|
end
|
|
local t = tablePack(...)
|
|
for i = 1, t.n do
|
|
t[i] = tostring(t[i])
|
|
end
|
|
local str = tableConcat(t, '\t', 1, t.n)
|
|
if level == 'error' then
|
|
str = str .. '\n' .. debugTraceBack(nil, 3)
|
|
end
|
|
local info = debugGetInfo(3, 'Sl')
|
|
local text = m.raw(0, level, str, info.source, info.currentline, monotonic())
|
|
|
|
return text
|
|
end
|
|
|
|
function m.trace(...)
|
|
pushLog('trace', ...)
|
|
end
|
|
|
|
function m.debug(...)
|
|
pushLog('debug', ...)
|
|
end
|
|
|
|
function m.info(...)
|
|
pushLog('info', ...)
|
|
end
|
|
|
|
function m.warn(...)
|
|
pushLog('warn', ...)
|
|
end
|
|
|
|
function m.error(...)
|
|
-- Don't use tail calls,
|
|
-- Otherwise, the count of `debug.getinfo` will be wrong
|
|
local msg = pushLog('error', ...)
|
|
return msg
|
|
end
|
|
|
|
function m.raw(thd, level, msg, source, currentline, clock)
|
|
if m.levelMap[level] < (m.levelMap[m.level] or m.levelMap['info']) then
|
|
return msg
|
|
end
|
|
if level == 'error' then
|
|
ioStdErr:write(msg .. '\n')
|
|
if not m.firstError then
|
|
m.firstError = msg
|
|
end
|
|
end
|
|
if m.size > m.maxSize then
|
|
return msg
|
|
end
|
|
init_log_file()
|
|
local sec, ms = mathModf((m.startTime + clock) / 1000)
|
|
local timestr = osDate('%H:%M:%S', sec)
|
|
local agl = ''
|
|
if #level < 5 then
|
|
agl = (' '):rep(5 - #level)
|
|
end
|
|
local buf
|
|
if currentline == -1 then
|
|
buf = ('[%s.%03.f][%s]%s[#%d]: %s\n'):format(timestr, ms * 1000, level, agl, thd, msg)
|
|
else
|
|
buf = ('[%s.%03.f][%s]%s[#%d:%s:%s]: %s\n'):format(timestr, ms * 1000, level, agl, thd, trimSrc(source), currentline, msg)
|
|
end
|
|
m.size = m.size + #buf
|
|
if m.file then
|
|
if m.size > m.maxSize then
|
|
m.file:write(buf:sub(1, m.size - m.maxSize))
|
|
m.file:write('[REACH MAX SIZE]')
|
|
else
|
|
m.file:write(buf)
|
|
end
|
|
end
|
|
|
|
if m.print then
|
|
print(buf)
|
|
end
|
|
|
|
return buf
|
|
end
|
|
|
|
function m.init(root, path)
|
|
local lastBuf
|
|
if m.file then
|
|
m.file:close()
|
|
m.file = nil
|
|
local file = ioOpen(m.path, 'rb')
|
|
if file then
|
|
lastBuf = file:read(m.maxSize)
|
|
file:close()
|
|
end
|
|
end
|
|
m.path = path:string()
|
|
m.prefixLen = #root:string()
|
|
m.size = 0
|
|
pcall(function ()
|
|
if not fs.exists(path:parent_path()) then
|
|
fs.create_directories(path:parent_path())
|
|
end
|
|
end)
|
|
if lastBuf then
|
|
init_log_file()
|
|
if m.file then
|
|
m.file:write(lastBuf)
|
|
m.size = m.size + #lastBuf
|
|
end
|
|
end
|
|
end
|
|
|
|
return m
|