如何优雅地查看 JS 错误堆栈?

  • 2019 年 12 月 30 日
  • 筆記

摘要: 堆栈是 Debug 的关键。

Fundebug经授权转载,版权归原作者所有。

在前端,我们经常会通过 window.onerror 事件来捕获未处理的异常。假设捕获了一个异常,上报的堆栈是这个:

TypeError: Cannot read property 'module' of undefined      at Object.exec (https://my.cdn.com/dest/app.efe91e855d7432e402545e7d6c25d2d9.js:16:29828)      at HTMLLIElement.<anonymous> (https://my.cdn.com/dest/app.efe91e855d7432e402545e7d6c25d2d9.js:25:6409)      at HTMLDivElement.dispatch (https://my.cdn.com/dest/vendor.eb28ded1876760b8e90973c9f4813a2c.js:1:248887)      at HTMLDivElement.y.handle (https://my.cdn.com/dest/vendor.eb28ded1876760b8e90973c9f4813a2c.js:1:245631)

这个堆栈,你看得出问题来吗?我们发布到 CDN 的脚本文件,普遍是经过 UglifyJS 压缩的,所以堆栈可读性相当的差。假如有下面的一个堆栈查看工具,又如何?

眼尖的同学,一眼就能找到问题。这里的 p[e] 出现了可能为 undefined 的情况。

这样一个工具,大大提高了问题定位的效率。

好,这里不卖瓜,我们来看下这当中的实现原理。

一步步来说的话:

  • 拿到原始堆栈字符串,使用 error-stack-parser 解析为堆栈帧,每个堆栈帧包含三个最重要的字段:
    • url – 源码的 URL 地址
    • line – 堆栈位置行号
    • col – 堆栈位置列号
  • 对于 url,我们可以用于加载源码内容,得到 source
  • source 使用 UglifyJs 反向美化成多行的代码 prettysource,并且同时生成 sourcemap
  • 堆栈帧中的 linecol 通过 sourcemap 反查,得到美化后对应的 prettylineprettycol
  • prettysourceprettylineprettycol 给到 Monaco Editor 渲染,就可以得到上述截图的效果

说那么多,不如贴代码是吧:

var result = UglifyJS.minify(source, {      output: {          beautify: true      },      sourceMap: {          filename: "pretty.js",          url: "pretty.js.map"      }  });  var code = result.code;  var rawSourceMap = JSON.parse(result.map);  var consumerPromise = new sourceMap.SourceMapConsumer(rawSourceMap);    resolve(      consumerPromise.then(function(consumer) {          return {              code: code,              sourceMapConsumer: consumer          };      })  );

上面就是使用 UglifyJs 对压缩代码进行反向美化的核心代码。下面给出 SourceMap 的使用源码:

var code = result.code;  var consumer = result.sourceMapConsumer;    var position = consumer.generatedPositionFor({      source: "0",      line: lineNumber,      column: columnNumber  });    parent.postMessage(      {          event: "js-prettify-callback",          payload: {              hash: payload.hash,              result: "success",              prettySource: code,              prettyLineNumber: position.line,              prettyColumnNumber: position.column + 1          }      },      sourceOrigin  );

完整源码有兴趣的读者也可以下下来把玩把玩:

js-loader.html.zip

源码只包含堆栈解析的实现,UI 的实现不在本文的讨论之内,用 React 随便画一画就好了。

版权声明

转载时请注明作者 Fundebug以及本文地址: https://blog.fundebug.com/2019/03/08/how-to-check-javascript-stacktrace/

您的用户遇到BUG了吗?

体验Demo 免费使用

.copyright *{box-sizing:border-box}