Debugging
Macromedia ColdFusion Debugging - Don't Forget Your Bug Spray!
'Sorry about that Adam, we'll have to take a deeper look at it. Uh-huh. Yeah.
Sep. 14, 2005 10:00 PM
Tags
While the scope of this article will not cover the ins and outs of the tags ColdFusion gives us to handle errors, a quick overview is necessary. <cferror> allows you to tell ColdFusion to run a particular template when an error of any given type is thrown. This is a great way to beautify the default error screen and give all pages, even ones that error, a consistent look and feel. <cftry> and <cfcatch> are used hand in hand. If an error occurs within a <cftry> construct, ColdFusion will immediately run the code in the corresponding <cfcatch> construct. Inside of a <cfcatch> construct you may also call <cfrethrow>, which will continue to throw the error as if there was no <cftry> and <cfcatch> clause at all. To make your own custom errors occur at any time, you can use the <cfthrow> tag. This is great for creating errors that pertain to just your application, not all applications in general, such as a business logic error. The attributes that make up <cfthrow> allow you to control all the main parts of an error, as explained earlier.
Administrator
ColdFusion also provides a site-wide error handler, set in the ColdFusion Administrator (under Server Settings/Settings at the bottom of the page). This setting allows you to provide a relative template path, which ColdFusion will execute when an error occurs. This template could do things such as send the sites administrator an e-mail with the error details, or just be used to dumb-down the errors that an end user sees.
Application.cfc
In ColdFusion MX 7.0 Macromedia introduced us to the Application.cfc. This upgrade to the Application.cfm/onRequestEnd.cfm also gave us developers ways to control many aspects of our applications. This control is implemented through specially named functions that ColdFusion automatically calls at the appropriate time. Of the most interest to us today is onError(). Any code placed in the onError() function in Application.cfc will run when the application errors. From this central location, developers can forward errors to the appropriate parties, redirect users to superficial error pages, and log errors. ColdFusion passes two arguments to the onError() function when it calls it (thus you should always have two <cfargument> tags in your onError() function). The first argument is an error structure, just like the one described earlier. The second argument is a string that contains the name of the Application.cfc method, if any, in which the error occurred. This argument has special implications for errors that happen in the onSessionEnd() and onApplicationEnd() functions. onSessionEnd() is fired when a session time-out setting is reached for a user's session, or, if using J2EE sessions, the user closes the browser. The onApplicationEnd() function is run when the server times out or is shut down. Because users are not requesting data from ColdFusion when these functions run, they cannot see any errors the functions may throw, and, because of this, logging errors that come from these functions is crucial.
In addition to onSessionEnd() and onApplicationEnd(), Application.cfc implements their opposites, onSessionStart() and onApplicationStart(). onSessionStart() fires when a new session is created by a user, including ColdFusion event gateway sessions. onApplicationStart() is triggered when the application first starts - either when the first request for a page is processed or the first CFC method is invoked by an event gateway instance, Flash Remoting request, or a Web service invocation. It's great for setting application-wide variables, such as the names of data sources or the location of mail servers.
As stated before, Application.cfc contains a replacement for Application.cfm and onRequestEnd.cfm. The onRequestStart() function runs when ColdFusion receives an HTTP request, a message to an event gateway, a SOAP request, or a Flash Remoting request. Following the completion of onRequestStart(), onRequest() will run. This function can contain the main display of a page. For example, this would be a good place to put all the core files for a Fusebox application. (For more information on Fusebox see www.fusebox.org/.) onRequestEnd() runs after all pages and CFCs in the request have been processed.
Order of Operations
Between all the new functions of the Application.cfc, error handling techniques, and debugging, it gets a little difficult to follow what ColdFusion is going to process and when. Here is a quick rundown of how ColdFusion processes requests:
- CFC initialization code at the top of Application.cfc
- onApplicationStart(), if not run before for current application
- onSessionStart(), if not run before for current session
- onRequestStart()
- onRequest(), or the requested page if there is no onRequest() method
- onRequestEnd()
ColdFusion could at anytime have an event fired that will run the onSessionEnd() function or onApplicationEnd() function. If onApplicationEnd() is triggered, it doesn't fire events to run onSessionEnd().
Also ColdFusion could have an error at anytime. ColdFusion executes its error handling in the following order:
- <cfcatch>, if present and if an error happens within a <cftry>
- onError(), if the application has an Application.cfc file with onError() within it
- <cferror>, if present and if exception type is not "request"
- ColdFusion Administrator's Site-wide Error Handler, if defined
- <cferror>, if present, with exception type of "request"
- Display standard error
Putting It All Together
I have finally found the bug that Adam called me about earlier. Let me tell you, it was difficult to track down. Among all the tools I have in ColdFusion, it still took me a long time to find the problem.
The reason Adam's problem was difficult to track down was because of what I will refer to as a "chain with nesting." I call it that because I found the error in a sequence of CFC function calls (many of which make a request to the database based on the value passed into them) in which each function receives arguments that are part of the results of a previous function call. To make matters worse, some of the functions in the chain call upon other functions internally, which are located in different components and sometimes create their own chain of calls. If one function returns the wrong result due to a bad query or perhaps some bad math, it could completely throw off everything all the way down the chain. This scenario often happens in large projects, especially those that are object-oriented or pseudo object-oriented.
To help myself out of these predicaments, I have put together a little framework using many of the components we have learned about today. The end result is a stack (array) of each function call, placed in the order they were processed. Each function call gets its passed-in arguments logged (important because ColdFusion, as does many programming languages, destroys a functions arguments once the function finishes executing), its results logged, as well as basic information such as when the function was executed and how long it took to execute.
About Nik MolnarNik Molnar is a ColdFusion/Flex developer with over seven years experience. He has led teams through the development of enterprise applications for the mortgage, sports ticketing, and stock industries. He is an amateur Iron Chef and posts regularly at his blog: foodDuo.com. He lives with his wife Katy and his dog Jacques in Orlando, Florida.