Implement logging

This commit is contained in:
Gabriel Tofvesson 2024-10-14 05:49:39 +02:00
parent ea4de6e988
commit cedf602e89
4 changed files with 232 additions and 49 deletions

View File

@ -1,4 +1,5 @@
local Event = require("gfx.event")
local Logger = require("logging").getGlobalLogger()
local Element = {
x = 1,
@ -31,6 +32,7 @@ function Element:new(o)
end
function Element:draw()
Logger:trace("draw", self)
local dirty = self:_isDirty()
if dirty then
local win = self:_getWindow()
@ -51,6 +53,7 @@ function Element:getY()
end
function Element:setPos(x, y)
Logger:trace("setPos", x, y)
if (x ~= nil and self.x ~= x) or (y ~= nil and self.y ~= y) then
self.x = x or self.x
self.y = y or self.y
@ -77,6 +80,7 @@ function Element:setY(y)
end
function Element:setParent(parent)
Logger:trace("setParent", self, parent)
if self.parent ~= parent then
self.parent = parent
self:setDirty()

View File

@ -1,38 +0,0 @@
local Prop = require("gfx.prop")
local Visibility = Prop:new{ defaultState = true, uid = "VISIBILITY" }
Visibility.VISIBLE = true
Visibility.INVISIBLE = not Visibility.VISIBLE
function Visibility:with(elementType)
local propSelf = self
local defaultDraw = elementType.draw
local defaultHandleEvent = elementType.handleEvent
function elementType:isVisible()
return propSelf:getState(self)
end
function elementType:setVisible(visibility)
return propSelf:setState(self, visibility)
end
function elementType:draw()
if self:isVisible() then
return defaultDraw(self)
end
return false
end
-- Ignore events when invisible
function elementType:handleEvent(evt)
if self:isVisible() then
return defaultHandleEvent(self, evt)
end
return false
end
return elementType
end
return Visibility

View File

@ -1,6 +1,7 @@
local Chest = require("storage.chest")
local ItemGroup = require("storage.itemgroup")
local Storage = require("storage")
local Logging = require("logging")
local Text = require("gfx.text")
local List = require("gfx.list")
local Event = require("gfx.event")
@ -8,13 +9,21 @@ local Padding = require("gfx.padding")
local Container = require("gfx.container")
local Element = require("gfx.element")
local Progress = require("gfx.progress")
local Prop = require("gfx.prop")
local Children = require("gfx.prop.children")
local Orientation = require("gfx.prop.orientation")
local CACHE_FILE = "/.storage.cache"
LOCK_FILE = "/.storage.lock"
local LOCK_FILE = "/.storage.lock"
local LOG_FILE_PATH = "latest.log"
local LOG_FILE = fs.open(LOG_FILE_PATH, "w+")
local Logger = Logging.getGlobalLogger()
Logger:setOutput(function(text)
LOG_FILE.write(text)
LOG_FILE.flush()
print(text)
end)
Logger:setLevel(Logging.LogLevel.TRACE)
local function lock()
if fs.exists(LOCK_FILE) then
@ -48,11 +57,12 @@ local function saveState(fname, ctrl)
local file = fs.open(fname, "w+")
file.write(textutils.serialize(ser))
file.close()
Logger:info("saved state to", fname)
end
local function loadState(fname, node)
if not isLocked() then
print("Not locked! Loading cache...")
Logger:info("Not locked! Loading cache...")
local file = fs.open(fname, "r")
if file ~= nil then
local ser = textutils.unserialize(file.readAll())
@ -61,7 +71,7 @@ local function loadState(fname, node)
end
end
print("Controller must scan chests...")
Logger:info("Controller must scan chests...")
local nodeName = peripheral.getName(node)
local storageChests = {peripheral.find("inventory")}
for i,v in ipairs(storageChests) do
@ -685,7 +695,7 @@ local PAGES = {
bindTabActionButtons()
keyboardButton:setOnClick(function()
print("Toggling keyboard...")
Logger:debug("Toggling keyboard...")
pageState.showKeyboard = not pageState.showKeyboard
reloadState()
@ -693,13 +703,13 @@ local PAGES = {
end)
importButton:setOnClick(function()
print("Importing...")
Logger:debug("Importing...")
state.node:rescan()
-- Safely handle transfers, priming computer for a full reset/rescan in case server stops mid-transaction
state:itemTransaction(function()
for _,nodeStack in ipairs(state.node:find(function(s) return not s:isEmpty() end)) do
if not state.controller:insertStack(nodeStack) then
print("Couldn't find a free slot for: "..nodeStack:getSimpleName())
Logger:warn("Couldn't find a free slot for: ", nodeStack:getSimpleName())
end
end
end)
@ -709,7 +719,7 @@ local PAGES = {
end)
sortButton:setOnClick(function()
print("Reorganizing...")
Logger:debug("Reorganizing...")
pageState.sortMode = (pageState.sortMode % #SORT_MODE) + 1
reloadState()
return true
@ -726,7 +736,7 @@ local PAGES = {
local group = state:getExtra()
if group == nil then
print("No group passed to GROUP_DETAIL")
Logger:error("No group passed to GROUP_DETAIL")
state:setPage("MAIN")
return NOOP
end
@ -856,7 +866,7 @@ local PAGES = {
REQUEST = function(state)
local group = state:getExtra()
if group == nil then
print("No group passed to REQUEST")
Logger:error("No group passed to REQUEST")
state:setPage("MAIN")
return NOOP
end
@ -993,6 +1003,7 @@ local PAGES = {
state.height,
state.width,
function()
Logger:debug("Exporting to access node")
state.node:rescan()
state:itemTransaction(function()
group:transferTo(state.node, pageState.request)
@ -1024,6 +1035,7 @@ local PAGES = {
-- Set up event management
local function updateDisplayState()
Logger:debug("updateDisplayState")
local dataRequestText = paddedRequestCount:findById("data_request")
local dataDividerPad = paddedRequestCount:findById("data_divider")
local dataAvailableText = paddedRequestCount:findById("data_available")
@ -1099,6 +1111,7 @@ function CONTROLLER_STATE:currentPageState(default, override)
end
function CONTROLLER_STATE:setPage(page, extra)
Logger:debug("new page", page, extra)
self.nextPage = page
self.extra = extra
end

204
logging.lua Normal file
View File

@ -0,0 +1,204 @@
local Logging = {}
Logging.__index = 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 = {}
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
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 function _simpleStringify(value, builder)
if type(value) == "table" then
table.insert(builder, "<")
if 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)
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)
table.insert(builder, "=")
_simpleStringify(v, builder)
end
table.insert(builder, "}")
end
table.insert(builder, ">")
else
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
_simpleStringify(cloneNonRecursive(v), result)
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