52
Don’t Ignore Your Error Messages Mary Jo Sminkey CF Webtools

Don't Ignore Your Errors!

Embed Size (px)

DESCRIPTION

Far too often we set up some kind of user friendly error handler, that sends us a copy of the error dump, but once our site goes live and is getting hit by hackers, and search bots, and having routine timeouts for whatever reason, we get deluged by so many emails that we just throw those errors into a folder and ignore them. But there's a better way! Learn how to set up a robust error handler that can send you lots of debug information while still handling issues like bots and hack attempts, commonly thrown errors you don't care about, secure information being included, and more. We'll also look at integrating our finished error handler with the excellent open source project Bug Log for further enhancing its capabilities and making it easier for individual users on the project to handle their subscriptions to receiving error reports from the site.

Citation preview

Page 1: Don't Ignore Your Errors!

Don’t Ignore!Your Error MessagesMary Jo Sminkey!CF Webtools

Page 2: Don't Ignore Your Errors!

About Me

• 20+ Years in Web Development!

• ColdFusion Developer since CF 3.0 Release by Allaire!

• Author of the e-commerce application CFWebstore!

• Currently working for CFWebtools as the Lead Developer for a large e-commerce website selling classic car parts (Classic Industries)!

• Hobbies include dog training and showing, photography, playing music, baking, board games, scrapbooking, origami, and more!

Page 3: Don't Ignore Your Errors!

There Will Always Be Bugs• "If debugging is the process of removing software bugs, then

programming must be the process of putting them in." !! – Edsger Dijkstra!!

• “Programming today is a race between software engineers striving to build bigger and better idiot-proof programs, and the Universe trying to produce bigger and better idiots. So far, the Universe is winning.”!! – Rich Cook

Page 4: Don't Ignore Your Errors!

Dealing with Bugs is Not FUN !(But Necessary)

Page 5: Don't Ignore Your Errors!

Developers Need To Be Brave

Page 6: Don't Ignore Your Errors!

Classic Industries• Large Ecommerce Site for Classic Car Parts!• 3-4 million page views on average per month!• Approx. 400K unique visitors!• Averages 1-5 ColdFusion error reports per day

Page 7: Don't Ignore Your Errors!

Why Are Bugs So Hard to Find?• "We have a bug that occurs on the 31st of a month so once a month

we get a bug report. It gets assigned to a developer within 24 hours who then fiddles for a bit before marking it 'unable to reproduce'." !

• Multiple OS/browser environments to test !

• Increasing use of javascript, Ajax, other client-side code!

• More complicated design patterns, use of frameworks, etc. versus procedural code: !

• “There are two ways of constructing a software design. One way is to make it so simple that there are obviously no deficiencies. And the other way is to make it so complicated that there are no obvious deficiencies."

Page 8: Don't Ignore Your Errors!

Some Common Approaches to Error Handling

• CFTry/Catch !• Global Error Handler (onError or cferror)!!

• Display User-Friendly Message to User!• Email Error Dump to Developer (maybe)

Page 9: Don't Ignore Your Errors!

Common Problems with the Normal Approaches

• Hiding errors that we may need to know about (try/catch blocks)!

• Not enough information to figure out what happened to throw the error!

• Receiving errors from hack attempts, causing us to waste time that should be spent tracking errors from legitimate users!

• Lots of copies of the same error, wasting time to determine which are new errors and which are ones we’ve already reviewed!

• Errors that we know about and expect to receive regularly

Page 10: Don't Ignore Your Errors!

Global Error Handler - onError or CFError?• onError will not include ALL variable scopes!• cferror will include all scopes (other than local vars, but we can deal

with that too)!• This does mean you will typically need to need to keep your

Application.cfc tag based to some degree (pre-CF11). !• Easiest is to put the cferror line in your OnRequestStart!• We will often wrap the cferror in a conditional test for which

environments we want to show the CF errors versus using the error handler. !

• We also want to disable the error handler for MXUnit tests

Page 11: Don't Ignore Your Errors!

Same CFError Include<cffunction name="OnRequestStart" returntype="boolean"> !<cfif NOT ListFind( "local", application.environment) AND NOT !! ! findNoCase("mxunit",cgi.script_name)>!! <cferror type="exception" template="errorhandler.cfm">!</cfif>!….!</cffunction>

Page 12: Don't Ignore Your Errors!

Improving The Global Error Handler!

• Remove any variable data internal to the error handler!• Filter out errors from search bots!• Allow use with try/catch blocks when we need to get data from the error !• Skip common errors!• Include all relevant variables scopes!• Remove any secure data that is not safe to send via email!• Clean the variable scopes so we don’t get data we don’t need!• Process any data that might be encrypted or in some other format that

what is most useful for the developer to get!• Don’t send lots of copies of the same error

Page 13: Don't Ignore Your Errors!

ErrorHandler.cfm - Remove Vars used internally!

• While this isn’t totally necessary, I don’t really care to see the variables I create within the error handler itself in my error emails. !

• So rather than using variables scope directly, I create a struct that is then used for all the local variables in the error handler, similar to how we used to typically do local variables in CFCs. !

!! <cfset localVars = structNew() />! !! <cfset localVars.findBot = false />!! <cfset localVars.findExclude = false />

Page 14: Don't Ignore Your Errors!

ErrorHandler.cfm - Skip Errors from Bots!

• You may prefer to receive these errors depending on your level of error handling for malformed URLs and invalid params. !

• There are other more sophisticated ways to detect browser types such as 3rd party plugins like Browser Hawk, but this is a simple way to exclude most of the common ones. !

!!

Page 15: Don't Ignore Your Errors!

ErrorHandler.cfm - Skip Errors from Bots!

<cfset localVars.botList = "bot,spider,crawl,jeeves,yahoo,slurp" />!!<cfloop index="localVars.item" list="#localVars.botList#">!! <cfif FindNoCase(localVars.item, CGI.HTTP_USER_AGENT)>!! ! <cfset localVars.findBot = true />!! </cfif>!</cfloop>!!<cfif NOT localVars.findBot>!! Continue with error handler… !</cfif>

Page 16: Don't Ignore Your Errors!

ErrorHandler.cfm - Use for both cferror and cfcatch!

• By coding the error handler to be available for use in a try/catch block we can drop it into places in the code where we are having trouble debugging what is happening, to get more information on the error. !

• This is particularly useful in CFC methods, where the global error handler won’t have any data about the local vars. !

• The error structure we receive for global errors versus inside a cfcatch is different. !

• We need to detect these and set up variables for the areas in the code where we are going to be outputting the error data. !

• When used in a cfcatch block, we may want to skip parts of the error handler, such as displaying a user-friendly error message. !

!

Page 17: Don't Ignore Your Errors!

ErrorHandler.cfm - Use for both cferror and cfcatch!

<cfif isDefined("error.Diagnostics")>!! <cfset localVars.errormess = error.Diagnostics>!! <cfset localVars.errorData = error>!! <cfparam name="request.errorType" default="fatal" >!<cfelseif isDefined("cfcatch.message")>!! <cfset localVars.errormess = cfcatch.message>!! <cfset localVars.errorData = cfcatch>!! <cfparam name="request.errorType" default="error" >!</cfif>!

!

Page 18: Don't Ignore Your Errors!

ErrorHandler.cfm - Skip Common Errors!

• For some sites, you may have errors that you wish to detect due to them occurring frequently and not being fixable. !

• How you handle them may vary based on your environment and other detection methods for your sites (up-time detection, etc.) !

• For instance, for one client we frequently have database issues where lots of timeout errors get thrown regularly. This is a known issue the client has decided not to address at the time so while we log the number of times and pages they occur on, we don’t need to receive emails for them. !

• We also ran into a bug where a malformed returnFormat in an Ajax call to a CFC throws an error that can not be handled via other methods. !

!

Page 19: Don't Ignore Your Errors!

ErrorHandler.cfm - Skip Common Errors!!!

<cfset localVars.errorExcludeList = "Execution timeout expired,request has exceeded the allowable time limit,Invalid returnFormat: jsPr”/>!!<cfloop index="localVars.item" list="#localVars.errorExcludeList#">!! <cfif isDefined("error") AND FindNoCase(localVars.item, error.diagnostics)>!! ! <cfset localVars.findExclude = true />!! <cfelseif isDefined("cfcatch") AND FindNoCase(localVars.item, cfcatch.message)>!! ! <cfset localVars.findExclude = true />!! </cfif>!</cfloop>

Page 20: Don't Ignore Your Errors!

ErrorHandler.cfm - What to Include!

• What scopes you include will depend on your site and what types of activities ColdFusion is doing. !

• We will also need to set up a list of “secure” variables that we are going to want to exclude!

• If you want to scrub the data for sensitive data, typically you will want to also create a list of scopes to scrub.!

!

Page 21: Don't Ignore Your Errors!

ErrorHandler.cfm - What to Include!

localVars.varstodump=“CFCATCH,ERROR,APPLICATION,ARGUMENTS, ATTRIBUTES,CALLER,CGI,CLIENT,CFHTTP,FILE,FORM,REQUEST, SESSION, COOKIE,THIS,THISTAG,URL,VARIABLES";!

!localVars.varstoscrub=“ATTRIBUTES,ARGUMENTS,CGI,FORM,REQUEST, SESSION,URL,VARIABLES,COOKIE,CFCATCH,ERROR";!

!localVars.securevars=“CFID,CFTOKEN,JSESSIONID,SessionID,URLToken, password,newpassword,customerID,ccName,ccNumber,cvv2,encryptkey";!

!

Page 22: Don't Ignore Your Errors!

ErrorHandler.cfm - Creating the Error Dumps!

• Now that we have our list of variables we want to output in our error logs and/or emails, we’ll loop over the list and dump each scope. !

• We will do some special handling for some scopes. This will include those that we list as potentially having secure information and scopes like Application that may have data like a bean factory that we don’t want to output in error messages. !

• Your application may have specific error scopes that have their own special handling. For instance, we set encrypted cookies for things like the customer ID on the site. For the error handler, I decrypt that string so that I have the actual customer ID to look up info in the database. Another example might be if I want to use the shopping cart ID to include a dump of the actual shopping cart contents in the emails. !

!

Page 23: Don't Ignore Your Errors!

ErrorHandler.cfm - Creating the Error Dumps<cfsavecontent variable="localVars.dataDump">!! !! <cfloop list="#localVars.varstodump#" index=“localVars.loopItem">!! ! !! ! Code to parse and dump out the variable scopes here!!!! </cfloop>!!</cfsavecontent>!!

!

Page 24: Don't Ignore Your Errors!

ErrorHandler.cfm - Checking for CFC methods!

• One thing I always want to exclude from error dumps are user-defined functions and components.!

• If you use a bean factory like Coldspring or Wirebox, it’s usually just an easy matter of excluding it in the appropriate scope dump. !

• If however, you have a collection of components and/or functions in something like Application scope you may need to manually exclude it.!

• For components, you can use IsCFC() on CFLib.org written by Nathan Dintenfass.!

• For user-defined (or built-in) functions, use IsFunction() also on CFLib.org, written by Ray Camden. !

• You can also use the showUDFs attribute on the cfdump tag itself. !

!

Page 25: Don't Ignore Your Errors!

ErrorHandler.cfm - Sample Dump for Application Scope!

<cfif IsDefined("#localVars.loopItem#") AND localVars.loopItem IS “APPLICATION">! <cfset localVars.ApplicationVars = StructNew()>! <cfset localVars.listExclude = "blogCache,errorLog" />! <cfloop item="localVars.each" collection=“#Application#">! <cfif NOT IsCFC( Application[localVars.each] ) AND NOT !! ! ListFindNoCase(localVars.listExclude, localVars.each)>! <cfset localVars.ApplicationVars[localVars.each] = Application[localVars.each]>! </cfif>! </cfloop>!! <cfset request.cfdumpinited=“false”>! <cfdump var="#localVars.ApplicationVars#" label=“Application" showUDFs=“false”>

Page 26: Don't Ignore Your Errors!

ErrorHandler.cfm - Sample Dump to Scrub Scopes<cfelseif IsDefined("#localVars.loopItem#") AND ListFind(localVars.varstoscrub,localVars.loopItem)>! <cfset localVars.ScrubbedVars = structNew()>! <cfset localVars.scopeToCheck = Evaluate(localVars.loopItem) />! <cfloop item="localVars.each" collection="#localVars.scopeToCheck#">! <cfif ListFindNoCase(localVars.securevars,localVars.each)>! <cfset localVars.ScrubbedVars[localVars.each] = "removed">! ! ! ! ! <cfelse>! <cfset localVars.listremove = "GeneratedContent,CFCatch,CFError,Error,localVars,IsCFC">! <cfif NOT ListFindNoCase(localVars.listremove,localVars.each) !! AND NOT IsCFC(localVars.scopeToCheck[localVars.each]) !! AND NOT structKeyExists(localVars.ScrubbedVars, localVars.each)>! <cfset localVars.ScrubbedVars[localVars.each]= localVars.scopeToCheck[localVars.each]>! </cfif>! </cfif>! </cfloop>!<cfset request.cfdumpinited=“false”>!<cfdump var="#localVars.ScrubbedVars#" label="Scrubbed #localVars.loopitem#” showUDFs=“false”>

Page 27: Don't Ignore Your Errors!

ErrorHandler.cfm - Dump Remaining Scopes<cfsavecontent>!

<cfloop … >!

<cfif … >!

Scrubbed var scopes!

<cfelseif IsDefined("#localVars.loopItem#")>!

<cfset request.cfdumpinited=“false”> !

<cfdump var="#localVars.ScrubbedVars#" label=“#localVars.loopitem#” showUDFs=“false”>!

</cfif>!

</cfloop>!

</cfsavecontent>!

Page 28: Don't Ignore Your Errors!

ErrorHandler.cfm - Error Summary<cfsavecontent variable="localVars.basicinfo">!

<cfoutput>!

<table width="100%" cellpadding="5" cellspacing="0" border="0">!

<cfloop collection="#localVars.errorData#" item="localVars.i">!

<cfset localVars.data = localVars.errorData[localVars.i]>!

<cfif IsSimpleValue(localVars.data) AND localVars.i IS NOT "GeneratedContent">!

<tr><td>!

<strong>#Ucase(localVars.i)#:</strong><br/>#localVars.data#!

</td></tr>!

</cfif>!

</cfloop>!

</table>!

</cfoutput>!

</cfsavecontent>

Page 29: Don't Ignore Your Errors!

ErrorHandler.cfm - Swap Settings for Error Type<cfswitch expression="#request.errorType#">!

<cfcase value="init">!

<cfset subject = "Error starting up #cgi.server_name#">!

<cfset errorMessage = "#cgi.server_name# was not able to start up.”>!

<cfset toAddr =“[email protected]" />!

</cfcase>!

<cfdefaultcase>!

<cfset subject = "Error on #cgi.server_name#">!

<cfset errorMessage = "An error has been encountered on #cgi.server_name#:”>!

<cfset toAddr = "[email protected]">!

</cfdefaultcase>!

</cfswitch>!

Page 30: Don't Ignore Your Errors!

ErrorHandler.cfm - Eliminating Duplicate Errors• We don’t need to get many copies of the same error via email!

• This makes it a lot harder to not only get an idea of how many different errors we have to review, but takes time opening and loading each email to see if it is unique. !

• There’s different ways you can handle this but the easiest is to just log each error in the application scope and then check it before adding new errors. !

• Typically I log each error only once every 4 hours, but you can use longer time frames based on tracking multiple errors as follows. !

• By keeping a count of the errors we can check if multiple copies of the error are being thrown. This can alert us to a critical situation on the server that may need to be addressed immediately. !

• I also typically use this with some of the errors that are common and I don’t need to get emails for single instances. !

• So we also want to track the number of times each error has been thrown in order to generate an email every X number of times (I use 25).

Page 31: Don't Ignore Your Errors!

ErrorHandler.cfm - Eliminating Duplicate Errors<cfif NOT StructKeyExists(Application.ErrorLog, localVars.Errormess) !! OR DateCompare( Application.ErrorLog[ localVars.Errormess ].TimeError, !! DateAdd("h", -4, Now() ) ) LT 0 >!! Generate Error Dumps Email here….! <cfif request.errorType IS NOT "init">! ! ! ! ! <cfset Application.ErrorLog[localVars.Errormess] = TimeError: Now(), Count: 1 } />! </cfif>!<cfelse>! <cfset Application.ErrorLog[localVars.Errormess].Count++ />! <cfif Application.ErrorLog[localVars.Errormess].Count MOD 25 EQ 0>! Generate Multiple Errors Email here….! </cfif>!

</cfif>

Page 32: Don't Ignore Your Errors!

ErrorHandler.cfm - Sample Email<cfif NOT localVars.findExclude>! <cfmail to="#toAddr#" from=“[email protected]“! subject="#subject#" type="HTML">! <br/><br/>! #errorMessage#<br/><br/>! #localVars.basicinfo#<br/><br/>! <strong>Full message:</strong><br/><br/>! #localVars.dataDump#<br/><br/>! </cfmail>!</cfif>

Page 33: Don't Ignore Your Errors!

ErrorHandler.cfm - Sample Multiple Errors Email<cfmail to="#toAddr#" from=“[email protected]“!

subject=“MULTIPLE ERRORS ON #subject#" type="HTML">!

<br/><br/>!

The following error has occurred #Application.ErrorLog[localVars.errormess].Count# times in the last four hours on #cgi.server_name#:!

#errorMessage#<br/><br/>!

#localVars.basicinfo#<br/><br/>!

<strong>Full message:</strong><br/><br/>!

#localVars.dataDump#<br/><br/>!

</cfmail>

Page 34: Don't Ignore Your Errors!

ErrorHandler.cfm - Show User Friendly Error Page

• Based on the error type, we may or may not need to display a message to the user (for instance, error in a try/catch block we most likely will not)!

• We will first try to display a page using the full site template. !

• If that page itself errors out though, we should have a very simple HTML only error page available as a fallback. !

• We also include analytics on the page itself to keep counts of different types of errors encountered.

Page 35: Don't Ignore Your Errors!

ErrorHandler.cfm - Show User Friendly Error Page<cfif NOT ListFind("email,cfcatch", request.errorType)>! <cftry>! ! <cfset action = "cferror">! ! <cfinclude template="/controller/linkcontroller.cfm" />! <cfabort>!! <cfcatch type="any" >! <cfinclude template="displayerror.cfm">! <cfabort>! </cfcatch>! </cftry>!</cfif>

Page 36: Don't Ignore Your Errors!

ErrorHandler.cfm - Using in Try/Catch Blocks• The error handler will be used automatically for any unhanded CF

errors. !

• However, one issue is that errors that occur inside CFCs will not include the local scoped variables. !

• Therefore, you may want to use the error handler inside a try/catch block particularly if you are trying to debug a specific error and need a copy of the local variables. !

• In order to see what the local vars are set to at the time of the error, you will need to copy them to another scope prior to calling the error handler. !

• We also use the error handler to notify us if there is some issue with application initialization.

Page 37: Don't Ignore Your Errors!

ErrorHandler.cfm - Using in Try/Catch Blocksfunction name…. {!

try {!

code here that we need to debug….!

} catch (any err) {!

! //copy our local scope to request!

! request.functionLocal = Duplicate(local);!

! include “/views/errors/errorhandler.cfm”;!

}!

}!

Page 38: Don't Ignore Your Errors!

Tracking User Actions - Page Tracker• One thing that the error handler does not tell us is what the user might have

done to cause the error. !

• We only know what happened at best for that request, but there are times when we may want to know the other actions the user performed prior to the error. !

• This can be particularly useful for errors thrown by Ajax requests, where we at least want to know the page there user was on and what they did to generate the Ajax call.!

• Since our application already makes use of sessions, it makes sense to log the page requests into the session so that they automatically show up in the error handler. !

• We also include this session object into support emails since users typically do not include detailed information about what they were doing when they ran into a problem.

Page 39: Don't Ignore Your Errors!

Tracking User Actions - Page Trackerfunction onSessionStart() {!

! //initialize the page tracker!

! session.pageTracker = arrayNew(1);!

}

Page 40: Don't Ignore Your Errors!

Tracking User Actions - Page Trackerfunction onRequestStart() {! request.thisPage = cgi.script_name & '?' & cgi.query_string;! if ( !arrayLen(session.pageTracker) IS 0 OR !! ! session.pageTracker[1] IS NOT request.thisPage ) {! ! lock scope="session" type="exclusive" timeout="10" {!! ! ! ! if ( arrayLen(session.pageTracker) LT 20 ) {! arrayPrepend(session.pageTracker,request.thisPage);! } else {! arrayDeleteAt(session.pageTracker,arrayLen(session.pageTracker));! arrayPrepend(session.pageTracker,request.thisPage);! }! }! }!}

Page 41: Don't Ignore Your Errors!

Tracking User Actions - User Info• If you use sessions, you might find it useful to load data into the session that

can help you debug.!

• For instance, we typically put the user email, customer ID, and name into the session when user logs in, even though this information is not otherwise used from the session scope. !

• This not only helps in cases where we need to review related customer data that might have caused the problem but that way we can also contact the customer once we have corrected the customer. Nothing impresses your users more than actually getting an email from your site telling them the problem they ran into when they were trying to shop has been fixed! !

• We also include this session information into support emails that get sent from the site, again since users have a tendency to do things like mistype their email if we ask them to enter it.

Page 42: Don't Ignore Your Errors!

BugLogHQ• BugLogHQ is a free, open source CFML tool to centralize the handling of

bug reports for multiple applications. !• It allows you to view, search, graph, report, and subscribe to bugs from

your applications. !• However, BugLogHQ does not include all the variable scopes that my

global error can send out. !• It does not typically include any scrubbing of the error data for possible

secure items.!• Also, if you are sending every single error to it, you can overwhelm the

database with error entries, particularly if you are working with an application that generates large numbers of errors, or cause an repetitive bug in development.

Page 43: Don't Ignore Your Errors!

BugLogHQ - Features• BugLogHQ has a powerful Rules feature to set up email notifications of

errors, which include different rule types such as only the FirstInstance of an error, specific error messages, or daily summaries of errors. !

• We find this particularly useful for clients where we often use different developers on tasks, so that they can turn error reporting on and off as needed. !

• You can also set BugLogHQ to purge its errors on a regular basis. !

• BugLogHQ can also run “heartbeat” monitors to check that your website is running properly.

Page 44: Don't Ignore Your Errors!

BugLogHQ - Installation• Software and documentation found at http://www.bugloghq.com. !

• Runs on CF 8+ or Railo and mySQL or SQL Server. !

• Errors can be sent to BugLog using web service, HTTP post, or CFC call. !

• Includes clients for ColdFusion, Python, PHP and Javascript!

!Application.cfc - onApplicationStart() :!

application.bugLogService = !

! createObject(“component", “buglog.client.bugLogService”).init(!

! bugLogListener = ‘http://buglog:8080/listeners/bugLogListenerREST.cfm’);!

! ! ! ! !

Page 45: Don't Ignore Your Errors!

BugLogHQ - Usage in Error Handler• To send all our variable scopes to BugLog, we need to have a single object

to include in the web service call. !

• Since BugLog already includes the error or cfcatch data, we don’t need to include those.!

• We can also include an error type, which can be used as a filter in the BugLogHQ reports.!

• I like to log even the errors that I typically don’t email on the first instance, so that I still have some idea how often those are occurring.

Page 46: Don't Ignore Your Errors!

BugLogHQ - Copying Variable ScopeslocalVars.strScopes = structNew();!

….!

<cfdump var="#localVars.ScrubbedVars#" label="Scrubbed #localVars.loopitem#">!

<cfif NOT ListFind(“ERROR, CFCATCH”, localVars.loopitem)>!

<cfset localVars.strScopes[localVars.loopitem] = localVars.ScrubbedVars />!

<cfelse>!

<cfset localVars.errorData = localVars.ScrubbedVars>!

</cfif>!

…!

<cfdump var="#Evaluate(localVars.loopItem)#" label="#localVars.loopItem#">!

<cfset localVars.strScopes[localVars.loopitem] = Evaluate(localVars.loopItem) />!

Page 47: Don't Ignore Your Errors!

BugLogHQ - Logging the Error<cfif NOT localVars.findExclude>!

! … mail the error as usual!

</cfif>!

<!—- we check to make sure the bug log service is available (local dev) —->!

<cfif structKeyExists(application,"bugLogService")>!

<cfset application.bugLogService.notifyService(!

! ! ! localVars.errormess, !

! ! ! localVars.errorData, !

! ! ! localVars.strScopes, !

! ! ! request.errorType)>!

</cfif>

Page 48: Don't Ignore Your Errors!

Debugging Javascript Errors• Today’s applications typically have considerable javascript running on

them!

• jQuery, Bootstrap, AngularJS, and other libraries make sites more feature rich and user friendly, but harder to debug remote client issues. !

• How then can we figure out what the cause of a user’s issue is when we can’t see the Javascript error thrown by their browser? !

• BugLogHS does include a JS client but you will need to include it into your JS code. !

• There are a variety of excellent 3rd party services you can use for logging and tracking JS bugs.

Page 49: Don't Ignore Your Errors!

Javascript Error Reporting Tools• trackjs.com!

• qbaka.com!

• errorception.com!

• jslogger.com!

• muscula.com!

• exceptionhub.com!

• jserrlog.appspot.com (OSS)!

• github.com/occ/TraceKit

Page 50: Don't Ignore Your Errors!

Mobile Error Monitoring ToolsThese can be used to monitor and track mobile device errors. !

• bugsense.com!

• bugsnag.com

Page 51: Don't Ignore Your Errors!

BrowserHawk• BrowserHawk is an Enterprise-level tool for testing client environments

for requirements for your websites or website features.!

• It includes both a web service, and a JAR you can install on the server, to use in ColdFusion pages to test and/or log information about the client environment. !

• They also have a Javascript logging tool which works via webservice. !

• We have used BrowserHawk not just for testing compatibility for our site, but to check for search bots prior to generating errors, as well as including data about the client in support emails.

Page 52: Don't Ignore Your Errors!

Questions?