Auto reporting JavaScript errors
Published on 29th of May 2008. Copyright Tavs Dokkedahl. Displayed 1447 time(s)Having JavaScript errors automatically logged can be a real benefit for you and your users. You can't test your own JavaScript code in all possible scenarios and errors which you did not think of are bound to happen at some point.
We're used to error handling when coding server side and most application code comes with some sort of error reporting but its not common for client side JavaScript applications to log the unexpected errors to a database.
As it turns out it is really easy to detect and report JavaScript errors.
Detecting a JavaScript error
There are two ways to catch errors. One is using try/catch blocks and the other is assigning an event handler to the window.onerror event. The first approach works in the 4 major browsers whereas the last only works in Internet Explorer and Firefox.
If you've never used try/catch here's a small example - more advanced examples can be found here.
1 // First we declare a function with an intentional error 2 function test() { 3 // Undeclared variable will raise an exception 4 notDeclared 5 }
This code will compile but when you call the function test an error will occur. We say that an error was thrown (or raised). Now things which are thrown can usually be catched and this we do by
1 // We are not sure about this function so we execute it 2 // in a try block 3 try { 4 test(); 5 } 6 // If an error is thrown the catch block is executed 7 catch(e) { 8 alert('We caught an error'); 9 }
Every error which might occur in the try block is caught and program execution is shifted to the catch block. Normally your program would halt if an error occured but by catching it we have a chance of reporting it first - or even better correcting it.
There is a lot more to say about try/catch but this should give you a foundation for understanding the rest of this article.
Catching all errors with error handlers
To catch all errors you simply wrap your entire program in a try block. Every error which occurs will then be caught.
1 // Wrap try block around entire program 2 try { 3 // Your program goes here... 4 } 5 catch(e) { 6 report(e); 7 }
This will ensure that all errors are forwarded to the report function which may compile a error report before seding the error to the server
Using the two methods setErrorHandler and removeErrorHandler of the Function object we can simplify this task
Suppose our program is started with the function init which is executed on load
1 function init() { 2 // Start program ... 3 } 4 // Assign init to onload 5 windown.onload = init;
To execute the entire program in a try/catch block we set an error handler before executing init
1 function init() { 2 // Start program ... 3 } 4 // Assign error handler to init before executing 5 windown.onload = 6 function() { 7 init = init.setErrorHandler(myHandler); 8 init(); 9 };
The setErrorHandler will wrap the init function in a try/catch block and replace the original function - there is absolutely no difference between the two init invocations except that the last one will call myHandler if an error occurs.
To remove the error handler simple do
1 function init() { 2 // Start program ... 3 } 4 // Assign error handler to init before executing 5 windown.onload = 6 function() { 7 init = init.setErrorHandler(myHandler); 8 init(); 9 init = init.removeErrorHandler(); 10 };
When myHandler is executed two arguments are passed - the function which threw the error and the error object. The error handler myHandler could be defined as
1 // Define error handler 2 function myHandler(f,e) { 3 // Output string representation of function which caused the error. 4 alert(f); 5 // Output error message. 6 alert(e); 7 }
If an error occurs the return value from the init call will be whatever myHandler returns.
Handling the error
When an error is thrown a Error object e is created and made available in the catch block. It is a special object with a class of Error or one of its sub types. The error object holds information about the error and being true to the JavaScript tradition all 4 browsers define things their own way.
I therefore made some cross browser functions for the Error object for collecting information from the client.
One such function is the Error.report which will gather information regarding the error, stacktrace, event object and client information. This function returns a string with a lot of information and has 4 verbosity levels for easy customization (demo).
To set up your entire program to use this function
1 function init() { 2 // Start program ... 3 } 4 // Assign error handler to init before executing 5 windown.onload = 6 function() { 7 // Verbosity level defaults to 1 (moderate) 8 init = init.setErrorHandler(Error.report); 9 var returnValue = init(); 10 };
If an error is thrown the error report will be returned to the returnValue variable
Logging the error on the server
We will be using XMLHttpRequest to send the error to the server and the XHR object is used for controlling th requests
First we make a function for sending a message to the server
1 // Function for sending string 2 function logError(s) { 3 // Timeout is 20 seconds, 2 retries 4 var xhr = new XHR(20000,2); 5 // Setup handler for readystate 4 6 xhr.onreadystateloaded = 7 function() { 8 alert('An error occured and has been logged.'); 9 }; 10 // Open for asynchronous request 11 xhr.open('POST','http://www.yourserver.com/js_error.php',true); 12 // Send the message 13 xhr.send(s); 14 }
The function will simply send the error report as the body of a POST request. If successful the user is informed that the error was logged. Your server side script may then read the request body and log it to a database.
Putting it all together
The final function is very simple. We set an error handler before executing the program. If an error is thrown an error report is created and send to the server.
1 // Our error handler 2 function myHandler(f,e) { 3 // Get error report with verbosity level 3 4 var report = Error.report(f,e,3); 5 // Send report to server 6 logError(report); 7 // Return value (or throw new error) 8 return 1; 9 } 10 function init() { 11 // Start program ... 12 } 13 // Assign error handler to init before executing 14 windown.onload = 15 function() { 16 // Verbosity level defaults to 1 (moderate) 17 init = init.setErrorHandler(myHandler); 18 var returnValue = init(); 19 };
Just logging the errors doesn't make them go away so keep an eye on the log from time to time.
Enjoy the code.
Tavs
