[解決済み] Node.jsの例外処理のベストプラクティス
質問内容
数日前からnode.jsを試用し始めたところです。プログラム中に処理されない例外が発生すると、Nodeが終了してしまうことに気づきました。これは、私がこれまで触れてきた通常のサーバーコンテナとは異なるもので、扱われていない例外が発生するとワーカースレッドだけが死に、コンテナはまだリクエストを受け取ることができます。このため、いくつかの疑問が生じます。
-
は
process.on('uncaughtException')
を防ぐ唯一の効果的な方法ですか? -
ウィル
process.on('uncaughtException')
は、非同期処理の実行中にも処理されない例外をキャッチするのですか? - キャッチできない例外が発生した場合に活用できる、すでに構築されているモジュール(メール送信やファイルへの書き込みなど)はないでしょうか?
node.jsでキャッチできない例外を処理するための一般的なベストプラクティスを示してくれるようなポインタや記事があれば、ありがたいです。
どのように解決するのですか?
アップデート:Joyentは現在 独自のガイド . 以下の情報は、どちらかというと要約です。
エラーを安全に "投げる"する
理想的には、キャッチできないエラーはできるだけ避けたいものです。そのため、文字通りエラーをスローする代わりに、コードアーキテクチャに応じて以下のメソッドのいずれかを使用して安全に "throw" エラーをスローすることができます。
-
同期コードでは、エラーが発生した場合、そのエラーを返します。
// 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) }
-
コールバックベースの(つまり非同期の)コードでは、コールバックの第1引数は
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 })
-
エラーが発生している場所が同期コードで、何らかの理由でドメインが使えない場合(おそらくnodeのバージョンが古い)、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
を使用して非同期関数を記述することができます。でもう一つ気をつけなければならないのは
try...catch
の内部で補完コールバックをラップしてしまう危険性があります。try
のように記述します。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)望んでいない実行をtry catchがキャッチしてしまう。JavaScriptの非同期イベントマシーンのスタイルではなく、適切なスレッド化を可能にする言語では、これはあまり問題ではありません。
-
最後に、ドメインや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
関連
-
[解決済み】npm 5で作成されたpackage-lock.jsonファイルはコミットするのでしょうか?
-
[解決済み] Node.jsのプログラムにコマンドライン引数を渡すにはどうしたらいいですか?
-
[解決済み] Node.jsを使うタイミングをどう判断するか?
-
[解決済み] Node.jsで終了する方法
-
[解決済み] Node.jsでファイルを書き込む
-
[解決済み] Node.jsアプリケーションをデバッグするにはどうすればよいですか?
-
[解決済み] Node.jsのmodule.exportsの目的と使い方を教えてください。
-
[解決済み] Node.jsで環境変数を読み込む
-
[解決済み] Node.jsでファイル/ディレクトリが存在するかどうかを同期的にチェックする
-
[解決済み] Node.jsのnpmモジュールをアンインストールするにはどうすればよいですか?
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み】AWS STS AssumeRoleへのアクセスを可能にする方法
-
[解決済み] エラー: nodejs の get 呼び出しで getaddrinfo ENOTFOUND が発生しました。
-
[解決済み】エラーです。EACCES: 権限が拒否されました、アクセス '/usr/local/lib/node_modules' 。
-
[解決済み】ExpressJSとMeteorJSの比較【終了しました
-
[解決済み】ENOENT, そのようなファイルまたはディレクトリがありません。
-
[解決済み】Nodejsの解決方法:Error: ENOENT: そのようなファイルまたはディレクトリがありません
-
[解決済み】passport.js passport.initialize() ミドルウェアが使用されていません。
-
[解決済み】Mongooseで配列の値を更新する方法
-
[解決済み] エラーメッセージ MongoError: bad auth URI 文字列で認証に失敗しました。
-
[解決済み] Node.js クイックファイルサーバ (静的ファイルオーバーHTTP)