Node.js最佳实践异常处理

Node.js Best Practice Exception Handling

几天前我刚开始尝试node.js。我已经意识到,只要程序中有未处理的异常,节点就会终止。这与我所接触的普通服务器容器不同,当发生未处理的异常时,只有工作线程死亡,并且容器仍然能够接收请求。这提出了几个问题:

  • 江户十一〔0〕是唯一有效的防范方法吗?
  • 在异步进程执行期间,process.on('uncaughtException')是否也会捕获未处理的异常?
  • 是否有一个模块已经构建(例如发送电子邮件或写入文件),我可以在未捕获的异常情况下利用它?

我非常感谢任何能向我展示在node.js中处理未捕获异常的常见最佳实践的指针/文章。


最新消息:乔伊特现在有了自己的向导。以下信息更像是一个总结:

安全地"抛出"错误

理想情况下,我们希望尽可能避免未捕获的错误,因此,我们可以使用以下方法之一(取决于我们的代码体系结构),而不是直接抛出错误,而是安全地"抛出"错误:

  • 对于同步代码,如果发生错误,返回错误:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    // Define divider as a syncrhonous function
    var divideSync = function(x,y) {
        // if error condition?
        if ( y === 0 ) {
            //"throw" the error safely by returning it
            return new Error("Can't divide by zero")
        }
        else {
            // no error occured, continue on
            return x/y
        }
    }

    // Divide 4/2
    var result = divideSync(4,2)
    // did an error occur?
    if ( result instanceof Error ) {
        // handle the error safely
        console.log('4/2=err', result)
    }
    else {
        // no error occured, continue on
        console.log('4/2='+result)
    }

    // Divide 4/0
    result = divideSync(4,0)
    // did an error occur?
    if ( result instanceof Error ) {
        // handle the error safely
        console.log('4/0=err', result)
    }
    else {
        // no error occured, continue on
        console.log('4/0='+result)
    }
  • 对于基于回调的(即异步)代码,回调的第一个参数是err,如果发生错误,err就是错误,如果没有发生错误,那么err就是null。任何其他论点都遵循err论点:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    var divide = function(x,y,next) {
        // if error condition?
        if ( y === 0 ) {
            //"throw" the error safely by calling the completion callback
            // with the first argument being the error
            next(new Error("Can't divide by zero"))
        }
        else {
            // no error occured, continue on
            next(null, x/y)
        }
    }

    divide(4,2,function(err,result){
        // did an error occur?
        if ( err ) {
            // handle the error safely
            console.log('4/2=err', err)
        }
        else {
            // no error occured, continue on
            console.log('4/2='+result)
        }
    })

    divide(4,0,function(err,result){
        // did an error occur?
        if ( err ) {
            // handle the error safely
            console.log('4/0=err', err)
        }
        else {
            // no error occured, continue on
            console.log('4/0='+result)
        }
    })
  • 对于事件代码,如果错误可能发生在任何地方,则不引发错误,而是激发error事件:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    // Definite our Divider Event Emitter
    var events = require('events')
    var Divider = function(){
        events.EventEmitter.call(this)
    }
    require('util').inherits(Divider, events.EventEmitter)

    // Add the divide function
    Divider.prototype.divide = function(x,y){
        // if error condition?
        if ( y === 0 ) {
            //"throw" the error safely by emitting it
            var err = new Error("Can't divide by zero")
            this.emit('error', err)
        }
        else {
            // no error occured, continue on
            this.emit('divided', x, y, x/y)
        }

        // Chain
        return this;
    }

    // Create our divider and listen for errors
    var divider = new Divider()
    divider.on('error', function(err){
        // handle the error safely
        console.log(err)
    })
    divider.on('divided', function(x,y,result){
        console.log(x+'/'+y+'='+result)
    })

    // Divide
    divider.divide(4,2).divide(4,0)

安全地"捕捉"错误

但是,有时仍然可能有代码在某个地方抛出错误,如果我们不能安全地捕获它,则可能导致未捕获的异常和应用程序的潜在崩溃。根据我们的代码体系结构,我们可以使用以下方法之一来捕获它:

  • 当我们知道错误发生在哪里时,我们可以将该部分包装在node.js域中。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    var d = require('domain').create()
    d.on('error', function(err){
        // handle the error safely
        console.log(err)
    })

    // catch the uncaught errors in this asynchronous or synchronous code block
    d.run(function(){
        // the asynchronous or synchronous code that we want to catch thrown errors on
        var err = new Error('example')
        throw err
    })
  • 如果我们知道发生错误的地方是同步代码,并且出于任何原因不能使用域(可能是旧版本的节点),我们可以使用try catch语句:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    // catch the uncaught errors in this synchronous code block
    // try catch statements only work on synchronous code
    try {
        // the synchronous code that we want to catch thrown errors on
        var err = new Error('example')
        throw err
    } catch (err) {
        // handle the error safely
        console.log(err)
    }

    但是,注意不要在异步代码中使用try...catch,因为异步抛出的错误不会被捕获:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    try {
        setTimeout(function(){
            var err = new Error('example')
            throw err
        }, 1000)
    }
    catch (err) {
        // Example error won't be caught here... crashing our app
        // hence the need for domains
    }

    如果您确实想与异步代码一起使用try..catch,那么在运行节点7.4或更高版本时,可以使用async/await本机编写异步函数。

    try...catch要小心的另一件事是,在try语句中包装完成回调的风险如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    var divide = function(x,y,next) {
        // if error condition?
        if ( y === 0 ) {
            //"throw" the error safely by calling the completion callback
            // with the first argument being the error
            next(new Error("Can't divide by zero"))
        }
        else {
            // no error occured, continue on
            next(null, x/y)
        }
    }

    var continueElsewhere = function(err, result){
            throw new Error('elsewhere has failed')
    }

    try {
            divide(4, 2, continueElsewhere)
            // ^ the execution of divide, and the execution of
            //   continueElsewhere will be inside the try statement
    }
    catch (err) {
            console.log(err.stack)
            // ^ will output the"unexpected" result of: elsewhere has failed
    }

    当代码变得更复杂时,这很容易做到。因此,最好要么使用域,要么返回错误以避免(1)异步代码中未捕获的异常(2)尝试捕获不希望捕获的执行。在允许正确线程化而不是JavaScript的异步事件机风格的语言中,这不是一个问题。

  • 最后,如果在未封装在域或try-catch语句中的位置发生未捕获错误,我们可以使用uncaughtException侦听器使应用程序不会崩溃(但是这样做会使应用程序处于未知状态):

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    // catch the uncaught errors that weren't wrapped in a domain or try catch statement
    // do not use this in modules, but only in applications, as otherwise we could have multiple of these bound
    process.on('uncaughtException', function(err) {
        // handle the error safely
        console.log(err)
    })

    // the asynchronous or synchronous code that emits the otherwise uncaught error
    var err = new Error('example')
    throw err


下面是对这个主题的许多不同来源的总结和整理,包括代码示例和所选博客文章的引用。最佳实践的完整列表可以在这里找到好的。node.js错误处理的最佳实践数字1:使用承诺进行异步错误处理

tl;dr:以回调方式处理异步错误可能是见鬼去的最快方式(也就是说,这是厄运的金字塔)。您可以给代码最好的礼物是使用一个声誉良好的Promise库,它提供了许多紧凑和熟悉的代码语法,如Try-Catch好的。

否则:由于错误处理与临时代码、过度嵌套和糟糕的编码模式混合在一起,因此node.js回调样式、函数(err,response)是一种不可维护的代码的很有希望的方法。好的。

代码示例-好好的。

1
2
3
4
5
6
doWork()
.then(doWork)
.then(doError)
.then(doWork)
.catch(errorHandler)
.then(verify);

代码示例反模式-回调样式错误处理好的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
getData(someParameter, function(err, result){
    if(err != null)
      //do something like calling the given callback function and pass the error
    getMoreData(a, function(err, result){
          if(err != null)
            //do something like calling the given callback function and pass the error
        getMoreData(b, function(c){
                getMoreData(d, function(e){
                    ...
                });
            });
        });
    });
});

博客引述:"我们的承诺有问题"(来自pouchdb博客,关键词"node promises"排名11)好的。

"…And in fact, callbacks do something even more sinister: they deprive us of the stack, which is something we usually take for granted in programming languages. Writing code without a stack is a lot like driving a car without a brake pedal: you don’t realize how badly you need it, until you reach for it and it’s not there. The whole point of promises is to give us back the language fundamentals we lost when we went async: return, throw, and the stack. But you have to know how to use promises correctly in order to take advantage of them."

Ok.

数字2:仅使用内置错误对象

tl;dr:将错误作为字符串或自定义类型抛出的代码是很常见的——这使错误处理逻辑和模块之间的互操作性变得复杂。无论您拒绝承诺、抛出异常还是发出错误—使用node.js内置错误对象都可以提高一致性并防止错误信息丢失。好的。

否则:在执行某些模块时,由于不确定返回哪种类型的错误,因此很难对即将出现的异常进行推理和处理。甚至值得一提的是,使用自定义类型来描述错误可能会导致关键错误信息(如堆栈跟踪)的丢失!好的。

代码示例-正确操作好的。

1
2
3
4
5
6
7
8
9
10
11
12
13
    //throwing an Error from typical function, whether sync or async
 if(!productToAdd)
 throw new Error("How can I add new product when no value provided?");

//'throwing' an Error from EventEmitter
const myEmitter = new MyEmitter();
myEmitter.emit('error', new Error('whoops!'));

//'throwing' an Error from a Promise
 return new promise(function (resolve, reject) {
 DAL.getProduct(productToAdd.id).then((existingProduct) =>{
 if(existingProduct != null)
 return reject(new Error("Why fooling us and trying to add an existing product?"));

反模式代码示例好的。

1
2
3
//throwing a String lacks any stack trace information and other important properties
if(!productToAdd)
    throw ("How can I add new product when no value provided?");

博客引用:"字符串不是错误"(来自blog devthought,关键字"node.js error object"排名6)好的。< Buff行情>

"…传递字符串而不是错误会导致模块之间的互操作性降低。它中断了与可能正在执行错误检查实例的API或希望了解有关错误的更多信息的API的协定。正如我们将看到的,错误对象在现代JavaScript引擎中除了保存传递给构造函数的消息外,还有非常有趣的属性。好的。< /块引用>数字3:区分操作错误和程序员错误

tl;dr:操作错误(例如,API接收到无效的输入)指的是已知的情况,其中错误影响是完全理解的,并且可以进行深思熟虑的处理。另一方面,程序员错误(例如试图读取未定义的变量)是指未知的代码故障,这些故障指示应用程序正常重新启动。好的。

否则:当出现错误时,您可能总是重新启动应用程序,但为什么会因为一个小的和预测到的错误(操作错误)而让约5000个在线用户停机?相反的情况也不理想——在发生未知问题(程序员错误)时保持应用程序正常运行可能会导致不可预测的行为。区分这两种情况可以使行动得体,并根据给定的背景应用平衡的方法。好的。

代码示例-正确操作好的。

1
2
3
4
5
6
7
8
9
10
11
12
13
    //throwing an Error from typical function, whether sync or async
 if(!productToAdd)
 throw new Error("How can I add new product when no value provided?");

//'throwing' an Error from EventEmitter
const myEmitter = new MyEmitter();
myEmitter.emit('error', new Error('whoops!'));

//'throwing' an Error from a Promise
 return new promise(function (resolve, reject) {
 DAL.getProduct(productToAdd.id).then((existingProduct) =>{
 if(existingProduct != null)
 return reject(new Error("Why fooling us and trying to add an existing product?"));

代码示例-将错误标记为可操作(可信)好的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//marking an error object as operational
var myError = new Error("How can I add new product when no value provided?");
myError.isOperational = true;

//or if you're using some centralized error factory (see other examples at the bullet"Use only the built-in Error object")
function appError(commonType, description, isOperational) {
    Error.call(this);
    Error.captureStackTrace(this);
    this.commonType = commonType;
    this.description = description;
    this.isOperational = isOperational;
};

throw new appError(errorManagement.commonErrors.InvalidInput,"Describe here what happened", true);

//error handling code within middleware
process.on('uncaughtException', function(error) {
    if(!error.isOperational)
        process.exit(1);
});

博客引述:"否则你就要冒国家风险"(从可调试的博客中,关键字"node.js uncaught exception"排名3)好的。

"…By the very nature of how throw works in JavaScript, there is almost never any way to safely"pick up where you left off", without leaking references, or creating some other sort of undefined brittle state. The safest way to respond to a thrown error is to shut down the process. Of course, in a normal web server, you might have many connections open, and it is not reasonable to abruptly shut those down because an error was triggered by someone else. The better approach is to send an error response to the request that triggered the error, while letting the others finish in their normal time, and stop listening for new requests in that worker"

Ok.

数字4:集中处理错误,通过但不在中间件内

tl;dr:错误处理逻辑(如邮件到管理和日志记录)应封装在一个专用的集中对象中,当出现错误时,所有端点(如Express中间件、cron作业、单元测试)都会调用该对象。好的。

否则:不在一个地方处理错误将导致代码重复,并可能导致处理不当的错误。好的。

代码示例-典型错误流好的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
//DAL layer, we don't handle errors here
DB.addDocument(newCustomer, (error, result) => {
    if (error)
        throw new Error("Great error explanation comes here", other useful parameters)
});

//API route code, we catch both sync and async errors and forward to the middleware
try {
    customerService.addNew(req.body).then(function (result) {
        res.status(200).json(result);
    }).catch((error) => {
        next(error)
    });
}
catch (error) {
    next(error);
}

//Error handling middleware, we delegate the handling to the centrzlied error handler
app.use(function (err, req, res, next) {
    errorHandler.handleError(err).then((isOperationalError) => {
        if (!isOperationalError)
            next(err);
    });
});

博客引述:"有时较低的级别除了将错误传播给调用方之外,不能做任何有用的事情。"(摘自博客Joyent,关键词"node.js错误处理"排名第一)好的。

"…You may end up handling the same error at several levels of the stack. This happens when lower levels can’t do anything useful except propagate the error to their caller, which propagates the error to its caller, and so on. Often, only the top-level caller knows what the appropriate response is, whether that’s to retry the operation, report an error to the user, or something else. But that doesn’t mean you should try to report all errors to a single top-level callback, because that callback itself can’t know in what context the error occurred"

Ok.

数字5:使用Swagger记录API错误

TL;DR:让您的API调用者知道哪些错误可能会作为回报,这样他们就可以在不崩溃的情况下仔细地处理这些错误。这通常是通过类似Swagger的RESTAPI文档框架完成的。好的。

否则:API客户机可能决定崩溃并重新启动,因为他收到了一个他无法理解的错误。注意:API的调用者可能是您(在微服务环境中非常典型)好的。

博客引述:"你必须告诉来电者会发生什么错误。"(摘自博客Joyent,关键词"node.js logging"排名第一)好的。

…We’ve talked about how to handle errors, but when you’re writing a new function, how do you deliver errors to the code that called your function? …If you don’t know what errors can happen or don’t know what they mean, then your program cannot be correct except by accident. So if you’re writing a new function, you have to tell your callers what errors can happen and what they mea

Ok.

第六:当陌生人进城时,优雅地关闭程序

tl;dr:当发生未知错误时(开发人员错误,请参阅最佳实践编号3)-应用程序的健康性存在不确定性。一个常见的实践建议使用像Forever和PM2这样的"重启器"工具小心地重新启动流程。好的。

否则:当捕获到不熟悉的异常时,某些对象可能处于错误状态(例如,全局使用的事件发射器,由于某些内部故障而不再触发事件),并且所有未来的请求可能失败或行为异常。好的。

代码示例-决定是否崩溃好的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//deciding whether to crash when an uncaught exception arrives
//Assuming developers mark known operational errors with error.isOperational=true, read best practice #3
process.on('uncaughtException', function(error) {
 errorManagement.handler.handleError(error);
 if(!errorManagement.handler.isTrustedError(error))
 process.exit(1)
});


//centralized error handler encapsulates error-handling related logic
function errorHandler(){
 this.handleError = function (error) {
 return logger.logError(err).then(sendMailToAdminIfCritical).then(saveInOpsQueueIfCritical).then(determineIfOperationalError);
 }

 this.isTrustedError = function(error)
 {
 return error.isOperational;
 }

博客引述:"错误处理有三种思路"(摘自JSRecipes博客)好的。

…There are primarily three schools of thoughts on error handling: 1. Let the application crash and restart it. 2. Handle all possible errors and never crash. 3. Balanced approach between the two

Ok.

数字7:使用成熟的记录器提高错误可见性

tl;dr:一组成熟的日志工具,如winston、bunyan或log4j,将加速错误发现和理解。所以忘记console.log。好的。

否则:浏览console.logs或手动浏览杂乱的文本文件而不使用查询工具或合适的日志查看器可能会使您在工作中一直忙到很晚。好的。

代码示例-Winston Logger正在运行好的。

1
2
3
4
5
6
7
8
9
10
11
//your centralized logger object
var logger = new winston.Logger({
 level: 'info',
 transports: [
 new (winston.transports.Console)(),
 new (winston.transports.File)({ filename: 'somefile.log' })
 ]
 });

//custom code somewhere using the logger
logger.log('info', 'Test Log Message with some parameter %s', 'some parameter', { anything: 'This is metadata' });

博客引用:"让我们确定一些需求(对于一个日志记录程序):"(来自博客strongblog)好的。

…Lets identify a few requirements (for a logger):
1. Time stamp each log line. This one is pretty self explanatory – you should be able to tell when each log entry occured.
2. Logging format should be easily digestible by humans as well as machines.
3. Allows for multiple configurable destination streams. For example, you might be writing trace logs to one file but when an error is encountered, write to the same file, then into error file and send an email at the same time…

Ok.

数字8:使用APM产品发现错误和停机时间

tl;dr:监控和性能产品(a.k.a apm)主动测量您的代码库或api,以便它们能够自动神奇地突出显示您丢失的错误、崩溃和缓慢的部分。好的。

否则:您可能会花费大量精力来度量API性能和停机时间,可能永远不会知道在真实场景下,哪些是最慢的代码部分,以及这些部分如何影响UX。好的。

博客引用:"APM产品细分"(摘自Yoni Goldberg博客)好的。

"…APM products constitutes 3 major segments:1. Website or API monitoring – external services that constantly monitor uptime and performance via HTTP requests. Can be setup in few minutes. Following are few selected contenders: Pingdom, Uptime Robot, and New Relic
2. Code instrumentation – products family which require to embed an agent within the application to benefit feature slow code detection, exceptions statistics, performance monitoring and many more. Following are few selected contenders: New Relic, App Dynamics
3. Operational intelligence dashboard – these line of products are focused on facilitating the ops team with metrics and curated content that helps to easily stay on top of application performance. This is usually involves aggregating multiple sources of information (application logs, DB logs, servers log, etc) and upfront dashboard design work. Following are few selected contenders: Datadog, Splunk"

Ok.

以上是一个简短的版本-请参阅这里的更多最佳实践和示例好的。好啊。


您可以捕获未捕获的异常,但它的使用是有限的。参见http://debuggable.com/posts/node js处理未捕获异常:4c933d54-1428-443c-928d-4e1ecbd56cb

当节点进程崩溃时,可以使用monitforeverupstart重新启动节点进程。您最好希望正常关闭(例如,将所有内存中的数据保存在未捕获的异常处理程序中)。


nodejs域是处理nodejs中错误的最新方法。域可以捕获错误/其他事件以及传统的抛出对象。域还提供了处理回调的功能,通过intercept方法将错误作为第一个参数传递。

与正常的Try/Catch样式的错误处理一样,通常最好在错误发生时抛出错误,并屏蔽希望隔离错误以避免影响其余代码的区域。"屏蔽"这些区域的方法是调用域。使用一个函数作为隔离代码块运行。

在同步代码中,上面的内容就足够了——当发生错误时,您要么让它被抛出,要么捕获它并处理它,从而恢复需要恢复的任何数据。

1
2
3
4
5
6
try {  
  //something
} catch(e) {
  // handle data reversion
  // probably log too
}

当异步回调中发生错误时,您要么需要能够完全处理数据的回滚(共享状态、外部数据(如数据库等)。或者,您必须设置一些指示发生了异常的内容——无论您在哪里关注这个标志,都必须等待回调完成。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
var err = null;
var d = require('domain').create();
d.on('error', function(e) {
  err = e;
  // any additional error handling
}
d.run(function() { Fiber(function() {
  // do stuff
  var future = somethingAsynchronous();
  // more stuff

  future.wait(); // here we care about the error
  if(err != null) {
    // handle data reversion
    // probably log too
  }

})});

上面的一些代码很难看,但您可以为自己创建模式,使其更漂亮,例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
var specialDomain = specialDomain(function() {
  // do stuff
  var future = somethingAsynchronous();
  // more stuff

  future.wait(); // here we care about the error
  if(specialDomain.error()) {
    // handle data reversion
    // probably log too
  }
}, function() { //"catch"
  // any additional error handling
});

更新(2013-09):

上面,我使用了一个包含纤维语义的未来,它允许您在线等待未来。这实际上允许您使用传统的尝试捕获块来处理所有事情——我发现这是最好的方法。但是,您不能总是这样做(即在浏览器中)。

还有一些未来不需要纤维语义(然后使用普通的浏览器JavaScript)。这些可以被称为期货、承诺或延期(我从这里开始只是指期货)。普通的旧JavaScript预购库允许在预购之间传播错误。只有其中一些库允许正确处理任何抛出的未来,所以要小心。

一个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
returnsAFuture().then(function() {
  console.log('1')
  return doSomething() // also returns a future

}).then(function() {
  console.log('2')
  throw Error("oops an error was thrown")

}).then(function() {
  console.log('3')

}).catch(function(exception) {
  console.log('handler')
  // handle the exception
}).done()

这模拟了一个正常的尝试捕获,即使片段是异步的。它会打印:

1
2
3
1
2
handler

请注意,它不会打印"3",因为引发了一个异常,该异常会中断该流。

看看蓝鸟承诺:

  • https://github.com/petkaantonov/bluebird

请注意,除了这些库之外,我没有找到其他许多可以正确处理抛出异常的库。例如,jquery的deferred,don't-"fail"处理程序永远不会得到抛出"then"处理程序的异常,在我看来,这是一个交易破坏者。


我最近在http://snmaynard.com/2012/12/21/node error handling/上写了这个。0.8版中节点的一个新特性是域,允许您将所有形式的错误处理组合成一个更简单的管理形式。你可以在我的帖子里读到。

您还可以使用Bugsnag之类的工具跟踪未捕获的异常,并通过电子邮件、聊天室或为未捕获的异常创建罚单(我是Bugsnag的联合创始人)。


使用try catch可能比较合适的一个实例是使用foreach循环时。它是同步的,但同时不能只在内部作用域中使用RETURN语句。相反,可以使用try-and-catch方法返回适当范围内的错误对象。考虑:

1
2
3
4
5
6
7
function processArray() {
    try {
       [1, 2, 3].forEach(function() { throw new Error('exception'); });
    } catch (e) {
       return e;
    }
}

它是上面@balupton描述的方法的组合。


我只想添加step.js库,它总是通过将异常传递给下一个step函数来帮助您处理异常。因此,作为最后一步,您可以使用一个函数来检查前面任何步骤中的任何错误。这种方法可以大大简化错误处理。

以下是Github页面的报价:

any exceptions thrown are caught and passed as the first argument to
the next function. As long as you don't nest callback functions inline
your main functions this prevents there from ever being any uncaught
exceptions. This is very important for long running node.JS servers
since a single uncaught exception can bring the whole server down.

此外,还可以使用步骤控制脚本的执行,以便最后一步使用清理部分。例如,如果您想在节点中编写一个构建脚本并报告它需要多长时间来编写,最后一步可以做到这一点(而不是试图挖掘最后一个回调)。


在一段时间前阅读了这篇文章之后,我想知道在API/函数级别上使用域进行异常处理是否安全。我想使用它们来简化我编写的每个异步函数中的异常处理代码。我担心为每个函数使用一个新的域会带来很大的开销。我的家庭作业似乎表明开销最小,而且在某些情况下,域的性能实际上比Try-Catch要好。

http://www.lighthouselogic.com//using-a-new-domain-for-each-async-function-in-node/


捕获错误在这里已经讨论得很好了,但是值得记住的是将错误记录在某个地方,这样您就可以查看它们并修复它们。

?Bunyan是一个流行的nodejs日志框架,它支持将输出写到一堆不同的输出位置,这使得它对于本地调试很有用,只要您不使用console.log。?在域的错误处理程序中,可以将错误输出到日志文件中。

1
2
3
4
5
6
7
8
9
var log = bunyan.createLogger({
  name: 'myapp',
  streams: [
    {
      level: 'error',
      path: '/var/tmp/myapp-error.log'  // log ERROR to this file
    }
  ]
});

如果您有很多错误和/或服务器需要检查,这可能会花费时间,因此有必要研究一种工具,如Raygun(免责声明,我在Raygun工作),将错误分组在一起,或者同时使用它们。?如果您决定使用Raygun作为工具,也很容易设置

1
2
var raygunClient = new raygun.Client().init({ apiKey: 'your API key' });
raygunClient.send(theError);

?与使用PM2或Forever这样的工具相比,您的应用程序应该能够崩溃、注销发生的事情并重新启动,而不会出现任何重大问题。


如果要在Ubuntu中使用服务(upstart):node as a service in Ubuntu 11.04 with upstart,monit and forever.js