<<

Error Handling and Debugging

Error Handling

By Simon Free (Bio)
gist

While the best efforts are made to stop errors from happening, they do happen. Sometimes the errors might be due to some bad code; other times it may be due to external resources that are out of one's control. During the development process, these errors hold valuable information that allows us to improve and fix our code, but in production environments, these errors hold information that can make our servers vulnerable to attack. It is important that as a developer you anticipate errors and gracefully handle them. Thankfully, ColdFusion offers a number of ways to trap those errors and even allow developers to react to those errors and call alternative functionality.

Understanding Errors

Error Types

Before we look at how to handle the errors, lets first take a look at the errors themselves. There are three types of ColdFusion Errors:

  • Exception: Where the error stops the request from completing its process.
  • Missing Template: When an HTTP request for a page can not be found.
  • Form Field Data Validation: When server side form validation fails.

The most common error type you are going to experience is the Exception type. Unless the users are requesting a page that does not exist, or you have decided to use ColdFusions in-built form validation (not recommended by most ColdFusion developers), you will receive an Exception error.

These exception errors, excluding custom errors, fall into one of the following exception types:

  • Database: When there is a problem with a database call, such as malformed SQL or database connection issue.
  • MissingInclude: When an included file can not be found.
  • Template: When a general error occurs, usually from a malformed tag or incorrect script syntax.
  • Object: When an error occurs with an object.
  • Security: When an error occurs related to security functionality.
  • Expression: When an expression fails, such as 1 + "a".
  • Lock: When an error occurs with a piece of code that has been locked by the application. This can be due to the code failing at runtime or a lock timing out.
  • SearchEngine: When there is an issue with the Verity Search Engine.
  • Application: When custom errors are generated by the cfthrow tag that do not have a type defined.

Knowing the type of exception thrown can be useful when handling your errors gracefully. Depending on the type of exception, it might be possible to retry a section of code again. For example, if a section of code that communicates with an external service times out, you might want to try again to see if the second time is successful. If you try this approach with some errors, it is important to keep in mind that your request might never be successful and that the error must then be handled a different way.

Error Data

For every error thrown, there are 2 standard error formats that contain relevant information to the error. The format of error you get depends on the settings within your ColdFusion Administrator. Under the Debugging & Output Settings there is an option called Debug Output Settings. On that page there is an option called 'Enable Robust Exception Information'. Checking that will provide additional information to the page. These two formats are only relevant if the error is displayed to the user. Checking this box will not alter the information that is provided to the system if the error is caught programmatically. It is strongly advised to never enable the 'Robust Exception Information' on a production server. If your error handling were to fail, this would display sensitive information to the user which you would not want them to see.

If the error is caught programmatically you will have access to the following information:

  • Message: This provides you a brief, one line summary of the error.
  • Detail: This provides additional information about the error along with suggested solutions, if there are any.
  • Stack Trace: This shows the contents of the java stack at the time of the exception.
  • Tag Context: This provides a list of all files that were called called and from what lines they were called.
  • Type: The type of the exception.

Depending on the type of exception thrown, you might have access to additional pieces of information; however, these fields will always be present.

Error Logs

ColdFusion has extensive capabilities when it comes to error logs. Later in this chapter we will review how to create your own log entries, but for now, let's look at the automatic log entries.

Every time that the default error handler is used, which is when ColdFusion displays the error for you on the screen, an entry is entered into the ColdFusion Error Log. This Error Log is accessible via a log viewer application or via the ColdFusion Administrator. To access the Error Log, simply open up your ColdFusion Administrator and go to: Debugging & Logging > Log Files. From this screen you will see all the log files that ColdFusion creates. If you created any custom logging, you would also see the log files here. Notice that each log file has a number of different icons, allowing for different actions on the log. These actions include searching and viewing the log, archiving the log, and deleting the log. To view the errors on your application, you can select the application.log file or the exception.log file. Each log file gives a different type of information and can be used to find errors within your application.

Error Management

Now that there is an understanding of what errors are, let's take a look at Error Management. There are a number of ways in which you can capture an error and handle it with ease. When capturing the error, you might decide to run an alternate piece of code, but sometimes you might just want to notify the user that an error has occurred and send the information back to yourself for resolving later. Whatever way you wish to handle the error, you will need to use one of these forms of Error Handling.

cftry/cfcatch

The use of the cftry and cfcatch tags allows you to provide error handling around a specific section of code. This method is often used when you wish to provide alternate processing of code. The cftry tag is wrapped around the section of code that you can monitor for issues. If anything within the cftry tag causes an error, the matching cfcatch tag will catch the error and allow you to provide alternate processing. Using this method will prevent the error from making its way to the user. Inside the cftry tag you can put any ColdFusion logic, including calling objects and including files. If any errors occur from within these external resources, the error will still be caught, assuming those external files do not have their own error handling in them.

At the end of the cftry block, but before the closing tag, you will place at least one cfcatch tag. The cfcatch tag is where you will place your alternate processing. The cfcatch tag must provide a type attribute which specifies which type of exception it will catch. Multiple cfcatch tags can be used within a cftry tag as long as they all have different types specified. Providing different cfcatch tags allows you to handle the different exception types differently. If you do not want to handle different exception types differently you can use the exception type of any, which will catch all exception types.

Here is an example of a cftry/cfcatch:

<cftry>
	<!--- Your code --->
	
	<cfcatch type="database" >
		<!--- Code to handle a database exception --->
	</cfcatch>	 
	<cfcatch type="any">
		<!--- Code to handle all other exceptions --->
	</cfcatch>
</cftry>

If you prefer your code to be script based, here is a script based example:

try{
	//Your code
}	
catch(database exception){
	//Code to handle database exception
}
catch(any exception){
	//code to handle all other exception
}

Once the cftry has run and any exceptions have been dealt with or ignored if you so choose, the page will continue to process from the closing cftry tag. If, once the exception has been caught, you do not wish to handle the exception, but want to pass it off to the next level of error handling, you can use the cfrethrow tag. The cfrethrow tag will then bubble the error up the chain of Error Management. The page will then no longer continue to process.

Here is an example of the cfrethrow in action:

<cftry>
	<!--- Your code --->
	
	<cfcatch type="database" >
		<!--- Code to handle a database exception --->
	</cfcatch>	 
	<cfcatch type="any">
		<cfrethrow />	
	</cfcatch>
</cftry>

And in script format:

try{
	//Your code
	}	
catch(database exception){
	//Code to handle database exception
}
catch(any exception){
	//code to handle all other exception
	rethrow;
}

cfthrow

Sometimes when developing an application, it may be necessary to create your own error to be thrown. Perhaps you are making an HTTP request and you did not receive the expected 200 response. In those situations you can use the cfthrow tag to throw your own exception. That exception will then get picked up by the first level of your Error Management solution.

When using the cfthrow tag, you have a number of useful attributes at your disposal. The most useful attributes are the type and message attributes. The type attribute allows you to specify the type of error being thrown. This type does not need to be one of ColdFusion's predefined exception types and can be a custom type of your own choosing. This can be very useful when used within a cftry/cfcatch block, as your cfcatch type can also match that custom type. The message attribute is also very useful; it allows you to provide a message, or reason, for the error. This message could then be relayed back to you via a global part of your Error Management solution.

Here is an example of a cfthrow inside of a cftry/cfcatch:

<cftry>
	<!--- Your code --->
	
	<cfcatch type="database" >
		<!--- Code to handle a database exception --->
	</cfcatch>	
	<cfcatch type="any">
		<cfthrow 
			message="I am an Error" 
			type="specialError"
			detail="This is where I put extra detail"	 	 
			/>
	</cfcatch>
</cftry>

And in script format:

try{
	//Your code
	}	
catch(database exception){
	//Code to handle database exception
}
catch(any exception){
	//code to handle all other exception
	throw(message='I am an error', type="specialError", detail="This is where I put extra detail");
}

cffinally

The cffinally tag is placed inside the cftry block of code after the cfcatch blocks. The cffinally tag will always execute, even if no errors occur. This tag can be useful when there is some functionality you always want to run, such as functionality that will free up resources.

Here is an example of cffinally in action:

<cftry>
	<!--- Your code --->
	
	<cfcatch type="database" >
		<!--- Code to handle a database exception --->
	</cfcatch>	 
	<cfcatch type="any">
		<!--- Code to handle all other exceptions --->	
	</cfcatch>
	<cffinally>
		<!--- This code will always run --->
	</cffinally>
</cftry>

And in script format:

try{
	//Your code
	}	
catch(database exception){
	//Code to handle database exception
}
catch(any exception){
	//code to handle all other exception
}
finally{
	//This code will always run
}

onMissingTemplate

Within the Application.cfc file you can specify a function called onMissingTemplate. This method will be called when a requested page does not exist. If this method is called, the standard onApplicationStart and onRequestStart methods are not called. If the onMissingTemplate function returns 'false', then the control is passed back to the servers 404 handler.

When the onMissingTemplate function is called, it is up to you how you handle the issue. The most common thing to do is to include a site specific custom 404 page and notify the user that the page is missing. As the method receives the path of the file that was requested, you also have the ability to perform an action based on that information. For example, if you have a file that is often mistyped, you could look at the provided information, decide what page they really wanted, and redirect them to that page.

onError

If you wanted to catch all errors within a specific site, you can use the onError method inside of the Application.cfc. The onError method will be fired by any error that occurs in your site that is not caught inside of a cftry/cfcatch block. The onError method will catch all exception types, although it will not catch exceptions thrown due to syntax errors such as malformed tags. Those errors will bubble up to the next level of Error Management.

The onError method is the most common Error Management method. Using this method allows you to track all errors from your site and handle them accordingly. The most common method for handling these errors is to display a 'Sorry' page to your users and notifying one of your development staff of the information.

The onError method receives 2 arguments, the Exception and the EventName. The exception is a structure that contains all the information about the error. The information that will be included in this structure was discussed previously in this chapter. If you choose to have the onError method send an email to one of your developers, then this information can be included in the email to provide specific information related to the issue and should help them resolve the issue.

Here is an example of an onError method in script format. This code will capture the error, include a 'Sorry' page, and email the information to a developer.

public void function onError(required any exception, required string eventname){
	include "sorry.cfm";
	
	var errorEmail = new mail();
	errorEmail.setTo(application.developerEmail);
	errorEmail.setFrom(application.systemEmail);
	errorEmail.setSubject('An Error has Occured');
	errorEmail.setBody('
		Message: #arguments.exception.message#<br />
		Details: #arguments.exception.detail#<br />
		Type: #arguments.exception.type#<br />
	');
	errorEmail.setType('html');
	errorEmail.send();
}

Site-wide Error Handling

The last possible level of Error Management you can create, prior to having the default ColdFusion Error displayed to the user, is to create a Site-wide Error page. The Site-wide Error page is not actually site specific. A better name for this page would be a Server-wide Error page, as only 1 can be set on the server and will be displayed to all websites on the server. It is important to remember this when it comes to styling this page, for if you have multiple sites on the server, you do not want it branded for one specific site. Even though the page is not specific to a site on the server, it is a useful back up to have in place as it will catch ALL errors, including tag syntax errors, which onError will not catch.

To specify a Site-wide Error Handler, you need to open up your ColdFusion Administrator and navigate to Server Settings > Settings. On this page, under the Error Handlers heading, you will see a Site-wide Error Handler box. In this box you will enter the location of the file you wish to be displayed. The path that you use should be relative to the server root. For example if you had a folder called serverWideFiles in the root of the server (remember, server, not site), then it might look like '/serverWideFiles/globalErrorHandler.cfm'.

Multiple Error Handling Strategy

When creating Error Handlers for your web site, it is often best to have multiple handlers in place. The main goal for any Error Handling Strategy is to prevent the generic ColdFusion error page from displaying to your user. Not only does this not look professional, it also shows users sensitive information about your server, such as file location paths. It is also important to remember that not all the Error Handlers catch all the errors, except the Site-wide error handler (which is the least accommodating handler).

When an error is thrown, it will bubble up the application until the first Error Handler catches it. The order in which the handlers will be called is:

  1. cfcatch
  2. onError
  3. Site-wide Handler
  4. ColdFusion Generic Handler

Things To Remember

When looking at Error Handling, there are a few things you should remember:

While in development, there is no need for error handling. You will want to see any errors immediately so that you can resolve them right away. Place checks in your Error Handlers that can tell if you are in a development or production environment. The most common method for this is to check if the CGI.remote_addr is 127.0.0.1. If it is, then that means you are running the code on your local machine.

When displaying a 'Sorry' page, use as little ColdFusion as possible and do not include any files. The sorry page is being displayed because there is a problem in the code. Imagine if the problem is an issue within the header of your web site and you include the header. The 'Sorry' page will now throw an error. If your 'Sorry' page throws an error, it is possible that the 'Sorry' page will get called again, and again, and again. The next thing you know, you will have created an infinite loop that could take down your server. If you want to have the site header and footer on the 'Sorry' page, then place the generated HTML in the 'Sorry' page so that you know the page will not throw any errors.