本节书摘来自异步社区《写给PHP开发者的Node.js学习指南》一书中的第1章,第1.1节,作者【美】Daniel Howard,更多章节内容可以访问云栖社区“异步社区”公众号查看
1.2 堆栈追踪
写给PHP开发者的Node.js学习指南
在转换过程中,会看到很多的堆栈信息,非常之多。以下示例显示了运行httpsvr.njs时,node-static npm包未安装导致的错误以及生成的堆栈信息:
堆栈信息的最上面一行显示了抛出异常的代码所在。这并不是产生错误的代码,而是创建并抛出error对象的代码。
这行之后,显示的是Error对象内部的错误信息。这个错误信息告诉了我们没有找到node-static模块。
其余的部分称为“调用栈”,是一系列由“at”开头的链式函数调用,直到到达抛出异常的代码。调用栈按照从内到外的顺序排列。在本例中,Function._resolveFilename()函数是调用栈的最顶端,表示它是最内层调用的函数,直接包含了抛出异常的代码。._resolveFilename()函数被Function._load()函数调用,该函数又被Module.require()函数调用,Module.require()又被require()函数函数,再上层就是Object.()函数,依次类推。
在调用栈中每个函数的调用之后,你都会看到包含该函数的源文件名,最后一行执行的代码(可能是调用函数之前的一行或者抛出异常的那行),以及该行代码中最后的执行位置。在上个例子中,可以看到涉及的两个文件是:module.js和httpsvr.njs。
module.js文件存在于node命令中,我们并不认为这是属于我们自己的代码文件,而httpsvr.njs则是我们自己的源代码。尽管调用栈中只显示了一次httpsvr.njs,但是可以认为引起错误的是我们自己的代码。一般来说,我们认为Node.js本身,其内建的模块,和其他安装好的npm模块都是安全可靠的。即使它们不是,我们也必须假定它们是正常工作的,除非我们证明排除了所有由我们的代码产生错误的可能。但是,即使我们发现了错误是由其他原因导致的,我们能控制的也只有我们自己的源代码。解决方式也应该是在我们的代码中找到变通方案而不是通过漫长而缓慢的过程找到其他开发人员来修改他们的代码。所以,结果是,不管最终的错误是什么,首先需要关注的都是httpsvr.njs文件。
我们需要关注的调用栈中的部分是:
函数调用在httpsvr.njs的第2行第14个位置。Httpsvr.njs文件为:
通过在源代码中交叉引用调用,用于加载的node-static模块的require()函数就是错误发生的地方。这和错误信息“Cannot find module ‘node-static’”一致。
如果我们再看看调用栈,可以看到栈上方的Function._load()函数和._resolveFilename()函数。看看这两个函数名,我们可以猜测Node.js环境加载该模块出错是因为找不到与模块相关的文件。所以可以做出假设找不到模块文件(可能是npm包)是因为模块没有安装。再一次,这验证了错误信息“Cannot find module ‘node-static’”。
Object.函数暗示我们,require()函数的调用是在全局范围内,而不是httpsvr.njs用户自定义范围。但是事实并不一定都是这样。一个匿名对象也有可能在用户自定义的函数内生成。但是,继续看调用栈,在Object.函数调用之后,我们看到调用者是module.js中的Module._compile函数。所以,require()函数是全局范围内的调用。
综合以上所有信息,一个解决方案是安装node-static npm package:
诚然,不需要每次在看Node.js调用栈时都做这些分析。但是你可能会看到很多很多的调用栈,你应该知道如何进行分析——特别是因为找到并修复错误占据了转换过程95%的时间。
总结一下,分析一个调用栈信息的过程是:找到错误,查看错误信息(如果有的话),做出假设并关注在自己代码中的特定函数调用,阅读代码并找出错误可能产生的位置,查看调用栈是否包含更多错误的可能信息,根据堆栈信息理解服务器如何执行到该调用函数。