220 lines
5.2 KiB
Lua
220 lines
5.2 KiB
Lua
local Logging = {}
|
|
|
|
local LogLevel = {
|
|
TRACE = 0,
|
|
DEBUG = 1,
|
|
INFO = 2,
|
|
WARNING = 3,
|
|
CRITICAL = 4,
|
|
ERROR = 5
|
|
}
|
|
|
|
for k,v in pairs(LogLevel) do
|
|
LogLevel[v] = k
|
|
end
|
|
|
|
function LogLevel.isValid(value)
|
|
return LogLevel[LogLevel[value]] ~= nil
|
|
end
|
|
|
|
function LogLevel.asNumber(value)
|
|
return type(value) == "number" and value or LogLevel[value]
|
|
end
|
|
|
|
function LogLevel.isGreaterOrEqual(a, b)
|
|
return a >= b
|
|
end
|
|
|
|
local Logger = {
|
|
ignoreGlobals = true
|
|
}
|
|
Logger.__index = Logger
|
|
|
|
function Logger:new(o)
|
|
local logger = {}
|
|
local output = print
|
|
if type(o) == "table" then
|
|
if type(o.level) == "number" and LogLevel.isValid(o.level) then
|
|
logger.level = o.level
|
|
end
|
|
if type(o.output) == "function" then
|
|
output = o.output
|
|
end
|
|
end
|
|
logger.output = output
|
|
setmetatable(logger, self)
|
|
logger.__index = logger
|
|
|
|
return logger
|
|
end
|
|
|
|
function Logger:setIgnoreGlobals(ignoreGlobals)
|
|
self.ignoreGlobals = ignoreGlobals
|
|
end
|
|
|
|
local RecursionSentinel = {}
|
|
function RecursionSentinel.make(table)
|
|
local obj = { table = table }
|
|
setmetatable(obj, RecursionSentinel)
|
|
return obj
|
|
end
|
|
|
|
function RecursionSentinel.isSentinel(value)
|
|
return type(value) == "table" and getmetatable(value) == RecursionSentinel
|
|
end
|
|
|
|
function RecursionSentinel.isKnown(knownTables, value)
|
|
return knownTables[value] ~= nil
|
|
end
|
|
|
|
function RecursionSentinel.getSentinel(knownTables, value)
|
|
local sentinel = knownTables[value]
|
|
if sentinel == nil then
|
|
sentinel = knownTables[value]
|
|
knownTables[value] = RecursionSentinel.make(value)
|
|
end
|
|
return sentinel
|
|
end
|
|
|
|
function RecursionSentinel.add(knownTables, value)
|
|
local sentinel = RecursionSentinel.make(value)
|
|
knownTables[value] = sentinel
|
|
return sentinel
|
|
end
|
|
|
|
function RecursionSentinel.remove(knownTables, value)
|
|
knownTables[value] = nil
|
|
end
|
|
|
|
local function cloneNonRecursive(value, sentinels)
|
|
if type(value) == "table" then
|
|
if RecursionSentinel.isKnown(sentinels, value) then
|
|
return RecursionSentinel.getSentinel(sentinels, value)
|
|
end
|
|
|
|
local clone = {}
|
|
local sentinel = RecursionSentinel.add(sentinels, value)
|
|
for i,v in ipairs(value) do
|
|
clone[i] = cloneNonRecursive(v, sentinels)
|
|
end
|
|
for k,v in pairs(value) do
|
|
clone[cloneNonRecursive(k, sentinels)] = cloneNonRecursive(v, sentinels)
|
|
end
|
|
RecursionSentinel.remove(sentinels, value)
|
|
|
|
sentinel.value = clone
|
|
return sentinel.value
|
|
else
|
|
return value
|
|
end
|
|
end
|
|
|
|
local G_ = {}
|
|
for k,v in pairs(_G) do
|
|
G_[v] = k
|
|
end
|
|
|
|
local function _simpleStringify(value, builder, ignoreGlobals, skipFunctions)
|
|
if type(value) == "table" then
|
|
table.insert(builder, "<")
|
|
if ignoreGlobals and G_[value] ~= nil then
|
|
table.insert(builder, "_G.")
|
|
table.insert(builder, G_[value])
|
|
elseif RecursionSentinel.isSentinel(value) then
|
|
table.insert(builder, "recurse ")
|
|
table.insert(builder, tostring(value.value))
|
|
else
|
|
table.insert(builder, tostring(value))
|
|
table.insert(builder, ">{")
|
|
local first = true
|
|
for i,v in ipairs(value) do
|
|
if not first then
|
|
table.insert(builder, ",")
|
|
else
|
|
first = false
|
|
end
|
|
_simpleStringify(v, builder, ignoreGlobals)
|
|
end
|
|
first = #value == 0
|
|
for k,v in pairs(value) do
|
|
if not first then
|
|
table.insert(builder, ",")
|
|
else
|
|
first = false
|
|
end
|
|
_simpleStringify(k, builder, ignoreGlobals)
|
|
table.insert(builder, "=")
|
|
_simpleStringify(v, builder, ignoreGlobals)
|
|
end
|
|
table.insert(builder, "}")
|
|
end
|
|
table.insert(builder, ">")
|
|
elseif not skipFunctions or type(value) ~= "function" then
|
|
local isString = type(value) == "string"
|
|
if isString then
|
|
table.insert(builder, "\"")
|
|
end
|
|
table.insert(builder, tostring(value))
|
|
if isString then
|
|
table.insert(builder, "\"")
|
|
end
|
|
end
|
|
end
|
|
|
|
function Logger:_doPrint(level, message, ...)
|
|
if LogLevel.isGreaterOrEqual(level, self.level) then
|
|
local result = { tostring(LogLevel[level]), message }
|
|
for _,v in ipairs({...}) do
|
|
local collect = {}
|
|
_simpleStringify(cloneNonRecursive(v, {}), collect)
|
|
table.insert(result, table.concat(collect))
|
|
end
|
|
self.output(table.concat(result, " "))
|
|
end
|
|
end
|
|
|
|
function Logger:trace(message, ...)
|
|
self:_doPrint(LogLevel.TRACE, message, ...)
|
|
end
|
|
|
|
function Logger:debug(message, ...)
|
|
self:_doPrint(LogLevel.DEBUG, message, ...)
|
|
end
|
|
|
|
function Logger:info(message, ...)
|
|
self:_doPrint(LogLevel.INFO, message, ...)
|
|
end
|
|
|
|
function Logger:warn(message, ...)
|
|
self:_doPrint(LogLevel.WARNING, message, ...)
|
|
end
|
|
|
|
function Logger:critical(message, ...)
|
|
self:_doPrint(LogLevel.CRITICAL, message, ...)
|
|
end
|
|
|
|
function Logger:error(message, ...)
|
|
self:_doPrint(LogLevel.ERROR, message, ...)
|
|
end
|
|
|
|
function Logger:setOutput(output)
|
|
self.output = output
|
|
end
|
|
|
|
function Logger:setLevel(level)
|
|
if LogLevel.isValid(level) then
|
|
self.level = LogLevel.asNumber(level)
|
|
end
|
|
end
|
|
|
|
Logging.Logger = Logger
|
|
Logging.LogLevel = LogLevel
|
|
|
|
function Logging.getGlobalLogger()
|
|
if _GLOBAL_LOGGER == nil then
|
|
_GLOBAL_LOGGER = Logger:new{ level = LogLevel.DEBUG, output = print }
|
|
end
|
|
return _GLOBAL_LOGGER
|
|
end
|
|
|
|
return Logging |