前端异常上报

1,115 阅读6分钟

Vue 异常捕获

app.config.errorHandler

用于为应用内抛出的未捕获错误指定一个全局处理函数。 错误处理器接收三个参数:错误对象、触发该错误的组件实例和一个指出错误来源类型信息的字符串。

import { createApp } from 'vue';
import App from './App.vue';
const app = createApp(App);

app.config.errorHandler = (err, instance, info) => {
  console.log('errorHandler: ', err, instance, info);
  // 处理错误,例如:报告给一个服务
};

对于Vue 的运行时警告,可以用app.config.warnHandle指定一个自定义处理函数。

errorCaptured

errorCaptured生命周期可以捕获后代组件传递的错误。

export default {
  /**
   * 钩子带有三个实参:
   * 错误对象、发生错误的组件实例
   * 以及一个包含错误来源信息的字符串。
   * 此钩子可以返回 false 以阻止该错误继续向上传播。
   */
  errorCaptured(err, instance, info) {
    console.log('errorCaptured', err, instance, info);
    return false;
  }
};

错误传递规则

  • 默认情况下,所有的错误都会被发送到应用级的 app.config.errorHandler (前提是这个函数已经定义),这样这些错误都能在一个统一的地方报告给分析服务。
  • 如果组件的继承链或组件链上存在多个 errorCaptured 钩子,对于同一个错误,这些钩子会被按从底至上的顺序一一调用。这个过程被称为“向上传递”,类似于原生 DOM 事件的冒泡机制。
  • 如果 errorCaptured 钩子本身抛出了一个错误,那么这个错误和原来捕获到的错误都将被发送到 app.config.errorHandler
  • errorCaptured 钩子可以通过返回 false 来阻止错误继续向上传递。即表示“这个错误已经被处理了,应当被忽略”,它将阻止其他的 errorCaptured 钩子或 app.config.errorHandler 因这个错误而被调用。

参考:cn.vuejs.org/api/options…

React错误处理

componentDidCatch(error, info) { 
    // Display fallback UI 
    this.setState({ hasError: true }); 
    // You can also log the error to an error reporting service 
    logErrorToMyService(error, info); 
}

window 全局错误捕获

window.onerror

当有js运行时错误触发时,window会触发error事件,并执行window.onerror()。onerror可以接受多个参数。

window.onerror = function(message, source, lineno, colno, error) { ... }

函数参数:

*   message:错误信息(字符串)。可用于HTML onerror=""处理程序中的event。
*   source:发生错误的脚本URL(字符串)
*   lineno:发生错误的行号(数字)
*   colno:发生错误的列号(数字)
*   error:Error对象

若该函数返回true,则阻止执行默认事件处理函数,如异常信息不会在console中打印。没有返回值或者返回值为false的时候,异常信息会在console中打印

window.addEventListener('error')

与onerror的功能大体类似,不过事件回调函数传参只有一个保存所有错误信息的参数,不能阻止默认事件处理函数的执行,但可以全局捕获资源加载异常的错误: 当一项资源(如img或script)加载失败,加载资源的元素会触发一个Event接口的error事件,并执行该元素上的onerror()处理函数。这些error事件不会向上冒泡到window,不过能被window.addEventListener在捕获阶段捕获。

window.addEventListener('error', function (event: ErrorEvent) {
  console.log('addEventListener error', event);
},true);
  • 通过window.addEventListener来实现,网络异常可以在事件捕获的阶段捕捉到,addEventListener第三个参数设置为true
  • 代码必须放在文档载入之前
  • 通过e.srcElement来判断网络错误事件发生的元素

注意,由于安全限制,window.onerror、window.addEventListener('error')直接在控制台运行时错误不会被捕获。

Promise Catch

// 全局监听 Uncaught Promise Error。
window.addEventListener("unhandledrejection", function(e){ 
    // e.preventDefault(); // 阻止异常向上抛出 
    console.log('捕获到异常:', e); 
});
window.onunhandledrejection = function(e){ 
    // e.preventDefault(); // 阻止异常向上抛出到全局,不在console中显示 
    console.log('捕获到异常:', e); 
};

跨域文件错误

如果引用外链不同源的文件,外链不同源js文件报错,onerror只会提示Script error,无法精确到指定文件和行数,可以通过script标签的:

crossorigin="anonymous"

资源服务器也需要开启cors验证,并允许引用页面的域名访问

Access-Control-Allow-Headers:Origin, X-Requested-With, Content-Type, Accept, Range
Access-Control-Allow-Origin:*

参考:blog.csdn.net/weixin_2890…

sourceMap

通常在生产环境下的代码是经过webpack打包后压缩混淆的代码,开启webpacksource-map,我们利用webpack打包后的生成的一份.map的脚本文件就可以让浏览器对错误位置进行追踪了。

开启source-map的缺陷是兼容性,目前只有Chrome浏览器和Firefox浏览器才对source-map支持。不过我们对这一类情况也有解决办法。可以使用引入npm库来支持source-map,可以参考mozilla/source-map。这个npm库既可以运行在客户端也可以运行在服务端,不过更为推荐的是在服务端使用Node.js对接收到的日志信息时使用source-map解析,以避免源代码的泄露造成风险,如下代码所示:

const express = require('express');
const fs = require('fs');
const router = express.Router();
const sourceMap = require('source-map');
const path = require('path');
const resolve = file => path.resolve(__dirname, file);
// 定义post接口
router.get('/error/', async function(req, res) {
    // 获取前端传过来的报错对象
    let error = JSON.parse(req.query.error);
    let url = error.scriptURI; // 压缩文件路径
    if (url) {
        let fileUrl = url.slice(url.indexOf('client/')) + '.map'; // map文件路径
        // 解析sourceMap
        let consumer = await new sourceMap.SourceMapConsumer(fs.readFileSync(resolve('../' + fileUrl), 'utf8')); // 返回一个promise对象
        // 解析原始报错数据
        let result = consumer.originalPositionFor({
            line: error.lineNo, // 压缩后的行号
            column: error.columnNo // 压缩后的列号
        });
        console.log(result);
    }
});
module.exports = router;
复制代码

数据上报方式

关闭或者最小化浏览器时回调

  1. beforeunload (关闭前确认弹窗) 部分移动端不触发
  2. unload 关闭时 部分移动端不触发
  3. pagehide 隐藏时 部分移动端不触发

延迟浏览器关闭或跳转的方法:

用户场景:当用户导航到另一个页面或者关闭页面,希望关闭或跳转前将数据完整发送

  1. 创建一个img元素并设置它的src。

    • 在src中创建埋点url,服务端通过get方式接收
    • 原理:占用浏览器主线程,大多数浏览器会延迟卸载以加载图像。
  2. 创建几秒钟的无操作循环。

let currTime=new Date().valueOf())
while(new Date()>currTime+3000){}
复制代码
  1. 通过配置将XHR同步发送请求

    • axios默认是异步的,浏览器可能会过早关闭或跳转导致取消发送
    • 通过配置将XHR改为同步发送请求,但是可能会影响用户跳转的速度。
  2. sendBeaconAPI 64KB限制 IE兼容性问题 post请求

    • body大小64KB限制 IE兼容性问题 post请求
    • 浏览器将 Beacon 请求排队让它在空闲的时候执行
    • 浏览器进行了优化:可以将 Beacon 请求合并到其他请求上,一同处理, 尤其在移动环境下。
    • 异步发送 不阻塞进程

参考: blog.csdn.net/chjj0904/ar…