1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
|
local fs = require 'bee.filesystem'
local time = require 'bee.time'
local monotonic = os.clock
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
_ENV = nil
local m = {}
m.file = nil
m.startTime = time.time() - monotonic()
m.size = 0
m.maxSize = 100 * 1024 * 1024
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')
return m.raw(0, level, str, info.source, info.currentline, monotonic())
end
function m.info(...)
pushLog('info', ...)
end
function m.debug(...)
pushLog('debug', ...)
end
function m.trace(...)
pushLog('trace', ...)
end
function m.warn(...)
pushLog('warn', ...)
end
function m.error(...)
return pushLog('error', ...)
end
function m.raw(thd, level, msg, source, currentline, clock)
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
end
init_log_file()
if not m.file then
return ''
end
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.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
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
if not fs.exists(path:parent_path()) then
fs.create_directories(path:parent_path())
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
|