数日前から node.js を試し始めました。プログラムで未処理の例外が発生すると、ノードが終了することに気付きました。これは、未処理の例外が発生したときにワーカー スレッドのみが終了し、コンテナーは引き続きリクエストを受信できる、私がこれまで使用してきた通常のサーバー コンテナーとは異なります。これにより、いくつかの疑問が生じます。
process.on('uncaughtException')
それを防ぐ唯一の効果的な方法は何ですか?process.on('uncaughtException')
非同期プロセスの実行中にも未処理の例外をキャッチしますか?- キャッチされない例外が発生した場合に活用できる、既に構築されているモジュール (電子メールの送信やファイルへの書き込みなど) はありますか?
node.jsでキャッチされない例外を処理するための一般的なベストプラクティスを示すポインタ/記事があればありがたいです。
ベストアンサー1
更新:ジョイエントは独自のガイド以下の情報は要約です。
安全にエラーを「スロー」する
理想的には、キャッチされないエラーを可能な限り回避したいので、文字通りエラーをスローするのではなく、コード アーキテクチャに応じて次のいずれかの方法を使用して、エラーを安全に「スロー」することができます。
同期コードの場合、エラーが発生した場合はエラーを返します。
// 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
引数の後に続きます。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
代わりにイベント:// 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 ドメイン
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 ステートメントを使用できます。
// 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
ただし、非同期にスローされたエラーはキャッチされないため、非同期コードでは使用しないように注意してください。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
、Node 7.4 以降を実行しているときに、async/await
ネイティブで非同期関数を記述できます。
Another thing to be careful about with `try...catch` is the risk of wrapping your completion callback inside the `try` statement like so:
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
}
This gotcha is very easy to do as your code becomes more complex. As such, it is best to either use domains or to return errors to avoid (1) uncaught exceptions in asynchronous code (2) the try catch catching execution that you don't want it to. In languages that allow for proper threading instead of JavaScript's asynchronous event-machine style, this is less of an issue.
最後に、ドメインやtry catch文で囲まれていない場所でキャッチされないエラーが発生した場合、リスナーを使用することでアプリケーションがクラッシュしないようにすることができます
uncaughtException
(ただし、そうすることでアプリケーションが不明な状態):// 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