• 详解JavaScript错误捕获和上报流程


    怎么捕获错误并且处理,是一门语言必备的知识。在JavaScript中也是如此。

    那怎么捕获错误呢?初看好像很简单,try-catch就可以了嘛!但是有的时候我们发现情况却繁多复杂。

    • Q1: 同步可以try-catch,但一个异步回调,比如setTimeOut里的函数还可以try-catch吗?

    • Q2: Promise的错误捕获怎么做?

    • Q3: async/await怎么捕获错误?

    • Q4: 我能够在全局环境下捕获错误并且处理吗?

    • Q5: React16有什么新的错误捕获方式吗?

    • Q6: 捕获之后怎么上报和处理?

     

    问题有点多,我们一个一个来。

     

    Q1. 同步代码里的错误捕获方式

    在同步代码里,我们是最简单的,只要try-catch就完了 

    function test1 () {
      try {
        throw Error ('callback err');
      } catch (error) {
        console.log ('test1:catch err successfully');
      }
    }
    test1();

    输出结果如下,显然是正常的

    Q2. 普通的异步回调里的错误捕获方式(Promise时代以前)

    上面的问题来了,我们还能通过直接的try-catch在异步回调外部捕获错误吗?我们试一试 

    // 尝试在异步回调外部捕获错误的结果
    function test2 () {
      try {
        setTimeout (function () {
          throw Error ('callback err');
        });
      } catch (error) {
        console.log ('test2:catch err successfully');
      }
    }
    test2(); 

    输出

    注意这里的Uncaught Error的文本,它告诉我们错误没有被成功捕捉。

    为什么呢? 因为try-catch的是属于同步代码,它执行的时候,setTimeOut内部的的匿名函数还没有执行呢。而内部的那个匿名函数执行的时候,try-catch早就执行完了。( error的内心想法:哈哈,只要我跑的够慢,try-catch还是追不上我!)

    但是我们简单想一想,诶我们把try-catch写到函数里面不就完事了嘛!

     

     

    function test2_1 () {
      setTimeout (function () {
        try {
          throw Error ('callback err');
        } catch (error) {
          console.log ('test2_1:catch err successfully');
        }
      });
    }
    test2_1();

    输出结果如下,告诉我们这方法可行

     

    总结下Promise时代以前,异步回调中捕获和处理错误的方法

    • 在异步回调内部编写try-catch去捕获和处理,不要在外部哦

    • 很多异步操作会开放error事件,我们根据事件去操作就可以了

    Q3. Promise里的错误捕获方式

    可通过Promise.catch方法捕获

    function test3 () {
      new Promise ((resolve, reject) => {
        throw Error ('promise error');
      }).catch (err => {
        console.log ('promise error');
      });
    }

    输出结果

    >> reject方法调用和throw Error都可以通过Promise.catch方法捕获

    function test4 () {
      new Promise ((resolve, reject) => {
        reject ('promise reject error');
      }).catch (err => {
        console.log (err);
      });
    } 

    输出结果

     

    >> then方法中的失败回调和Promise.catch的关系

    • 如果前面的then方法没写失败回调,失败时后面的catch是会被调用的

    • 如果前面的then方法写了失败回调,又没抛出,那么后面的catch就不会被调用了

    // then方法没写失败回调
    function test5 () {
      new Promise ((resolve, reject) => {
        throw Error ('promise error');
      })
        .then (success => {})
        .catch (err => {
          console.log ('the error has not been swallowed up');
        });
    }
    // then方法写了失败回调
    function test5 () {
      new Promise ((resolve, reject) => {
        throw Error ('promise error');
      })
        .then (success => {},err => {})
        .catch (err => {
          console.log ('the error has not been swallowed up');
        });
    }

    输出分别为

    1.the error has not been swallowed up
    2.无输出

    Q4.async/await里的错误捕获方式

    对于async/await这种类型的异步,我们可以通过try-catch去解决

    async function test6 () {
      try {
        await getErrorP ();
      } catch (error) {
        console.log ('async/await error with throw error');
      }
    }
     
    function getErrorP () {
      return new Promise ((resolve, reject) => {
        throw Error ('promise error');
      });
    }
    test6();
    
    

    输出结果如下

     

    >> 如果被await修饰的Promise因为reject调用而变化,它也是能被try-catch的

    (我已经证明了这一点,但是这里位置不够,我写不下了)

    Q5.在全局环境下如何监听错误

    window.onerror可以监听全局错误,但是很显然错误还是会抛出

    window.onerror = function (err) {
      console.log ('global error');
    };
    throw Error ('global error');

    输出如下

     

    Q6.在React16以上如何监听错误

    >> componentDidCatch和getDerivedStateFromError钩子函数

    class Bar extends React.Component {
      // 监听组件错误
      componentDidCatch(error, info) {
        this.setState({ error, info });
      }
      // 更新 state 使下一次渲染能够显示降级后的 UI
      static getDerivedStateFromError(error) {
        return { hasError: true };
      }
      render() {
      }
    }

    有错误,那肯定要上报啊!不上报就发现不了Bug这个样子。Sentry这位老哥就是个人才,日志记录又好看,每次见面就像回家一样

     

     

    Sentry简单介绍

    Sentry provides open-source and hosted error monitoring that helps all software
    teams discover, triage, and prioritize errors in real-time.
    One million developers at over fifty thousand companies already ship
    better software faster with Sentry. Won’t you join them?
    —— Sentry官网

    Sentry是一个日志上报系统,Sentry 是一个实时的日志记录和汇总处理的平台。专注于错误监控,发现和数据处理,可以让我们不再依赖于用户反馈才能发现和解决线上bug。让我们简单看一下Sentry支持哪些语言和平台吧

     

    在JavaScript领域,Sentry的支持也可以说是面面俱到

     

    参考链接
    https://docs.sentry.io/platforms/ 

    Sentry的功能简单说就是,你在代码中catch错误,然后调用Sentry的方法,然后Sentry就会自动帮你分析和整理错误日志,例如下面这张图截取自Sentry的网站中

     

    在JavaScript中使用Sentry 

    1.首先呢,你当然要注册Sentry的账号

    这个时候Sentry会自动给你分配一个唯一标示,这个标示在Sentry里叫做 dsn

    2. 安卓模块并使用基础功能

    安装@sentry/browser 

    npm install @sentry/browser

    在项目中初始化并使用

    import * as Sentry from '@sentry/browser';
     
    Sentry.init ({
      dsn: 'xxxx',
    });
     
    try {
      throw Error ('我是一个error');
    } catch (err) {
        // 捕捉错误
      Sentry.captureException (err);
    }

    3.上传sourceMap以方便在线上平台阅读出错的源码

     

    // 安装
    $ npm install --save-dev @sentry/webpack-plugin
    $ yarn add --dev @sentry/webpack-plugin
     
    // 配置webpack
    const SentryWebpackPlugin = require('@sentry/webpack-plugin');
    module.exports = {
      // other configuration
      plugins: [
        new SentryWebpackPlugin({
          include: '.',
          ignoreFile: '.sentrycliignore',
          ignore: ['node_modules', 'webpack.config.js'],
          configFile: 'sentry.properties'
        })
      ]
    }; 

    4. 为什么不是raven.js?

     

    // 已经废弃,虽然你还是可以用
    var Raven = require('raven-js');
    Raven
      .config('xxxxxxxxxxx_dsn')
      .install();

    Sentry的核心功能总结

    捕获错误

    try {
      aFunctionThatMightFail();
    } catch (err) {
      Sentry.captureException(err);
    }

    设置该错误发生的用户信息

    下面每个选项都是可选的,但必须 存在一个选项 才能使Sentry SDK捕获用户: id 

    Sentry.setUser({
        id:"penghuwan12314"
      email: "penghuwan@example.com",
      username:"penghuwan",
      ip_addressZ:'xxx.xxx.xxx.xxx'
      });

    设置额外数据

    Sentry.setExtra("character_name", "Mighty Fighter");
    设置作用域 
    Sentry.withScope(function(scope) {
        // 下面的set的效果只存在于函数的作用域内
      scope.setFingerprint(['Database Connection Error']);
      scope.setUser(someUser);
      Sentry.captureException(err);
    });
    // 在这里,上面的setUser的设置效果会消失

    设置错误的分组

    整理日志信息,避免过度冗余 

    Sentry.configureScope(function(scope) {
      scope.setFingerprint(['my-view-function']);
    });

    设置错误的级别

    在阅读日志时可以确定各个bug的紧急度,确定排查的优先书序

    Sentry.captureMessage('this is a debug message', 'debug');
    //fatal,error,warning,info,debug五个值
    // fatal最严重,debug最轻

    自动记录某些事件

    例如下面的方法,会在每次屏幕调整时完成上报 

    window.addEventListener('resize', function(event){
      Sentry.addBreadcrumb({
        category: 'ui',
        message: 'New window size:' + window.innerWidth + 'x' + window.innerHeight,
        level: 'info'
      });
    })

    Sentry实践的运用

    根据环境设置不同的dsn

    let dsn;
      if (env === 'test') {
        dsn = '测试环境的dsn';
      } else {
        dsn =
          '正式环境的dsn';
      }
     
    Sentry.init ({
      dsn
    });
     

     

  • 相关阅读:
    缓存服务Ehcache方案
    sql的一点总结<一>
    消息队列-ActiveMQ
    Kettle数据抽取解决方案
    在VMware上安装VMTools
    数组去重方法
    横向tab计算滚动位置
    无高度内容垂直居中
    常见富文本处理方法
    极简触感反馈Button组件
  • 原文地址:https://www.cnblogs.com/penghuwan/p/11905429.html
Copyright © 2020-2023  润新知