4

How do you pinpoint the client-side errors that occur in scripts from another domains?

For clarity let's suppose that we have an average size web application that uses several scripts hosted by another domains (like google maps JS SDK).

And one day you started receiving Script error in your error logs, which means there is a error occurred in a 3rd party code.

But how would you find the exact method in your code that invoked a 3rd party code that eventually failed?

PS: the error is not reproducible by developers and just occurs on the client machines quite rarely.

PPS: For the errors above window.onerror DOES NOT provide call stack, proper error message, file name and line number. So it provides literally nothing helpful apart of Script error error message.

PPPS:

The third party script is included using <script src="http://..."></script> tags and executed using someFunctionFromTheThirdPartyScript();

9
  • use elmah code.google.com/p/elmah this can log all exceptions with most possible information Commented Oct 7, 2013 at 1:22
  • @HaBo: uhm, asp.net? o_O Commented Oct 7, 2013 at 1:23
  • if you want it to be frame work independent, you can try this window.onerror call a Ajax method to store the error. Commented Oct 7, 2013 at 1:29
  • 1
    @HaBo: window.onerror doesn't provide sufficient information. That's why I asked the question. Commented Oct 7, 2013 at 1:29
  • Perhaps you can show some code, how that script is included and executed. Commented Oct 8, 2013 at 9:25

1 Answer 1

1

I had a similar problem once and my solution was...

//  The parameters are automatically passed to the window.onerror handler...
function myErrorFunction(message, url, linenumber) {
    $.post(
        "https://host.and/path/to/script/that/stores/entries",
        {
            "url":url, //  URL of the page where the error occured
            "lineNumber":linenumber, //  Line number where the error occer
            "message":message  //error message
        },
        function(){
            //callback function for the $.post()
            if(console)
                if(console.log)
                    console.log("Error reported.");
        }
    );
}
window.onerror = myErrorFunction;  //adds  function "myErrorFunction" to the onError Event

To get more sophisticated you will need to utilize some tricks I have put into my debug project at: https://github.com/luke80/JavaScript-DebugTools-Luke

EDIT: Ok, I'll gather out of that project the important bits that apply to your problem:

/*
  String prototype .hashCode()
  From: http://stackoverflow.com/questions/7616461/generate-a-hash-from-string-in-javascript-jquery
*/
if(typeof String['hashCode'] == "undefined") {
    String.prototype.hashCode = function(){
    var hash = 0, i, char;
    if (this.length == 0) return hash;
    for (i = 0, l = this.length; i < l; i++) {
        char  = this.charCodeAt(i);
        hash  = ((hash<<5)-hash)+char;
        hash |= 0; // Convert to 32bit integer
    }
    return hash;
};
//  Start of vars
var _LOG_CALLERARGS_ON = true,
    getCallerHash = function(funcString) {
        return callerFunc.toString().hashCode();
    },
    getCallerArgs = function(obj) {
        return JSON.stringify(Array.prototype.slice.call(obj),this._detectCircularity(Array.prototype.slice.call(obj))).replace(/^\[/,"(").replace(/\]$/,")");
    },
    detectCircularity = function(obj) {  //  From: http://stackoverflow.com/questions/4816099/chrome-sendrequest-error-typeerror-converting-circular-structure-to-json
        return (function() {
            var i = 0;
            return function(key, value) {
                if(i !== 0 && typeof(obj) === 'object' && typeof(value) == 'object' && obj == value) return '[Circular]'; 
                if(i >= 29) return '[Too deep, not mined]';
                ++i;
                return value;  
            }
        })(detectCircularity);
    },
    caller = this.o.caller || arguments.callee.caller || "top";
//  End of vars
if(typeof caller != "string") {
    if(caller) {
        var callerData = ((caller.name)?caller.name:"Unnamed Caller:"+getCallerHash(caller))+((_LOG_CALLERARGS_ON)?getCallerArgs(caller.arguments):"");
        //  Since this loop could easily become problematic (infinite loop, anyone?) lets impose a limit.
        var maxLoops = 64;
        var loopCounter = 0;
        //  Now we gather all (or 64 of them) the caller names (and optionally their parameters)
        while(caller.caller && loopCounter < maxLoops) {  //  <--- there was an error here that I fixed on Oct 15, 2013 @ 11:55AM
            callerData += " <- "+((caller.caller.name)?caller.caller.name:"Unnamed Caller:"+getCallerHash(caller.caller))+((_LOG_CALLERARGS_ON)?getCallerArgs(caller.caller.arguments):"")
            caller = caller.caller;
            loopCounter++;
        }
        // callerData is now populated with your stack trace.
    } else {
        // Can't get errors from a non-existent caller
    }
}

The callerData variable should be populated with a string of function names (or a hash of the function contents so you can semi-identify them) optionally with the parameters the functions were called with.

While you can't always get the function names, the contents of those functions can be identified (as they should stay the same) and you can still get useful-ish debug information from the parameters that were passed, etc.

NOTE: I didn't actually test that code above, but it should be mostly the same as the code from my repo. If it doesn't work, please refer to the repo and re-factor the code there to your needs. :)

I hope that helps.

Sign up to request clarification or add additional context in comments.

7 Comments

If an error happens in a 3rd party script, then url will be empty, linenumber will be 0 and message will be a Script error. Please read the PPS part of the question. Sorry, but not an answer.
You're right, but "To get more sophisticated you will need to utilize some tricks I have put into my debug project at: github.com/luke80/JavaScript-DebugTools-Luke" -- I was going to put that as my answer, and describe how exactly one would do that. Perhaps I will.
hm, I'm not sure how to use your Log class
Did you look at the sample html file? Also - if you tried my above additional javascript; are there errors? Did it work?
yes, I have looked. It's a logger. And I cannot understand how it will help to debug.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.