817 votes

Gestion des exceptions de node.js Best Practice

J'ai juste commencé à essayer node.js il y a quelques jours. J'ai réalisé que le Nœud est mis fin à chaque fois que j'ai une exception non gérée dans mon programme. C'est différent de la normale conteneur du serveur que j'ai été exposé à où seul le Thread meurt lorsque les exceptions non gérées produit et le conteneur serait toujours en mesure de recevoir la demande. Cela soulève quelques questions:

  • Est - process.on('uncaughtException') le seul moyen efficace pour se prémunir contre elle?
  • Va process.on('uncaughtException') attraper l'exception non gérée au cours de l'exécution de processus asynchrones?
  • Est-il un module qui est déjà construit (comme l'envoi d'e-mail ou d'écrire dans un fichier) que j'ai pu l'effet de levier dans le cas de ce type d'exception?

J'apprécierais toute pointeur/article que de me montrer le commun des meilleures pratiques pour gérer les exceptions non traitées dans node.js

789voto

balupton Points 17805

Mise à jour: Joyent a maintenant son propre guide mentionné dans cette réponse. L'information suivante est plus un résumé:

En toute sécurité "lancer" des erreurs

Idéalement, nous aimerions éviter les erreurs non interceptées autant que possible, en tant que tel, au lieu de littéralement jeter l'erreur, nous pouvons en toute sécurité au lieu de "jeter" l'erreur en utilisant l'une des méthodes suivantes, selon le code de l'architecture:

  • Pour le code, si une erreur se produit, le retour de l'erreur:

    // 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;
    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);
    }
    
  • Pour rappel (ie. asynchrone) du code, le premier argument de la fonction de rappel est - err, si une erreur se produit err est l'erreur, si une erreur ne se produit pas alors err est null. Tous les autres arguments de suivre l' err argument:

    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);
        }
    });
    
  • Pour bien remplie code, où l'erreur peut se produire n'importe où, au lieu de jeter l'erreur, le feu de l' error événement à la place:

    // 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);
    

En toute sécurité "rattrapage" des erreurs

Parfois, cependant, il peut toujours y avoir un code qui renvoie une erreur quelque part, ce qui peut conduire à une exception non interceptée et un crash potentiel de notre application, si nous n'avons pas l'attraper en toute sécurité. Selon notre architecture du code, nous pouvons utiliser l'une des méthodes suivantes pour l'attraper:

  • Quand on sait où l'erreur se produit, nous pouvons conclure que l'article dans un node.js domaine

    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;
    });
    
  • Si nous savons où l'erreur se produit est synchrone de code, et pour quelque raison que ce soit ne peut pas utiliser des domaines (peut-être l'ancienne version de nœud), on peut utiliser le try catch déclaration:

    // 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);
    }
    
  • Cependant, il reste peut-être un cas où une erreur non interceptée qui se passe dans un endroit qui n'était pas emballé dans un domaine ou un try catch déclaration, dans ce cas de faire de notre application se bloque pas, nous pouvons utiliser l' uncaughtException d'écoute (mais c'pouvez mettre l'application dans un état inconnu):

    // 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;
    

32voto

nponeccop Points 8111

Vous pouvez attraper les exceptions, mais c'est d'une utilité limitée. Voir http://debuggable.com/posts/node-js-dealing-with-uncaught-exceptions:4c933d54-1428-443c-928d-4e1ecbdd56cb

monit, forever ou upstart peuvent être utilisées pour redémarrer le nœud du processus quand il se bloque. Un arrêt normal est mieux que vous pouvez espérer (par exemple, enregistrer toutes les données en mémoire dans l'exception non interceptée).

15voto

B T Points 4868

nodejs domaines est la plus à jour la manière de gérer les erreurs dans nodejs. Domaines peut capturer à la fois d'erreur/d'autres événements ainsi que, traditionnellement, jeté des objets. Domaines également fournir des fonctionnalités de manipulation des rappels avec une erreur passé comme premier argument par l'ordonnée à l'origine de la méthode.

Comme avec les try/catch-style de gestion d'erreur, est généralement préférable de jeter les erreurs lorsqu'elles se produisent, et de bloquer les zones où vous souhaitez isoler les erreurs d'affecter le reste du code. Le moyen de "bloquer" ces zones sont à l'appel de domaine.courir avec une fonction comme un bloc isolé du code.

Dans le code synchrone, le ci-dessus est assez - lorsqu'une erreur se produit soit vous vous permettez d'être jeté à travers, ou que vous l'attraper et à manipuler-il, en se retournant toutes les données dont vous avez besoin pour revenir.

try {  
  //something
} catch(e) {
  // handle data reversion
  // probably log too
}

Lorsque l'erreur se produit dans un rappel asynchrone, vous devez être en mesure de gérer complètement la restauration des données (état partagé, de données externes comme les bases de données, etc). OU vous devez mettre quelque chose pour indiquer qu'une exception s'est passé - où jamais vous vous souciez de ce drapeau, vous devez attendre pour l'exécution du rappel.

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
  }

})});

Certains de code ci-dessus est moche, mais vous pouvez créer des modèles pour vous-même pour le rendre plus joli, par exemple:

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
});

Mise à JOUR (2013-09):

Ci-dessus, j'utilise un avenir qui implique des fibres de la sémantique, qui vous permettent de vous attendre sur les contrats à terme en ligne. Cela vous permet d'utiliser les blocs try-catch pour tout ce - que je trouve la meilleure façon d'aller. Cependant, on ne peut pas toujours le faire (c'est à dire dans le navigateur)...

Il existe également des contrats à terme qui ne nécessitent pas de fibres sémantique (qui travaillent ensuite avec la normale, browsery JavaScript). Ceux-ci peuvent être appelés contrats à terme, des promesses, ou deferreds (je vais juste faire référence à des contrats à terme à partir d'ici). Plaine-de-vieille-JavaScript futures bibliothèques permettent d'être des erreurs propagées entre les contrats à terme. Seuls certains de ces bibliothèques permettent à tout jeté l'avenir pour être traités correctement, alors méfiez-vous.

Un exemple:

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()

Cette imite normale try-catch, même si les morceaux sont asynchrones. Il serait d'impression:

1
2
handler

Notez qu'il n'a pas l'impression de " 3 " parce que une exception a été levée qui interrompt le flux.

Jetez un oeil à ces bibliothèques:

Notez que je n'ai pas trouvé beaucoup d'autres bibliothèques autres que celles qui traitent correctement les exceptions lancées. jQuery est différé, par exemple, ne sont pas - le "fail" de gestionnaire ne pourrait jamais obtenir les exceptions lancées une à une", puis " gestionnaire, qui à mon avis est un briseur d'affaire.

11voto

Simon Maynard Points 81

J'ai écrit récemment sur ce sujet à http://snmaynard.com/2012/12/21/node-error-handling/. Une nouvelle fonctionnalité de nœud dans la version 0.8 est domaines et vous permettent de combiner toutes les formes de gestion des erreurs dans un plus facile à gérer. Vous pouvez lire à leur sujet dans mon post.

Vous pouvez également utiliser quelque chose comme Bugsnag pour le suivi de votre les exceptions non traitées et être notifié par e-mail, tchat ou avoir un ticket créé pour une exception non interceptée (je suis le co-fondateur de Bugsnag).

4voto

yagudaev Points 1286

Un cas où, à l’aide d’un try-catch pourrait être approprié est lorsque vous utilisez une boucle forEach. Il est synchrone, mais en même temps vous ne peut pas utiliser une instruction return dans la portée interne. Plutôt une approche try et catch peut servir pour renvoyer un objet d’erreur dans l’étendue appropriée. Tenir compte :

C’est une combinaison des approches décrites par @balupton ci-dessus.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X