Error handling
Note
This how to does not cover best practices around error handling. For some rationales behind how you would choose to throw or handle an error, see the error management topic guide.
Error tracking is used for capturing VoltScript errors. These are errors triggered by VoltScript error codes (e.g. 13, Type mismatch) or custom errors thrown by Error errCode, errMsg
. The ErrorEntry object will automatically parse the error code, message, line number and stack trace to store the relevant information.
Warning
Remember that the error line and stack trace are based on where the Try/Catch is. If the error occurs in a sub or function being called, but that does not have error handling, the line number and stack trace will be based on the calling code. To get more granular information of precisely where the error was triggered, you will need to add error handling to the sub or function being called.
The error tracking uses two classes:
- ErrorEntry: Instances of this class carry the aforementioned information about an error.
- ErrorSession, accessed via
getErrorSession()
, is a container ofErrorEntry
object instances. These instances are automatically created and added to theErrorSession
via thecreateErrorEntry()
function. Custom error information can also be added to theErrorSession
by calling thecreateCustomErrorEntry()
method.
ErrorEntry instances carry the error code, but do not have an associated error or log level: level is only relevant when logging the information. Implementation code for differing handling behavior based upon the value of the error code will typically be contained inside of a Try/Catch block.
Get ErrorSession
Like the LogSession, the ErrorSession instance is a singleton, lazy-loaded on the first call to getErrorSession()
. Always use this method to retrieve the current ErrorSession; attempting to call Dim variable_name as New ErrorSession()
will throw an error.
Info
VoltScript objects cannot persist between script runs. So when the Sub Initialize
is first triggered, the ErrorSession instance will be Nothing
. When the Sub Initialize
ends, the ErrorSession instance will be deleted.
Add errors to the session
The expected way to create an ErrorEntry object is via Call getErrorSession().createErrorEntry()
. That function returns the ErrorEntry
object. Use this instance if you wish to do any additional processing such as checking the error code for expected or unexpected errors.
Sub performFatalLoop()
Dim i as Integer
Dim ee as ErrorEntry
Call globalLogSession.createLogEntry(LOG_INFO, "Performing a Fatal Loop", "", Nothing)
For i = 0 to 10
Try
' Do stuff
Error 1000, "Generic Error on loop " & i
Catch
Set ee = getErrorSession().createErrorEntry(NO_LOGGING)
End Try
Next
Call globalLogSession.createLogEntry(LOG_INFO, "Finished Performing a Fatal Loop", "", Nothing)
End Sub
Logging errors
The errorCount
can be used to check whether errors have been logged. This can avoid passing errors up the stack, as below:
ErrorEntry.getLogMessage()
formats the error string, error code and line number in a human-readable string. Additional information about the library, class, and method are in contained int the stack trace. When spawning a LogEntry we pass the stack trace as extended information.
Important
This code uses a ForAll loop from line 12 to log entries. You don't need to do that, we'll see a better way to log errors below.
Important
This code uses a ForAll loop from line 12 to log entries. You don't need to do that, we'll see a better way to log errors below.
Clearing the error session
There may be a scenario where you are not interested in the actual errors, but just want to clear the ErrorSession to continue processing. ErrorSession.reset()
will do this, clearing all ErrorEntry objects from the session and setting error count back to 0.
Important
The ErrorSession and LogSession are separate. Resetting the ErrorSession does not remove any logs logged for errors. Equally, resetting the LogSession does not reset the ErrorSession: ErrorSession.errorCount
will still be the same as before you called globalLogSession.reset()
.
Note
Another option is to capture the error count at the start of a particular step, then check the error count has not increased at the end of the step. This is particularly relevant if you are running a loop that may generate errors and an inner loop that may also generate errors. You will see this approach in VoltScript JSON Converter.
Custom Errors
Custom ErrorEntry instances can be created at any time, they do not require an Error to be thrown and caught. A common programming pattern of the past would be to intentionally throw an exception, then catch or trap any thrown errors, gather and log information about the error, and then throw a new error with additional contextual information. This pattern is messy and should be avoided; being able to create an ErrorEntry instance independent of throwing an exception allows a developer to do just that. The method createCustomErrorEntry()
has four arguments:
- message Error message describing the error. If
code
is less than 1 the value of Error$() will be used. - code Numeric code of the error. If less than 1 the value of Err() will be used.
- lineNum Line number in the source code where the error occurred. If
code
is less than 1 the value of Erl() will be used. - levelNum The Logging Level for conditionally spawning a LogEntry.
Consider the following example:
Sub performCreateCustomErrorEntryInstances()
' Create custom ErrorEntry instances without throw / catch.
Call getErrorSession().createCustomErrorEntry("The email address requires an '@' character", 2021, 101, NO_LOGGING)
Call getErrorSession().createCustomErrorEntry("The passed in argument is invalid", 1195, 201, LOG_WARN)
Call getErrorSession().createCustomErrorEntry("The file cannot be found", 1065, 351, LOG_ERROR)
End Sub
Logging Errors with context
The sample code in the above Sub Initialize()
will create LogEntries for ALL errors, using log level of LOG_FATAL. While this may be useful in some instances, this all-or-nothing approach is not the only way to add error information to the log.
The levelNum
argument of the createErrorEntry()
method specifies the logging level. If set to NO_LOGGING
then the ErrorEntry will simply be created and added to the ErrorSession. Passing any other valid log level will cause a new LogEntry object to be immediately created by the LogSession, using information from the ErrorEntry.
This capability allows the developer to add information to the log based upon the context of the error (LOG_TRACE
, LOG_DEBUG
, LOG_INFO
... LOG_FATAL
).
Additionally, there is no need for the developer to subsequently process the ErrorEntry instances to add information to the LogSession.
- "80" is the current line number
Note
Bear in mind line 12 will create an ErrorEntry and increment the error count, but is logging at level LOG_INFO. In reality, it would be better for this line to just create a LogEntry because there is no real error. But createCustomErrorEntry()
is used to provide easy comparison with createErrorEntry()
.
See sample code