Blog coding and discussion of coding about JavaScript, PHP, CGI, general web building etc.

Wednesday, March 1, 2017

Printing stack trace and continuing after error occurs in R

Printing stack trace and continuing after error occurs in R


I'm writing some R code that calls other code that may fail. If it does, I want to print a stack trace (to track down what went wrong), then carry on regardless. However, the traceback() function only provides information about uncaught exceptions. I can get the result I want via a rather complex, natty construction involving tryCatch and dump.frames, but is there not an easier way of doing this?

Answer by Shane for Printing stack trace and continuing after error occurs in R


I think that you will need to use tryCatch(). You can do whatever you want in the tryCatch() function, so it's not clear to me why you are viewing this as complex. Maybe post your code example?

Answer by Dirk Eddelbuettel for Printing stack trace and continuing after error occurs in R


Have you tried the

 options(error=recover)  

setting? Chambers 'Software for Data Analysis' has some useful hints on debugging.

Answer by Bob Albright for Printing stack trace and continuing after error occurs in R


I wrote this code about a week ago to help me track down errors that come primarily from non-interactive R sessions. It's still a little rough, but it prints a stack trace and continues on. Let me know if this is useful, I'd be interested in how you would make this more informative. I'm also open into cleaner ways to get this information.

options(warn = 2, keep.source = TRUE, error =     quote({       cat("Environment:\n", file=stderr());         # TODO: setup option for dumping to a file (?)      # Set `to.file` argument to write this to a file for post-mortem debugging          dump.frames();  # writes to last.dump        #      # Debugging in R      #   http://www.stats.uwo.ca/faculty/murdoch/software/debuggingR/index.shtml      #      # Post-mortem debugging      #   http://www.stats.uwo.ca/faculty/murdoch/software/debuggingR/pmd.shtml      #      # Relation functions:      #   dump.frames      #   recover      # >>limitedLabels  (formatting of the dump with source/line numbers)      #   sys.frame (and associated)      #   traceback      #   geterrmessage      #      # Output based on the debugger function definition.        n <- length(last.dump)      calls <- names(last.dump)      cat(paste("  ", 1L:n, ": ", calls, sep = ""), sep = "\n", file=stderr())      cat("\n", file=stderr())        if (!interactive()) {        q()      }    }))  

PS: you might not want warn=2 (warnings converted to errors)

Answer by Christian Hudon for Printing stack trace and continuing after error occurs in R


If something that triggers on option(error...) is of interest, you can also do this:

options(error=traceback)  

From what I can tell, it does most of what Bob's suggested solution do, but has the advantage of being much shorter.

(Feel free to combine with keep.source=TRUE, warn=2, etc. as needed.)

Answer by chrispy for Printing stack trace and continuing after error occurs in R


I ended up writing a general-purpose logger that produces Java-like logging messages when the standard R "message", "warning" and "stop" methods are called. It includes timestamps, and stack traces for warnings and above.

Many thanks to Man Group for permission to distribute this! Thanks also to Bob Albright, whose answer gave me a leg-up to what I was looking for.

withJavaLogging = function(expr, silentSuccess=FALSE, stopIsFatal=TRUE) {      hasFailed = FALSE      messages = list()      warnings = list()      logger = function(obj) {          # Change behaviour based on type of message          level = sapply(class(obj), switch, debug="DEBUG", message="INFO", warning="WARN", caughtError = "ERROR",                  error=if (stopIsFatal) "FATAL" else "ERROR", "")          level = c(level[level != ""], "ERROR")[1]          simpleMessage = switch(level, DEBUG=,INFO=TRUE, FALSE)          quashable = switch(level, DEBUG=,INFO=,WARN=TRUE, FALSE)            # Format message          time  = format(Sys.time(), "%Y-%m-%d %H:%M:%OS3")          txt   = conditionMessage(obj)          if (!simpleMessage) txt = paste(txt, "\n", sep="")          msg = paste(time, level, txt, sep=" ")          calls = sys.calls()          calls = calls[1:length(calls)-1]          trace = limitedLabels(c(calls, attr(obj, "calls")))          if (!simpleMessage && length(trace) > 0) {              trace = trace[length(trace):1]              msg = paste(msg, "  ", paste("at", trace, collapse="\n  "), "\n", sep="")          }            # Output message          if (silentSuccess && !hasFailed && quashable) {              messages <<- append(messages, msg)              if (level == "WARN") warnings <<- append(warnings, msg)          } else {              if (silentSuccess && !hasFailed) {                  cat(paste(messages, collapse=""))                  hasFailed <<- TRUE              }              cat(msg)          }            # Muffle any redundant output of the same message          optionalRestart = function(r) { res = findRestart(r); if (!is.null(res)) invokeRestart(res) }          optionalRestart("muffleMessage")          optionalRestart("muffleWarning")      }      vexpr = withCallingHandlers(withVisible(expr),              debug=logger, message=logger, warning=logger, caughtError=logger, error=logger)      if (silentSuccess && !hasFailed) {          cat(paste(warnings, collapse=""))      }      if (vexpr$visible) vexpr$value else invisible(vexpr$value)  }  

To use it, just wrap it around your code:

withJavaLogging({    // Your code here...  })  

For a quieter output in the absence of errors (useful for tests!), set the silentSuccess flag. Messages will only be output if an error occurs, to give context to the failure.

To achieve the original goal (dump stack trace + carry on), just use try:

try(withJavaLogging({    // Your code here...  }, stopIsFatal=FALSE))  

Answer by user1133275 for Printing stack trace and continuing after error occurs in R


no line numbers but this is the closest I found so far:

run = function() {      // Your code here...  }  withCallingHandlers(run(), error=function(e)cat(conditionMessage(e), sapply(sys.calls(),function(sc)deparse(sc)[1]), sep="\n   "))   

Answer by Berry Boessenkool for Printing stack trace and continuing after error occurs in R


I wrote a solution that works like try, except that it also returns the call stack.

tryStack <- function(  expr,  silent=FALSE  )  {  tryenv <- new.env()  out <- try(withCallingHandlers(expr, error=function(e)    {    stack <- sys.calls()    stack <- stack[-(2:7)]    stack <- head(stack, -2)    stack <- sapply(stack, deparse)    if(!silent && isTRUE(getOption("show.error.messages")))       cat("This is the error stack: ", stack, sep="\n")    assign("stackmsg", value=paste(stack,collapse="\n"), envir=tryenv)    }), silent=silent)  if(inherits(out, "try-error")) out[2] <- tryenv$stackmsg  out  }    lower <- function(a) a+10  upper <- function(b) {plot(b, main=b) ; lower(b) }    d <- tryStack(upper(4))  d <- tryStack(upper("4"))  cat(d[2])  

More info in my answer here: http://stackoverflow.com/a/40899766/1587132

Answer by HaroldFinch for Printing stack trace and continuing after error occurs in R


This is a followup to @chrispy's answer above where he presented a withJavaLogging function. I commented that his solution is inspirational, but for me, is marred by some output at the start of the stack trace that I do not want to see.

To illustrate, consider this code:

f1 = function() {          # line #2 of the function definition; add this line to confirm that the stack trace line number for this function is line #3 below          catA("f2 = ", f2(), "\n", sep = "")      }        f2 = function() {          # line #2 of the function definition; add this line to confirm that the stack trace line number for this function is line #4 below          # line #3 of the function definition; add this line to confirm that the stack trace line number for this function is line #4 below          stop("f2 always causes an error for testing purposes")      }  

If I execute the line withJavaLogging( f1() ) I get the output

2017-02-17 17:58:29.556 FATAL f2 always causes an error for testing purposes        at .handleSimpleError(function (obj)       {          level = sapply(class(obj), switch, debug = "DEBUG", message = "INFO", warning = "WARN", caughtError = "ERROR", error = if (stopIsFatal)               "FATAL"          else "ERROR", "")          level = c(level[level != ""], "ERROR")[1]          simpleMessage = switch(level, DEBUG = , INFO = TRUE        at #4: stop("f2 always causes an error for testing purposes")        at f2()        at catA.R#8: cat(...)        at #3: catA("f2 = ", f2(), "\n", sep = "")        at f1()        at withVisible(expr)        at #43: withCallingHandlers(withVisible(expr), debug = logger, message = logger, warning = logger, caughtError = logger, error = logger)        at withJavaLogging(f1())      Error in f2() : f2 always causes an error for testing purposes  

I do not want to see that at .handleSimpleError(function (obj) line followed by the source code of the logger function defined inside the withJavaLogging function. I commented above that I could suppress that undesired output by changing trace = trace[length(trace):1] to trace = trace[(length(trace) - 1):1]

For the convenience of anyone else reading this, here is a complete version of the function that I now use (renamed from withJavaLogging to logFully, and slightly reformatted to fit my readability preferences):

logFully = function(expr, silentSuccess = FALSE, stopIsFatal = TRUE) {      hasFailed = FALSE      messages = list()      warnings = list()        logger = function(obj) {          # Change behaviour based on type of message          level = sapply(              class(obj),              switch,              debug = "DEBUG",              message = "INFO",              warning = "WARN",              caughtError = "ERROR",              error = if (stopIsFatal) "FATAL" else "ERROR",              ""          )          level = c(level[level != ""], "ERROR")[1]          simpleMessage = switch(level, DEBUG = TRUE, INFO = TRUE, FALSE)          quashable = switch(level, DEBUG = TRUE, INFO = TRUE, WARN = TRUE, FALSE)            # Format message          time = format(Sys.time(), "%Y-%m-%d %H:%M:%OS3")          txt = conditionMessage(obj)          if (!simpleMessage) txt = paste(txt, "\n", sep = "")          msg = paste(time, level, txt, sep = " ")          calls = sys.calls()          calls = calls[1:length(calls) - 1]          trace = limitedLabels(c(calls, attr(obj, "calls")))          if (!simpleMessage && length(trace) > 0) {              trace = trace[(length(trace) - 1):1]              msg = paste(msg, "  ", paste("at", trace, collapse = "\n  "), "\n", sep = "")          }            # Output message          if (silentSuccess && !hasFailed && quashable) {              messages <<- append(messages, msg)              if (level == "WARN") warnings <<- append(warnings, msg)          } else {              if (silentSuccess && !hasFailed) {                  cat(paste(messages, collapse = ""))                  hasFailed <<- TRUE              }              cat(msg)          }            # Muffle any redundant output of the same message          optionalRestart = function(r) { res = findRestart(r); if (!is.null(res)) invokeRestart(res) }          optionalRestart("muffleMessage")          optionalRestart("muffleWarning")      }        vexpr = withCallingHandlers( withVisible(expr), debug = logger, message = logger, warning = logger, caughtError = logger, error = logger )        if (silentSuccess && !hasFailed) {          cat(paste(warnings, collapse = ""))      }        if (vexpr$visible) vexpr$value else invisible(vexpr$value)  }  

If I execute the line logFully( f1() ) I get the output I desire, which is simply

2017-02-17 18:05:05.778 FATAL f2 always causes an error for testing purposes    at #4: stop("f2 always causes an error for testing purposes")    at f2()    at catA.R#8: cat(...)    at #3: catA("f2 = ", f2(), "\n", sep = "")    at f1()    at withVisible(expr)    at logFully.R#110: withCallingHandlers(withVisible(expr), debug = logger, message = logger, warning = logger, caughtError = logger, error = logger)    at logFully(f1())  Error in f2() : f2 always causes an error for testing purposes  


Fatal error: Call to a member function getElementsByTagName() on a non-object in D:\XAMPP INSTALLASTION\xampp\htdocs\endunpratama9i\www-stackoverflow-info-proses.php on line 72

0 comments:

Post a Comment

Popular Posts

Powered by Blogger.