1

背景

前端生产环境的代码都是经过压缩、合并、语言转换等操作的,导致日志系统收集到的错误信息都是像这样的:

TypeError: Cannot read property 'hhm' of undefined
    at o.getIconBgStyle (https://catflow-fe.gugugame.com/dist.v131/js/chunk-1981f8d5.3b0b8a28.js:1:2147)
    at https://catflow-fe.gugugame.com/dist.v131/js/chunk-1981f8d5.3b0b8a28.js:1:1122
    at o.Pt [as _l] (https://catflow-fe.gugugame.com/dist.v131/js/chunk-vendors.1e39618c.js:63:16912)
    at o.l (https://catflow-fe.gugugame.com/dist.v131/js/chunk-1981f8d5.3b0b8a28.js:1:975)
    at o.e._render (https://catflow-fe.gugugame.com/dist.v131/js/chunk-vendors.1e39618c.js:63:23733)
    at o.r (https://catflow-fe.gugugame.com/dist.v131/js/chunk-vendors.1e39618c.js:63:27824)
    at nr.get (https://catflow-fe.gugugame.com/dist.v131/js/chunk-vendors.1e39618c.js:63:30684)
    at nr.run (https://catflow-fe.gugugame.com/dist.v131/js/chunk-vendors.1e39618c.js:63:31417)
    at Xn (https://catflow-fe.gugugame.com/dist.v131/js/chunk-vendors.1e39618c.js:63:29624)
    at Array.<anonymous> (https://catflow-fe.gugugame.com/dist.v131/js/chunk-vendors.1e39618c.js:63:12761)

错误位置的行数和列数都很大,并且很多代码都压缩到了一行,即使定位到正确的地方也是混淆压缩过的代码,很难阅读,更别说依靠此来修复bug了。

理想状态

修复线上bug本身就是一个繁杂的工作,如果没有好的方法很工具会让难度提升很多。理想中的形式是像chrome的devtools一样点击报错信息的位置自动跳转到相关文件的相关行数,像这样:
image

实现原理

  1. 通过Source map文件逆向还原文件的原始状态。
  2. 编写一个交互体验友好的ui,点击错误信息跳转显示相应原始文件行数,并高亮提示错误位置。

步骤1

Source map是一个存储原始文件信息的文件,通过Source map可以将转换后的文件位置对应到原始文件的位置。具体关于Source map的原理就不展开。
无论你是使用webpack或者其它构建工具,都可以在构建的时候为每个js文件生成对应的Source map文件。
明白了Source map文件的作用,接下来就是用Source map文件获取原始文件,及如何定位错误位置。
使用source-map即可轻易做到上面提到的两个需求。

const { SourceMapConsumer } = require('source-map');
const line = 目标js报错行数
const column = 目标js报错列数
const rawSourceMap = JSON.parse('目标js的map文件字符串');
SourceMapConsumer.with(rawSourceMap, null, consumer => {
  const pos = consumer.originalPositionFor({
    line: line,
    column: column,
  });
  // pos.source 原始文件名
  // pos.line 原始文件报错行数
  // pos.column 原始文件报错列数
  // pos.name 原始错误位置的标识符
  sourceFileContent = consumer.sourceContentFor(pos.source) // 原始文件内容
});

以上通过简单几句代码即可获取到原始文件的内容报错行数报错列数

步骤2

步骤1中获取到了原始文件的内容报错行数报错列数
接下来就是通过前端展示原始文件内容,这里使用codemirror来展示原始文件的内容
在这之前我们先在页面中展示错误信息,并将错误信息中的文件名替换成原文件名,再把错误信息设置为红色,以达到模仿chrome的devtools的效果。替换后效果如下:
image
当用户点击其中任何一个文件名时,我们弹窗展示原文件的内容,并且通过codemirror的相关api滚动到报错行数,再将报错行高亮,并且把报错具体位置设置为红色背景。
完成上面步骤后,交互效果如下所示(gif加载较慢,请等待):
image

实现相关细节

高亮报错行及报错位置

codemirror上高亮显示报错的行可以使用下面的api给报错行添加一个class在通过css动画实现效果。

doc.addLineClass(line: integer|LineHandle, where: string, class: string)

报错位置设置背景色则可以通过以下api给报错的位置添加一个class在通过css去控制背景色。

doc.markText(from: {line, ch}, to: {line, ch}, ?options: object)

将报错行滚动到屏幕中间

const top = codemirror.charCoords({line: line - 1, ch: 0}, 'local').top
const middleHeight = codemirror.getScrollerElement().offsetHeight / 2
codemirror.scrollTo(null, top - middleHeight - 5)

hhm1999
12 声望0 粉丝