Quality Reads

Sunday, August 26, 2007

Client-Side Error Logging

If you've done any amount of server-side scripting (CF, PHP, RoR, .NET, etc.), you already know the benefits of error handling. It allows you to capture unusual, sometimes unforeseen, errors that can occur in your script and handle them gracefully so your users get proper feedback on what just happened. No matter how great a programmer your are, something out of your control will break your code. Its your responsibility to build in a safety net so your users don't break their neck when they step off the correct path.

Since most developers would generally agree with my sentiment, why don't you see AJAX applications properly handling and logging client-side errors. The application that sticks out for me is Gmail. At least once a week I'll see a host of non-fatal errors pop up. Why? Can't errors that bubble up to the application scope be captured? Yes.

Be better than Google. Here's how:


var ErrorLogger = Class.create();
ErrorLogger.prototype = {
initialize: function(url, opts){
this.url = url;
this.active = true;
this.opts = opts;
window.onerror = this.onError.bind(this);
},
onError: function(msg, URI, line){
try{
if(this.active){
var body = 'URI=' + escape(URI) + '&line=' + line + '&msg=' + escape(msg) + '&brw=' + escape(Object.toJSON(Prototype.Browser)) + '&pv=' + escape(Prototype.Version);
if('onError' in this.opts)this.opts.onError.apply(this, arguments);
var opts = {
onSuccess : this.onSuccess.bind(this),
onFailure : this.onComplete,
postBody : body
};
new Ajax.Request(this.url, opts);
}
}catch(e){
this.onFailure();
}
return true;
},
onSuccess: function(){
if('onSuccess' in this.opts)this.opts.onSuccess.apply(this, arguments);
},
onFailure: function(){
this.active = false;
if('onFailure' in this.opts)this.opts.onFailure.apply(this, arguments);
}
};


What's it doing? Well, first off this is a Prototype Class so you need to include prototype.js in order to use it. Here's the breakdown:
  1. The window.onerror event is set to call ErrorLogger.onError. When an unhandled error bubbles up to the window scope, onError will be called to handle it.
  2. Three arguments are passed into the onError function ( msg, URI, line ) and an Ajax request with some additional browser information is constructed and posted to the URL specified in the constructor.
  3. Lastly, the onError function returns true so the browser knows to disregard the error.
Can you just show the syntax for use? No prob.



<html>
<head>
<title>Logger Test</title>
<script type="text/javascript" src="prototype.js"></script>
<script type="text/javascript" src="ErrorLogger.js"></script>
<script type="text/javascript">
var er;
var init = function(){
er = new ErrorLogger('TestResult.html',
{
onSuccess : function()
{
$('btn').setStyle({'backgroundColor' : 'red'});
},
onError : function()
{
alert('An error occurred on the page. Run for your life!');
}
});
};
</script>
</head>
<body onload="init()">
<button id="btn" onclick="nonExistentFunction()">Throw Error</button>
</body>
</html>

When you click on the button labeled "Throw Error", the ErrorLogger class will gracefully handle the error and post some useful debug information to your server. From there you can do whatever you want. Personally, I just toss it in a log file that I monitor. All that in less than 1KB, not bad.

I've tested it on IE6/7 and FF2. Its definitely not production ready quite yet but I'll throw an update up with my final version in a day or two.

Cheers,
Todd

No comments: