vb.net process bar 显示异常_快速定位生产环境 javascript 异常堆栈对应的源码位置...

背景

前端项目发布到生产环境的最终代码一般是后缀为*.min.js的文件,这是对源码进行了一系列的转换(如babel)、压缩和混淆操作后的产物,经过这些步骤后的*.min.js代码具兼容性更好、体积更小、源码更安全的优点,但同时也导致阅读和调试目标代码更加困难了。

常见的场景之一是,生产环境上报了一个 JS 异常错误,除了从错误消息中可以获取到部分有用信息外(有时可能完全没用),无法从堆栈报错文件位置中获取有用的信息。

下面是一个JS异常错误:

TypeError: Cannot read property 'flow_id' of undefined
    at render (http://x.y.z.com/index.min.js:1:12314)
    at http://x.y.z.com/main_dep.8cba5f027cf7.min.js:1:135578
    at http://x.y.z.com/vendors/alpha.850360b98988406d8c20.min.js:119:45314

文件 index.min.js 的第1行的第12314列的附近代码:

function(e,t,n){"use strict";function i(e){return"function"==typeof e.dispose&&0===e.dispose.length}function o(e){for(var t=[],n=1;n<arguments.length;n++)t[n-1]=arguments[n];return Array.isArray(e)?(e.forEach((function(e){return e&&e.dispose()})),[]):0===t.length?e?(e.dispose(),e):void 0:(o(e),o(t),[])}

从上面堆栈信息中离报错地方最近的位置(index.min.js:1:12314),可以看到这个位置只有一行,明显已经被压缩成了一行,通过代码格式化工具美化后会好一些,但是其中的变量名均已被混淆,依然难以阅读,更糟糕的是像babel/webpack这类工具会插入一些额外代码到最终生成的代码,这些导致难以从目标代码报错位置中解读对应源代码的信息。

从上面的堆栈信息中我们已经知道了报错的是哪个文件,以及所在文件的行号和列号,现在缺的是如何根据这些信息映射回源码中?如果能完成这一步,就可以成功定位线上问题在源码中的准确位置,进而快速分析和修复问题。

sourcemap

早期编写 Web JS 代码是没有转换、压缩和混淆这些步骤的,源码即使最终生产环境部署的代码(知道现在依然有很多站点是这样的),所以不存在上面提到的映射问题。随着前端工程化的发展,各种工具被使用,我们编写的代码与最终部署到生产环境的目标代码已经天差地别了,为了解决目标代码映射回源码的问题,诞生了 sourcemap 技术,其与目标代码一起生成,记录了目标代码中每个字符在源码中的原始位置,有了它可以将目标文件映射回源文件。

8a512c705987664c52a69998c76199a3.png

下面是 sourcemap 的例子,其中的*.min.js是目标代码,*.min.js.map是对应的 source-map 文件(约定的命名规则是在目标文件后面加上.map)。

├── editor.worker.min.js
├── editor.worker.min.js.map
├── index.min.js
├── index.min.js.map
├── index.html

sourcemap 文件打开是 JSON 格式,其字段说明如下:

{
// source map的版本,目前(2020年)为 3
  "version" : 3,
// 转换后的输出文件名
  "file": "out.js",
// 转换前的文件所在的目录。如果与转换前的文件在同一目录,该项为空
  "sourceRoot" : "",
// 转换前的文件。该项是一个数组,表示可能存在多个文件合并
  "sources": ["foo.js", "bar.js"],
// 转换前的所有变量名和属性名
  "names": ["src", "maps", "are", "fun"],
// 记录位置信息的字符串
// 分号表示源码的一行
// 逗号表示源码的一个位置
// 逗号之间的base64字符串是以 VLQ 编码的数值,表示该位置在转换前后文件中的位置信息,编码方式及含义请阅读文末的参考链接
  "mappings": "...,SAAUvpH,QACnCiC;IAAlBjC,..."
}

要实现利用 sourcemap 将目标文件位置信息映射回源码文件,需要掌握 sourcemap 的编码规则和计算方式,幸运的是已经有现成的 mozilla/source-map[1] 库可以方便地完成映射过程。

示例

下面是使用 nodejs 编写的工具脚本,它从命令行接收目标文件报错的位置信息(文件名、行号、列号),并输出对应的源文件的文件路径、行号、列号和该位置的符号名称。

脚本 sourcemap.js

const fs = require("fs")
const path = require("path")
const { SourceMapConsumer } = require("source-map")

const location = process.argv[2]
const matches = /([^:]+):(\d+):(\d+)/.exec(location)
if (!matches) {
  console.log(`command 
    location 由::组成,例如 index.min.js:1:12314
`)
  process.exit(1)
}
const [, filename, line, column] = matches
const sourcemapPath = path.resolve(__dirname, `../dist/${filename}.map`)
const rawSourceMap = JSON.parse(fs.readFileSync(sourcemapPath))

// SourceMapConsumer 的第一个参数是 source-map 信息,其格式如下:
// const rawSourceMap = {
//   version: 3,
//   file: "min.js",
//   names: ["bar", "baz", "n"],
//   sources: ["one.js", "two.js"],
//   sourceRoot: "http://example.com/www/js/",
//   mappings: "CAAC,IAAI,IAAM,SAAUA,GAClB,OAAOC,IAAID;CCDb,IAAI,IAAM,SAAUE,GAClB,OAAOA"
// };
SourceMapConsumer.with(rawSourceMap, null, consumer => {
  const originInfo = consumer.originalPositionFor({
    line: parseInt(line),
    column: parseInt(column)
  })
  console.log(originInfo)
})

测试脚本

$ node sourcemap.js index.min.js:1:12314

# 输出
{ source: 'webpack:///src/routes/SampleManage/SampleListPage.jsx',
  line: 224,
  column: 26,
  name: 'record' }

小结

随着前端使用的工具越来越多,源代码和目标代码已天差地别,导致目标代码报错时难以关联源代码进行分析,幸运的是 sourcemap 技术弥补了其中的鸿沟,实现了目标代码到源代码的逆向映射。sourcemap 技术不仅用于 JS 代码,理论上可以应用于任意文本之间的映射,例如 css 代码到 less/sass 等逆向映射。本文最后的小工具可以实现快速定位线上目标代码报错,不过更好的方式是将 sourcemap 融入前端工程化体系,构建时自动生成、上传、存储,当生产环境目标代码报错时实时映射成对应版本的源代码。

如果对 sourcemap 的格式和转换原理感兴趣,可进一步阅读:

  • JavaScript Source Map 详解[2]
  • Introduction to JavaScript Source Maps[3]

参考资料

[1]

mozilla/source-map: https://github.com/mozilla/source-map

[2]

JavaScript Source Map 详解: https://www.ruanyifeng.com/blog/2013/01/javascript_source_map.html

[3]

Introduction to JavaScript Source Maps: https://www.html5rocks.com/en/tutorials/developertools/sourcemaps/

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值