记录客户端错误和“脚本错误”

时间:2013-10-07 01:17:01

标签: javascript

如何查明来自其他域的脚本中发生的客户端错误?

为清楚起见,我们假设我们有一个平均大小的Web应用程序,它使用由其他域托管的多个脚本(如google maps JS SDK)。

有一天,您开始在错误日志中收到Script error,这意味着第三方代码中发生了错误。

但是,您如何在代码中找到调用最终失败的第三方代码的确切方法?

PS:开发人员无法重现该错误,而且很少发生在客户端计算机上。

PPS:对于上述错误window.onerror 不提供提供调用堆栈,正确的错误消息,文件名和行号。因此,除了Script error错误消息之外,它几乎没有任何帮助。

PPPS:

使用<script src="http://..."></script>标记包含第三方脚本,并使用someFunctionFromTheThirdPartyScript();

执行

1 个答案:

答案 0 :(得分:1)

我曾遇到类似的问题而我的解决方案是...

//  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

为了更复杂,你需要利用我在调试项目中添加的一些技巧: https://github.com/luke80/JavaScript-DebugTools-Luke

编辑: 好的,我将从该项目中收集适用于您的问题的重要部分:

/*
  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
    }
}

callerData变量应填充一串函数名称(或函数内容的散列,以便您可以半识别它们),可选地使用调用函数的参数。

虽然您不能总是获取函数名称,但可以识别这些函数的内容(因为它们应该保持不变),您仍然可以从传递的参数中获得有用的调试信息,等等。 / p>

注意:我实际上没有测试上面的代码,但它应该与我的repo中的代码大致相同。如果它不起作用,请参考仓库并根据您的需要重新计算代码。 :)

我希望有所帮助。