-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathlog4l.lua
244 lines (218 loc) · 6.92 KB
/
log4l.lua
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
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
-------------------------------------------------------------------------------
-- includes a new tostring function that handles tables recursively
--
-- @author Danilo Tuler ([email protected])
-- @author Andre Carregal ([email protected])
-- @author Thiago Costa Ponte ([email protected])
--
-- @copyright 2004-2013 Kepler Project
-------------------------------------------------------------------------------
local type, table, string, _tostring, tonumber = type, table, string, tostring, tonumber
local select = select
local error = error
local format = string.format
local pairs = pairs
local ipairs = ipairs
local logging = {
-- Meta information
_COPYRIGHT = "Copyright (C) 2004-2013 Kepler Project",
_DESCRIPTION = "A simple API to use logging features in Lua",
_VERSION = "LuaLogging 1.3.0",
-- The DEBUG Level designates fine-grained instring.formational events that are most
-- useful to debug an application
DEBUG = "DEBUG",
-- The INFO level designates instring.formational messages that highlight the
-- progress of the application at coarse-grained level
INFO = "INFO",
-- The WARN level designates potentially harmful situations
WARN = "WARN",
-- The ERROR level designates error events that might still allow the
-- application to continue running
ERROR = "ERROR",
-- The FATAL level designates very severe error events that will presumably
-- lead the application to abort
FATAL = "FATAL",
}
local LEVEL = {"DEBUG", "INFO", "WARN", "ERROR", "FATAL"}
local MAX_LEVELS = #LEVEL
-- make level names to order
for i=1,MAX_LEVELS do
LEVEL[LEVEL[i]] = i
end
-- private log function, with support for formating a complex log message.
local function LOG_MSG(self, level, fmt, ...)
local f_type = type(fmt)
if f_type == 'string' then
if select('#', ...) > 0 then
local status, msg = pcall(format, fmt, ...)
if status then
return self:append(level, msg)
else
return self:append(level, "Error formatting log message: " .. msg)
end
else
-- only a single string, no formating needed.
return self:append(level, fmt)
end
elseif f_type == 'function' then
-- fmt should be a callable function which returns the message to log
return self:append(level, fmt(...))
end
-- fmt is not a string and not a function, just call tostring() on it.
return self:append(level, logging.tostring(fmt))
end
-- create the proxy functions for each log level.
local LEVEL_FUNCS = {}
for i=1,MAX_LEVELS do
local level = LEVEL[i]
LEVEL_FUNCS[i] = function(self, ...)
-- no level checking needed here, this function will only be called if it's level is active.
return LOG_MSG(self, level, ...)
end
end
-- do nothing function for disabled levels.
local function disable_level() end
-- improved assertion function.
local function assert(exp, ...)
-- if exp is true, we are finished so don't do any processing of the parameters
if exp then return exp, ... end
-- assertion failed, raise error
error(format(...), 2)
end
-------------------------------------------------------------------------------
-- Creates a new logger object
-- @param append Function used by the logger to append a message with a
-- log-level to the log stream.
-- @return Table representing the new logger object.
-------------------------------------------------------------------------------
function logging.new(append)
if type(append) ~= "function" then
return nil, "Appender must be a function."
end
local logger = {}
logger.append = append
logger.setLevel = function (self, level)
local order = LEVEL[level]
assert(order, "undefined level `%s'", _tostring(level))
if self.level then
self:log(logging.WARN, "Logger: changing loglevel from %s to %s", self.level, level)
end
self.level = level
self.level_order = order
-- enable/disable levels
for i=1,MAX_LEVELS do
local name = LEVEL[i]:lower()
if i >= order then
self[name] = LEVEL_FUNCS[i]
else
self[name] = disable_level
end
end
end
-- generic log function.
logger.log = function (self, level, ...)
local order = LEVEL[level]
assert(order, "undefined level `%s'", _tostring(level))
if order < self.level_order then
return
end
return LOG_MSG(self, level, ...)
end
-- initialize log level.
logger:setLevel(logging.DEBUG)
return logger
end
-------------------------------------------------------------------------------
-- Prepares the log message
-------------------------------------------------------------------------------
function logging.prepareLogMsg(pattern, dt, level, message)
local logMsg = pattern or "%date %level %message\n"
message = string.gsub(message, "%%", "%%%%")
logMsg = string.gsub(logMsg, "%%date", dt)
logMsg = string.gsub(logMsg, "%%level", level)
logMsg = string.gsub(logMsg, "%%message", message)
return logMsg
end
-------------------------------------------------------------------------------
-- Converts a Lua value to a string
--
-- Converts Table fields in alphabetical order
-------------------------------------------------------------------------------
local function tostring(value, seen)
seen = seen or {}
local str = ''
if (type(value) ~= 'table') then
if (type(value) == 'string') then
str = string.format("%q", value)
else
str = _tostring(value)
end
else
local mt = getmetatable(value) or {}
if mt.__tostring then
return _tostring(value)
end
if seen[value] then
return '...'
else
seen[value] = true
end
local strTable = {}
local numTable = {}
local outOfOrderTable = {}
local outOfOrderKeys = {}
local otherTable = {}
local mapping = {}
for k, v in ipairs(value) do
numTable[k] = tostring(v, seen)
end
for k, v in pairs(value) do
if type(k) == 'number' then
if k < 1 or k > #numTable then
outOfOrderTable[k] = tostring(v, seen)
table.insert(outOfOrderKeys, k)
end
elseif type(k) == 'string' then
table.insert(strTable, k)
else
local as_string = tostring(k, seen)
mapping[as_string] = mapping[as_string] or {}
table.insert(otherTable, as_string)
table.insert(mapping[as_string], k)
end
end
table.sort(outOfOrderKeys)
table.sort(strTable)
table.sort(otherTable)
str = str..'{'
local separator = ""
for _, v in ipairs(numTable) do
str = str..separator..v
separator = ", "
end
for _, v in ipairs(outOfOrderKeys) do
str = str..separator.."["..v.."]".." = "..outOfOrderTable[v]
separator = ", "
end
for _, fieldName in ipairs(strTable) do
str = str..separator..fieldName.." = "..tostring(value[fieldName], seen)
separator = ", "
end
for _, fieldName in ipairs(otherTable) do
for _, field in ipairs(mapping[fieldName]) do
str = str..separator.."["..fieldName.."]".." = "..tostring(value[field], seen)
separator = ", "
end
end
str = str..'}'
end
seen[value] = nil
return str
end
logging.tostring = tostring
local luamaj, luamin = _VERSION:match("Lua (%d+)%.(%d+)")
if tonumber(luamaj) == 5 and tonumber(luamin) < 2 then
-- still create 'logging' global for Lua versions < 5.2
_G.logging = logging
end
return logging