Node.js のベストプラクティス例外処理 質問する

Node.js のベストプラクティス例外処理 質問する

数日前から 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
    

おすすめ記事