Edge 浏览器:
异常监控系统
这里以 sentry
为例 简单演示 一下,大致包括:
- 接入 sentry
- 异常捕获
- 添加 sourcemap
首先,在 Sentry
监控平台上注册/登录拥有自己的账号,然后可以构建一个对应的项目(首次注册登录会有指引),项目创建好以后会生成一个 dsn
,在接入 sentry
时需要传入。
其次,按照引导页的提示在你的项目(以 vue3
为例)中安装依赖 npm install --save @sentry/vue @sentry/tracing
。
最后,在你项目入口文件(main.js
)中初始化接入 Sentry
即可:
import { createApp } from "vue"; import { createRouter } from "vue-router"; import * as Sentry from "@sentry/vue"; import { BrowserTracing } from "@sentry/tracing"; const app = createApp({ // ... }); const router = createRouter({ // ... }); Sentry.init({ app, dsn: "https://x@x.ingest.sentry.io/x", integrations: [ new BrowserTracing({ routingInstrumentation: Sentry.vueRouterInstrumentation(router), tracePropagationTargets: ["localhost", ...], }), ], // Set tracesSampleRate to 1.0 to capture 100% // of transactions for performance monitoring. // We recommend adjusting this value in production tracesSampleRate: 1.0, }); app.use(router); app.mount("#app"); 复制代码
经过以上处理,默认情况下 Sentry
已经可以自动获取到错误信息了,如:
显然,没有接入 sourcemap
的错误信息在 sentry
中也无法进行快速定位,因此下一步就是需要给 sentry
上传 sourcemap
相关的文件。
sentry 上传 sourcemap 流程(
sentry 文档
)
- 安装 webpack 插件:
npm install --save-dev @sentry/webpack-plugin
- 配置 webpack 插件
// vue.config.js const { defineConfig } = require('@vue/cli-service') const SentryWebpackPlugin = require('@sentry/webpack-plugin') module.exports = defineConfig({ configureWebpack(config) { if (process.env.NODE_ENV === 'production') { config.devtool = 'hidden-source-map' config.plugins.push( new SentryWebpackPlugin({ include: './dist', ignoreFile: '.gitignore', ignore: ['node_modules', 'webpack.config.js'], configFile: './.sentryclirc', release: '0.0.1', urlPrefix: '~/js/', }), ) } }, }) 复制代码
- 在项目根目录下新建
.sentryclirc
文件
[auth] token = 在 sentry 平台生成 [defaults] url = https://sentry.io/ // 如果是自己部署的就填部署地址,如果不是就不改 org = sentry 平台的 org project = sentry 平台的 project 复制代码
- 如皋设置
release
字段,那么要保证main.js
中的Sentry。init({...})
和SentryWebpackPlugin
中的要保持一致性,或者都不设置 - 构建产物
npm run build
- 进入
dist
目录通过http-server
启动本地服务模拟生产环境产生错误 - 进入
sentry
中查看异常信息,上传sourcemap
文件后,错误信息如下:
手动映射
通常情况下为了安全性,不推荐使用 浏览器 映射的方式,虽然这种方式对你来说很简便,但也为别有用心的人提供了便捷,因此,通常都会有接入对应的 监控平台,当然除此之外还可以通过 手动映射 的方式进行定位:
- 安装
source map
库:npm install source-map
- 新建
sourcemap.js
const { SourceMapConsumer } = require('source-map') const fs = require('fs') const rawSourceMap = fs.readFileSync('./dist/js/app.dde017e5.js.map', 'utf-8') // 填入错误信息 originalPositionFor('app.dde017e5.js:1:11871') function originalPositionFor(errInfo) { const [budleName, line, column] = errInfo.split(':') SourceMapConsumer.with(rawSourceMap, null, (consumer) => { const originalPosition = consumer.originalPositionFor({ line: parseInt(line), column: parseInt(column), }) console.log('bundle name = ', budleName) console.log('original position = ', originalPosition) }) 复制代码
- 演示如下:
sourcemap 的映射原理
在 .map
文件中有 mappings
字段,其内容很难让人不注意,毕竟和其他内容相比看起来太与众不同了:
实际上,mappings
以 Base64 VLQ 编码形式存储了映射到源代码 行、列 等相关信息。
为什么使用 Base64 VLQ 编码?
源代码通常都是很庞大的,单纯使用 数字 表示 行信息 和 列信息 会使得整个 .map
文件体积变大,而 Base64 VLQ 是一种 压缩数字 内容的编码方式,因此可以用来减少文件体积。
由于 Base64 所能表示的数字存在 上限,如果需要表示超过上限的数字该怎么办,实际上只有 每个分号中的第一串英文 是用来表示代码的 第几行、第几列 的绝对位置外,后面的都是相对于之前的位置来做 加减法 的。
可以通过 BASE64 VLQ CODEC 这个网站了解具体的映射关系.
mappings 的组成
mappings
的内容主要由三部分组成:
- 英文串
- 每段英文串表示 运行时代码 和 开发时代码 位置关联的 base64VLQ 编码内容
- 每段英文串拥由 5 部分组成:
运行时代码
所在的列,通常源代码经压缩后只有1行
,因此不需要存储行信息,只需要存储列信息- 对应
sources
字段下标,即对应哪个源文件 开发时代码
的第几行开发时代码
的第几列- 对应
names
字段下标,即对应哪个变量名
- 逗号
,
- 用于分隔一行代码中的内容或位置,例如
"var a = 1;console.log(a);"
相当于"var, a, =, 1, console, log"
- 分号
;
- 表示 运行时代码 的行信息,用来定位是编译后代码的第几行,如果启用代码压缩那么就不会有 分号,因为代码会被压缩在一行上
简单解析 mappings
下面以 console.log(1);
为例子简单介绍下对应关系,毕竟源码内容复杂的不好分析:
// main.js console.log(1); // main.js.map { "version": 3, "file": "main.js", "mappings": "AAAAA,QAAQC,IAAI", "sources": ["webpack://vue3-wp5/./src/main.js"], "sourcesContent": ["console.log(1);\n"], "names": ["console", "log"], "sourceRoot": "" } 复制代码
还是先关注 mappings
字段,其内容由于是编码后的内容,为了更直观的看到其代表的具体数字内容,我们可以通过 BASE64 VLQ CODEC 网站来得到结果:
现在,我们知道了 "AAAAA,QAAQC,IAAI"
对应 [0,0,0,0,0], [8,0,0,8,1], [4,0,0,4]
,结合上述其内容就表示:
[0,0,0,0,0]
对应console
- 0 :编译代码 第
0
列 - 0 :对应
sources[0]
,即main.js
- 0 :源代码 第
0
行 - 0 :源代码 第
0
列 - 0 :对应
names[0]
,即console
[8,0,0,8,1]
对应log
- 8 :编译代码 第
8
列,其实是 第8+0=8
列 - 0 :对应
sources[0]
,即main.js
- 0 :源代码 第
0
行 - 8 :源代码 第
8
列,其实是 第8+0=8
列 - 1 :对应
names[1]
,即log
[4,0,0,4]
对应1
- 4 :编译代码 第
4
列,其实是 第8+4=12
列 - 0 :对应
sources[0]
,即main.js
- 0 :源代码 第
0
行 - 4 :源代码 第
4
列,其实是8+4=12
列 - 不是变量,因此没有和
names
相关信息
是不是有些奇怪,明明 1
的位置比 log
的位置更靠后,为什么编码显示的列数却更小,别忘了下面这个规则:
实际上只有 每个分号中的第一串英文 是用来表示代码的 第几行、第几列 的绝对位置外,后面的都是相对于之前的位置来做 加减法 的
即实际显示的列号数应为:
最后
以上就是对 sourcemap
相关内容的介绍,希望本文对你有所帮助!!!
说个题外话,有人好奇我哪有那么内容要写,其实大多文章内容只是将自己需要解决或者同事遇到的问题进行总结和扩展而已,所以大多数文章的想法就来源于此,其次我认为写文章的原则就是:写出来的文章首先要保证自己有收获! 另外,更多的是看看各位掘友对同一个问题都会有什么更好的方案!