前端错误收集与处理

前言

做好错误监控,将用户使用时的错误日志上报,可以帮助我们更快的解决一些问题。

那前端监控是怎么实现的呢?要想了解这个,需要知道前端错误大概分为哪些以及如何捕获处理。

前端错误分为JS运行时错误、资源加载错误和接口错误三种。

本文档介绍有关js的错误类型、错误事件、vue3的app.config.errorHandler全局处理函数。

用nodejs+Express模拟接口,将监听到的错误信息发送到nodejs写的接口里,再通过一个getErrorList接口展示错误信息列表。

js的错误对象和onerror事件

2.1错误对象类型

ECMA-262定义了下列7种错误类型:https://baike.baidu.com/item/ECMAScript/1889420?fr=aladdin

Error() // 基类型

EvalError() // eval错误

RangeError() // 范围错误

ReferenceError() // 引用错误

SyntaxError() // 语法错误

TypeError() // 类型错误

URIError() // URI错误

Error: 所有错误类型的父类型。

SyntaxError: 语法错误,表示程序的语法使用错误。

console.log(【】) ; VM115:1 Uncaught SyntaxError: Invalid or unexpected token

ReferenceError: 引用错误,表示引用的变量不存在。

console.log(a) ; VM274:1 Uncaught ReferenceError: a is not defined at <anonymous>:1:13

TypeError: 类型错误,表示使用了错误的数据类型。

let a;
console.log(a.name); 
VM279:2 Uncaught TypeError: Cannot read properties of undefined (reading 'name')
    at <anonymous>:2:15

RangeError:范围错误, 数据值不在其所允许的范围内(函数递归调用容易出现此错误)。

(10.24).toFixed(-1)
VM363:2 Uncaught RangeError: toFixed() digits argument must be between 0 and 100
    at Number.toFixed (<anonymous>)
    at <anonymous>:2:9

URIError: URI错误,向全局 URI 处理函数(decodeURI、decodeURIComponent)传递一个不合法的URI时,URIError 错误会被抛出。

// 解码URI地址   错误:格式不正确
console.log(decodeURI("%") );
VM368:2 Uncaught URIError: URI malformed
    at decodeURI (<anonymous>)
    at <anonymous>:2:13

EvelError: eval函数执行错误 EvalError 不在当前ECMAScript规范中使用,因此不会被运行时抛出。但是对象本身仍然与规范的早期版本向后兼容。

通过error的构造器可以创建一个错误对象。如 new error() throw new Error(); 这个是创建错误,创造一个错误类型抛出。

 

// 抛出的地方
function parseExcel(con) {
   try {
        // doSomething
    } catch (error) {
        throw new Error('parse excel failed');
    }
}
// 捕获的地方 
try {
    parseExcel(con);
} catch (error) {
    if (error.message === 'parse excel failed') {
        //doSomething
    }
}

2.2Error对象

--error 对象是当错误发生时提供错误信息的 JS 内置对象。

当错误发生时,浏览器会生成 error 对象并抛出,并且中断后面代码的执行。

console.log(js)
console.log('中断后面代码的执行,不会被执行到')
VM488:1 Uncaught ReferenceError: js is not defined
    at <anonymous>:1:13

任何没有通过try-catch处理的错误都会触发window对象的error事件。

error事件接收五个参数:

message:错误信息(字符串)

source:发生错误的脚本URL(字符串)

lineno:发生错误的行号(数字)

colno:发生错误的列号(数字)

error:Error对象(对象)

 

 

图像也支持error事件。

var img = new Image()
img.src='a.jpg'
img.onerror = function(e) {
  console.log(e)
}

2.3资源加载失败

window.addEventListener 当一项资源(如图片或脚本)加载失败,加载资源的元素会触发一个 Event 接口的 error 事件,这些 error 事件不会向上冒泡到 window,但能被捕获。而window.onerror不能监测捕获。

// 图片、script、css加载错误,都能被捕获 ✅
<script>
  window.addEventListener('error', (error) => {
     console.log('捕获到异常:', error);
  }, true)
</script>
<img src="https://yun.tuia.cn/image/kkk.png">
<script src="https://yun.tuia.cn/foundnull.js"></script>
<link href="https://yun.tuia.cn/foundnull.css" rel="stylesheet"/>
  
// new Image错误,不能捕获 ❌
<script>
  window.addEventListener('error', (error) => {
    console.log('捕获到异常:', error);
  }, true)
</script>
<script>
  new Image().src = 'https://yun.tuia.cn/image/lll.png'
</script>

// fetch错误,不能捕获 ❌
<script>
  window.addEventListener('error', (error) => {
    console.log('捕获到异常:', error);
  }, true)
</script>
<script>
  fetch('https://tuia.cn/test')
</script>

到目前为止,还有语法错误、promise异步错误、new Image资源加载错误还没有捕获方法。

语法错误可以在开发阶段依靠编辑器发现。

new Image运用的比较少,可以单独自己处理自己的错误,可以通过img.onerror = fn来单独处理。

但通用的fetch怎么办呢,fetch返回Promise,但Promise的错误不能被捕获,怎么办呢?

Promise错误

1.普通Promise错误

try/catch不能捕获Promise中的错误

// try/catch 不能处理 JSON.parse 的错误,因为它在 Promise 中
try {
  new Promise((resolve,reject) => { 
    JSON.parse('')
    resolve();
  })
} catch(err) {
  console.error('in try catch', err)
}

// 需要使用catch方法
new Promise((resolve,reject) => { 
  JSON.parse('')
  resolve();
}).catch(err => {
  console.log('in catch fn', err)
})

2.async错误

try/catch不能捕获async包裹的错误

const getJSON = async () => {
  throw new Error('inner error')
}

// 通过try/catch处理
const makeRequest = async () => {
    try {
        // 捕获不到
        JSON.parse(getJSON());
    } catch (err) {
        console.log('outer', err);
    }
};

try {
    // try/catch不到
    makeRequest()
} catch(err) {
    console.error('in try catch', err)
}

try {
    // 需要await,才能捕获到
    await makeRequest()
} catch(err) {
    console.error('in try catch', err)
}

3.import chunk错误

import其实返回的也是一个promise,因此使用如下两种方式捕获错误

// Promise catch方法
import(/* webpackChunkName: "incentive" */'./index').then(module => {
    module.default()
}).catch((err) => {
    console.error('in catch fn', err)
})

// await 方法,try catch
try {
    const module = await import(/* webpackChunkName: "incentive" */'./index');
    module.default()
} catch(err) {
    console.error('in try catch', err)
}

小结:全局捕获Promise中的错误

以上三种其实归结为Promise类型错误,可以通过unhandledrejection捕获。 为了防止有漏掉的 Promise 异常,可通过unhandledrejection用来全局监听Uncaught Promise Error。

// 全局统一处理Promise
window.addEventListener("unhandledrejection", function(e){
  console.log('捕获到异常:', e);
});
fetch('https://tuia.cn/test')

当promise被reject并且错误信息没有被处理的时候,会抛出一个unhandledrejection,并且这个错误不会被window.onerror以及window.addEventListener('error')捕获,需要用专门的window.addEventListener('unhandledrejection')捕获处理。

window.addEventListener('unhandledrejection', event => 
    { 
       console.log('unhandledrejection:' + event.reason); // 捕获后自定义处理
    });

https://developer.mozilla.org... 例子: http://sandbox.runjs.cn/show/eatrbc1w 请打开页面打开控制台查看。点击button抛出unhandledrejection错误,并且该错误仅能被window.addEventListener('unhandledrejection')捕获。

Uncaught RangeError: Maximum call stack

这是Chrome浏览器在几种情况下出现的错误,一种是调用不终止的递归函数。你可以在Chrome开发者控制台中对此进行测试。

8、js FileReader 读取文件错误处理

const reader = new FileReader()

reader.onload = ()=>{}

reader.οnerrοr= ()=>{}

9、图片onerror事件

img标签支持onerror 事件,在装载文档或图像的过程中如果发生了错误,就会触发onerror事件。可以使用一张提示错误的图片代替显示不了的图片。

10、其他资源的onerror事件---脚本、iframe等

浏览器允许我们跟踪外部资源的加载 —— 脚本,iframe,图片等。

因此,在 onload 中我们可以使用脚本中的变量,运行函数等。 ……如果加载失败怎么办?例如,这里没有这样的脚本(error 404)或者服务器宕机(不可用)。

发生在脚本加载期间的 error 会被 error 事件跟踪到。

例如,我们请求一个不存在的脚本:

let script = document.createElement('script');
script.src = "https://example.com/404.js"; // 没有这个脚本
document.head.append(script);

script.onerror = function() {
  alert("Error loading " + this.src); // Error loading https://example.com/404.js
};

请注意,在这里我们无法获取更多 HTTP error 的详细信息。我们不知道 error 是 404 还是 500 或者其他情况。只知道是加载失败了。

  • 对于 <iframe> 来说,iframe 加载完成时会触发 iframe.onload 事件,无论是成功加载还是出现 error。

1.5 特别说明sourceMap

在线上由于JS一般都是被压缩或者打包(webpack)过,打包后的文件只有一行,因此报错会出现第一行第5000列出现JS错误,给排查带来困难。sourceMap存储打包前的JS文件和打包后的JS文件之间一个映射关系,可以根据打包后的位置快速解析出对应源文件的位置。

但是出于安全性考虑,线上设置sourceMap会存在不安全的问题,因为网站使用者可以轻易的看到网站源码,此时可以设置.map文件只能通过公司内网访问降低隐患

sourceMap配置devtool: 'inline-source-map' 如果使用了uglifyjs-webpack-plugin 必须把 sourceMap设置为true https://doc.webpack-china.org...icon-default.png?t=N5K3https://doc.webpack-china.org/guides/development/#%E4%BD%BF%E7%94%A8-source-map

三、vue的app.config.errorHandler全局处理函数

由于Vue会捕获所有Vue单文件组件或者Vue.extend继承的代码,所以在Vue里面出现的错误,并不会直接被window.onerror捕获,而是会抛给Vue.config.errorHandler。

app.config.errorHandler用于为应用内抛出的未捕获错误指定一个全局处理函数。https://cn.vuejs.org/api/application.html#app-config-errorhandler

  • 详细信息

  • 错误处理器接收三个参数:错误对象、触发该错误的组件实例和一个指出错误来源类型信息的字符串。

  • 它可以从下面这些来源中捕获错误:

    • 组件渲染器

    • 事件处理器

    • 生命周期钩子

    • setup() 函数

    • 侦听器

    • 自定义指令钩子

    • 过渡 (Transition) 钩子

import { createApp } from 'vue'
import App from './App.vue'

let app = createApp(App)
app.config.errorHandler = (err, vm, info) => {
    console.log(123)
    // err:
    // vm:
    // info: 是vue特定的错误信息,比如错误所在的生命周期钩子
    console.log(err, vm, info);
    fetch("http://127.0.0.1:8081/process_post", {
        method: "POST",
        headers: {
                "Content-Type": "application/json",
        },
        mode: "no-cors",
        body: JSON.stringify({
                errorMsg: err
        })
    }).then(function(res) {
        if (res.status === 200) {
                return res.json()
        } else {
                //return Promise.reject(res.json())
        }
    }).then(function(data) {
            console.log(data);
    }).catch(function(err) {
            console.log(err);
    });
}
app.mount('#app')

仅供参考,侵权必删。

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值