2023年11月28日发(作者:)
前端代码异常⽇志收集与监控
☞ 收集⽇志的⽅法
平时收集⽇志的⼿段,可以归类为两个⽅⾯,⼀个是逻辑中的错误判断,为主动判断;⼀个是利⽤语⾔给我们提供的捷径,暴⼒式获取错误
信息,如 atch 和 r。
1. 主动判断
我们在⼀些运算之后,得到⼀个期望的结果,然⽽结果不是我们想要的
//
function calc(){
//
return val;
}
if(calc() !== "someVal"){
({
position: "::
msg: "calc error"
});
/**
* @param {String} errorMessage 错误信息
* @param {String} scriptURI 出错的⽂件
* @param {Long} lineNumber 出错代码的⾏号
* @param {Long} columnNumber 出错代码的列号
* @param {Object} errorObj 错误的详细信息,Anything
*/
r = function(errorMessage, scriptURI, lineNumber,columnNumber,errorObj) {
// code..
}
r 算是⼀种特别暴⼒的容错⼿段,atch 也是如此,他们底层的实现就是利⽤ C/C++ 中的 goto 语句实现,⼀旦发现错
误,不管⽬前的堆栈有多深,不管代码运⾏到了何处,直接跑到顶层或者 atch 捕获的那⼀层,这种⼀脚踢开错误的处理⽅式并不是很
好。
☞ 收集⽇志存在的问题
收集⽇志的⽬的是为了及时发现问题,最好⽇志能够告诉我们,错误在哪⾥,更优秀的做法是,不仅告诉错误在哪⾥,还告诉我们,如何处
理这个错误。终极⽬标是,发现错误,⾃动容错,这⼀步是最难的。
1. ⽆具体报错信息,Script error.
先看下⾯的例⼦,
r = function(){
(arguments);
};
⽽拿到的结果却是:
翻开 Chromium 的 WebCore ,可以看到:
跨域情况下,返回的结果是 Script error.。
// /browser/branches/chromium/1453/Source/WebCore/dom/#L333
String message = errorMessage;
int line = lineNumber;
String sourceName = sourceURL;
// 已经拿到了所有的错误信息,但如果发现是⾮同源情况,`sanitizeScriptError` 中复写错误信息
sanitizeScriptError(message, line, sourceName, cachedScript);
的 WebCore 中只判断了 securityOrigin()->canRequest(targetURL),新版中还多了⼀个 cachedScript 的判断,可以看出浏览器对这⽅⾯
的限制越来越严格。
在本地测试了下:
可见在 file:// 协议下,securityOrigin()->canRequest(targetURL) 也是 false。
☞ 为何Script error.?
简单报错: Script error,⽬的是避免数据泄露到不安全的域中,⼀个简单的例⼦:
上⾯我们并没有引⼊⼀个 js ⽂件,⽽是⼀个 html,这个 html 是银⾏的登录页⾯,如果你已经登录了 ,那 login 页⾯就会⾃动
跳转到 ,如果未登录则跳转到 ,那么 JS 报错也会是 is not defined, is not
defined,通过这些信息可以判断⼀个⽤户是否登录他的银⾏帐号,给 hacker 提供了⼗分便利的判断渠道,这是相当不安全的。
☞ crossOrigin参数跳过跨域限制
image 和 script 标签都有 crossorigin 参数,它的作⽤就是告诉浏览器,我要加载⼀个外域的资源,并且我信任这个资源。
然⽽,却报错了:
这是意料之中的错误,跨域资源共享策略要求,服务器也设置 Access-Control-Allow-Origin 的响应头:
header('Access-Control-Allow-Origin: *');
回头看看我们 CDN 的资源,
Javascript/CSS/Image/Font/SWF 等这些静态资源其实都已经早早地加上了 CORS 响应头。
2. 压缩代码⽆法定位到错误的具体位置
线上的代码⼏乎都是经过打包压缩的,⼏⼗上百的⽂件压缩后打包成⼀个,⽽且只有⼀⾏。当我们收到 a is not defined 的时候,如果只在
特定场景下才报错,我们根本⽆法定位到这个被压缩的 a 是个什么东西,那么此时的错误⽇志就是⽆效的。
第⼀个想到的办法是利⽤ sourceMap,利⽤它可以定位到压缩代码某⼀点在未压缩代码的具体位置。下⾯是 sourceMap 引⼊的格式,在
代码的最后⼀⾏加⼊:
(function(){})(); // file 1
// 1000 个空⾏
(function(){})(); // file 2
// 1000 个空⾏
(function(){})(); // file 3
// 1000 个空⾏
(function(){})(); // file 4
var _fileConfig = ['file 1', 'file 2', 'file 3', 'file 4']
如果报错在第 3001 ⾏,
r = function(msg, url, line, col, error){
// line = 3001
var lineNum = line;
("错误位置:" + _fileConfig[parseInt(lineNum / 1000) - 1]);
// -> "错误位置:file 3"
function needReport (sampling){
// sampling: 0 - 1
return () <= sampling;
}
= function(errInfo, sampling) {
if(needReport(sampling || 1)){
Reporter._send(errInfo);
}
};
这个采样率可以按需求来处理,可以同上,使⽤⼀个随机数,也可以使⽤ cookie 中的某个字段(如 nickname)的最后⼀个字母/数字来
判定,也可以将⽤户的 nickname 进⾏ hash 计算,再通过最后⼀位的字母/数字来判断,总之,⽅法是很多的。
☞ 收集⽇志布点位置
为了更加精准的拿到错误信息,有效地统计错误⽇志,我们应该更多地采⽤主动式埋点,⽐如在⼀个接⼝的请求中:
// Module A Get Shops Data
$.ajax({
url: URL,
dataType: "jsonp",
success: function(ret) {
if( === "failed") {
类似这样的错误都是不太可控的。可以在使⽤到 atch 的地⽅思考是否可以使⽤其他⽅式做兼容。感谢 EtherDream 的。
☞ 关于 r 的使⽤
可以尝试如下代码:
//
throw new Error("SHOW ME");
r = function(){
(arguments);
// 阻⽌在控制台中打印错误信息
return true;
};
上⾯的代码直接报错了,没有继续往下执⾏。页⾯中可能有好⼏个 script 标签,但是
r
这个错误监听⼀定要放到最前头!
☞ 错误的警报与提⽰
什么时候该警报?不能有错就报。上⾯也说了,因为⽹络环境和浏览器环境因素,复杂页⾯我们允许千分之⼀的错误率。⽇志处理后的数据
图:
发布评论