One of the biggest benefits from creating an automatic reporting framework is that you no longer need to directly supervise the creation and distribution of reports. However, when things go wrong it can be difficult to understand what went wrong and why. Luckily, R’s tryCatchLog package makes it easy to trap and log errors as they occur.

Benefits of tryCatchLog

tryCatchLog makes error logging and debugging easy by providing:

  • Configurable error and warning logging
  • Logging of the call stack with line numbers
  • Error dump files for post-mortem analysis

This means that not only will tryCatchLog create a file showing where and when an error occurred in the code, but it will also save a copy of the environment at the time of the error.

A common issue for batch jobs (like automated reporting) is that the object and variable values are discarded when an error occurs, which makes debugging extremely difficult. tryCatchLog alleviates this by creating and saving an environment file at the time of the error, so you can examine the variables and objects that might have caused an error at a later date.

Prerequisites

tryCatchLog can be downloaded and installed from CRAN with the following command:

install.packages("tryCatchLog")

Adding tryCatchLog to your script

Functionally, tryCatchLog works as a wrapper for your code. You start by loading and configuring tryCatchLog, and then you call the main code you want to execute using the tryCatchLog function. To call your code using tryCatchLog, you can either wrap your main code in a function or place it in a separate file and reference it using the source function.

Based on this, the easiest way to start using tryCatchLog for error logging is:

#
# Start by defining a function with the code you want to run
#

mainCode <- function() {
  
  # Put the actual code you want to run here
  print("test")
  
  # Let's generate an error for test purposes
  log("test")
  
}

#
# Load tryCatchLog and dependencies
#

library(futile.logger)
library(tryCatchLog)

#
# Configure tryCatchLog
#

# Enable source code and line number tracking in the log
options(keep.source = TRUE)

# Enable the creation of dump files for post-mortem analysis
options("tryCatchLog.write.error.dump.file" = TRUE)

# Choose the file you would like to log errors to
flog.appender(appender.file("error.log"))

# Set the level of error logging
# Options are: TRACE, DEBUG, INFO, WARN, ERROR, FATAL
flog.threshold(ERROR)    

#
# Run code using tryCatchLog
#

tryCatchLog(mainCode())

If we run this, it will generate an error on line 11 since you can’t compute the logarithm of a string. Consequently,tryCatchLog will create a file called error.log in the working directory with the call stack (the functions that lead to the error). Looking at the top of the call stack file, we’ll see the error that was created and the functions that proceeded it. On the last line in the compact call stack, we can see that the error arose on line 11 of our code:

ERROR [2018-11-03 12:00:33] [ERROR] non-numeric argument to mathematical function

Created dump file: dump_20181103_120033.rda

Compact call stack:
  1 source("~/.active-rstudio-document", echo = TRUE)
  2 .active-rstudio-document#43: tryCatchLog(mainCode())
  3 .active-rstudio-document#11: .handleSimpleError(function (c) 

From here we can then review our code and try to fix the error.

Logging warnings in addition to errors

If you want finer-grained logging, you can change the value of flog.threshold to WARN or INFO to also log less severe issues in your code. The standard warning with automated logging applies here: setting the threshold to lower and lower levels will result in progressively larger log files, which could use a significant amount of disk space if not regularly deleted.

Sending push notifications for errors

While it is nice to have logging and debugging information, for automated jobs it can still be difficult to know when errors occur. To solve this we can create an error handler (a block of code which is run when an error is detected). If set up our error handler to send email, we can create email push notifications when there are errors in our scripts.

#
# Start by defining a function with the code you want to run
#

mainCode <- function() {
  
  # Put the code you want to run here
  print("test")
  
  # Let's generate an error for test purposes
  log("test")
  
}

#
# An error handler that sends a email with the error and call stack
#

pushNotification <- function(scriptName, recipients, error) {
  
  # Load the DCOM library
  library(RDCOMClient)
  
  # Open Outlook
  Outlook <- COMCreate("Outlook.Application")
  
  # Create a new message
  Email = Outlook$CreateItem(0)
  
  # Set the recipient, subject, and body
  Email[["to"]] = recipients
  Email[["subject"]] = paste0("Error - ", scriptName)
  Email[["htmlbody"]] = paste0(
    "<h1>Error - ", scriptName, "</h1>
    <p>", scriptName, " has stopped with the following error:</p>
    <code>", error, "</code>
    <h2>Call stack</h2>
    <pre><code>", readChar("error.log", file.info("error.log")$size), 
    "</code></pre>")
  
  # Send the message
  Email$Send()
  
  # Close Outlook, clear the message
  rm(Outlook, Email)
  
}

#
# Load tryCatchLog and dependencies
#

library(futile.logger)
library(tryCatchLog)

#
# Configure tryCatchLog
#

# Enable source code and line number tracking in the log
options(keep.source = TRUE)

# Enable the creation of dump files for post-mortem analysis
options("tryCatchLog.write.error.dump.file" = TRUE)

# Choose the file you would like to log errors to
flog.appender(appender.file("error.log"))

# Set the level of error logging
# Options are: TRACE, DEBUG, INFO, WARN, ERROR, FATAL
flog.threshold(INFO)    

#
# Run code using tryCatchLog, and
# set our email function as the error handler
#

tryCatchLog(mainCode(), 
  error = function(e) { 
    pushNotification("My report name", "recipient1@test.com", e) 
  })

Learn more

While I have tried to cover core functionality and useful settings for automated jobs, there are a lot more configuration options availble for the tryCatchLog package. To learn more, check out the developers vignette, frequently asked questions, and the reference manual.

Will you be making use of the tryCatchLog package? Do you have any recommendations on how to handle errors in your automated workflows? Join the conversation by leaving a comment below.

Leave a Reply

Your email address will not be published. Required fields are marked *