Aaron Greenlee.com | A Personal Journey.

Track and Report Errors in ColdFusion Apps using Hoth

POSTED Wednesday, February 9, 2011  |   | DOWNLOAD CODE
Keywords: Hoth, Project, ColdBox, ColdFusion

Last updated 3/1/2011

How many errors are happening in your Web application?

If you answer "not many" you may not know enough about what is happening with your application. I love building Web applications–specifically, I build applications that deliver a great experience and quality service. In the game I play even a single error is one error to many. I am an advocate of test-driven development and I build all sorts of unit/integration tests to confirm my applications behave as expected. But, Web applications are constantly evolving and the way clients interact with our services constantly changing. Errors happen. They just happen. Sometimes it's a hardware failure and sometimes it is a bug in your code. I've been searching for a way to easily parse logs to pluck the relevant data. I've looked at logging products and definitely see their value; but, none solved my problem the way I required.

Introducing Hoth: ColdFusion Error Tracking and Reporting

Hoth is a lightweight, framework agnostic solution to understanding the answers to two important questions without wading through log files:

  • Is this a new, unique error I have not seen before?
  • How many times has this error occurred?

What does Hoth do?

Hoth reports ColdFusion errors and optionally notifies you when a new error occurs. Hoth gives you a very simple UI that allows you to see each recorded error, how often these errors have occurred and optionally delete an error once you believe it will not occur again. Hoth can optionally e-mail you when a new error is observed so you can make a real-time decision if you need to get up off the couch and stop watching the Jersey Shore.

What can Hoth tell you about your errors?

Hoth records all of the details of a exception every time it is given an exception it has not encountered before. By accessing Hoth's report, you can see the details of the very first error. After the first error, Hoth simply keeps count of the frequency of the errors by logging a timestamp for each occurrence.

How does Hoth work?

Hoth is quite simple. You provide it with a ColdFusion Exception and Hoth will hash the stacktrace to determine if the exception has been observed before. Each unique stacktrace is considered to be a unique error. Hashing the stacktrace allows Hoth to assign unique exceptions their own ID and quickly determine if new exceptions have already been reported.

If Hoth does not recognize the stacktrace the following files will be created in a directory you define when you configure Hoth:

/exceptions/{hash}.log
The logs written to the exceptions directory contain the details of each exception. This information has been serialized into JSON.
/incidents/{hash}.log
The logs written to the incidents directory contain timestamps for each occurrence of an exception.

If Hoth recognizes the hash of a stacktrace a current timestamp will be appended to the incident/{hash}.log file. Every exception should have a exception and incident log.

How does Hoth work–this time in a pretty graphic!

Before you implement Hoth you need to know...

Hoth is fast!
Hoth was designed to be very fast. But, if you see a way to make it faster I hope you'll make a pull request!
Hoth fails silently!
Since Hoth would not do you any good if it had errors of it's own that allowed stacktraces to be seen by your visitors Hoth has been designed to fail silently. Unfortunately, this means you won't know if Hoth failed. You should test Hoth by setting a variable to some undefined variable after you have everything working to verify the logs were written and the UI is accessible to you.
Tracking errors takes very little work on your part!
Implementing Hoth to track exceptions should be the same for all CFML applications.
Accessing Hoth Reports WILL take work on your part!
Things become a little (but not a lot) more complex here. Reporting requires access to your server from a HTTP call. This is your responsibility to setup–and there are lots of paths you can take to implement reporting.

If you take the path of using the HothReportUI.cfc you will definitely want to open that file and make some edits (like adding the path to your own HothConfig).

How can you implement Hoth?

  1. Download or Clone Hoth from GitHub.
  2. Put Hoth on your server, or, in the root of your project–I recommend a /lib folder or somewhere inaccessible to Web visitors.
  3. Watch the Hoth repository so I know you care!
  4. Create/identify a directory for Hoth to write logs to.
  5. Create an mapping "/hoth" for the directory you installed Hoth into.
  6. Edit the Configuration Object (details below).
  7. Add Hoth anywhere you want to track exceptions (details below).
  8. Tweet about Hoth so someone else knows!

Editing the Hoth Configuration Object

The Hoth Configuration Object is unique for each application. In fact, it can be unique to each environment if your application code supports multiple environments.

HothConfig.cfc
/**
 * Copyright Aaron Greenlee
 *
 * <h4>Description</h4>
 * An example configuration object for your appication.
 * Customize this configuration object for your needs.
 *
 * Created
 * 2/9/2011 10:22:19 AM
 *
 * @author Aaron Greenlee
 * @version 1
 * @see N/A
 **/

component
	implements	= 'Hoth.object.iHothConfig'
	extends		= 'Hoth.object.CoreConfig'
	accessors	= true
	{

	/** What is the name of your application? */
	property
		name='applicationName'
		default='Amazing ColdFusion Club';

	/** How many seconds should we lock file operations?
		For most operations this is exclusive to a unique exception. */
	property
		name='timeToLock'
		default='1';

	/** Where would you like Hoth to save exception data?
		This folder should be empty. */
	property
		name='logPath'
		default='/your_mapping/hoth/cfmlClub';

	// ------------------------------------------------------------------------------
	/** Would you like new exceptions to be emailed to you? */
	property
		name='EmailNewExceptions'
		default='true';

	/** What address(es) should receive these e-mails? */
	property
		name='EmailNewExceptionsTo'
		default='you@email.com;co-worker@email.com';

	/** What address would you like these emails sent from? */
	property
		name='EmailNewExceptionsFrom'
		default='you@email.com';

	/** Would you like the raw JSON attached to the e-mail? */
	property
		name='EmailNewExceptionsFile'
		default='true';
	// ------------------------------------------------------------------------------

	/**
	The mapping where you would like Hoth to write it's log files.
	Without this setting, Hoth will write log files to the same directory
	Hoth is located within. This is not recomended as your will have content
	mixed into your Hoth code.
	**/
	setGlobalDatabasePath(path='/logs/hoth/');
}

Implementing Hoth within a ColdFusion Application

Once you have completed the steps outlined above you can simply add the two highlighted lines to begin tracking Hoth exceptions. The provided code assumes you have placed your HothConfig.cfc in a /config directory within your application.

Implement Hoth–Simplest Way
<!--- ColdFusion Application.cfc OnError Method --->
<cffunction name="OnError" returnType="void" output="true">
	<cfargument name="Except" required=true />
	<cfargument type="String" name="EventName" required=true/>
	
	<!--- Track this exception with Hoth --->
	<cfset local.Hoth = new Hoth.HothTracker(new config.HothConfig())/>
	<cfset local.Hoth.track(arguments.Except) />
	
	<!--- Anything you need here ... --->
		
	<cfreturn />
</cffunction>
Implement Hoth–Recommended Way
<cffunction name="onRequestStart" returnType="boolean" output="true">
	<cfargument name="targetPage" type="string" required="true" />
	<cfscript>
		// Place Hoth into Application Memory.
		if (!structKeyExists(application, 'HothTracker')
		{
			application.HothTracker =
				new Hoth.HothTracker( new config.HothConfig() );
		}

		// anything you need here...

		return true;
	</cfscript>
</cffunction>

<cffunction name="OnError" returnType="void" output="true">
    <cfargument name="Except" required=true />
    <cfargument type="String" name="EventName" required=true/>
	<cfscript>
		// Create an instance of Hoth if one does not exist in the
		// application scope. Hoth should exist in the Application Scope
		// but, if something went wrong there we are ensured tracking.
		local.HothTracker = (structKeyExists(application, 'HothTracker'))
			? application.HothTracker
			: new Hoth.HothTracker( new config.HothConfig() );

		local.HothTracker.track(Except);
		
		// anything you need here...
		
	</cfscript>
</cffunction>

Implementing Hoth within a ColdFusion/ColdBox Application

In addition to adding Hoth to your Application.cfc you can also add Hoth to your ColdBox Exception Handler. This will help you track exceptions caught by the ColdBox Framework.

Implement Hoth–Within a ColdBox Application
<!--- handlers/Main.cfc --->
<cffunction name="onException" returntype="void" output="false">
	<cfargument name="event" required="true">
	<cfscript>
		local.HothTracker = (structKeyExists(application, 'HothTracker'))
			? application.HothTracker
			: new Hoth.HothTracker( new atv.config.HothConfig() );

		try {
			//Grab Exception From request collection, placed by ColdBox
			local.ExceptionBean = Event.getValue("ExceptionBean");
		} catch (any e) {
			// Record that the exception bean was not found!
			// This should also provide the context of the original error
			// however, the error details will be different. This is important
			// in the event ColdBox has an issue capturing the error. 
			local.HothTracker.track(e);
			
			return;
		}

		local.HothTracker.track(local.ExceptionBean);
	</cfscript>
</cffunction>

Using Hoth with Graceful Exceptions

Sometimes, we have a reason to suspect an error may occur in a specific area of your application. Hoth is great at watching "high risk" areas and reporting when these expected exceptions occur. For example, what if you wanted to know how many times your Twitter API is failing? If you are talking to Twitter you probably already have graceful error handling for this area of your application. Hoth can complement your application and report errors you throw–or–errors you trap.

Trapping and Reporting Errors with Hoth
<cftry>
	<cfinvoke webservice="#webservice#" method="createAccount" returnvariable="local.result">
		<cfinvokeargument name="email" value="#User.getEmail()#">
		<cfinvokeargument name="password" value="#User.getPassword()#">
	</cfinvoke>
	<cfcatch type="any">
		
		<cfset gracefulErrorCapture(
			 arguments.User
			,cfcatch.faultString
		) />
		
		<cfcatch type="any">
			<!--- Record this event with Hoth --->
			<cfif structKeyExists(application, 'HothTracker')>
				<cfset application.HothTracker.track(cfcatch) />
			</cfif>
		</cfcatch>
	</cfcatch>
</cftry>

Accessing Hoth's Data

You can simply view the contents of the logs stored in the exceptions and incidents directory. Or, you can access Hoth through a UI. How you integrate the Hoth UI into your application is really up to you. I'll show you how I have implemented the reporting within my applications. These applications are ColdBox applications; however, the CFML is the same anywhere.

Important!
Currently, the report UI expects SES links in the following format: http://foo.bar.com/Hoth/report/exception/{id}. Hoth was implemented within a ColdBox application; however, this should be easy to setup with any framework. The next update of Hoth will allow you to configure your URIs within the HothConfig.cfc.
A ColdFusion CFC to Access Hoth Reports
// This CFC offers no protection--thus, anyone can access this.
// You can implement this by renaming the CFC with a UUID--that can secure
// this through obscurity. But, I prefer the approach shown in the ColdBox
// example. That is what I use. I have not tried this code :(

// You should replace the "new Hoth.config.HothConfig()" lines with the
// classname path to your HothConfig. Hoth will generate a report for the
// application's HothConfig you provide.

// -----------------------------------------------------------------------
// 					THIS IS AN EXAMPLE AND ONLY AN EXAMPLE
// -----------------------------------------------------------------------
// HOW YOU IMPLEMENT REPORTING IS REALLY YOUR BUSINESS SINCE NO ONE SHOULD
// ACCESS YOUR REPORT--OR KNOW HOW TO ACCESS YOUR REPORT--BUT YOU!!!!!!!!!
//
// This CFC should work right out of the box for the Example HOTH data.
// You will need to change the path to your config for sure.
//
// Feel free to add anything else you want (password/IP check?) to this file
// as it should become part of your code base.

component {

	// You will definitely want to change this path...
	variables.ApplicationsHothConfig = new Hoth.config.HothConfig();

	/** Loads the Web UI (HTML) **/
	remote function index () returnformat='plain' {
		local.HothReport = new Hoth.HothReporter( new Hoth.config.HothConfig() );
		return local.HothReport.getReportView(variables.ApplicationsHothConfig);
	}

	/** Access Hoth report data as JSON.
		@exception 	If not provided a list of exceptions will be returned.
					If provided, the value should be an exception hash which
					modified the behavior to return information for only
					that exception. **/
	remote function report (string exception) returnformat='JSON' {
		local.report = (structKeyExists(arguments, 'exception')
		? arguments.exception
		: 'all');

		local.HothReport = new Hoth.HothReporter(variables.ApplicationsHothConfig);
		return local.HothReport.report(local.report);
	}

	/** Delete a report. **/
	remote function delete (string exception)returnformat='JSON'  {
		if (!structKeyExists(arguments, 'exception'))
		{
			// We can delete all exceptions at once!
			arguments.exception = 'all';
		}

		local.HothReport = new Hoth.HothReporter(variables.ApplicationsHothConfig);

		// Delete!
		return local.HothReporter.delete(arguments.exception);
	}
}
A ColdBox Handler to Access Hoth Reports
component {
	public void function preHandler(required Event)
	{
		var rc = Event.getCollection();
		var prc = Event.getCollection(private=true);

		// Only allow access for debug mode.
		if (!getDebugMode())
		{
			redirectToNewEvent(event='home',eventObject=Event);
		}
		
		// Create a new Hoth Reporter using our "Hoth" mapping
		// and pass a HothConfig object for this application. I am keeping
		// the HothConfig.cfc for my application in my /config folder where
		// I also keep my ColdBox, Routes, Cache and other configs.
		prc.HothReporter = new Hoth.HothReporter( new config.HothConfig() );
	}

	public void function index (required Event) {
		var rc = Event.getCollection();
		var prc = Event.getCollection(private=true);

		if (!Event.valueExists('exception'))
		{
			Event.setView(name='site/hoth',noLayout=true);
			Event.renderData(type='html',data=prc.HothReporter.getReportView());
		} else {
			local.report = (structKeyExists(rc, 'exception')
			? rc.exception
			: 'all');

			Event.renderData(type='json',data=prc.HothReporter.report(local.report));
		}

		return;
	}
	public void function delete (required Event) {
		var rc = Event.getCollection();
		var prc = Event.getCollection(private=true);

		if (!structKeyExists(rc, 'exception'))
		{
			rc.exception = 'all';
		}

		local.result = prc.HothReporter.delete(rc.exception);

		Event.renderData(type='json',data=local.result);
		return;
	}
}

What's next for Hoth?

Hoth is working very well for me and my team. It supports exception tracking for millions of unique visitors; but, things can always get better! The only thing I can think of for immediate improvement is to not return JSON when an exception is deleted via the UI. But, by sharing Hoth with you I hope to gain your feedback. I'm sure it could benefit from pretty charts showing the frequency of errors...

I am having trouble getting Hoth to work...

A Google Group has been setup and is a great place to discuss any trouble you may be having with Hoth.

What do you think?

Take a moment and provide a comment to share your thoughts. Thanks!

Updated February 09, 2011
Added a note to draw attention that Hoth requires a mapping with proper case: i.e. "Hoth"
Updated February 15, 2011
Added a link to the new Google Group to discuss any issues developers are having.
Updated March 1, 2011
Simplified the example of reporting for ColdBox.
Updated March 11, 2011
Added a note to hopefully help you better understand that reporting takes work on your part. How you access Hoth reports is your responsibility. I have only shared examples. In fact, my own implementations are much more complex and secure.
blog comments powered by Disqus
You can send me email or work with
   me for digital marketing, web design
      and application development.

         I own proprietary web application development
         company and work with a leading digital marketing firm.
© 2009 - 2014 Aaron Greenlee. Powered by my own code on the ColdBox Framework.

This site is best viewed on Chrome, FireFox and Safari. Subscribe to my RSS feed.