cc-mek-scada/scada-common/log.lua

354 lines
9.2 KiB
Lua
Raw Permalink Normal View History

--
-- File System Logger
--
2022-05-31 20:09:06 +00:00
local util = require("scada-common.util")
---@class logger
local log = {}
2022-05-10 21:06:27 +00:00
---@alias MODE integer
local MODE = { APPEND = 0, NEW = 1 }
log.MODE = MODE
2023-05-10 20:57:23 +00:00
local logger = {
not_ready = true,
2022-05-06 14:48:46 +00:00
path = "/log.txt",
mode = MODE.APPEND,
2023-05-10 20:57:23 +00:00
debug = false,
2023-09-03 21:54:39 +00:00
file = nil, ---@type table|nil
dmesg_out = nil,
dmesg_restore_coord = { 1, 1 },
dmesg_scroll_count = 0
2022-05-06 14:48:46 +00:00
}
2022-05-10 21:06:27 +00:00
---@type function
2022-05-10 15:35:52 +00:00
local free_space = fs.getFreeSpace
-----------------------
-- PRIVATE FUNCTIONS --
-----------------------
2022-05-10 15:35:52 +00:00
-- private log write function
2022-05-10 21:06:27 +00:00
---@param msg string
2022-05-31 20:09:06 +00:00
local function _log(msg)
if logger.not_ready then return end
local out_of_space = false
2022-05-06 14:48:46 +00:00
local time_stamp = os.date("[%c] ")
2022-05-26 03:24:15 +00:00
local stamped = time_stamp .. util.strval(msg)
2022-04-27 22:43:07 +00:00
-- attempt to write log
2022-05-10 15:35:52 +00:00
local status, result = pcall(function ()
2023-05-10 20:57:23 +00:00
logger.file.writeLine(stamped)
logger.file.flush()
2022-04-27 22:43:07 +00:00
end)
2022-05-06 14:48:46 +00:00
-- if we don't have space, we need to create a new log file
2022-04-27 22:43:07 +00:00
if (not status) and (result ~= nil) then
out_of_space = string.find(result, "Out of space") ~= nil
if out_of_space then
2022-05-06 14:48:46 +00:00
-- will delete log file
else
2022-05-06 14:48:46 +00:00
util.println("unknown error writing to logfile: " .. result)
2022-04-27 22:43:07 +00:00
end
end
2023-10-01 04:20:19 +00:00
if out_of_space or (free_space(logger.path) < 512) then
-- delete the old log file before opening a new one
2023-05-10 20:57:23 +00:00
logger.file.close()
fs.delete(logger.path)
-- re-init logger and pass dmesg_out so that it doesn't change
2023-05-10 20:57:23 +00:00
log.init(logger.path, logger.mode, logger.debug, logger.dmesg_out)
2022-04-27 22:43:07 +00:00
-- leave a message
2023-05-10 20:57:23 +00:00
logger.file.writeLine(time_stamp .. "recycled log file")
logger.file.writeLine(stamped)
logger.file.flush()
2022-04-27 22:43:07 +00:00
end
end
----------------------
-- PUBLIC FUNCTIONS --
----------------------
-- initialize logger
---@param path string file path
2023-05-10 20:57:23 +00:00
---@param write_mode MODE file write mode
---@param include_debug boolean whether or not to include debug logs
---@param dmesg_redirect? table terminal/window to direct dmesg to
2023-05-10 20:57:23 +00:00
function log.init(path, write_mode, include_debug, dmesg_redirect)
logger.path = path
logger.mode = write_mode
logger.debug = include_debug
2023-05-10 20:57:23 +00:00
if logger.mode == MODE.APPEND then
logger.file = fs.open(path, "a")
else
2023-05-10 20:57:23 +00:00
logger.file = fs.open(path, "w")
end
if dmesg_redirect then
2023-05-10 20:57:23 +00:00
logger.dmesg_out = dmesg_redirect
else
2023-05-10 20:57:23 +00:00
logger.dmesg_out = term.current()
end
logger.not_ready = false
end
-- close the log file handle
function log.close()
2023-05-10 20:57:23 +00:00
logger.file.close()
end
-- direct dmesg output to a monitor/window
---@param window table window or terminal reference
2023-05-10 20:57:23 +00:00
function log.direct_dmesg(window) logger.dmesg_out = window end
-- dmesg style logging for boot because I like linux-y things
---@param msg string message
---@param tag? string log tag
---@param tag_color? integer log tag color
2022-07-06 03:47:13 +00:00
---@return dmesg_ts_coord coordinates line area to place working indicator
2022-05-31 20:09:06 +00:00
function log.dmesg(msg, tag, tag_color)
2022-07-06 03:47:13 +00:00
---@class dmesg_ts_coord
local ts_coord = { x1 = 2, x2 = 3, y = 1 }
msg = util.strval(msg)
tag = tag or ""
tag = util.strval(tag)
local t_stamp = string.format("%12.2f", os.clock())
2023-05-10 20:57:23 +00:00
local out = logger.dmesg_out
2022-05-16 16:50:51 +00:00
if out ~= nil then
local out_w, out_h = out.getSize()
2022-05-16 16:50:51 +00:00
local lines = { msg }
2022-05-16 16:50:51 +00:00
-- wrap if needed
if string.len(msg) > out_w then
local remaining = true
local s_start = 1
local s_end = out_w
local i = 1
2022-05-16 16:50:51 +00:00
lines = {}
2022-05-16 16:50:51 +00:00
while remaining do
local line = string.sub(msg, s_start, s_end)
if line == "" then
remaining = false
else
lines[i] = line
2022-05-16 16:50:51 +00:00
s_start = s_end + 1
s_end = s_end + out_w
i = i + 1
end
2022-05-16 16:50:51 +00:00
end
end
-- start output with tag and time, assuming we have enough width for this to be on one line
local cur_x, cur_y = out.getCursorPos()
if cur_x > 1 then
if cur_y == out_h then
out.scroll(1)
out.setCursorPos(1, cur_y)
logger.dmesg_scroll_count = logger.dmesg_scroll_count + 1
else
out.setCursorPos(1, cur_y + 1)
end
end
-- colored time
local initial_color = out.getTextColor()
out.setTextColor(colors.white)
out.write("[")
out.setTextColor(colors.lightGray)
out.write(t_stamp)
ts_coord.x2, ts_coord.y = out.getCursorPos()
ts_coord.x2 = ts_coord.x2 - 1
out.setTextColor(colors.white)
out.write("] ")
-- print optionally colored tag
if tag ~= "" then
out.write("[")
if tag_color then out.setTextColor(tag_color) end
out.write(tag)
out.setTextColor(colors.white)
out.write("] ")
end
out.setTextColor(initial_color)
2022-05-16 16:50:51 +00:00
-- output message
for i = 1, #lines do
cur_x, cur_y = out.getCursorPos()
if i > 1 and cur_x > 1 then
if cur_y == out_h then
out.scroll(1)
out.setCursorPos(1, cur_y)
logger.dmesg_scroll_count = logger.dmesg_scroll_count + 1
else
out.setCursorPos(1, cur_y + 1)
end
2022-05-16 16:50:51 +00:00
end
out.write(lines[i])
2022-05-16 16:50:51 +00:00
end
logger.dmesg_restore_coord = { out.getCursorPos() }
_log(util.c("[", t_stamp, "] [", tag, "] ", msg))
2022-05-16 16:50:51 +00:00
end
2022-07-06 03:47:13 +00:00
return ts_coord
end
-- print a dmesg message, but then show remaining seconds instead of timestamp
2023-02-21 15:31:05 +00:00
---@nodiscard
2022-07-06 03:47:13 +00:00
---@param msg string message
---@param tag? string log tag
---@param tag_color? integer log tag color
---@return function update, function done
function log.dmesg_working(msg, tag, tag_color)
local ts_coord = log.dmesg(msg, tag, tag_color)
local initial_scroll = logger.dmesg_scroll_count
2022-07-06 03:47:13 +00:00
2023-05-10 20:57:23 +00:00
local out = logger.dmesg_out
2022-07-06 03:47:13 +00:00
local width = (ts_coord.x2 - ts_coord.x1) + 1
if out ~= nil then
local initial_color = out.getTextColor()
2022-07-06 03:47:13 +00:00
local counter = 0
2022-07-06 03:47:13 +00:00
local function update(sec_remaining)
local new_y = ts_coord.y - (logger.dmesg_scroll_count - initial_scroll)
if new_y < 1 then return end
local time = util.sprintf("%ds", sec_remaining)
local available = width - (string.len(time) + 2)
local progress = ""
2022-07-06 03:47:13 +00:00
out.setCursorPos(ts_coord.x1, new_y)
out.write(" ")
2022-07-06 03:47:13 +00:00
if counter % 4 == 0 then
progress = "|"
elseif counter % 4 == 1 then
progress = "/"
elseif counter % 4 == 2 then
progress = "-"
elseif counter % 4 == 3 then
progress = "\\"
end
2022-07-06 03:47:13 +00:00
out.setTextColor(colors.blue)
out.write(progress)
out.setTextColor(colors.lightGray)
out.write(util.spaces(available) .. time)
out.setTextColor(initial_color)
2022-07-06 03:47:13 +00:00
counter = counter + 1
out.setCursorPos(table.unpack(logger.dmesg_restore_coord))
end
2022-07-06 03:47:13 +00:00
local function done(ok)
local new_y = ts_coord.y - (logger.dmesg_scroll_count - initial_scroll)
if new_y < 1 then return end
out.setCursorPos(ts_coord.x1, new_y)
2022-07-06 03:47:13 +00:00
if ok or ok == nil then
out.setTextColor(colors.green)
out.write(util.pad("DONE", width))
else
out.setTextColor(colors.red)
out.write(util.pad("FAIL", width))
end
out.setTextColor(initial_color)
out.setCursorPos(table.unpack(logger.dmesg_restore_coord))
2022-07-06 03:47:13 +00:00
end
return update, done
else
return function () end, function () end
2022-07-06 03:47:13 +00:00
end
2022-05-16 16:50:51 +00:00
end
2022-05-10 15:35:52 +00:00
-- log debug messages
2022-05-10 21:06:27 +00:00
---@param msg string message
---@param trace? boolean include file trace
2022-05-31 20:09:06 +00:00
function log.debug(msg, trace)
2023-05-10 20:57:23 +00:00
if logger.debug then
2022-03-23 19:41:08 +00:00
local dbg_info = ""
if trace then
2022-05-06 14:48:46 +00:00
local info = debug.getinfo(2)
2022-03-23 19:41:08 +00:00
local name = ""
2022-05-06 14:48:46 +00:00
if info.name ~= nil then
name = ":" .. info.name .. "():"
2022-03-23 19:41:08 +00:00
end
2022-05-06 14:48:46 +00:00
dbg_info = info.short_src .. ":" .. name .. info.currentline .. " > "
2022-03-23 19:41:08 +00:00
end
2022-05-26 03:24:15 +00:00
_log("[DBG] " .. dbg_info .. util.strval(msg))
2022-03-23 19:41:08 +00:00
end
end
2022-05-10 15:35:52 +00:00
-- log info messages
2022-05-10 21:06:27 +00:00
---@param msg string message
2022-05-31 20:09:06 +00:00
function log.info(msg)
2022-05-26 03:24:15 +00:00
_log("[INF] " .. util.strval(msg))
end
2022-05-10 15:35:52 +00:00
-- log warning messages
2022-05-10 21:06:27 +00:00
---@param msg string message
2022-05-31 20:09:06 +00:00
function log.warning(msg)
2022-05-26 03:24:15 +00:00
_log("[WRN] " .. util.strval(msg))
2022-03-23 19:41:08 +00:00
end
2022-05-10 15:35:52 +00:00
-- log error messages
2022-05-10 21:06:27 +00:00
---@param msg string message
---@param trace? boolean include file trace
2022-05-31 20:09:06 +00:00
function log.error(msg, trace)
local dbg_info = ""
2022-05-10 15:35:52 +00:00
if trace then
2022-05-06 14:48:46 +00:00
local info = debug.getinfo(2)
local name = ""
2022-05-06 14:48:46 +00:00
if info.name ~= nil then
name = ":" .. info.name .. "():"
end
2022-05-10 15:35:52 +00:00
2022-05-06 14:48:46 +00:00
dbg_info = info.short_src .. ":" .. name .. info.currentline .. " > "
end
2022-05-26 03:24:15 +00:00
_log("[ERR] " .. dbg_info .. util.strval(msg))
end
2022-05-10 15:35:52 +00:00
-- log fatal errors
2022-05-10 21:06:27 +00:00
---@param msg string message
2022-05-31 20:09:06 +00:00
function log.fatal(msg)
2022-05-26 03:24:15 +00:00
_log("[FTL] " .. util.strval(msg))
end
return log