Brought to you by LEAP™ 5 and Lasso 9

Effective Error Control

Appropriate error handling is essential to any code - and yet it's often included as an afterthought to systems large and small.

I'm going to cover some basic Lasso 9 techniques for handling errors in this article that you can easily add to existing code as well as use as you construct your systems from the ground up.

No developer likes to have their client emailing them with something this:

A Failure
Error Code: -1
Error Msg: Position was out of range: 10 max is 3

… because you assume that a certain set of conditions exist.

Yes, in your code you could simply test to see if the array was long enough, but that's not the point.

My point is that users will enter data you don't expect. API's will send you data with exceptions that are not documented. And, well, we make mistakes and assumptions - so we should learn how to deal with them properly.

The protect block

The easiest way to prevent errors from interrupting is to use the protect capture.

protect => {
     local(x = #y->get(10))
}

This will no longer prevent the rest of the page continuing. You might be missing some important processing as the protect block will stop at the error and exit there, but we will get to that later. 

However, read down* about why the code above can be really, really bad...

A quick note about "auto collect"

Capture blocks are new to Lasso 9, so it's important right now to mention the role of the caret.

protect => { 'Hello world' } 

This outputs nothing without the caret which signals "auto collect" mode. The caret is shift-6 on English keyboards.

protect => {^ 'Hello world' ^} 

This will output 'Hello world' as you'd expect.

Captures and auto-collect are very useful when considering larger blocks of code processing. They can limit the production of unnecessary whitespace and debug output, fulfilling the flow of the ugly, cumbersome "output_none" container.

*Failing silently is failure personified

I've seen a lot of code that uses protect simply to hide errors. This is *not* an effective strategy. The whole point of error handling is to deal with errors appropriately.

Enter [handle_error].

Let's take our hypothetical array size overrun situation from before. Let's say that we expect to have an array size of 10 but really if it's less then it's ok to take the last element instead, and report that case to the user. We achieve this by using handle_error within the protect capture.

Note: the handle_error must be the first thing in the protect capture for it to be processed correctly.

// feedback thread var. It's a thread var because it persists throughout the "page"
var(my_error_feedback = string)

// initialize some local vars for us to use.
local(y = array(1,2,3), x = integer)

// the protect block
protect => {
     // the handle error which will be executed 
     // if and only if there's an error generated
     handle_error => {
          local(x = #y->last)
          $my_error_feedback->append(
              'We expected a larger array'
          )
     }
     local(x = #y->get(10))
}

Ok, so my example's a bit lame, but it illustrates the point.

Intentional Failure

Fail_If provides us with a great way of knocking into the handle_error when certain conditions are met:

// feedback var. It's a thread var because it persists throughout the "page"
var(my_error_feedback = string)
 
// initialize some local vars for us to use.
local(y = array(1,2,3), x = integer)
 
// the protect block
protect => {
     handle_error => {
          $my_error_feedback->append('An error has been generated: '+error_currenterror)
     }
     fail_if(#y->size < 3,'#y was tiny!')
     fail_if(#y->size < 10,'#y was too small')
     local(x = #y->last)
}

Once again, my examples are really basic and not really applicable to the real world because I'm trying to simplify. Where to use them? Be inventive!

One place I use protect, handle and fail_if is in methods where I want to tightly qualify what data I want to be used for input. Yes, we can specify data types and default data in the signature of a method, but beyond that we need to do a bunch of filtering - which is where this process can come in handy.

Be informed

Reporting the errors is really something that is dependant on your circumstance.

Some examples: 

  • log_critical (and similar) - send the data to the main log file. Be careful with this because logs can grow, and excessive logging has been linked to instability on some systems.
  • stdoutnl('message') - log just to console. Simpler than log_critical in that it cuts out the possibility of being logged to the error database and just writes to the console.
  • log(filename) => {^ … ^} - log to a file. The output of the capture is written to a log file. Note that Lasso 9.2.6 has the fix that appends rather than overwrites!
  • $myThreadVar->append('message') - add the message to a thread var (i.e. at the page level) to be output later on the page, or added to a database. Note I used append: it's a little faster than $myThreadVar += 'message'
  • Write errors to a database. This is the extreme option but some will like this method. Use this with caution.

Error pages

error.lasso - Having error.lasso pages in your site or LassoApp is a must. It means you can gracefully display your error.

404 and 500 errors are painfully common. Handle them well. Specify them with Apache (or your web server of choice) and while it might not stop the errors at least you can present a nice face to your users. Perception is 99% of the battle!

Error Messaging

Learn the art of good error reporting - there are many options for manipulating errors.

I'm not going to go into the details of error codes, messages, pushing and popping - the docs cover that part quite well (http://lassoguide.com/language/error-handling.html#error-methods)

Suffice to say that presenting the error could be as simple as displaying on the page:

The current error is [Error_Code]: [Error_Msg]

For debugging purposes however, I usually include [error_stack] as it shows me the nested details so I can see what line, what file, and what parents (with the line numbers) that invoked that code, then I can trace it back. Not a good idea to leave it in play when you go live though!

Relevant links

Lasso 9 Error Handling docs - http://lassoguide.com/language/error-handling.html

Protect - http://www.lassosoft.com/lassoDocs/languageReference/obj/protect

Fail_If - http://www.lassosoft.com/lassoDocs/languageReference/obj/fail_if

Log_critical - http://www.lassosoft.com/lassoDocs/languageReference/obj/log_critical

comments powered by Disqus