简体   繁体   English

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

[英]Logging client-side errors and “Script error”

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). 为清楚起见,我们假设我们有一个平均大小的Web应用程序,它使用由其他域托管的多个脚本(如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. 有一天,您开始在错误日志中收到Script error ,这意味着第三方代码中发生了错误。

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. PS:开发人员无法重现错误,而且很少发生在客户端计算机上。

PPS: For the errors above window.onerror DOES NOT provide call stack, proper error message, file name and line number. PPS:对于window.onerror以上的错误, 提供调用堆栈,正确的错误消息,文件名和行号。 So it provides literally nothing helpful apart of Script error error message. 所以它提供了几乎没有任何帮助的Script error错误消息。

PPPS: PPPS:

The third party script is included using <script src="http://..."></script> tags and executed using someFunctionFromTheThirdPartyScript(); 使用<script src="http://..."></script>标记包含第三方脚本,并使用someFunctionFromTheThirdPartyScript();执行someFunctionFromTheThirdPartyScript();

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 为了更复杂,你需要利用我在调试项目中使用的一些技巧: 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. callerData变量应填充一串函数名称(或函数内容的散列,以便您可以半识别它们),可选地使用调用函数的参数。

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. 注意:我实际上没有测试上面的代码,但它应该与我的repo中的代码大致相同。 If it doesn't work, please refer to the repo and re-factor the code there to your needs. 如果它不起作用,请参考仓库并根据您的需要重新计算代码。 :) :)

I hope that helps. 我希望有所帮助。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM