一、概述
在前端开发中,监控和错误追踪是确保应用稳定性和用户体验的重要环节。
随着前端应用的复杂性增加,JavaScript错误监控变得尤为重要。在生产环境中,为了优化加载速度和性能,前端代码通常会被压缩和混淆。这虽然提升了性能,但也给错误追踪带来了挑战,因为错误报告中显示的代码位置和实际代码不一致,使得开发者难以定位和修复问题。
Sourcemap技术应运而生,它是一种将压缩后的代码映射回原始源代码的工具。通过sourcemap,开发者可以准确地从错误报告中找到原始代码中的问题所在,即使这些代码已经被压缩或混淆。这种映射关系使得错误追踪变得更加精确和高效。
在这篇文章中,将探讨如何在前端项目中集成sourcemap,以及如何利用sourcemap进行错误监控和代码还原。我将介绍sourcemap的生成、部署和使用,以及它在前端监控中的最佳实践。通过这些内容,同学们将能够理解sourcemap在前端错误监控中的重要性,并学会如何利用这一工具提高开发效率和应用质量。
二、实践
通过sourcemap技术实现对前端代码中错误位置的精确定位和还原。
2.1、Sourcemap结构
Sourcemap文件通常包含以下字段:
version:Sourcemap的版本,目前常用的是版本3。file:转换后的文件名。sourceRoot:源文件的根目录路径,用于重新定位源文件。sources:源文件列表,可能包含多个文件。sourcesContent:源文件的内容列表(可选)。names:转换前的变量名和属性名列表。mappings:一个编码的字符串,记录了源代码和转换后代码之间的详细映射关系。2.2、初始化项目
使用我自己实现的前端action-cli脚手架,从自己GitHub仓库拉取模板并初始化一个Vite+Vue3+TS项目,安装依赖运行项目。
更多关于action-cli可以查看这两篇文章
实现一个自定义前端脚手架_前端自定义脚手架-CSDN博客
重构Action-cli前端脚手架-CSDN博客
添加下面两个路由
const routes=[ { path: '/errorView', name: 'ErrorView', component: () => import('@/views/sourcemap/ErrorView.vue'), meta: { title: '生产错误' } }, { path: '/errorList', name: 'ErrorList', component: () => import('@/views/sourcemap/ErrorList.vue'), meta: { title: '错误列表' } }];// https://github.com/Topskys/admin-pro/tree/monitor
2.3、生产Js错误
创建./views/sourcemap/ErrorView.vue页面,通过throw模拟抛出Js代码错误。
// ./views/sourcemap/ErrorView.vue<template> <div> <h1>Error</h1> <button @click="triggerTypeError()">触发TypeError</button> <button @click="triggerReferenceError">触发ReferenceError</button> <button @click="triggerSyntaxError">触发SyntaxError</button> </div></template><script setup lang="ts">const triggerTypeError = () => { throw new TypeError('This is a type error');};const triggerReferenceError = () => { throw new ReferenceError('This is a reference error');};const triggerSyntaxError = () => { throw new SyntaxError('This is a syntax error');};</script>
配置vite.config.ts,将该页面打包成单个文件,并且需要开启sourcemap,方便测试。
// ...build: { sourcemap: true, rollupOptions: { input: { index: fileURLToPath(new URL('./index.html', import.meta.url)) }, output: { // 将依赖单独打包 manualChunks: (id: string) => { if (id.includes('node_modules')) { return 'vendor'; } if (id.includes('src/views/sourcemap/ErrorView')) { return 'errorView'; } return 'index'; }, } }},
2.4、捕捉Js错误
这里需要安装两个依赖包,分别用于解析错误和解析sourcemap文件。
pnpm i error-stack-parser source-map-js
Vue3提供了一个捕捉全局错误的回调函数app.config.errorHandler,去main.ts实现这个方法的回调函数。
// main.ts// ...// 捕捉错误app.config.errorHandler = (err: any, vm, info) => { console.log('err', err, '\nvm', vm, '\ninfo', info); const errorStack = ErrorStackParser.parse(err as Error); console.log('errorStack', errorStack);}
点击触发TypeError,errorHandler函数就会捕捉到错误,并打印出错误和 error-stack-parser处理err后的效果
把错误信息存储到localStorage,方便其他页面展示错误列表信息。
import { createApp } from 'vue';import App from './App.vue';import router from './router';import ErrorStackParser from 'error-stack-parser';const app = createApp(App);app.use(router);// 捕捉错误app.config.errorHandler = (err: any, vm, info) => { const errorStack = ErrorStackParser.parse(err as Error); const jsError = { stack_frames: errorStack, message: err.message, stack: err.stack, error_name: err.name }; console.error(`触发一个${err.name}错误`); localStorage.setItem('jsErrorList', JSON.stringify(jsError));}app.mount('#app');
2.5、展示错误列表
新建./views/sourcemap/ErrorList.vue错误列表页面,用于展示错误信息和映射到源码操作。
<template> <div v-if="isErr"> <pre> {{ js_error.stack }} </pre> <el-collapse v-model="activeNames" @change="handleChange"> <el-collapse-item v-for="(item, index) in js_error.stack_frames" :key="index" :title="item.source" :name="index"> <el-row :gutter="20"> <el-col :span="20">{{ item.fileName }}</el-col> <el-col :span="4"> <el-button type="primary" size="small" @click="openDialog(item, index)">映射源码</el-button> </el-col> </el-row> <el-row :gutter="20"> <template v-if="item.originalSource"> {{ item.originalSource }} <!-- <Preview :origin="item.originalSource" /> --> </template> <template v-else>{{ item.functionName }}</template> </el-row> </el-collapse-item> </el-collapse> <el-dialog v-model="dialogVisible" title="SourceMap源码映射" width="500"> <el-tabs v-model="tabActiveName"> <el-tab-pane label="本地上传" name="local"> <el-upload drag :before-upload="sourcemapUpload"> <i class="el-icon-upload"></i> <div>将文件拖拽到此处,或者<em>点击上传</em></div> </el-upload> </el-tab-pane> <el-tab-pane label="远程加载" name="remote"> </el-tab-pane> </el-tabs> </el-dialog> </div></template><script setup lang="ts">// https://github.com/Topskys/admin-pro/tree/monitor/src/views/sourcemap</script>
从localStorage获取错误数据初始化列表
const getErrorList = () => { try { const errorString = localStorage.getItem('jsErrorList'); if (!errorString) return; js_error.value = JSON.parse(errorString); isErr.value = true; } catch (error: any) { console.log('获取错误列表失败', error); }};onMounted(() => { getErrorList();});
弹窗用于上传本地打包后的sourcemap文件。
// 打开弹窗const openDialog = (item: any, index: number) => { dialogVisible.value = true; stackFrameObj = { line: item.lineNumber, // 错误代码行号 column: item.columnNumber, // 错误代码列号 index: index // 列表需序号 };};
错误列表
处理sourcemap上传事件和解析
const sourcemapUpload = async (file: any) => { if (file.name.substring(file.name.lastIndexOf('.') + 1) !== 'map') { ElMessage({ message: '请上传正确的sourcemap文件', type: 'error' }); return; } const reader = new FileReader(); reader.readAsText(file); reader.onload = async function (e: any) { const code = await getSource(e?.target?.result, stackFrameObj.line, stackFrameObj.column); js_error.value.stack_frames[stackFrameObj.index].originalSource = code; dialogVisible.value = false; }; return false; // 不写这一行会报错};
获取报错ErrorView.vue页面的sourcemap文件,一般真实项目会把打包后的源码和sourcemap文件分别部署到不同的服务器,来提升安全性。 注意在本地不能还原,上传sourcemap后会报错404,因此需要把项目部署到github 静态资源服务器。
// 请求并解析sourcemap文件const getSource = async (sourcemap: any, line: number, column: number) => { try { const consumer = await new sourceMap.SourceMapConsumer(JSON.parse(sourcemap)); const originalPosition = consumer.originalPositionFor({ line, column }); const source = consumer.sourceContentFor(originalPosition.source); // console.log('本地报错404source', source); return { source: source, line: originalPosition.line, column: originalPosition.column }; } catch (e) { ElMessage.error('sourcemap解析失败'); }};
2.6、错误源代码展示
新建./views/sourcemap/Preview.vue组件,用于处理和展示还原后的源代码。
<template> <div class="pre-code"> <div class="error-detail"> <pre class="error-code" v-html="preLine()"></pre> </div> </div></template>// js部分可参考github仓库monitor分支// https://github.com/Topskys/admin-pro/tree/monitor
2.7、构建CI/CD
基于github actions构建ci/cd流水线,这里就不详细赘述了,不知道的同学可以阅读这篇文章基于Github Actions实现前端CI/CD持续集成与部署_github cicd-CSDN博客
把触发流水线的分支改成monitor分支,因为当前是在monitor发开并测试sourcemap映射源代码。
三、效果
3.1、触发ci/cd
运行打包命令
提交代码到monitor分支触发流水线自动化部署。
3.2、测试还原
测试sourcemap还原错误源代码,访问https://topskys.github.io/admin-pro/#/生产错误页面,点击触发错误按钮触发错误。
打开错误列表页面,可以看到错误信息生成。展开列表第一个错误,点击映射源码。
选择上传errorView.vue报错页面对应的map文件(真正项目线上环境从其它服务器获取map文件),以解析出源代码。
上传之后,就能看到错误列表中已经还原出精准的行错误源代码(标红行)了,行号和源代码都一致。
具体代码实现过程可前往github仓库的monitor分支查看
admin-pro/src/views/sourcemap at monitor · Topskys/admin-prohttps://github.com/Topskys/admin-pro/tree/monitor/src/views/sourcemap
四、参考
基于Github Actions实现前端CI/CD持续集成与部署_github cicd-CSDN博客
重构Action-cli前端脚手架-CSDN博客
实现一个自定义前端脚手架_前端自定义脚手架-CSDN博客