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

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对我们的支持。

(0)

相关推荐

  • 使用nodejs中httpProxy代理时候出现404异常的解决方法

    在公司中使用nodejs构建代理服务器实现前后台分离,代码不能拿出来,然后出现httpProxy代理资源的时候老是出现404.明明被代理的接口是存在的.代码大概如下: var http = require('http'), httpProxy = require('http-proxy'); var proxy = httpProxy.createProxyServer({}); var server = http.createServer(function(req, res) { proxy.

  • 使用Raygun对Node.js应用进行错误处理的方法

    用我们的 raygun4node 包,能提供一种把您的Node.js错误发送给Raygun的便利办法. 它可以很容仪的使用 npm 安装: npm install raygun 其能给您提供一个raygun客户端,您可以用它来配置您的API key,并且可以用来手动发送错误消息. 但稍后你可能会说, "我不想手动地把所有的错误都发给Raygun,那样听起来像是有大量的工作要做!" 如果你正用着 express.js ,那么用express的处理器就可以很容易解决这份担忧了. var r

  • NodeJS处理Express中异步错误

    摘要 比起回调函数,使用 Promise 来处理异步错误要显得优雅许多. 结合 Express 内置的错误处理机制和 Promise 极大地降低产生未捕获错误(uncaught exception)的可能性. Promise 在ES6中是默认选项.如果使用 Babel 转译,它也可以与 Generators 或者 Async/Await 相结合. 本文主要阐述如何在 Express 中使用错误处理中间件(error-handling middleware)来高效处理异步错误.在 Github 上

  • 解析Node.js异常处理中domain模块的使用方法

    NodeJS 提供了 domain 模块,可以简化异步代码的异常处理.在介绍该模块之前,我们需要首先理解"域"的概念.简单的讲,一个域就是一个 JS 运行环境,在一个运行环境中,如果一个异常没有被捕获,将作为一个全局异常被抛出.NodeJS 通过 process 对象提供了捕获全局异常的方法,示例代码如下 process.on('uncaughtException', function (err) { console.log('Error: %s', err.message); });

  • Nodejs异步回调之异常处理实例分析

    本文实例讲述了Nodejs异步回调之异常处理.分享给大家供大家参考,具体如下: 目前我们项目的Nodejs异常是通过express next 到 errorhandler 中间件去处理的, 原本以为此方法可以捕获到所有的异常,但事实发现并非如此. 下面以一个异常举例子: req.get('',function(req, res, next){ var a = undefined.b; // 产生了一个exception }) req.use(function(){req, res, next}{

  • 浅谈NodeJs之数据库异常处理

    本文介绍了NodeJs之数据库异常处理,分享给大家,具体如下: NodeJs版本:4.4.4 数据库链接错误 使用nodejs处理异常最麻烦不过,这里我抛开nodejs提供的domain和一些第三方库专门处理的东西.操作数据库是我们常用的功能.通过回调,我们这里会有很多err出没. 如下: var pool = require('../db.js'); var runtimeLog = require('../log.js').getLogger('runlog'); var Promise =

  • Nodejs异步回调的优雅处理方法

    前言 Nodejs最大的亮点就在于事件驱动, 非阻塞I/O 模型,这使得Nodejs具有很强的并发处理能力,非常适合编写网络应用.在Nodejs中大部分的I/O操作几乎都是异步的,也就是我们处理I/O的操作结果基本上都需要在回调函数中处理,比如下面的这个读取文件内容的函数: 复制代码 代码如下: fs.readFile('/etc/passwd', function (err, data) {   if (err) throw err;   console.log(data); }); 那,我们

  • Node.js 异步异常的处理与domain模块解析

    异步异常处理 异步异常的特点 由于node的回调异步特性,无法通过try catch来捕捉所有的异常: try { process.nextTick(function () { foo.bar(); }); } catch (err) { //can not catch it } 而对于web服务而言,其实是非常希望这样的: //express风格的路由 app.get('/index', function (req, res) { try { //业务逻辑 } catch (err) { lo

  • Node错误处理笔记之挖坑系列教程

    前言 前段时间要在项目中加上日志的上报,顺势了解了下怎么在node中完善错误信息的收集.下面话不多说了,来一起看看详细的介绍吧 01 Error JS 中的 Error 对象,包含了错误的具体信息,包括 name.message.错误堆栈 stack 等.可以以 new Error 方式创建实例抛出,或调用 Error.captureStackTrace 为已有对象添加 stack 错误堆栈信息 而后抛出. 02 错误抛出几种方式 * Throw*:Javascript 抛出的异常,是以 thr

  • 使用Node.js搭建静态资源服务详细教程

    对于Node.js新手,搭建一个静态资源服务器是个不错的锻炼,从最简单的返回文件或错误开始,渐进增强,还可以逐步加深对http的理解.那就开始吧,让我们的双手沾满网络请求! Note: 当然在项目中如果有使用express框架,用express.static一行代码就可以达到目的了: app.use(express.static('public')) 这里我们要实现的正是express.static背后所做工作的一部分,建议同步阅读该模块源码. 基本功能 不急着写下第一行代码,而是先梳理一下就基

  • LNMP系列教程之一 添加域名建立站点

    昨天老左分享了"Linux VPS CentOS安装LNMP系统环境教程",有些朋友说网上已经有过类似的教程,可能你再发布有些多余.我认为还是有必要的,一来是我自己学习使用,二来可以作为记录自己使用的时候笔记,以便下次使用的时候直接利用起来,同时别人的毕竟是别人的,自己还是需要梳理.就像我从现在开始,整理的关于LNMP的系列教程一样,从这一篇开始我将把LNMP的使用全过程做一个记录,一直到最后的维护升级等等.我相信通过这么一个积累,也是为以后跟我一样的新手可以有一个很好的参考.大家认为

  • Spring MVC温故而知新系列教程之请求映射RequestMapping注解

    RequestMapping注解说明 @RequestMapping注解的作用将Web请求映射到特定处理程序类和/或处理程序方法,这个注解可以用于类或者方法上,并通过属性value指定请求路径.用在Controller类上表示提供初步的URL请求映射信息,相对于Web应用的根目录,这是一个前置请求路径.用在Controller中方法上,表示提供详细的URL映射.如果Controller类上没有加RequestMapping注解,则方法上注解标记的URL则是相对于Web应用的根目录. @Reque

  • MySQL系列教程小白数据库基础

    目录 1.基础概念与基础命令 1)基础概念 2)基础命令 2.SQL语句的书写顺序和执行顺序 1)一个完整SQL查询语句的书写顺序 2)一个完整的SQL语句执行顺序 3)关于select和having执行顺序谁前谁后的说明 这里介绍的MySQL相关知识,并不是针对那些想学习DBA的同学,而是针对那些想转行.从事数据分析行业的同学. 下面这些知识点,是我学习MySQL数据库时,觉得有必要掌握的一些知识点. 1.基础概念与基础命令 1)基础概念 ① 数据库 按照一定数据结构,存储和管理数据的仓库.

  • SpringBoot系列教程之防重放与操作幂等

    目录 前言 具体方案 前端页面 Redis 数据库 后记 代码开源 总结 前言 日常开发中,我们可能会碰到需要进行防重放与操作幂等的业务,本文记录SpringBoot实现简单防重与幂等 防重放,防止数据重复提交 操作幂等性,多次执行所产生的影响均与一次执行的影响相同 解决什么问题? 表单重复提交,用户多次点击表单提交按钮 接口重复调用,接口短时间内被多次调用 思路如下: 1.前端页面表提交钮置灰不可点击+js节流防抖 2.Redis防重Token令牌 3.数据库唯一主键 + 乐观锁 具体方案 p

  • MySQL系列教程之使用C语言来连接数据库

    目录 写在前面 一.准备工作 1.1 把 libmysql.dll 和 libmysql.lib 文件复制到工程目录下 1.2 添加 libmysql.lib 1.3 添加 include目录 1.4 包含头文件 1.5 其他准备工作 1.5.1 代码准备 1.5.2 创建数据库连接 1.6 选择相应方案配置 Release,解决方案平台 x64 二.代码测试案例 2.1 向数据库插入数据 2.2 向数据库删除数据 2.3 修改数据 2.4 查询数据,打印出来 三.使用C语言连接MySQL数据库

  • es6系列教程_ Map详解以及常用api介绍

    ECMAScript 6中的Map类型是一种存储着许多键值对的有序列表.键值对支持所有的数据类型. 键 0 和 '0'会被当做两个不同的键,不会发生强制类型转换. 如何使用Map? let map = new Map(); 常用方法: set( 键,值 ): 添加新的键值对元素 get( 键 ): 获取键对应的值,如果这个值不存在,返回undefined let map = new Map(); map.set( '0', 'ghostwu' ); map.set( 0, 'ghostwu' )

  • 彻底弄懂CSS盒子模式系列教程集合

    blueidea终于可以出点让人惊呀的东西了,一直对clear和position不太理解,没想到这次的系列教程全给提及到了,不错,赞一下. 彻底弄懂CSS盒子模式之一彻底弄懂CSS盒子模式之二彻底弄懂CSS盒子模式之三彻底弄懂CSS盒子模式之四彻底弄懂CSS盒子模式之五

  • js es6系列教程 - 新的类语法实战选项卡(详解)

    其实es6的面向对象很多原理和机制还是ES5的,只不过把语法改成类似php和java老牌后端语言中的面向对象语法. 一.用es6封装一个基本的类 class Person{ constructor( uName ){ this.userName = uName; } sayName(){ return this.userName; } } 是不是很向php和java中的类, 其实本质还是原型链,我们往下看就知道了 首先说下语法规则: class Person中的Person就是类名,可以自定义

随机推荐