A Better Print Statement For Logging!

I do a lot of logging in my apps to watch execution flow in the terminal, it also helps find what was last done before something tragic has happened along with the stack trace.

 

So what I did was override the default print to append the filename to it, just replace the contents of find(’/app/’) with your applications root and you’ll get a nice output that includes the filename with your logging statements.

 

original\_print = print print = function(message) source\_file = debug.getinfo(2).source path\_start, path\_end = source\_file:find('/app/') debug\_path = source\_file:sub(path\_end, source\_file:len()) original\_print(debug\_path..": "..message) end

 

 

Before:

 

 

Device located Lat(37.448485), Lon(-122.158911)

 

 

After:

 

 

/libs/location.lua: Device located Lat(37.448485), Lon(-122.158911)

 

Awesome! This is very helpful when hunting down bugs. Thanks!

You can also try using these to give you more information:

This gets the line number:

local line = debug.getinfo(1).currentline

This gets the function name:

local funcName = debug.getinfo(1, “n”).name

I must be doing something wrong, when I try to use it I get the following error:

bad argument #1 to 'sub' (number expected, got nil)

on this line:

debug\_path = source\_file:sub(path\_end, source\_file:len())

I’ve replaced /app/ with the directory that my project is in. Have I missed something else?

Awesome! This is very helpful when hunting down bugs. Thanks!

You can also try using these to give you more information:

This gets the line number:

local line = debug.getinfo(1).currentline

This gets the function name:

local funcName = debug.getinfo(1, “n”).name

I must be doing something wrong, when I try to use it I get the following error:

bad argument #1 to 'sub' (number expected, got nil)

on this line:

debug\_path = source\_file:sub(path\_end, source\_file:len())

I’ve replaced /app/ with the directory that my project is in. Have I missed something else?

Thanks Borderleap and josellausas! this is really handy.

I have taken your code and made a few more mods for anyone who wants to use it.

usage: add this to a file.lua and require (“file.lua”) in your main for example

[lua]

– debug print overide

– you can use _print() in code if you want original

_print = print

print = function(message)

   local projectCodeRootString = ‘/project/code/’

   local source_file = debug.getinfo(2).source

   local path_start, path_end = source_file:find(projectCodeRootString)

   local debug_path = source_file:sub(path_end, source_file:len())

   – This gets the line number:

   local line = debug.getinfo(2).currentline

   – This gets the function name:

   local funcName = debug.getinfo(2, “n”).name

   – _print(debug_path…"("…line…")"…": "…message)

   – _print(debug_path…"("…line…")["…funcName…"()]"…": "…message)

   _print(debug_path…"("…line…"):"…funcName…"()"…": "…message)

end

[/lua]

output looks like:

/main.lua(10):setupGlobals(): loaded globals

cheers

Added a little more checking as ‘nil’ values broke the concat

[lua]

– debug print overide

– you can use _print() in code if you want original

_print = print

print = function(message)

local function cleanText(text)

   if text == nil then

      return “nil”

   end

   return text

end

local projectCodeRootString = ‘/project/code/’

local source_file = cleanText(debug.getinfo(2).source)

local path_start, path_end = source_file:find(projectCodeRootString)

local debug_path = cleanText(source_file:sub(path_end, source_file:len()))

– This gets the line number:

local line = cleanText(debug.getinfo(2).currentline)

– This gets the function name:

local funcName = cleanText(debug.getinfo(2, “n”).name)

_print(debug_path…":"…funcName…"()"…":"…line…": "…message)

end

[/lua]

I used a string joining function found here to allow print statements separated by commas to be used:

\_print = print function string.join( ... ) -- function requires a string, a separator, and at least one argument; -- if this condition is not met, print and error and return nil if ( #arg \< 3 ) then \_print( "Error: required string and separator missing" ) return nil end -- the string is the first argument local str = arg[1] -- the separator is the second argument local separator = arg[2] -- loop from the third index (the first real argument to join to the string) for i = 3, #arg do -- if the argument is a string, append it along with the separator if ( type(arg[i]) == "string" ) then if ( str:len() ~= 0 ) then str = str .. separator end str = str .. arg[i] -- if the argument is a table, loop through it just like above elseif ( type(arg[i]) == "table" ) then local t = arg[i] for j = 1, #t do if ( type( t[j] ) == "string" ) then if ( str:len() ~= 0 ) then str = str .. separator end str = str .. t[j] end end end end return str end print = function(...) if arg == nil then return end local function cleanText(text) if text == nil then return "nil" end return text end local projectCodeRootString = 'project/code/' local source\_file = cleanText(debug.getinfo(2).source) local path\_start, path\_end = source\_file:find(projectCodeRootString) if path\_start == nil then return end local debug\_path = cleanText(source\_file:sub(path\_end, source\_file:len())) -- This gets the line number: local line = cleanText(debug.getinfo(2).currentline) -- This gets the function name: local funcName = cleanText(debug.getinfo(2, "n").name) local str = string.join("", " ", arg) \_print(debug\_path..":"..funcName.."()"..":"..line..": "..str) end

Now if you have print statements like this:

local doggiePrice = 1.99 print("How much is that doggie in the window?", doggyPrice)

It will print out “How much is that doggie in the window? 1.99”, rather than just printing the first arg. 

I also added another nil check for path_start, as certain files caused “bad argument #1 to ‘sub’ (number expected, got nil)” for some reason.

Does anyone know a way to set the projectCodeRootString automatically? I have a few projects which share a code base, but when I compile them they are in their own unique folders, so a variable/function that gets the working directory would be great. 

Worked out an easy way to get the local directory name:

local fp = system.pathForFile( "one\_of\_my\_files.txt", system.ResourceDirectory ) local localdir = fp:gsub( "one\_of\_my\_files.txt", "" ) local projectCodeRootString = localdir

It fails on iOS though, did anyone ever get it working on iOS?

nice alan. thx… sorry i cant help with the iOS fail.  Maybe you can just disable print override in IOS as work around

I was thinking it would also be nice to add a modification to the corona-sublime pluging file to enable linking to the file as well via double click. like the error messages do.

but i am not sure how. but this is where i think

https://github.com/coronalabs/CoronaSDK-SublimeText/blob/master/run_project.py line 89

# Supplying the "file\_regex" allows users to double-click errors and warnings in the # build panel and go to that point in the code if sublime.platform() == 'osx': self.window.run\_command('exec', {'cmd': cmd, "file\_regex": "^[^/]\*(/[^:]\*) :([0-9]+) :([0-9]\*)(.\*)$" }) else: # windows self.window.run\_command('exec', {'cmd': cmd, "file\_regex": "(?i)^[^C-Z]\*([C-Z]:[^:]\*) :([0-9]+) :([0-9]\*)(.\*)$" })

ok so I had a crack at trying to enable my own linkable code comments in sublime text. But it hasnt’ worked yet.

I added the “°” degree symbol to provide a nice obvious regex target in the print output

\_print("°"..debug\_path..":"..funcName.."()"..":"..line..": "..str) -- using the ° degree symbol as easy file\_regex target

So output looks like this

2015-02-23 10:03:59.882 Corona Simulator[74695:507] °/main.lua:setupGlobals():21: loading globals

Then i added the following command to the run_project.py file in a forked repo of the corona-sublime

https://github.com/kadlugan/CoronaSDK-SublimeText.git

&nbsp; &nbsp; # Save our changes before we run &nbsp; &nbsp; self.window.run\_command("save\_all") &nbsp; &nbsp; &nbsp; # Supplying the "file\_regex" allows users to double-click errors and warnings in the &nbsp; &nbsp; # build panel and go to that point in the code &nbsp; &nbsp; if sublime.platform() == 'osx': &nbsp; &nbsp; &nbsp; # for the system error messages &nbsp; &nbsp; &nbsp; self.window.run\_command('exec', {'cmd': cmd, "file\_regex": "^[^/]\*(/[^:]\*):([0-9]+):([0-9]\*)(.\*)$" }) &nbsp; &nbsp; &nbsp; # for my generated error messages &nbsp; &nbsp; &nbsp; self.window.run\_command('exec', { 'cmd': cmd, &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; "file\_regex": "°(.\*):.\*:([0-9]+):",&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; "working\_dir": "${project\_path}"&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }) &nbsp; &nbsp; &nbsp; &nbsp; else: # windows &nbsp; &nbsp; &nbsp; self.window.run\_command('exec', {'cmd': cmd, "file\_regex": "(?i)^[^C-Z]\*([C-Z]:[^:]\*):([0-9]+):([0-9]\*)(.\*)$" })

from some notes on build systems

http://docs.sublimetext.info/en/sublime-text-2/reference/build_systems.html

So whilst i think this is along the right direction, i don’t really know what i am editing to make things work :slight_smile:

Possible errors i can think of are the duplicate run commands are wrong, and the regex and working dir variables are wrong

If theres a more seasoned coder out there who does please tell me what i am doing wrong.

I have a small little module that prints in colors and other handy things, hope it can inspire some new cool things for logging print statements in Corona: It currently only works on Mac Os. 

https://github.com/zunware/luaColors

thanks jose95. Alan, heres that quick workaround for iOS builds breaking on that fp system.PathForFile returning nil.  Means your console print is not overiden but at least it doesnt break builds. Sorry its only a workaround.

for those of you just picking up on this print override. just name this a .lua file and require it in your main before your print statements start for it to work 

[lua]

– -- debug print overide

– -- you can use _print() in code if you want original

_print = print

local localdir

local projectCodeRootString 

function string.join( … )

    – function requires a string, a separator, and at least one argument;

    – if this condition is not met, print and error and return nil

    if ( #arg < 3 ) then

        _print( “Error: required string and separator missing” )

        return nil

    end

    – the string is the first argument

    local str = arg[1]

    – the separator is the second argument

    local separator = arg[2]

    – loop from the third index (the first real argument to join to the string)

    for i = 3, #arg do

        – if the argument is a string, append it along with the separator

        if ( type(arg[i]) == “string” ) then

            if ( str:len() ~= 0 ) then

                str = str … separator

            end

            str = str … arg[i]

        – if the argument is a table, loop through it just like above

        elseif ( type(arg[i]) == “table” ) then

            local t = arg[i]

            for j = 1, #t do

                if ( type( t[j] ) == “string” ) then

                    if ( str:len() ~= 0 ) then

                        str = str … separator

                    end

                    str = str … t[j]

                end

            end

        end

    end

    return str

end

function initialise_print( … )

    print = function(…)

        

        if arg == nil then 

            return

        end

        local function cleanText(text)

           if text == nil then

              return “nil”

           end

           return text

        end

        – local fp = system.pathForFile( “main.lua”, system.ResourceDirectory )

        – local localdir = fp:gsub( “main.lua”, “” )

        – local projectCodeRootString = localdir

         

        – local projectCodeRootString = ‘/LeapFrog/code/’

        local source_file = cleanText(debug.getinfo(2).source)

        local path_start, path_end = source_file:find(projectCodeRootString)

         

        if path_start == nil then

            return 

        end

        local debug_path = cleanText(source_file:sub(path_end, source_file:len()))

        – This gets the line number:

        local line = cleanText(debug.getinfo(2).currentline)

         

        – This gets the function name:

        local funcName = cleanText(debug.getinfo(2, “n”).name)

            

        local str = string.join("", " ", arg)

        _print(“°”…debug_path…":"…funcName…"()"…":"…line…": "…str) – using the ° degree symbol as easy file_regex target

        – _print(debug_path…":"…funcName…"()"…":"…line…": "…str)

    end

end

– this is done so that the ios build doesnt break

local fp = system.pathForFile( “main.lua”, system.ResourceDirectory )

if fp ~= nil then

    localdir = fp:gsub( “main.lua”, “” )

    projectCodeRootString = localdir

    _print("Project Location: "…“°°”…localdir…“°°”) – this is to create a nice easy tag

    initialise_print()

else

    print = _print – cancel overriding

    print( “ios error: fp: is nil. customm print overide cancelled” )

end

[/lua]

Thanks Borderleap and josellausas! this is really handy.

I have taken your code and made a few more mods for anyone who wants to use it.

usage: add this to a file.lua and require (“file.lua”) in your main for example

[lua]

– debug print overide

– you can use _print() in code if you want original

_print = print

print = function(message)

   local projectCodeRootString = ‘/project/code/’

   local source_file = debug.getinfo(2).source

   local path_start, path_end = source_file:find(projectCodeRootString)

   local debug_path = source_file:sub(path_end, source_file:len())

   – This gets the line number:

   local line = debug.getinfo(2).currentline

   – This gets the function name:

   local funcName = debug.getinfo(2, “n”).name

   – _print(debug_path…"("…line…")"…": "…message)

   – _print(debug_path…"("…line…")["…funcName…"()]"…": "…message)

   _print(debug_path…"("…line…"):"…funcName…"()"…": "…message)

end

[/lua]

output looks like:

/main.lua(10):setupGlobals(): loaded globals

cheers

Added a little more checking as ‘nil’ values broke the concat

[lua]

– debug print overide

– you can use _print() in code if you want original

_print = print

print = function(message)

local function cleanText(text)

   if text == nil then

      return “nil”

   end

   return text

end

local projectCodeRootString = ‘/project/code/’

local source_file = cleanText(debug.getinfo(2).source)

local path_start, path_end = source_file:find(projectCodeRootString)

local debug_path = cleanText(source_file:sub(path_end, source_file:len()))

– This gets the line number:

local line = cleanText(debug.getinfo(2).currentline)

– This gets the function name:

local funcName = cleanText(debug.getinfo(2, “n”).name)

_print(debug_path…":"…funcName…"()"…":"…line…": "…message)

end

[/lua]

I used a string joining function found here to allow print statements separated by commas to be used:

\_print = print function string.join( ... ) -- function requires a string, a separator, and at least one argument; -- if this condition is not met, print and error and return nil if ( #arg \< 3 ) then \_print( "Error: required string and separator missing" ) return nil end -- the string is the first argument local str = arg[1] -- the separator is the second argument local separator = arg[2] -- loop from the third index (the first real argument to join to the string) for i = 3, #arg do -- if the argument is a string, append it along with the separator if ( type(arg[i]) == "string" ) then if ( str:len() ~= 0 ) then str = str .. separator end str = str .. arg[i] -- if the argument is a table, loop through it just like above elseif ( type(arg[i]) == "table" ) then local t = arg[i] for j = 1, #t do if ( type( t[j] ) == "string" ) then if ( str:len() ~= 0 ) then str = str .. separator end str = str .. t[j] end end end end return str end print = function(...) if arg == nil then return end local function cleanText(text) if text == nil then return "nil" end return text end local projectCodeRootString = 'project/code/' local source\_file = cleanText(debug.getinfo(2).source) local path\_start, path\_end = source\_file:find(projectCodeRootString) if path\_start == nil then return end local debug\_path = cleanText(source\_file:sub(path\_end, source\_file:len())) -- This gets the line number: local line = cleanText(debug.getinfo(2).currentline) -- This gets the function name: local funcName = cleanText(debug.getinfo(2, "n").name) local str = string.join("", " ", arg) \_print(debug\_path..":"..funcName.."()"..":"..line..": "..str) end

Now if you have print statements like this:

local doggiePrice = 1.99 print("How much is that doggie in the window?", doggyPrice)

It will print out “How much is that doggie in the window? 1.99”, rather than just printing the first arg. 

I also added another nil check for path_start, as certain files caused “bad argument #1 to ‘sub’ (number expected, got nil)” for some reason.

Does anyone know a way to set the projectCodeRootString automatically? I have a few projects which share a code base, but when I compile them they are in their own unique folders, so a variable/function that gets the working directory would be great. 

Worked out an easy way to get the local directory name:

local fp = system.pathForFile( "one\_of\_my\_files.txt", system.ResourceDirectory ) local localdir = fp:gsub( "one\_of\_my\_files.txt", "" ) local projectCodeRootString = localdir

It fails on iOS though, did anyone ever get it working on iOS?

nice alan. thx… sorry i cant help with the iOS fail.  Maybe you can just disable print override in IOS as work around

I was thinking it would also be nice to add a modification to the corona-sublime pluging file to enable linking to the file as well via double click. like the error messages do.

but i am not sure how. but this is where i think

https://github.com/coronalabs/CoronaSDK-SublimeText/blob/master/run_project.py line 89

# Supplying the "file\_regex" allows users to double-click errors and warnings in the # build panel and go to that point in the code if sublime.platform() == 'osx': self.window.run\_command('exec', {'cmd': cmd, "file\_regex": "^[^/]\*(/[^:]\*) :([0-9]+) :([0-9]\*)(.\*)$" }) else: # windows self.window.run\_command('exec', {'cmd': cmd, "file\_regex": "(?i)^[^C-Z]\*([C-Z]:[^:]\*) :([0-9]+) :([0-9]\*)(.\*)$" })

ok so I had a crack at trying to enable my own linkable code comments in sublime text. But it hasnt’ worked yet.

I added the “°” degree symbol to provide a nice obvious regex target in the print output

\_print("°"..debug\_path..":"..funcName.."()"..":"..line..": "..str) -- using the ° degree symbol as easy file\_regex target

So output looks like this

2015-02-23 10:03:59.882 Corona Simulator[74695:507] °/main.lua:setupGlobals():21: loading globals

Then i added the following command to the run_project.py file in a forked repo of the corona-sublime

https://github.com/kadlugan/CoronaSDK-SublimeText.git

&nbsp; &nbsp; # Save our changes before we run &nbsp; &nbsp; self.window.run\_command("save\_all") &nbsp; &nbsp; &nbsp; # Supplying the "file\_regex" allows users to double-click errors and warnings in the &nbsp; &nbsp; # build panel and go to that point in the code &nbsp; &nbsp; if sublime.platform() == 'osx': &nbsp; &nbsp; &nbsp; # for the system error messages &nbsp; &nbsp; &nbsp; self.window.run\_command('exec', {'cmd': cmd, "file\_regex": "^[^/]\*(/[^:]\*):([0-9]+):([0-9]\*)(.\*)$" }) &nbsp; &nbsp; &nbsp; # for my generated error messages &nbsp; &nbsp; &nbsp; self.window.run\_command('exec', { 'cmd': cmd, &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; "file\_regex": "°(.\*):.\*:([0-9]+):",&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; "working\_dir": "${project\_path}"&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }) &nbsp; &nbsp; &nbsp; &nbsp; else: # windows &nbsp; &nbsp; &nbsp; self.window.run\_command('exec', {'cmd': cmd, "file\_regex": "(?i)^[^C-Z]\*([C-Z]:[^:]\*):([0-9]+):([0-9]\*)(.\*)$" })

from some notes on build systems

http://docs.sublimetext.info/en/sublime-text-2/reference/build_systems.html

So whilst i think this is along the right direction, i don’t really know what i am editing to make things work :slight_smile:

Possible errors i can think of are the duplicate run commands are wrong, and the regex and working dir variables are wrong

If theres a more seasoned coder out there who does please tell me what i am doing wrong.

I have a small little module that prints in colors and other handy things, hope it can inspire some new cool things for logging print statements in Corona: It currently only works on Mac Os. 

https://github.com/zunware/luaColors