ColdFusion Quickie - Scan and Report on Exception Logs

This post is more than 2 years old.

A client sent me a set of exception logs and asked me to make fixes where I could. I have a limited set of hours so I needed to focus on the errors that occurred most often. I wrote up a quick ColdFusion script that would parse the exception logs and keep count of unique errors. Here is the code I came up with along with some explanation as to how it works.

First, I specified a list of files. This could be a cfdirectory call too I suppose:

<!--- list of logs to parse ---> <cfset logs = "/Users/ray/Desktop/fwfeedsubmission/examples-sjc1-3_exception.log,/Users/ray/Desktop/fwfeedsubmission/examples-sjc1-4_exception.log">

I then created a structure to store unique errors:

<cfset errors = {}>

Next, I looped over each file and each line in the file:

<cfloop index="logfile" list="#logs#"> <cfloop index="line" file="#logFile#">

Exception logs have 'blocks' of errors where one line looks like a standard CFML log and is then followed by more details and a stack track. So for example:

"Error","jrpp-541","06/21/09","20:24:31",,"Context validation error for the cfmail tag.The start tag must have a matching end tag. An explicit end tag can be provided by adding. If the body of the tag is empty, you can use the shortcut. The specific sequence of files included or processed is: /Library/WebServer/Documents/test3.cfm, line: 3 "
coldfusion.compiler.UnmatchedStartTagException: Context validation error for the cfmail tag.
at coldfusion.compiler.cfml40.start(cfml40.java:2769)
at coldfusion.compiler.NeoTranslator.parsePage(NeoTranslator.java:503)

So my code simply says - look for "Error", in front, and if so, get the item:

<!--- only use if line begins with "Error", ---> <cfif find("""Error"",", line)> <!--- convert to array, keeping nulls ---> <cfset arr = listToArray(line, "," , true)> <!--- remove 1-5 ---> <cfloop index="x" from="1" to="5"> <cfset arrayDeleteAt(arr, 1)> </cfloop>
	  &lt;cfset errorLog = arrayToList(arr, " ")&gt;
	  &lt;cfif not structKeyExists(errors, errorLog)&gt;
	      &lt;cfset errors[errorLog] = 0&gt;
	  &lt;/cfif&gt;
	  &lt;cfset errors[errorLog]++&gt;
	&lt;/cfif&gt;

What's with the funky listToArray/arrayToList? Well some of the error detail messages includes commas, but the first 5 items never do. So I convert the line to an array and tell it to include empty items. I then delete the first 5. I'm not left with N items, where N is dependent on how many commas were in the message. I convert it back to a list with a space delimiter and I'm good to go.

Next I wrap the loops:

</cfloop> </cfloop>

Reporting is as simple as doing a structSort and displaying an ugly table:

<cfset sorted = structSort(errors,"numeric","desc")> <table border="1"> <tr> <th>Error</th> <th>Count</th> </tr> <cfloop index="k" array="#sorted#"> <cfoutput> <tr> <td>#k#</td> <td>#numberFormat(errors[k])#</td> </tr> </cfoutput> </cfloop> </table>

Here is some sample output from my local exception.log:

Enjoy. The complete script may be found here:

<!--- list of logs to parse ---> <cfset logs = "/Users/ray/Desktop/fwfeedsubmission/examples-sjc1-3_exception.log,/Users/ray/Desktop/fwfeedsubmission/examples-sjc1-4_exception.log,/Users/ray/Desktop/fwfeedsubmission/examples-sjc1-5_exception.log,/Users/ray/Desktop/fwfeedsubmission/examples-sjc1-6_exception.log">

<cfset errors = {}> <cfloop index="logfile" list="#logs#"> <cfloop index="line" file="#logFile#"> <!--- only use if line begins with "Error", ---> <cfif find("""Error"",", line)> <!--- convert to array, keeping nulls ---> <cfset arr = listToArray(line, "," , true)> <!--- remove 1-5 ---> <cfloop index="x" from="1" to="5"> <cfset arrayDeleteAt(arr, 1)> </cfloop>

	  &lt;cfset errorLog = arrayToList(arr, " ")&gt;
	  &lt;cfif not structKeyExists(errors, errorLog)&gt;
	      &lt;cfset errors[errorLog] = 0&gt;
	  &lt;/cfif&gt;
	  &lt;cfset errors[errorLog]++&gt;
	&lt;/cfif&gt;
&lt;/cfloop&gt;

</cfloop>

<cfset sorted = structSort(errors,"numeric","desc")> <table border="1"> <tr> <th>Error</th> <th>Count</th> </tr> <cfloop index="k" array="#sorted#"> <cfoutput> <tr> <td>#k#</td> <td>#numberFormat(errors[k])#</td> </tr> </cfoutput> </cfloop> </table>

Raymond Camden's Picture

About Raymond Camden

Raymond is a senior developer evangelist for Adobe. He focuses on document services, JavaScript, and enterprise cat demos. If you like this article, please consider visiting my Amazon Wishlist or donating via PayPal to show your support. You can even buy me a coffee!

Lafayette, LA https://www.raymondcamden.com

Archived Comments

Comment 1 by duncan posted on 8/23/2009 at 1:23 PM

Unfortunately your screenshot, at least when I'm viewing it in Firefox on 1024px wide resolution, doesn't fit your page width, cutting off the Count column.

Comment 2 by Raymond Camden posted on 8/23/2009 at 11:13 PM

Um... so... ok. Guess I'm not getting the issue here. If you really want to see the results you can run the code. :) If you want to see the picture real bad, right click on it, view it by itself, and zoom out. Or download to your desktop even. It's just a count column showing the #s so it isn't that big of a deal. The real meat is the code.

Comment 3 by Srinivas posted on 4/16/2011 at 11:17 AM

Thanks Ray.
Very useful topic in fixing issues.

Comment 4 by prashant roy posted on 2/22/2013 at 8:22 AM

thank you. its very hand code and very useful.