Files

129 lines
2.6 KiB
Lua

-- Usage:
-- package.path = package.path .. ";path-to-this-file"
-- local log = require("log")
-- log.info("Engine initialized.")
-- log.error("Failed: %s (%d)", reason, code)
local M = {}
M.levels = {
NONE = -1,
ERROR = 0,
WARN = 1,
INFO = 2,
DEBUG = 3,
TRACE = 4,
}
local level_names = {
[-1] = "NONE",
[0] = "ERROR",
[1] = "WARN",
[2] = "INFO",
[3] = "DEBUG",
[4] = "TRACE",
}
M.min_level = M.levels.TRACE
local function normalize_level(level)
if type(level) == "string" then
local n = M.levels[level:upper()]
if n == nil then
error("invalid log level: " .. level, 3)
end
return n
end
if type(level) ~= "number" or level ~= math.floor(level) then
error("log level must be integer or level name", 3)
end
if level < M.levels.NONE or level > M.levels.TRACE then
error("invalid log level: " .. tostring(level), 3)
end
return level
end
local function clean_source(source)
if type(source) ~= "string" then
return "=[?]"
end
if source:sub(1, 1) == "@" then
source = source:sub(2)
end
local base = source:match(".*[/\\](.*)$")
return base or source
end
local function caller_name(info)
if info.name and info.name ~= "" then
return info.name
end
if info.what == "main" then
return "main"
end
if info.what == "C" then
return "C"
end
return "anonymous"
end
local function format_message(message, ...)
if select("#", ...) == 0 then
return tostring(message)
end
local ok, formatted = pcall(string.format, tostring(message), ...)
if ok then
return formatted
end
return tostring(message)
end
local function emit(level, message, ...)
if level == M.levels.NONE or level > M.min_level then
return
end
local info = debug.getinfo(3, "Snl") or {}
local level_str = level_names[level] or "MISSING"
local func = caller_name(info)
local filename = clean_source(info.source)
local line = info.currentline or -1
local text = format_message(message, ...)
io.stderr:write(string.format("[%s] [%s] [%s:%d] %s\n",
level_str, func, filename, line, text))
end
function M.log(level, message, ...)
emit(normalize_level(level), message, ...)
end
function M.error(message, ...) emit(M.levels.ERROR, message, ...) end
function M.warn(message, ...) emit(M.levels.WARN, message, ...) end
function M.info(message, ...) emit(M.levels.INFO, message, ...) end
function M.debug(message, ...) emit(M.levels.DEBUG, message, ...) end
function M.trace(message, ...) emit(M.levels.TRACE, message, ...) end
function M.set_level(level)
M.min_level = normalize_level(level)
end
M.ERROR = M.error
M.WARN = M.warn
M.INFO = M.info
M.DEBUG = M.debug
M.TRACE = M.trace
return M