Node错误处理笔记之挖坑系列教程
前言
前段时间要在项目中加上日志的上报,顺势了解了下怎么在node中完善错误信息的收集。下面话不多说了,来一起看看详细的介绍吧
01 error
js 中的 error 对象,包含了错误的具体信息,包括 name、message、错误堆栈 stack 等。可以以 new error 方式创建实例抛出,或调用 error.capturestacktrace 为已有对象添加 stack 错误堆栈信息 而后抛出。
02 错误抛出几种方式
* throw*:javascript 抛出的异常,是以 throw 方法抛出,未必都是 error 的实例,但通过 nodejs 或者 js 运行时发生的错误,都是 error 的实例。
* eventemitter*:nodejs 形式的错误回调,大部分流 & 异步事件都衍生自 eventemitter 类 || 实例,如 fs, process, stream 等。
process:程序运行过程中抛出的异常,或由底层库抛出,或是运行中发生的一些 syntaxerror 之类。
03 错误捕获几种方式
try .. catch:常用的一种捕获错误方式,浏览器 || node 环境均适用。
缺点:只针对同步异常有效。
*eventemitter *
由 events 模块提供的 eventemitter 类,基于 observer 模式做的 publish/subscribe,通过 .on('error', ...)|| .addeventlistener('error', ...) 注册 subscriber,.emit() 发布事件,但会有最大的 maxlistener 的限制,可更改。
不 show 源码了,特别简单,自己去 look 一下。如 koa 的 app 就是基于 eventemitter 的扩展,因此可以通过监听 error。
*process *
process 进程对象也是 eventemitter 的实例,可通过如下两事件监听 error。
unhandledrejection :promise 的回调报错,可通过监听该事件 catch,但要注意由于 promise 的 rejection 不知道会在啥时候才发生,所以实际上可能在 unhandledrejection 事件触发后才 catch 了这个信息,对应有 rejectionhandled 事件监听,如下:
(承上) uncaughtexception :其余 js 运行中发生 || 抛出的未捕获错误,均可通过监听该事件解决,若不进行该事件的监听,发生异常时,会直接导致程序 crash。但不建议用这种方式 catch,程序运行中的错误 更应该是抛出来,所有的 error 都 catch 的话,岂不是程序都可以看成无 bug 了。but 在打错误日志的时候是需要 catch 上报日志的,但是在上报完后,需要继续把 error throw,对于 uncaughtexception callback 中抛出的异常不会再捕获,而是以非 0 的状态码 exit。
04 小结
说了这么多,就做个小小总结吧 以上关系网可以概括为如图:
个人见解,实际处理中基于几点准则:
1、对于需要具象化的错误信息,也就是我们需要知道具体是哪一块的错误,并且在错误发生时即进行个性化处理。这一类型的错误,在程序中执行时要对可能会出错的函数进行 catch,方式有多种: try ... catch(同步); 或 通过 node 形式的标准错误回调 (err) => {...}(要求所调用的方法,支持该种写法); 或 监听 error 事件 .on('error', err => {...})(要求所调用的对象继承自 eventemitter 实例,并内部发生错误时,会 emit('error'))。
2、promise 形式的错误,可在 promise 实例的末尾再引入 .catch,可捕获该实例所有 then 中抛出的异常;另外 async 的引入也允许了我们可以如同步操作一样捕获错误。
3、但有时候程序的运行,不可能针对所有的函数操作等都去 try ... catch,也不能保证程序运行一定就无 bug,但必须要能够把这些异常都捕获并上报。因此 process 的两个大 boss: unhandledrejection & uncaughtexception,是必须引入的,在程序最外层引入,且越靠前越好。此外针对 unhandledrejection,为防止说把不必要的 rejection 上报,可以在收到事件时,先把被 reject 的 promise 和 error 存储起来,设置时间 maxtimeout ,超过 maxtimeout 仍未收到 rejectionhandled 的 promise 事件,再上报。
koa 项目为例:
05 源码解读
下图为 node 中对于 process 的初始化等系列流程:
node.cc 其实是 node 运行主要的文件,其中定义了三个重载函数 start,调用顺序为 3 → 2 → 1,每个函数参数不同处理不同的逻辑;
isolate->addmessagelistener 的监听事件 onmessage 会在 js 运行发生错误时触发,嗯,是的,只有 error 才会传递 ;这很好的解释了为什么 process 监听 uncaughtexception 就可以监听到所有的抛出的非 promise 异常;
onmessage 调用了 fatalexception 函数,fatalexception 引用 process.fatalexception 并传入 error (env 是 env.h 中声明的 environment 类实例,fatalexceptionstring 也是其中定义的,返回值为 'fatalexception'); 以下为 fatalexception 函数的部分内容。
在调用 fatalexception 函数前,先调用 v8::trycatch::setverbose 把 verbose 设置为 false,则运行时抛出的异常就不会再触发 fatalexception ;在捕获到运行错误后,把 exitcode 设为 7,并返回;这就解释了为什么在 uncaughtexception 时抛出的异常不会再重新触发回调,要知道 eventemitter 可没帮你做这样的事情。
_fatalexception 在触发 "uncaughtexception" 事件前其实是会优先检查 domain 是否存在,当 domain 不存在时才会调用 uncaughtexception 的,但 domain 这个已经被废除了,也就不累述了。
unhandledrejection 事件的触发整体思路也差不多,首先会调用 setuppromises 初始化,然后调用 v8::isolate::setpromiserejectcallback 进行监听 ... 并且跟 uncaughtexception 不同的是 unhandledrejection 的触发只会打印 warning 并不会把整个程序给 crash 了。
06 end
参考网站: https://v8.paulfryzel.com/docs/master/index.html
总结
以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对的支持。
上一篇: 在ASP应用中验证用户身份(4)
下一篇: 深入讲解Java编程中类的生命周期