先看浏览器中的错误:
Error: foobar
at new bar (:241:11)
at foo (:245:5)
at callFunction (:229:33)
at Object.InjectedScript._evaluateOn (:875:140)
at Object.InjectedScript._evaluateAndWrap (:808:34)
以上的报错是不是很熟悉,就是我们经常在浏览器开发者工具的console板块里见到的,可以清楚的看到报错的文件还有位置。这种报错是怎么发生的?又是如何可以定位的?
首先介绍下“Script error”。
“Script error.”是因为错误源来自不同域的Js文件时,浏览器发送到onerror回调的内容。onerror是一个特殊的浏览器事件,只要抛出未捕获的JavaScript错误就会触发该事件。这是记录客户端错误并将其报告给服务器的最简单方法之一。
window.onerror = function (msg, url, lineNo, columnNo, error) {
// ... handle error ...
return false;
}
函数参数:
- msg: 错误信息
- url: 发生错误的脚本url
- lineno: 发生错误的行号
- colno: 发生错误的列号
- error: Error对象
Error对象的非标准属性Error.prototype.stack是最有价值的,告诉了错误发生的位置。
举例:
example.com/test
// another-domain.com/app.js
function foo() {
bar(); // ReferenceError: bar is not a function
}
以上例子会输出如下, 未显示具体错误的原因是因为浏览器为了保护不同源的脚本文件。
"Script error.", "", 0, 0, undefined
那如何得到这些错误的具体信息呢?两种方案:
方案一 CORS属性和headers
crossorigin=”anonymous”
, 浏览器不会将任何潜在的用户识别信息(如Cookie)等传输到服务器。Access-Control-Allow-Origin: https://www.example.com
方案二 try/catch
function invoke(obj, method, args) {
try {
return obj[method].apply(this, args);
} catch (e) {
captureError(e); // report the error
throw e; // re-throw the error
}
}
invoke(Math, 'highest', [1, 2]); // throws error, no method Math.highest
- 在程序开始的地方 (e.g. jQuery中 $(document).ready)
- 在事件处理函数中, e.g. addEventListener 或 $.fn.click
- 基于计时器的回调, e.g. setTimeout 或 requestAnimationFrame
$(wrapErrors(function () { // application start
doSynchronousStuff1(); // doesn't need to be wrapped
setTimeout(wrapErrors(function () {
doSynchronousStuff2(); // doesn't need to be wrapped
});
$('.foo').click(wrapErrors(function () {
doSynchronousStuff3(); // doesn't need to be wrapped
});
}));
获取具体的错误之后,可能还需要将错误上报到服务器,举个简单的例子:
function captureError(ex) {
var errorData = {
name: ex.name, // e.g. ReferenceError
message: ex.line, // e.g. x is undefined
url: document.location.href,
stack: ex.stack // stacktrace string; remember, different per-browser!
};
$.post('/logger/js/', {
data: errorData
});
}
尽管浏览器基本都实现了window.onerror,但是每个浏览器实现的方式不同,特别是onerror lisener的参数个数和参数结构不同。
当然,以上是两种比较简单的方法,如果不麻烦的话还是推荐设置CORS和header方法。也有一些工具自动帮你用try-catch包裹代码,也会尝试去捕获跨域的js错误信息,例如sentry js.
参考:
https://blog.sentry.io/2016/05/17/what-is-script-error
https://developer.mozilla.org/zh-CN/docs/Web/API/GlobalEventHandlers/onerror