I have a few custom logfunctions that are extensions of cat
. A basic example is something like this:
catt<-function(..., file = "", sep = " ", fill = FALSE, labels = NULL,
append = FALSE)
{
cat(..., format(Sys.time(), "(%Y-%m-%d %H:%M:%S)"), "\n", file = file,
sep = sep, fill = fill, labels = labels, append = append)
}
Now, I work a lot with (selfmade) functions, and use some of these logfuntions to see the progress, which works quite well. What I notice, though, is that I almost always use these functions like this:
somefunc<-function(blabla)
{
catt("somefunc: start")
#do some very useful stuff here
catt("somefunc: some time later")
#even more useful stuff
catt("somefunc: the end")
}
Notice how every call to catt
begins with the name of the function it is called from. Very neat until I start to refactor my code and rename functions etc.
Thanks to some old R-list post from Brian Ripley, if I'm not mistaken, I found this code to get the 'current function name':
catw<-function(..., file = "", sep = " ", fill = FALSE, labels = NULL,
append = FALSE)
{
curcall<-sys.call(sys.parent(n=1))
prefix<-paste(match.call(call=curcall)[[1]], ":", sep="")
cat(prefix, ..., format(Sys.time(), "(%Y-%m-%d %H:%M:%S)"), "\n",
file = file, sep = sep, fill = fill, labels = labels, append = append)
}
This is very nice, but it doesn't always work, because:
- my functions are scattered with anonymous functions used in
lapply
type of functions, like this:
aFunc<-function(somedataframe) { result<-lapply(seq_along(somedataframe), function(i){ catw("working on col", i, "/", ncol(somedataframe)) #do some more stuff here and return something return(sum(is.na(somedataframe[[i]]))) } }
-> for these cases, apparently (and understandably) I need n=3 in the sys.parent
call in my catw
function.
- I occasionally use
do.call
: it appears my current implementation doesn't work either (once again I can somewhat understand it, though I haven't figured it out completely.
So, my question is: is there a way to find the first named function higher in the callstack (skipping the logging function itself, and maybe some other "wellknown" exceptions), which would allow me to write one single version of catw
for all cases (so that I can happily refactor without worrying about my logging code)? How would you go about something like this?
Edit: these cases should be supported:
testa<-function(par1)
{
catw("Hello from testa, par1=", par1)
for(i in 1:2) catw("normal loop from testa, item", i)
rv<-sapply(1:2, function(i){catw("sapply from testa, item", i);return(i)})
return(rv)
}
testb<-function(par1, par2)
{
catw("Hello from testb, par1=", par1)
for(i in 1:2) catw("normal loop from testb, item", i)
rv<-sapply(1:2, function(i){catw("sapply from testb, item", i);return(i)})
catw("Will now call testa from testb")
rv2<-testa(par1)
catw("Back from testa call in testb")
catw("Will now do.call testa from testb")
rv2<-do.call(testa, list(par1))
catw("Back from testa do.call in testb")
return(list(rv, rv2))
}
testa(123)
testb(123,456)
do.call(testb, list(123,456))