阅读 592

前端监控了解与简易实现

从自测到QA测试以及上线前的Code Review ,进过层层检查,还是会有疏忽。代码上线后,我们更多的是通过用户反馈或者后台的数据统计、异常数据来分析,找问题、定位问题。那么前端是否也能弄用一套比较完善的代码异步监控?

例子错误:

src = img.getAttribute("src");
src.indexOf("http://XXX.com/");
复制代码

在某些情况下,上诉的src可能是null,导致这里调用null的indexOf方法发生异常。导致代码不往下走了。

正常情况下,前端页面的语法错误以及运行时错误,都会在浏览器的console里边体现出来,包含错误的文件、行号、堆栈信息等。 那么有什么方法可以抓到错误日志?有两个方案:

  • try...catch(e) 针对某个代码块使用try...catch(e) 包装,这样错误就能被捕获到。缺点:
    • 捕获不了异步错误(所以很多node,走的是回调的形式)
    • 捕获不了全局错误事件
  // demo2  可以捕获
  try{
    var i = 0;
    console.log(I)
    var c = b + cc
  }catch(e){
    console.log(e)
  }

  // demo3  无法捕获
  try{
    setTimeout(()=>{
      var i = 0;
      console.log(I)
      var c = b + cc
    }, 0)
  }catch(e){
    console.log(e)
  }
复制代码
  • window.onerror

window.onerror 可以拿到出错信息的文件名、行号、列号,还可以在window.onerror最后return true让浏览器不输出错误信息到控制台。 注意:

  • window.onerror 代码块需要独立出去,避免与业务代码块、语法错误的代码块一起,避免事件不生效。
  • 对于跨域的JS 资源,window.onerror 也无法监听到。
    • js 处加上 crossorigin
    • 加上 Access-Control-Allow-Origin

因此通常路径是监听全局的window.onerror 事件捕获到运行时的错误,然后上报到采集端,最后再做一个页面展示数据。

利用new Image 的方式采集日志

window.onerror = function (msg, url, line, col, error) {
  // 最后实时过滤出日志
  (new Image).src = `/data?msg=${msg}&url=${url}&line=${line}&col=${col}&error=${error}`
  console.log(msg)
  console.log(url)
  console.log(line)
  console.log(col)
  console.log(error)
  // return true 实现错误日志不输出到console 处
  return true
}

复制代码

最终形式:

window.onerror = function(msg,url,line,col,error){
    //没有URL不上报!上报也不知道错误
    if (msg != "Script error." && !url){
        return true;
    }
    //采用异步的方式
    //我遇到过在window.onunload进行ajax的堵塞上报
    //由于客户端强制关闭webview导致这次堵塞上报有Network Error
    //我猜测这里window.onerror的执行流在关闭前是必然执行的
    //而离开文章之后的上报对于业务来说是可丢失的
    //所以我把这里的执行流放到异步事件去执行
    //脚本的异常数降低了10倍
    setTimeout(function(){
        var data = {};
        //不一定所有浏览器都支持col参数
        col = col || (window.event && window.event.errorCharacter) || 0;

        data.url = url;
        data.line = line;
        data.col = col;
        if (!!error && !!error.stack){
            //如果浏览器有堆栈信息
            //直接使用
            data.msg = error.stack.toString();
        }else if (!!arguments.callee){
            //尝试通过callee拿堆栈信息
            var ext = [];
            var f = arguments.callee.caller, c = 3;
            //这里只拿三层堆栈信息
            while (f && (--c>0)) {
               ext.push(f.toString());
               if (f  === f.caller) {
                    break;//如果有环
               }
               f = f.caller;
            }
            ext = ext.join(",");
            data.msg = ext;
        }
        //把data上报到后台!
    },0);

    return true;
};

复制代码

前端日志存储,通常不会去实时上传日志,而是先存储,比如: Cookie, localStorage, sessionStorage, IndexDB, webSQL, FileSystem 几种毕竟方式

存储方式 cookie localStorage sessionStorage IndexDB webSQL
容量 4K 5M 5M 500M 60M
进程 同步 同步 同步 异步 异步
检索 key key key key, index field

IndexedDB是最好的选择,它具有容量大、异步的优势,异步的特性保证它不会对界面的渲染产生阻塞。而且IndexedDB是分库的,每个库又分store,还能按照索引进行查询,具有完整的数据库管理思维,比localStorage更适合做结构化数据管理。但是它有一个缺点,就是api非常复杂,不像localStorage那么简单直接。针对这一点,我们可以使用hello-indexeddb这个工具,它用Promise对复杂api进行来封装,简化操作,使IndexedDB的使用也能做到localStorage一样便捷

腾讯的参考

框架层异常处理

vue 中的Vue.config.errorHandler, React 有ErrorBoundary 。

class ErrorBoundary extends React.Component {
  constructor(props) {
    super(props);
    this.state = { hasError: false };
  }

  componentDidCatch(error, info) {
    this.setState({ hasError: true });

    // 在这里可以做异常的上报
    logErrorToMyService(error, info);
  }

  render() {
    if (this.state.hasError) {
      return <h1>Something went wrong.</h1>;
    }
    return this.props.children;
  }
}
// 在使用时,用 ErrorBoundary 包裹你的业务组件即可:
<ErrorBoundary>
  <MyWidget />
</ErrorBoundary>

复制代码

window.performance 有浏览器相关时间的api

监听hash change 变化上报pv

webp 格式的实现

第三方前端监控平台

阿里的前端监控平台,是按量收费的

阿里监控

文章

文章

文章

3-5年内部岗位(平安、乐信、500万、vivo、oppo)推荐机会,欢迎发简历到: zgxie@126.com

关注下面的标签,发现更多相似文章
评论