起源

Node 应用功能越来越复杂,很多业务都开始尝试使用 TypeScript 来开发。现在前端写的 JS 大部分是经过编译过程的,浏览器中通过 source map 的使用,可以很好的解决源码和编译运行时代码差异的问题。

那么,在 Node 服务器环境应该如何使用 source map 呢?最近在重新搭建一个完全基于 ts 的 Node 应用,所有的相关流程看起来都挺好的,唯一的缺陷是报错信息错误信息指向的是 js 文件。我觉得应该探索下如何让 Node 支持 source map 。

原理

对于 Node 而言,服务器 source map 最大的价值在于错误信息有正确的错误堆栈,所以只要我们能够实现自定义错误堆栈信息就可以了。

恰好 v8 引擎有提供一个私有的 (Stack-Trace-API), 这个提供了让开发者自定义错误 stack 信息的能力。具体来说,开发者可以实现 Error 对象的 prepareStackTrace 方法,如果 Error 对象上定义了这个方法,那么每次错误信息都会经过 Error.prepareStackTrace 处理后返回。

Error.prepareStackTrace 方法可以拿到两个参数,错误基本信息和结构化错误堆栈,第二个参数是一个数组,通过这个数组可以拿到错误文件以及位置信息。最后基于这些信息重新返回一个字符串,这样就可以覆盖 Error 对象的 stack 属性了。

基本代码结构如下:

function prepareStackTrace(error, stack) {return error + stack.map(function(frame) {return '\n    at ' + wrapCallSite(frame);;}).join('');
}
Error.prepareStackTrace = prepareStackTrace;

wrapCallSite 方法里面可以通过分析源码,找到 sourceMap 然后返回正确的位置信息。

原理很简单,已经有一个 npm 包 source-map-support 封装好了相关功能。

这看起来已经很完美了。source map 读取只在出现错误的时候才执行,所以这个功能不会有性能问题,在生成环境也可以开启

问题

Stack Trace API 看起来很美好,但现实场景总是更加复杂。我在引入 source-map-support 后,运行起来没什么问题,但在跑测试用例的时候,错误堆栈的位置信息完全不对。

这个问题排查了很久,最终定位到在 wrapCallSite 方法中拿到的 frame 对象返回的行号就是错误的,而这个获取行号的方法是 native code ,这个几乎没法调试了。我想,难道是 Node 的问题?要调试到 Node 源码么?

折腾了很久没有什么效果,就在我打算放弃的时候,我换了一个假设,会不会是某个包依赖影响的?然后我尝试依次删除跑用例时 require 的包,终于发现是因为 egg-bin 默认引入的 power-assert 导致的。

问题定位到后,解决就容易了。但解决这个问题得先讲讲 power-assert 是如何实现的。

power-assert 与 sourceMap

power-assert 作为一个断言库,最大的特色在于错误信息的报告是非常友好的,一张图可以很清晰看到区别

实现这样炫酷的报告是需要做一些特殊的处理,把测试用例的代码进行一次转换,举个例子

it('foo', function foo() {var a = 'foo';var b = 'b';assert(a === b);
});

经过 espower-source 处理后,变成了这样

it('foo', function foo() {var a = 'foo';var b = 'b';assert(expr(capture(capture(a, '/0/left') === capture(b, '/0/right'), '/0'), {content: 'assert(a === b)',filepath: 'bizLogger.test.ts',line: 107}));
})

注:上面的代码不是真实运行的代码,经过一些删减

对于 assert(a === b); 这样一个表达式,会通过 capture 捕获每一个运算过程的位置和值,最终通过 expr 运算。这样经过转换后,代码运行逻辑不变,但是异常发生的时候可以返回 assert 表达式中每一步的返回值。

我所遇到的问题也就是因为 power-assert 对代码进行了转换,最终异常抛出时,真实 js 异常位置信息是转换后的位置,这个位置自然是无法正确定位到源码位置了。

但只运行 power-assert ,不引用 source-map-support 的时候,错误行号还是对的。这是因为 espower-source 返回重新编译后的源码后,还同时对源码文件的 sourceMap 进行了重新转换。所以大部分情况,我们是无法感知到源码有经过重新编译。

运行简单流程图如下

解决问题

回到最初的问题,跑用例的时候行号不对了。power-assert 的影响在于两点

  1. 测试文件源码会被 power-assert 修改,增加一些信息收集代码
  2. power-assert 同时有引入 source-map-support 来对错误堆栈进行重新定位

当我在我自己的业务中也引入 source-map-support 来重新定位错误堆栈时,我所拿到的源码是被 power-assert 修改过的,所以这时候是无法重新定位到正确的 ts 位置了。

既然 power-assert 有引入 sourceMap ,那么是不是我关闭自己引入的 sourceMap 就可以了呢?理论上是应该如此的,但是因为 power-assert 对 sourceMap 文件不支持(inline sourcemap 是支持的),所以只能定位到 js 源码,无法定位到 ts 源码。

问题确定了,就可以自己动手解决了,我发了一个 mr 让 espower-source 支持 sourceMap 文件,最新版的 power-assert 已经可以正确定位到 ts 源码位置了。

另外,还有一个问题,正常情况 source-map-support 同时引入两遍,只要引入的文件路径一直,也是不会有问题的。但 power-assert 使用的还是老版本 source-map-support ,而且老版定位位置信息还是不够准确,这个也很好解决,升级依赖版本既可以。

这两个问题解决后,在自己的业务用引入 source-map-support 也没有问题了,power-assert 返回的错误堆栈也可以正确的指向 sourceMap 位置了。

总结

基于 V8 的 Stack Trace API 的使用,浏览器的 sourceMap 能力也可以应用到 Node 服务器场景下,使用 npm 包 source-map-support 就可以了。

有时候可能会遇到一些奇怪的错误行号的问题,这可能是某个依赖包对 js 进行了转换,毕竟这在前端太常见了,动不动就重新编译 js 源码。

Node.js 中 source map 使用问题总结相关推荐

  1. node/js 漏洞_6个可用于检查Node.js中漏洞的工具

    node/js 漏洞 Vulnerabilities can exist in all products. The larger your software grows, the greater th ...

  2. node.js中模块_在Node.js中需要模块:您需要知道的一切

    node.js中模块 by Samer Buna 通过Samer Buna 在Node.js中需要模块:您需要知道的一切 (Requiring modules in Node.js: Everythi ...

  3. boa支持https_Boa: 在 Node.js 中使用 Python

    Hello,大家好,有一段时间不见了. 这次主要给大家带来一个好东西,它的主要用途就是能让大家在 Node.js 中使用 Python 的接口和函数.可能你看到这里会好奇,会疑惑,会不解,我 Node ...

  4. 掌握Node.js中的Async和Await

    在本文中,你将学习如何使用Node.js中的async函数(async/await)来简化callback或Promise. 异步语言结构在其他语言中已经存在了,像c#的async/await.Kot ...

  5. react性能优化方案_React灵敏且性能卓越的Spray + Akka解决方案,以“在Java和Node.js中发挥并发性和性能”...

    react性能优化方案 在我以前的文章中,我研究了一个虚拟的交易引擎,并将基于Java的阻止解决方案与基于Node.js的非阻止解决方案进行了比较. 在文章的结尾,我写道: 我怀疑随着Node.js的 ...

  6. 反应灵敏且性能卓越的Spray + Akka解决方案,以“在Java和Node.js中发挥并发性和性能”...

    在我以前的文章中,我研究了一个虚拟的交易引擎,并将基于Java的阻止解决方案与基于Node.js的非阻止解决方案进行了比较. 在文章的结尾,我写道: 我怀疑在Node.js近期取得成功之后,越来越多的 ...

  7. 在 Node.js 中操作 Redis

    在 Node.js 中操作 Redis Node.js 中可以操作 Redis 的软件包推荐列表:https://redis.io/clients#nodejs. 推荐下面两个: node-redis ...

  8. Node.js中使用百度统计API统计站点的访问量、访客数、IP数相关信息

    详情访问:详情 欢迎来我的个人网站:TanJia 今天,在个人网站的关于版块,添加了本站点的总访问量.总访客数以及当日的访问量等相关信息.使用的是百度统计的相关API. 首先,我们需要在百度统计中开通 ...

  9. 一道题学习node.js中的CRLF注入

    前言 这几天刷题遇到在node.js题目中注入CRLF实现ssrf的题目,对于我来说知识听新颖.在此记录一下. CRLF注入 学习过http请求走私漏洞的师傅对于这个CRLF肯定不会陌生.所谓的CRL ...

最新文章

  1. 全球智能驾驶产业全景图
  2. DevExpress v15.1:WPF控件升级(四)
  3. Paxos 实现日志复制同步(Multi-Paxos)
  4. java synchronized wait
  5. 数据结构课上笔记13
  6. 完全二叉树的判断java,判断二叉树是否为完全二叉树的实例
  7. mysql维护计划_MySQL服务维护笔记
  8. mac osx 系统 brew install hadoop 安装指南
  9. 金蝶检测服务器响应异常,连接金蝶云服务器异常 请检查
  10. 14072202(带IK的Recoil)
  11. 云大使成长精华指引(全)
  12. div html 下边加横线,如何在HTML文本下添加一个虚线下划线
  13. C语言-输入任意多个数字,存到整型数组,支持任意间隔符,同时支持输入字母存到字符数组中
  14. 【Xmind】Linux
  15. 排队论在食堂排队中的运用
  16. java.io.CharConversionException: isHexDigit JS转码问题
  17. python ansys workbench联动_【干货】经典ANSYS 与 Workbench如何实现联合仿真,相互切换操作。...
  18. 计算机网络 高层实验,清华大学计算机网络实验指示书
  19. 《ZigBee开发笔记》第二部分 基础篇-第5章 CC2530 PWM调光
  20. 矿大北京迅飞组 | 愿热爱长存!

热门文章

  1. LeetCode 2 两数相加
  2. 聚合Aggregation与合成Composition
  3. Swift监听网络状态
  4. mysql 导入导出 csv_mysql 导出导入数据 -csv
  5. python模块之datetime
  6. python --那些你应该知道的知识点
  7. 基于ThinkPHP的在线编辑器调用
  8. ThinkPHP URL模式和URL重写
  9. navicat for mysql 显示中文乱码解决办法
  10. 获取AD用户和OU属性字段名称