项目上线之后,用户如果出现错误(代码报错、资源加载失败以及其他情况),基本上没有办法复现,如果用户出了问题但是不反馈或直接不用了,对开发者或公司来说都是损失。前端实现错误自动上报(Error Reporting 或 Error Tracking)是一个重要的功能,它帮助开发者及时发现并修复生产环境中的错误,从而提升用户体验和应用的稳定性。
实现
- window.onerror和window.addEventListener相比,无法监听网络异常。但window.onerror和window.addEventListener,对Promise报错都是无法捕获。
- 每一个Promise写一个catch,如果觉得麻烦,那么就要使用一个新的事件,unhandledrejection。
- vue异常处理
- 上报错误数据
使用tracekit库来解析错误的堆栈信息,创建文件errorReporting.js在src/main.js中引入。
import Vue from 'vue'
import TraceKit from 'tracekit'
import dayjs from 'dayjs'
import http from '@/base/http'
const protcol = window.location.protocol
// 用于订阅错误报告的通知或事件
TraceKit.report.subscribe((error, service) => {
const { message, stack = [] } = error || {}
const obj = {
message,
stack: {
column: stack[0].column,
line: stack[0].line,
func: stack[0].func,
url: stack[0].url
}
}
const params = {
error: obj,
data: {
errTime: dayjs().format('YYYY-MM-DD HH:mm:ss'),
isMobile: /iPhone|iPad|iPod|Android/i.test(navigator.userAgent), // 是否移动端
isWechat: /MicroMessenger/i.test(navigator.userAgent), // 是否微信浏览器
isIOS: /iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream, // 两个都是false就是未知设备
isAndroid: /Android/.test(navigator.userAgent) && !/Windows Phone/.test(navigator.userAgent)
},
browserInfo: {
userAgent: navigator.userAgent,
protcol: protcol
}
}
console.log(params, '错误上报')
// http.post(service, params).then(() => {
// console.log('错误上报成功')
// }).catch(() => {
// console.log('错误上报失败')
// })
})
// window.onerror和window.addEventListener,对Promise报错都是无法捕获
// 图片、同步、网络、try catch(不写catch会不会抛)、try catch 不能捕获异步,同时阻塞js
window.addEventListener('error', args => {
console.log(args, 'windowArgs')
const err = args.target.src || args.target.href
const obj = {
message: '加载异常' + err
}
if (!err) {
return true
}
TraceKit.report(obj, 'reportWindowError')
}, true) // 捕获
// Promise、async/await报错
window.addEventListener('unhandledrejection', error => {
console.log('unhandledrejection', error)
TraceKit.report(error.reason, 'reportPromiseError')
}, true)
// vue框架
Vue.config.errorHandler = (err, vm, info) => {
console.log('Error: ', err) // 错误对象,包含错误消息、堆栈信息等
console.log('vm', vm) // 引发错误的vue组件实例,可以获取组件的状态、数据和方法
console.log('info: ', info) // 错误的额外信息,描述错误发生的位置或上下文
TraceKit.report(err, 'reportVueError')
}
// 至此,js中同步、异步、资源加载、Promise、async/await都有相对应的捕获方式
-
搭建监控后台
npm init -y 使用express进行搭建后端,source-map用于解析js.map文件,npm i express nodemon source-map。
- 设置允许跨域
可以自己手动设置响应头实现跨域,我这里选择使用cors库,npm i cors,
const cors = require('cors');
app.use(cors()); // 这条需要放在 const app = express(); 后
- monitor后台项目,server.js文件
const express = require('express')
const SourceMap = require('source-map')
const dayjs = require('dayjs')
const cors = require('cors')
const path = require('path')
const fs = require('fs')
const PORT = 9999
const app = express()
app.use(cors())
// 解析表单中的application/x-www-form-urlencoded和application/json
app.use(express.urlencoded({
extended: true
}))
app.use(express.json())
const errorLog = (urlParams, res, req) => {
const type = urlParams.error.source || 'Vue'
const today = dayjs().format('YYYY-MM-DD') // 今天
const logDirPath = path.join(__dirname, 'log')
const logFilePath = path.resolve(__dirname, 'log/' + `log-${today}.txt`)
if (!fs.existsSync(logDirPath)) {
fs.mkdirSync(logDirPath, { recursive: true })
}
if (!fs.existsSync(logFilePath)) {
fs.writeFileSync(logFilePath, '', 'utf8')
}
const writeStream = fs.createWriteStream(logFilePath, { flags: 'a' })
writeStream.on('open', () => {
writeStream.write(`错误类型:${type}` + '\n')
writeStream.write('错误发生时间:' + urlParams.data.errTime + '\n')
writeStream.write('IP:' + req.ip + '\n')
writeStream.write(`安卓: ${urlParams.data.isAndroid} IOS: ${urlParams.data.isIOS} 移动端: ${urlParams.data.isMobile} 微信: ${urlParams.data.isWechat} (安卓和ios同时为false表示未知设备)` + '\n')
writeStream.write('用户代理:' + urlParams.browserInfo.userAgent + '\n')
writeStream.write('错误信息:' + JSON.stringify(urlParams.error) + '\n')
writeStream.write('---------------------------------- \n')
writeStream.end(() => {
res.status(200).send({
data: '错误上报成功',
code: 200,
})
})
})
writeStream.on('error', err => {
res.status(404).send({
data: '错误上报失败' + err,
code: 404,
})
})
}
app.post('/reportError', async (req, res) => {
const urlParams = req.body
const source = urlParams.error.source
if (source !== 'window') {
const stack = urlParams.error.stack || {}
// 获取文件名
const fileName = path.basename(stack.url || '')
// 查找map文件
const filePath = path.join(__dirname, 'uploads', fileName + '.map')
const readFile = filePath => {
return new Promise((resolve, reject) => {
fs.readFile(filePath, {
encoding: 'utf-8'
}, (err, data) => {
if (err) {
return reject(err)
}
resolve(JSON.parse(data))
})
})
}
const searchSource = async ({ filePath, line, column }) => {
const rawSourceMap = await readFile(filePath)
const consumer = await new SourceMap.SourceMapConsumer(rawSourceMap)
const res = consumer.originalPositionFor({
line,
column
})
consumer.destroy()
return res
}
let sourceMapParseResult = ''
try {
// 解析sourceMap结果
sourceMapParseResult = await searchSource({
filePath,
line: stack.line,
column: stack.column
})
} catch (err) {
sourceMapParseResult = err
}
console.log('解析结果', sourceMapParseResult)
}
errorLog(urlParams, res, req)
})
// 监听端口
app.listen(PORT, () => {
console.log(`服务启动成功,端口号为:${PORT}`)
})
通过报错提示的js文件,查看后都是压缩混淆之后的js代码,这时候就需要打包时生成的source map文件了,这个文件中保存着打包后代码和源码对应的位置,我们只需要拿到报错的堆栈信息,通过转换,就能通过source map找到对应我们源码的文件及出错的代码行列信息
tracekit、Sentry 和 Bugsnag
都是流行的错误追踪和监控工具,它们帮助开发者在应用程序运行时捕获、报告和分析错误。尽管它们都有类似的目标,但它们在功能、集成方式、定价模型等方面有所不同。下面是对这三个工具的简要概述:
TraceKit
概述:TraceKit 是一个轻量级的 JavaScript 错误追踪库,它可以帮助你捕获和报告浏览器中的未捕获异常。它通常被集成到前端项目中,以便在客户端捕获错误。
特点:
捕获并报告未捕获的异常。
支持跨域脚本错误(如果浏览器允许)。
可以自定义错误报告的内容和格式。
易于集成到现有的前端项目中。
限制:
主要关注于前端错误追踪。
可能需要额外的后端服务来接收和存储错误报告。
缺少一些高级功能,如性能监控、崩溃报告等。
Sentry
概述:Sentry 是一个开源的错误追踪系统,它支持多种语言和平台,包括 JavaScript、Python、Ruby、Node.js 等。它提供了一个强大的平台来监控和修复崩溃、错误和性能问题。
特点:
实时错误追踪和报告。
详细的错误堆栈跟踪和上下文信息。
性能监控和崩溃报告。
强大的查询和过滤功能,以便快速定位问题。
集成到多个开发工具和平台中,如 GitHub、Slack、Jira 等。
支持自定义错误处理和警报。
定价:
Sentry 提供免费的基础计划,但高级功能需要付费订阅。
Bugsnag
概述:Bugsnag 是另一个流行的错误监控和报告工具,它专注于帮助开发者快速发现和修复应用中的错误。它支持多种编程语言和平台,包括移动应用、Web 应用和服务器端应用。
特点:
实时错误追踪和报告。
详细的错误堆栈跟踪和上下文信息。
支持自定义错误处理和警报。
崩溃报告和性能监控。
易于集成到现有的开发流程中。
提供了丰富的 API 和 SDK,以便在多种环境中使用。
定价:
Bugsnag 提供免费的基础计划,但高级功能和更大的存储空间需要付费订阅。
总结
选择哪个工具取决于你的具体需求、预算以及你正在使用的技术栈。如果你只需要一个简单的前端错误追踪解决方案,并且不介意自己处理错误报告的存储和检索,那么 TraceKit 可能是一个不错的选择。然而,如果你正在寻找一个更全面的错误监控和报告解决方案,包括性能监控、崩溃报告以及与其他开发工具的集成,那么 Sentry 或 Bugsnag 可能更适合你的需求。最终的选择应该基于你的具体需求和对这些工具的评估。