We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
前端代码异常监控实战 可以在浏览器控制台运行代码
对于 JavaScript,异常的出现不会直接导致 JS 引擎崩溃,会使当前执行的任务终止。
<script> error console.log('这里永远不会执行'); </script> <script> console.log('我继续执行') </script>
所以在对脚本错误进行上报之前,我们需要对异常进行处理,程序需要先感知到脚本错误的发生,然后再谈异常上报。
脚本错误一般分为两种:语法错误,运行时错误。
try-catch 在我们的代码中经常见到,通过给代码块进行 try-catch 进行包装后,当代码块发生出错时 catch 将能捕捉到错误的信息,页面也将可以继续执行。
但是 try-catch 处理异常的能力有限,只能捕获捉到运行时非异步错误,对于语法错误和异步错误就显得无能为力,捕捉不到。
// 示例:运行时错误 try { error; // 未定义变量 } catch (e) { console.log("我知道错误了"); console.log(e); }
对于语法错误和异步错误就捕捉不到了。
// 示例:语法错误 try { var error = 'error'; // 大写分号 } catch(e) { console.log('我捕获不到错误'); console.log(e); }
语法错误在编译的时候就会抛出异常,会导致应用崩溃,编码的时候一般会发现。
// 示例:异步异常 try { setTimeout(() => { error; // 异步错误 }); } catch (e) { console.log("我感知不到错误"); console.log(e); } // --------------------- // // 除非在 setTimeout 函数中再套上一层 try-catch;否则是无法检测到的 try { setTimeout(() => { try { error; // 异步错误 } catch (e) { console.log("抛出的错误", e); } }); } catch (e) { console.log("我感知不到错误"); console.log(e); }
window.onerror 捕获异常能力比 try-catch 稍微强点,异步或者非异步错误,onerror 都能捕获到运行时错误。
try-catch
// 示例:运行时同步错误 /** * @param {String} msg 错误信息 * @param {String} url 出错文件 * @param {Number} row 行号 * @param {Number} col 列号 * @param {Object} error 错误详细信息 */ window.onerror = function (msg, url, row, col, error) { console.log("我知道错误了"); console.log({ msg, url, row, col, error, }); return true; }; error; // ------------------------------------------------- // // 示例:异步错误 window.onerror = function (msg, url, row, col, error) { console.log("我知道异步错误了"); console.log({ msg, url, row, col, error, }); return true; }; setTimeout(() => { error; });
然而 window.onerror 对于语法错误也还是无能为力,所以编码的时候要避免语法错误。不过一般语法错误都会使页面崩溃,比较能够察觉到。
window.onerror
window.onerror 函数只有在返回 true 的时候,异常才不会向上抛出,否则即使是知道异常的发生控制台还是会显示 Uncaught Error: xxxxx。
还需要注意的点:
<script> window.onerror = function (msg, url, row, col, error) { console.log("我知道异步错误了"); console.log({ msg, url, row, col, error, }); return true; }; </script> <img src="./404.png" />
网络请求异常不会事件冒泡,必须在捕获阶段将其捕捉到才行。
window.addEventListener
<script> window.addEventListener('error', (msg, url, row, col, error) => { console.log('我知道 404 错误了'); console.log( msg, url, row, col, error ); return true; }, true); </script> <img src="./404.png" alt="">
window.addEventListener 虽然能捕获到网络请求的异常,但是无法判断 HTTP 的状态,是 404 还是其他,比如 500。所以还需要配合服务端日志才进行排查分析才行。
这点局限性还是要知道的,否则用户访问网站,图片 CDN 无法服务导致图片加载不出来,开发人员确无法察觉。
通过 Promise 可以帮助我们解决异步回调地狱的问题,但是一旦 Promise 实例抛出异常而你没有用 catch 去捕获的话,onerror 或 try-catch 也无能为力无法捕捉到错误。
onerror
window.addEventListener( "error", (msg, url, row, col, error) => { console.log("我感知不到 promise 错误"); console.log(msg, url, row, col, error); }, true ); Promise.reject("promise error"); new Promise((resolve, reject) => { reject("promise error"); }); new Promise((resolve) => { resolve(); }).then(() => { throw "promise error"; });
虽然说写 Promise 的时候在最后加上 catch 是个好习惯,但是难免有时候不会加上,给忘记了。
现在很多库都会有 Promise 的语法,例如常用的 axios,不知道什么时候这些异步请求会抛出异常而你并没有处理它,所以最好添加一个 Promise 全局异常捕获事件 unhandledrejection。
window.addEventListener("unhandledrejection", function (e) { e.preventDefault(); console.log("我知道 promise 的错误了"); console.log(e.reason); return true; }); Promise.reject("promise error"); new Promise((resolve, reject) => { reject("promise error1"); }); new Promise((resolve) => { resolve(); }).then(() => { throw "promise error2"; });
没做 Promise 全局异常处理的话,看看控制台你就知道什么花儿为什么这样红。
监控拿到报错信息之后,接下来就需要将捕捉到的错误信息发送到信息收集平台上,常用的发送形式主要有两种:
// 实例 - 动态创建 img 标签进行上报 function report(error) { var reportUrl = "http://xxxx/report"; new Image().src = reportUrl + "error=" + error; }
因为我们在线上的版本,经常做静态资源 CDN 化,这就会导致我们常访问的页面跟脚本文件来自不同的域名,这时候如果没有进行额外的配置,就会容易产生 Script error。
可通过 npm run nocors 查看效果。
npm run nocors
Script error 是浏览器在同源策略限制下产生的,浏览器处于对安全性上的考虑,当页面引用非同域名外部脚本文件时中抛出异常的话,此时本页面是没有权利知道这个报错信息的,取而代之的是输出 Script error 这样的信息。
Script error
当你的页面有使用 iframe 的时候,你需要对你引入的 iframe 做异常监控的处理,否则一旦你引入的 iframe 页面出现了问题,你的主站显示不出来,而你却浑然不知。
首先需要强调,父窗口直接使用 window.onerror 是无法直接捕获,如果你想要捕获 iframe 的异常的话,有分好几种情况。
1.iframe 页面和主站是同域名的情况,直接给 iframe 添加 onerror 事件即可。
<iframe src="./iframe.html" frameborder="0"></iframe> <script> window.frames[0].onerror = function (msg, url, row, col, error) { console.log('我知道 iframe 的错误了,也知道错误信息'); console.log({ msg, url, row, col, error }) return true; }; </script>
2.嵌入的 iframe 页面和你的主站不是同个域名的,但是 iframe 内容不属于第三方,是你可以控制的,那么可以通过与 iframe 通信的方式将异常信息抛给主站接收。
与 iframe 通信的方式有很多,常用的如:postMessage,hash 或者 name 字段跨域等等,这里就不展开了,感兴趣的话可以看:跨域,你需要知道的全在这里(暂未添加)
3.非同域且网站不受自己控制的话
除了通过控制台看到详细的错误信息外,没办法捕获,这是出于安全性的考虑,你引入了一个百度首页,人家页面报出的错误凭啥让你去监控呢,这会引出很多安全性的问题。
线上的代码几乎都经过了压缩处理,几十个文件打包压缩并丑化成了一个代码,当我们收到 a is not defined 的时候,我们根本不知道这个变量 a 究竟是什么含义,此时报错的错误日志显然是无效的。
a is not defined
第一想到的办法是利用 sourcemap 定位到错误代码的具体位置。
另外也可以通过在打包的时候,在每个合并的文件之间添加几行空格,并相应加上一些注释,这样在定位问题的时候很容易可以知道是哪个文件报的错误,然后再通过一些关键词的搜索,可以快速地定位到问题的所在位置。
如果你的网站访问量很大,假如网页的 PV 有 1kw,那么一个必然的错误发送的信息就有 1kw 条,我们可以给网站设置一个采集率:
Reporter.send = function (data) { // 只采集 30% if (Math.random() < 0.3) { send(data); // 上报错误信息 } };
这个采集率可以通过具体实际的情况来设定,方法多样化,可以使用一个随机数,也可以具体根据用户的某些特征来进行判定。
The text was updated successfully, but these errors were encountered:
No branches or pull requests
前端错误监控
JS 异常处理
对于 JavaScript,异常的出现不会直接导致 JS 引擎崩溃,会使当前执行的任务终止。
所以在对脚本错误进行上报之前,我们需要对异常进行处理,程序需要先感知到脚本错误的发生,然后再谈异常上报。
脚本错误一般分为两种:语法错误,运行时错误。
try-catch 异常处理
try-catch 在我们的代码中经常见到,通过给代码块进行 try-catch 进行包装后,当代码块发生出错时 catch 将能捕捉到错误的信息,页面也将可以继续执行。
但是 try-catch 处理异常的能力有限,只能捕获捉到运行时非异步错误,对于语法错误和异步错误就显得无能为力,捕捉不到。
对于语法错误和异步错误就捕捉不到了。
语法错误在编译的时候就会抛出异常,会导致应用崩溃,编码的时候一般会发现。
window.onerror
window.onerror 捕获异常能力比
try-catch
稍微强点,异步或者非异步错误,onerror 都能捕获到运行时错误。然而
window.onerror
对于语法错误也还是无能为力,所以编码的时候要避免语法错误。不过一般语法错误都会使页面崩溃,比较能够察觉到。还需要注意的点:
网络请求异常不会事件冒泡,必须在捕获阶段将其捕捉到才行。
window.addEventListener
window.addEventListener
虽然能捕获到网络请求的异常,但是无法判断 HTTP 的状态,是 404 还是其他,比如 500。所以还需要配合服务端日志才进行排查分析才行。这点局限性还是要知道的,否则用户访问网站,图片 CDN 无法服务导致图片加载不出来,开发人员确无法察觉。
Promise 错误
通过 Promise 可以帮助我们解决异步回调地狱的问题,但是一旦 Promise 实例抛出异常而你没有用 catch 去捕获的话,
onerror
或try-catch
也无能为力无法捕捉到错误。虽然说写 Promise 的时候在最后加上 catch 是个好习惯,但是难免有时候不会加上,给忘记了。
现在很多库都会有 Promise 的语法,例如常用的 axios,不知道什么时候这些异步请求会抛出异常而你并没有处理它,所以最好添加一个 Promise 全局异常捕获事件 unhandledrejection。
没做 Promise 全局异常处理的话,看看控制台你就知道什么花儿为什么这样红。
异常上报方式
监控拿到报错信息之后,接下来就需要将捕捉到的错误信息发送到信息收集平台上,常用的发送形式主要有两种:
监控上报常见问题
Script error 脚本错误是什么
可通过
npm run nocors
查看效果。Script error
是浏览器在同源策略限制下产生的,浏览器处于对安全性上的考虑,当页面引用非同域名外部脚本文件时中抛出异常的话,此时本页面是没有权利知道这个报错信息的,取而代之的是输出 Script error 这样的信息。window.onerror 能否捕获 iframe 的错误
当你的页面有使用 iframe 的时候,你需要对你引入的 iframe 做异常监控的处理,否则一旦你引入的 iframe 页面出现了问题,你的主站显示不出来,而你却浑然不知。
首先需要强调,父窗口直接使用 window.onerror 是无法直接捕获,如果你想要捕获 iframe 的异常的话,有分好几种情况。
1.iframe 页面和主站是同域名的情况,直接给 iframe 添加 onerror 事件即可。
2.嵌入的 iframe 页面和你的主站不是同个域名的,但是 iframe 内容不属于第三方,是你可以控制的,那么可以通过与 iframe 通信的方式将异常信息抛给主站接收。
与 iframe 通信的方式有很多,常用的如:postMessage,hash 或者 name 字段跨域等等,这里就不展开了,感兴趣的话可以看:跨域,你需要知道的全在这里(暂未添加)
3.非同域且网站不受自己控制的话
除了通过控制台看到详细的错误信息外,没办法捕获,这是出于安全性的考虑,你引入了一个百度首页,人家页面报出的错误凭啥让你去监控呢,这会引出很多安全性的问题。
压缩代码如何定位到脚本异常位置
线上的代码几乎都经过了压缩处理,几十个文件打包压缩并丑化成了一个代码,当我们收到
a is not defined
的时候,我们根本不知道这个变量 a 究竟是什么含义,此时报错的错误日志显然是无效的。第一想到的办法是利用 sourcemap 定位到错误代码的具体位置。
另外也可以通过在打包的时候,在每个合并的文件之间添加几行空格,并相应加上一些注释,这样在定位问题的时候很容易可以知道是哪个文件报的错误,然后再通过一些关键词的搜索,可以快速地定位到问题的所在位置。
收集异常信息量太多,怎么办
如果你的网站访问量很大,假如网页的 PV 有 1kw,那么一个必然的错误发送的信息就有 1kw 条,我们可以给网站设置一个采集率:
这个采集率可以通过具体实际的情况来设定,方法多样化,可以使用一个随机数,也可以具体根据用户的某些特征来进行判定。
参考文章
The text was updated successfully, but these errors were encountered: