loggingconsole.lua
local homedir = getMudletHomeDir():gsub("\\", "/")
local pathOfThisFile = (...):match("(.-)[^%.]+$")
local dt = require(pathOfThisFile .. "demontools")
local exists, htmlHeader, htmlHeaderPattern = dt.exists, dt.htmlHeader, dt.htmlHeaderPattern
local LoggingConsole = {log = true, logFormat = "h", path = "|h/log/consoleLogs/|y/|m/|d/", fileName = "|n.|e"}
--- Creates and returns a new LoggingConsole.
-- @param cons table of constraints. Includes all the valid Geyser.MiniConsole constraints, plus
-- <table class="tg">
-- <thead>
-- <tr>
-- <th>option name</th>
-- <th>description</th>
-- <th>default</th>
-- </tr>
-- </thead>
-- <tbody>
-- <tr>
-- <td class="tg-1">log</td>
-- <td class="tg-1">Should the miniconsole be logging?</td>
-- <td class="tg-1">true</td>
-- </tr>
-- <tr>
-- <td class="tg-2">logFormat</td>
-- <td class="tg-2">"h" for html, "t" for plaintext, "l" for log (with ansi)</td>
-- <td class="tg-2">h</td>
-- </tr>
-- <tr>
-- <td class="tg-1">path</td>
-- <td class="tg-1">The path the file lives in. It is templated.<br>|h is replaced by the profile homedir.<br>|y by 4 digit year.<br>|m by 2 digit month<br>|d by 2 digit day<br>|n by the name constraint<br>|e by the file extension (html for h logType, log for others)</td>
-- <td class="tg-1">"|h/log/consoleLogs/|y/|m/|d/"</td>
-- </tr>
-- <tr>
-- <td class="tg-2">fileName</td>
-- <td class="tg-2">The name of the log file. It is templated, same as path above</td>
-- <td class="tg-2">"|n.|e"</td>
-- </tr>
-- </tbody>
-- </table>
-- @param container the container for the console
-- @usage
-- local LoggingConsole = require("MDK.loggingconsole")
-- myLoggingConsole = LoggingConsole:new({
-- name = "my logging console",
-- x = 0,
-- y = 0,
-- height = 200,
-- width = 400,
-- }) -- just like making a miniconsole, really
function LoggingConsole:new(cons, container)
cons = cons or {}
local consType = type(cons)
assert(consType == "table", "LoggingConsole:new(cons, container): cons must be a valid table of constraints. Got: " .. consType)
local me = Geyser.MiniConsole:new(cons, container)
setmetatable(me, self)
self.__index = self
return me
end
--- Returns the file extension of the logfile this console will log to
function LoggingConsole:getExtension()
local extension = "log"
if table.contains({"h", "html"}, self.logFormat) then
extension = "html"
end
return extension
end
--- Returns a string with all templated items replaced
---@tparam string str The templated string to transform
---@local
function LoggingConsole:transformTemplate(str)
local ttbl = getTime()
local year = ttbl.year
local month = string.format("%02d", ttbl.month)
local day = string.format("%02d", ttbl.day)
local name = self.name
local extension = self:getExtension()
str = str:gsub("|h", homedir)
str = str:gsub("|y", year)
str = str:gsub("|m", month)
str = str:gsub("|d", day)
str = str:gsub("|n", name)
str = str:gsub("|e", extension)
return str
end
--- Returns the path to the logfile for this console
function LoggingConsole:getPath()
local path = self:transformTemplate(self.path)
if not path:ends("/") then
path = path .. "/"
end
return path
end
--- Sets the path to use for the log file.
-- @param path the path to put the log file in. It is templated.<br>|h is replaced by the profile homedir.<br>|y by 4 digit year.<br>|m by 2 digit month<br>|d by 2 digit day<br>|n by the name constraint<br>|e by the file extension (html for h logType, log for others)
function LoggingConsole:setPath(path)
self.path = path
end
--- Returns the filename for the logfile for this console
function LoggingConsole:getFileName()
local fileName = self:transformTemplate(self.fileName)
fileName = fileName:gsub("[<>:'\"/\\?*]", "_")
return fileName
end
--- Sets the fileName to use for the log file.
-- @param fileName the fileName to use for the logfile. It is templated.<br>|h is replaced by the profile homedir.<br>|y by 4 digit year.<br>|m by 2 digit month<br>|d by 2 digit day<br>|n by the name constraint<br>|e by the file extension (html for h logType, log for others)
function LoggingConsole:setFileName(fileName)
self.fileName = fileName
end
--- Returns the pull path and filename for the logfile for this console
function LoggingConsole:getFullFilename()
local path = self:getPath()
local fileName = self:getFileName()
local fullPath = path .. fileName
fullPath = fullPath:gsub("|", "_")
return fullPath
end
--- Turns logging for this console on
function LoggingConsole:enableLogging()
self.log = true
end
--- Turns logging for this console off
function LoggingConsole:disableLogging()
self.log = false
end
--- Creates the path for the logfile for this console if necessary
---@local
function LoggingConsole:createPathIfNotExists()
local path = self:transformTemplate(self.path)
if not path:ends("/") then
path = path .. "/"
end
if not exists(path) then
local ok, err = dt.mkdir_p(path)
if not ok then
assert(false, "Could not create directory for log files:" .. path .. "\n Reason was: " .. err)
end
end
return true
end
--- Handles actually writing to the log file
---@local
function LoggingConsole:writeToLog(str)
local fileName = self:getFullFilename()
self:createPathIfNotExists()
if self:getExtension() == "html" then
if not io.exists(fileName) then
str = htmlHeader .. str
end
str = str
end
local file, err = io.open(fileName, "a")
if not file then
echo(err .. "\n")
return
end
file:write(str)
file:close()
end
local parent = Geyser.MiniConsole
--- Handler function which does the lifting for c/d/h/echo and appendBuffer to provide the logfile writing functionality
---@param str the string to echo. Use "" for appends
---@param etype the type of echo. Valid are "c", "d", "h", "e", and "a"
---@param log Allows you to override the default behaviour defined by the .log property. Pass true to definitely log, false to skip logging.
---@local
function LoggingConsole:xEcho(str, etype, log)
if log == nil then
log = self.log
end
local logStr
local logType = self.logFormat
if logType:find("h") then
logType = "h"
elseif logType ~= "t" then
logType = "l"
end
if etype == "d" then -- decho
if logType == "h" then
logStr = dt.decho2html(str)
elseif logType == "t" then
logStr = dt.decho2string(str)
else
logStr = dt.decho2ansi(str)
end
parent.decho(self, str)
elseif etype == "c" then -- cecho
if logType == "h" then
logStr = dt.cecho2html(str)
elseif logType == "t" then
logStr = dt.cecho2string(str)
else
logStr = dt.cecho2ansi(str)
end
parent.cecho(self, str)
elseif etype == "h" then -- hecho
if logType == "h" then
logStr = dt.hecho2html(str)
elseif logType == "t" then
logStr = dt.hecho2string(str)
else
logStr = dt.hecho2ansi(str)
end
parent.hecho(self, str)
elseif etype == "a" then -- append
str = dt.append2decho()
str = str .. "\n"
if logType == "h" then
logStr = dt.decho2html(str)
elseif logType == "t" then
logStr = dt.decho2string(str)
else
logStr = dt.decho2ansi(str)
end
parent.appendBuffer(self)
elseif etype == "e" then -- echo
if logType == "h" then
logStr = dt.decho2html(str)
else
logStr = str
end
parent.echo(self, str)
end
if log then
self:writeToLog(logStr)
end
end
--- Does the actual lifting of echoing links/popups
-- @local
function LoggingConsole:xEchoLink(text, lType, command, hint, useFormat, log)
if log == nil then
log = self.log
end
local logStr = ""
if lType:starts("c") then
if self.logFormat == "h" then
logStr = dt.cecho2html(text)
elseif self.logFormat == "l" then
logStr = dt.cecho2ansi(text)
elseif self.logFormat == "t" then
logStr = dt.cecho2string(text)
end
if lType:ends("p") then
parent.cechoPopup(self, text, command, hint, useFormat)
else
parent.cechoLink(self, text, command, hint, useFormat)
end
elseif lType:starts("d") then
if self.logFormat == "h" then
logStr = dt.decho2html(text)
elseif self.logFormat == "l" then
logStr = dt.decho2ansi(text)
elseif self.logFormat == "t" then
logStr = dt.decho2string(text)
end
if lType:ends("p") then
parent.dechoPopup(self, text, command, hint, useFormat)
else
parent.dechoLink(self, text, command, hint, useFormat)
end
elseif lType:starts("h") then
if self.logFormat == "h" then
logStr = dt.hecho2html(text)
elseif self.logFormat == "l" then
logStr = dt.hecho2ansi(text)
elseif self.logFormat == "t" then
logStr = dt.hecho2string(text)
end
if lType:ends("p") then
parent.hechoPopup(self, text, command, hint, useFormat)
else
parent.hechoLink(self, text, command, hint, useFormat)
end
elseif lType:starts("e") then
logStr = text
if lType:ends("p") then
parent.echoPopup(self, text, command, hint, useFormat)
else
parent.echoLink(self, text, command, hint, useFormat)
end
end
if log then
self:writeToLog(logStr)
end
end
--- cechoLink for LoggingConsole
-- @param text the text to use for the link
-- @param command the command to send when the link is clicked, as text. IE [[send("sleep")]]
-- @param hint A tooltip which is displayed when the mouse is over the link
-- @param log Should we log this line? Defaults to self.log if not passed.
function LoggingConsole:cechoLink(text, command, hint, log)
self:xEchoLink(text, "c", command, hint, true, log)
end
--- dechoLink for LoggingConsole
-- @param text the text to use for the link
-- @param command the command to send when the link is clicked, as text. IE [[send("sleep")]]
-- @param hint A tooltip which is displayed when the mouse is over the link
-- @param log Should we log this line? Defaults to self.log if not passed.
function LoggingConsole:dechoLink(text, command, hint, log)
self:xEchoLink(text, "d", command, hint, true, log)
end
--- hechoLink for LoggingConsole
-- @param text the text to use for the link
-- @param command the command to send when the link is clicked, as text. IE [[send("sleep")]]
-- @param hint A tooltip which is displayed when the mouse is over the link
-- @param log Should we log this line? Defaults to self.log if not passed.
function LoggingConsole:hechoLink(text, command, hint, log)
self:xEchoLink(text, "h", command, hint, true, log)
end
--- echoLink for LoggingConsole
-- @param text the text to use for the link
-- @param command the command to send when the link is clicked, as text. IE [[send("sleep")]]
-- @param hint A tooltip which is displayed when the mouse is over the link
-- @param useCurrentFormat If set to true, will look like the text around it. If false it will be blue and underline.
-- @param log Should we log this line? Defaults to self.log if not passed. If you want to pass this you must pass in useCurrentFormat
-- @usage myLoggingConsole:echoLink("This is a link!", [[send("sleep")]], "sleep") -- text "This is a link" will send("sleep") when clicked and be blue w/ underline. Defaut log behaviour (self.log)
-- @usage myLoggingConsole:echoLink("This is a link!", [[send("sleep")]], "sleep", false, false) -- same as above, but forces it not to log regardless of self.log setting
-- @usage myLoggingConsole:echoLink("This is a link!", [[send("sleep")]], "sleep", true, true) -- same as above, but forces it to log regardless of self.log setting and the text will look like anything else echoed to the console.
function LoggingConsole:echoLink(text, command, hint, useCurrentFormat, log)
self:xEchoLink(text, "e", command, hint, useCurrentFormat, log)
end
--- cechoPopup for LoggingConsole
-- @param text the text to use for the link
-- @param commands the commands to send when the popup is activated, as table. IE {[[send("sleep")]], [[send("stand")]]}
-- @param hints A tooltip which is displayed when the mouse is over the link. IE {{"sleep", "stand"}}
-- @param log Should we log this line? Defaults to self.log if not passed.
function LoggingConsole:cechoPopup(text, commands, hints, log)
self:xEchoLink(text, "cp", commands, hints, true, log)
end
--- dechoPopup for LoggingConsole
-- @param text the text to use for the link
-- @param commands the commands to send when the popup is activated, as table. IE {[[send("sleep")]], [[send("stand")]]}
-- @param hints A tooltip which is displayed when the mouse is over the link. IE {{"sleep", "stand"}}
-- @param log Should we log this line? Defaults to self.log if not passed.
function LoggingConsole:dechoPopup(text, commands, hints, log)
self:xEchoLink(text, "dp", commands, hints, true, log)
end
--- hechoPopup for LoggingConsole
-- @param text the text to use for the link
-- @param commands the commands to send when the popup is activated, as table. IE {[[send("sleep")]], [[send("stand")]]}
-- @param hints A tooltip which is displayed when the mouse is over the link. IE {{"sleep", "stand"}}
-- @param log Should we log this line? Defaults to self.log if not passed.
function LoggingConsole:hechoPopup(text, commands, hints, log)
self:xEchoLink(text, "hp", commands, hints, true, log)
end
--- echoPopup for LoggingConsole
-- @param text the text to use for the link
-- @param commands the commands to send when the popup is activated, as table. IE {[[send("sleep")]], [[send("stand")]]}
-- @param hints A tooltip which is displayed when the mouse is over the link. IE {{"sleep", "stand"}}
-- @param useCurrentFormat If set to true, will look like the text around it. If false it will be blue and underline.
-- @param log Should we log this line? Defaults to self.log if not passed. If you want to pass this you must pass in useCurrentFormat
-- @usage myLoggingConsole:echoPopup("This is a link!", {[[send("sleep")]], [[send("stand")]], {"sleep", "stand"}) -- text "This is a link" will send("sleep") when clicked and be blue w/ underline. Defaut log behaviour (self.log)
-- @usage myLoggingConsole:echoPopup("This is a link!", {[[send("sleep")]], [[send("stand")]], {"sleep", "stand"}, false, false) -- same as above, but forces it not to log regardless of self.log setting
-- @usage myLoggingConsole:echoPopup("This is a link!", {[[send("sleep")]], [[send("stand")]], {"sleep", "stand"}, true, true) -- same as above, but forces it to log regardless of self.log setting and the text will look like anything else echoed to the console.
function LoggingConsole:echoPopup(text, commands, hints, useCurrentFormat, log)
self:xEchoLink(text, "ep", commands, hints, useCurrentFormat, log)
end
--- Append copy()ed text to the console
-- @param log should we log this?
function LoggingConsole:appendBuffer(log)
self:xEcho("", "a", log)
end
--- Append copy()ed text to the console
-- @param log should we log this?
function LoggingConsole:append(log)
self:xEcho("", "a", log)
end
--- echo's a string to the console.
function LoggingConsole:echo(str, log)
self:xEcho(str, "e", log)
end
function LoggingConsole:hecho(str, log)
self:xEcho(str, "h", log)
end
function LoggingConsole:decho(str, log)
self:xEcho(str, "d", log)
end
function LoggingConsole:cecho(str, log)
self:xEcho(str, "c", log)
end
function LoggingConsole:replay(numberOfLines)
local fileName = self:getFullFilename()
if not exists(fileName) then
return
end
local file = io.open(fileName, "r")
local lines = file:read("*a")
if self:getExtension() == "html" then
for _, line in ipairs(htmlHeaderPattern:split("\n")) do
if line ~= "" then
lines = lines:gsub(line .. "\n", "")
end
end
lines = dt.html2decho(lines)
else
lines = ansi2decho(lines)
end
local linesTbl = lines:split("\n")
local result
if #linesTbl <= numberOfLines then
result = lines
else
result = ""
local start = #linesTbl - numberOfLines
for index, str in ipairs(linesTbl) do
if index >= start then
result = string.format("%s\n%s", result, str)
end
end
end
self:decho(result, false)
end
setmetatable(LoggingConsole, parent)
return LoggingConsole