views:

2897

answers:

4

I'm trying to create a javascript error logging infrastructure.

I'm trying to set window.onerror to be my error handler. It works in IE 6, but when I run it in Firefox, it runs into some conflicting onerror method.

var debug = true;

MySite.Namespace.ErrorLogger.prototype = {

   //My error handling function.  
   //If it's not in debug mode, I should get an alert telling me the error.
   //If it is, give a different alert, and let the browser handle the error.
   onError: function(msg, url, lineNo) {
        alert('onError: ' + msg);
        if (!debug) {
            alert('not debug mode');
            return true;
        }
        else {
            alert(msg);
            return false;
        }
    }
}

//Document ready handler (jQuery shorthand)
$(function() {
    log = $create(MySite.Namespace.ErrorLogger);

    window.onerror = log.onError;

    $(window).error(function(msg, url, line) {
        log.onError(msg, url, line);
    });
});

If I use setTimeout("eval('a')", 1); where 'a' is an undefined variable, my error handler is what's fired (it works). However, my error-logger needs to catch all errors thrown by clients accessing the website, not just incorrect code in one place.

The code is on a .js page that is being called from the base page (C#) of a website. The site also uses jQuery, so I have a function that overrides the jQuery bind function, and that function works fine in both Firefox 3 and IE 6.

I know that Firefox is seeing the error because it shows up in both the Error Console and Firebug, but my window.onerror function is still not being called.

Any thoughts on how to override what Firefox is doing?

+4  A: 

Remember to return true from any custom window.onerror handler, or firefox will handle it anyway.

kennebec
+7  A: 

The following is tested and working in IE 6 and Firefox 3.0.11:

<html>
<head>
<title>Title</title>
</head>
<body>
    <script type="text/javascript">
    window.onerror = function (msg, url, num) {
        alert(msg + ';' + url + ';' + num);
        return true;
    }
    </script>
    <div>
    ...content...
    </div>
    <script type="text/javascript">
    blah;
    </script>
</body>
</html>

If some other JavaScript library you are loading is also attaching itself to window.onerror you can do this:

<script type="text/javascript">
function addHandler(obj, evnt, handler) {
    if (obj.addEventListener) {
        obj.addEventListener(evnt.replace(/^on/, ''), handler, false);
    // Note: attachEvent fires handlers in the reverse order they
    // were attached. This is the opposite of what addEventListener
    // and manual attachment do.
    //} else if (obj.attachEvent) {
    //    obj.attachEvent(evnt, handler);
    } else {
        if (obj[evnt]) {
            var origHandler = obj[evnt];
            obj[evnt] = function(evt) {
                origHandler(evt);
                handler(evt);
            }
        } else {
            obj[evnt] = function(evt) {
                handler(evt);
            }
        }
    }
}
addHandler(window, 'onerror', function (msg, url, num) {
    alert(msg + ';' + url + ';' + num);
    return true;
});
addHandler(window, 'onerror', function (msg, url, num) {
    alert('and again ' + msg + ';' + url + ';' + num);
    return true;
});
</script>

The above lets you attach as many onerror handlers as you want. If there is already an existing custom onerror handler it will invoke that one, then yours.

Note that addHandler() can be used to bind multiple handlers to any event:

addHandler(window, 'onload', function () { alert('one'); });
addHandler(window, 'onload', function () { alert('two'); });
addHandler(window, 'onload', function () { alert('three'); });

This code is new and somewhat experimental. I'm not 100% sure addEventListener does precisely what the manual attachment does, and as commented, attachEvent fires the handlers in the reverse order they were attached in (so you would see 'three, two, one' in the example above). While not necessarily "wrong" or "incorrect", it is the opposite of what the other code in addHandler does and as a result, could result in inconsistent behaviour from browser to browser, which is why I removed it.

EDIT:

This is a full test case to demonstrate the onerror event:

<html>
<head>
<title>Title</title>
</head>
<body>
<script type="text/javascript">
function addHandler(obj, evnt, handler) {
    if (obj.addEventListener) {
        obj.addEventListener(evnt.replace(/^on/, ''), handler, false);
    } else {
        if (obj[evnt]) {
            var origHandler = obj[evnt];
            obj[evnt] = function(evt) {
                origHandler(evt);
                handler(evt);
            }
        } else {
            obj[evnt] = function(evt) {
                handler(evt);
            }
        }
    }
}
addHandler(window, 'onerror', function (msg, url, num) {
    alert(msg + ';' + url + ';' + num);
    return true;
});
</script>
<div>
...content...
</div>
<script type="text/javascript">
blah;
</script>
</body>
</html>

When the above code is put in test.htm and loaded into Internet Explorer from the local idks, you should see a dialog box that says 'blah' is undefined;undefined;undefined.

When the above code is put in test.htm and loaded into Firefox 3.0.11 (and the latest 3.5 as of this edit - Gecko/20090616) from the local disk, you should see a dialog box that says [object Event];undefined;undefined. If that is not happening then your copy of Firefox is not configured correctly or otherwise broken. All I can suggest is that you remove Firefox, remove your local profile(s) (information about how to find your profile is available here) and reinstall the latest version and test again.

Grant Wagner
The if statement is what's fired on the events that I want handled by my error logger in Firefox. What is the relevance of the addEventListener that's created in that if statement?
norabora
The only thing that happens with this is that any time I open Firefox, whatever I put in the "if" statement fires. It's not a reaction to my error though.
norabora
addHandler() is a generic way to add multiple handler functions to an event. The if (obj.addEventListener) tests to see if the browser supports the addEventListener() method on the object, if it does, it adds a the handler using the built-in browser supported method. If the browser does not support the addEventListener() method, it chains your new handler behind the last added handler.
Grant Wagner
addHandler() by itself does not actually do anything other than wire your handler to the event. To make it do something, you need to write copy addHandler(), then use: addHandler(window, 'onerror', function (msg, url, num) { alert(msg + ';' + url + ';' + num); return true; }); - If you do not get an alert from a JavaScript error after using addHandler(), then you've most likely got an add-on in Firefox that is somehow overriding the default Firefox onerror behaviour.
Grant Wagner
The only add-ons I have are IE Tab and Microsoft .NET Framework Assistant. Is there any other reason that Firefox would be overriding it?
norabora
I've added an EDIT section to my answer with a complete test case you can just copy/paste into an empty file and load into Firefox. If the test case is not behaving as described, you should probably remove Firefox, remove your profile(s) and reinstall the newest version.
Grant Wagner
Deleted my profile, uninstalled Firefox, installed the newest version, and it's still not firing. I don't think this should have any effect, but I'm not using an HTML file, I'm using a .js that's being called by a webservice.
norabora
@norabora: The .js you're using might be assigning its own handler to window.onerror. If that is the case, you might be able to hook into window.onerror using addHandler() AFTER the external JavaScript executes. In other words, the external code might set window.onerror =, then you could use addHandler() after that to tie into the event. If the external code assigns window.onerror as part of a function call you make to it then there will simply never be any opportunity to trap the event.
Grant Wagner
+1 for effort haha
Polaris878
+1  A: 

It appears there is an open bug (#3982) in jQuery 1.3.x - jQuery.error event gets incorrect arguments. The last update was 2 months ago with the comment:

Given how crazy the error event is, and how frequently it's used, I agree that it's easiest to remove support for it. I've added a note to the docs already since it didn't work anyway.

I guess I would just set window.onerror myself, similar to what Grant posted. Also, FireFox: addEventListener Vs. window.onerror mentions that "element.addEventListener", causing an error does not activate "window.onerror".

Kevin Hakanson
A: 

Is Firebug messing with your onerror handler? See http://www.dojotoolkit.org/forum/dojo-core-dojo-0-9/dojo-core-support/onerror-problem-firefox

edsoverflow
I thought that Firebug was the problem, so I uninstalled it. That didn't change anything, so just in case, I uninstalled and reinstalled Firefox. Still had a problem, so I wiped all the Firefox profiles I had, just to make sure. Still did no good.
norabora