Apr
15

Downloadable Code - Exception Handler

Posted by Aaron West at 1:39 AM in ColdFusion

Well over a year ago I built a CFC to help me manage ColdFusion exceptions. I needed a good way to handle the display of user-friendly error messages (in ColdFusion and Flash) and generate e-mails to the development team when problems occurred in my code. So I sat down and wrote a CFC to do as much work as possible allowing me to simply make calls to the CFC as needed. The time I spent has proven to be invaluable as we now use the CFC across our entire ColdFusion architecture. Since then, I've adopted the CFC for use on this Web site and thought it would be helpful to share it with other developers. Read on for the full story and the downloadable code.

First things first, you might want to download the CFC so you can follow along below. The idea is pretty straightforward, so let's take a look at two code examples to see how things work.

Example 1:

<cfset variables.exceptions = CreateObject("component", "com.utils.ExceptionHandler")>

<cftry>
   <cfset ArrayNew(4)>
   
   <cfcatch type="any">
      <cfset variables.errMsg = variables.exceptions.GetExceptionMessage(cfcatch)>
      <cfoutput>#variables.errMsg#</cfoutput>
      <cfset variables.exceptions.SendExceptionEmail("TestExceptions", cfcatch)>
   </cfcatch>
</cftry>

This code example illustrates how the CFC is called. I typically cache the CFC in the Application scope since it gets used all over the place. Here, I'm simply defining it in the variables scope. The try/catch block has a glaring error, which will cause the exception handler to do its thing when the code is executed. The ExceptionHandler.cfc has two methods in it. One is for retrieving friendly error messages (something other than hard CF errors) than can be displayed any way you like. Depending on the type of work I'm doing the messages could be displayed in-line in HTML or passed in to Flash for display in an "alert" box. The GetExceptionMessage function of the CFC expects the entire cfcatch scope to be passed in. This scope is a special scope in ColdFusion that for all accounts and purposes is a struct. However, ColdFusion does not see it as a struct so you can't define the argument with type="struct". Hence, the "any" type in the function. Another gotcha related to the cfcatch scope is that you can't use it in a cfdump tag.

If you move on down and look at the function body you'll see that all I am doing is checking the exception type. Based on the type of exception thrown I configure an appropriate message to display to the user. This not only keeps hard CF errors from displaying, but also gives the user a general idea of what happened. Keep in mind the code here is oversimplified on purpose. There are more exception types that can be added and while I'm only using the cfcatch.type there are other properties that could prove useful in a number of ways.

The last line of the cfcatch is where the real sweetness resides. The SendExceptionEmail function takes two arguments. The first is a unique name for the block of code encompassing the try/catch. This could be an entire cffunction body, a certain part of the cffunction, a specific block of code in a CF template, etc. You can get creative as you want here. For instance, I will typically use syntax like "CFC_Name [function_in_cfc]" when I have one try inside a cffunction. When errors occur with the function I can get right to the heart of the problem quickly after reading the error e-mail. The second argument to the function is the cfcatch scope. Taking a look at the SendExceptionEmail function you'll see that it sets up an e-mail will all the information contained in the cfcatch scope. This includes the basic error message, the error detail, the sqlstate for "database" errors, and the full tagcontext (which shows all templates in the CF process that generated the error and the line number indicating where the exception occurred). The date and time the error occurred will also be listed along with the first argument you passed to the function indicating the module the problem occurred in. It's nothing overly complex, but it sure is handy.

What about the generated e-mails, what will they look like? Take a look at Example 1 (shown again below) and Example 2 and then click the links for the screen shots of the generated e-mails.

Example 1: (ArrayNew function error)

<cfset variables.exceptions = CreateObject("component", "com.utils.ExceptionHandler")>

<cftry>
   <cfset ArrayNew(4)>
   
   <cfcatch type="any">
      <cfset variables.errMsg = variables.exceptions.GetExceptionMessage(cfcatch)>
      <cfoutput>#variables.errMsg#</cfoutput>
      <cfset variables.exceptions.SendExceptionEmail("TestExceptions", cfcatch)>
   </cfcatch>
</cftry>
Click here for Example 1 e-mail screenshot

And here's another example:

Example 2 (Table doesn't exist error)

<cfset variables.exceptions = CreateObject("component", "com.utils.ExceptionHandler")>

<cftry>
   <cfquery name="qryAaron" datasource="myDatasource">
      SELECT *
      FROM Employees
      ORDER BY ID
   </cfquery>
   
   <cfcatch type="any">
      <cfset variables.errMsg = variables.exceptions.GetExceptionMessage(cfcatch)>
      <cfoutput>#variables.errMsg#</cfoutput>
      <cfset variables.exceptions.SendExceptionEmail("TestExceptions", cfcatch)>
   </cfcatch>
</cftry>
Click here for Example 2 e-mail screenshot

Aaron West's Gravatar
About this post:

This entry was posted by Aaron West on April 15, 2006 at 1:39 AM. It was filed in the following categories: ColdFusion. It has been viewed 2032 times and has 2 comments.

2 Responses to Downloadable Code - Exception Handler

  1. Jeff Coughlin

    Rather than modifying all those .cfm files, why not use a global setting to catch all/every CF error on the website (and not just ones within the cftry/cfcatch blocks)? You can still define which exception types to catch. Of course, there are useful times to use cftry/cfcatch (I use them if I want a "specific" result to happen on a specific piece of code if an error occurs), but the code you're showing seems to cover generic errors (unless I missed something in your blog post which I admit I skimmed through. Forgive me if I have).

    CF6 and CF7 support <cferror/> (placed within the Application.cfm file) and CF7 now supports the onError method within the Application.cfc file.

  2. Jeff, you're right on, the CFC is not meant to replace the global exception handling capable in CF6 and CF7. It's more an addition to a developers toolbox than a replacement of other things. I typically use both types - global exception handling and local. Thanks for mentioning this.