I was working on an embedded log API for an ARM system in Lua. The problem definition required limiting the amount of data that’s being written on the disk and a log file was eating all of that space to an extent that the machine was unusable. This happens when you hire inexperienced consultants who don’t understand the basics of embedded system programming. Anyway, I had to replace his code with a log API that only holds the latest log messages and here it is: the result of 3 days of my work.
The code has a lot of inline documentation so it should be obvious how it works but basically it makes for example 5 log files and the latest one is always the one with a bigger number (log-5.log is newer than log-4.log). The log message itself is also interesting. It tries to find the source of the log message and date/time.
--There can be several files. Their names are like:
-- LOG_FILE_PREFIX..N..LOG_FILE_POSTFIX
-- N will be variable between 1 to LOG_FILE_MAX_NUM
-- When file size reaches LOG_MAX_SIZE, an older log
-- file is removed and a new one is created
--{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{Options
--- LOG_FILE_PREFIX is the prefix that will be used to create the log file name.
-- it also indicates the absolute path for the value. Default="/tmp/log"
LOG_FILE_PREFIX ="./logfile-"
---Number of log files. The files will have a number in their name that range from [1-N] inclusive
LOG_FILE_MAX_NUM =5
---The postfix to the log file. Default=".log"
LOG_FILE_POSTFIX=".log"
---Maximum length of a log file before trying to make a new one
LOG_MAX_SIZE =100*1024 --bytes
--}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}Options
require "lfs"
local currFile,n
local function writeToFile(str)
local function fname(n) return LOG_FILE_PREFIX..n..LOG_FILE_POSTFIX end
if not currFile then
--trying to find the latest log file (from its number)
for i=LOG_FILE_MAX_NUM,1,-1 do
local fn=fname(i)
local f=io.open(fn)
if f then
f:close()
currFile,n=io.open(fn,"a+"),i
break
end
end
--if still couldn't find the latest file, make the file#1
if not currFile then currFile,n=io.open(fname(1),"w+"),1 end
end
str=str or ""
--if there's not enough space
print("currsize="..currFile:seek("end")+str:len())
if (currFile:seek("end")+str:len())>LOG_MAX_SIZE then
currFile:close()
currFile=nil
--do we need to create a brand new file?
for i=n+1,LOG_FILE_MAX_NUM do
local fn=fname(i)
local f=io.open(fn)
if not f then
currFile,n=io.open(fn,"a+"),i
break
end
f:close()
end
--did the above loop succeed to find a new file?
if currFile then
--remove all the files with a bigger number
for i=n+1,LOG_FILE_MAX_NUM do os.remove(fname(i)) end
else
--ok, then we need to use a currently existing file
os.rename(fname(1),fname(LOG_FILE_MAX_NUM+1))
--shift file names
for i=1,LOG_FILE_MAX_NUM do os.rename(fname(i+1),fname(i)) end
currFile,n=io.open(fname(LOG_FILE_MAX_NUM),"w+"),LOG_FILE_MAX_NUM
end
end
currFile:write(str)
currFile:write("\n")
currFile:flush(str)
end
---Write something as a log to the screen (or maybe later to a file)
-- Note: this function adds the current date and time to the log report.
-- @msg the message to be logged
function log(msg)
local debuginfo=debug.getinfo(2,"lSn")
if debuginfo then
if debuginfo.what=="main" then sourcename="MAIN"
elseif debuginfo.what=="C" then sourcename="C "..debuginfo.name
elseif debuginfo.what=="Lua" then sourcename="function "..debuginfo.name.."()"
end
sourcefile=debuginfo.source:match("@?(.*)")
linenumber=debuginfo.currentline
datetime=os.date("%H:%M:%S %d/%m/%Y")
return writeToFile(datetime.." "..sourcefile..":"..linenumber..":["..sourcename.."]: "..msg)
else
return writeToFile("--"..msg)
end
end
for i=1,3000 do
log("hi, this is a log "..i)
end
Lua function to remove JavaScript comments
Nov 28 2011
JavaScript is an interpreted language. Therefore when the code is put in a web page, the client can easily see how it works. Commenting the code is a good coding practice to improve readability but we don’t want to waste user bandwidth with comments. Before releasing JavaScript codes it’s good to remove the comments from them. Here is a little Lua script that does the job.
--- Gets a valid compilable javascript code and removes its comments -- @note the javascript code should be ready to compile that is: there are no syntax -- errors whatsoever. Otherwise the behavior of this function is unpredictable. -- @param txt the compilable javascript code -- @return a text string the semantically behaves the same as the txt that was passed -- to the function but doesn't have comments function removeCommentsJS(txt) txt=txt:gsub("/%*.-%*/","\n") --remove all multi-line comments txt=txt:gsub("//.-\n","\n") --remove all single-line comments txt=txt:gsub("\n[%s\n]*\n","\n")--remove all empty lines txt=txt:gsub("^\n","") --remove first empty new line txt=txt:gsub("\n$","") --remove last empty new line return txt endAnd here is a test script that will not print any error if finished successfully:
--removeCommentsJS() test txt={ [[ code; ]], [[code; ]], [[code;]] , [[/** comments */ code; ]], [[//comments code; ]], [[code;//comments ]], [[//comments code; //comments ]], [[//comments code; //comments //comments //comments ]], [[//comments /*comments*/ /** comments */ /** * comments */ /** * comments * comments * comments * comments */ code; //comments //comments //comments ]] } for _,t in ipairs(txt) do local uncommentedJS=removeCommentsJS(t) if uncommentedJS~="code;" then print("Test failed for :\n'"..t.."'! Return value is:\n'"..uncommentedJS.."'") end endComments are off for this post