Je viens de commencer à essayer node.js il y a quelques jours. J'ai réalisé que le nœud est terminé chaque fois que j'ai une exception non gérée dans mon programme. Ceci est différent du conteneur de serveur normal auquel j'ai été exposé, où seul le thread de travail meurt lorsque des exceptions non gérées se produisent et le conteneur serait toujours en mesure de recevoir la demande. Cela soulève quelques questions:
- Est-ce
process.on('uncaughtException')
le seul moyen efficace de s'en prémunir? - Attrapera-t-il également
process.on('uncaughtException')
l'exception non gérée lors de l'exécution des processus asynchrones? - Existe-t-il un module déjà construit (comme l'envoi d'e-mails ou l'écriture dans un fichier) que je pourrais exploiter dans le cas d'exceptions non interceptées?
J'apprécierais tout pointeur / article qui me montrerait les meilleures pratiques courantes pour gérer les exceptions non capturées dans node.js
try .. catch
, et vérifiez que cela est également fait pour toutes vos bibliothèquessetTimeout
ousetInterval
ou quelque chose de ce genre enfoui quelque part en profondeur qui ne peut pas être capturé par votre code.Réponses:
Mise à jour: Joyent a maintenant son propre guide . Les informations suivantes sont plutôt un résumé:
Erreurs de «lancement» en toute sécurité
Idéalement, nous aimerions éviter les erreurs non capturées autant que possible, en tant que tel, au lieu de jeter littéralement l'erreur, nous pouvons à la place "jeter" l'erreur en utilisant l'une des méthodes suivantes en fonction de notre architecture de code:
Pour le code synchrone, si une erreur se produit, renvoyez l'erreur:
Pour le code basé sur le rappel (c'est-à-dire asynchrone), le premier argument du rappel est
err
, si une erreur se produiterr
est l'erreur, si une erreur ne se produit pas, elle l'err
estnull
. Tout autre argument suit l'err
argument:Pour le code mouvementé , où l'erreur peut se produire n'importe où, au lieu de lancer l'erreur, déclenchez l'
error
événement à la place :"Attraper" les erreurs en toute sécurité
Parfois, cependant, il peut toujours y avoir du code qui génère une erreur quelque part, ce qui peut entraîner une exception non interceptée et un crash potentiel de notre application si nous ne l'attrapons pas en toute sécurité. Selon notre architecture de code, nous pouvons utiliser l'une des méthodes suivantes pour l'attraper:
Lorsque nous savons où l'erreur se produit, nous pouvons envelopper cette section dans un domaine node.js
Si nous savons où l'erreur se produit, c'est du code synchrone, et pour une raison quelconque, nous ne pouvons pas utiliser de domaines (peut-être l'ancienne version du nœud), nous pouvons utiliser l'instruction try catch:
Cependant, veillez à ne pas utiliser
try...catch
dans le code asynchrone, car une erreur levée de manière asynchrone ne sera pas détectée:Si vous souhaitez travailler avec
try..catch
en conjonction avec du code asynchrone, lorsque vous exécutez Node 7.4 ou supérieur, vous pouvez utiliserasync/await
nativement pour écrire vos fonctions asynchrones.Une autre chose à laquelle vous devez faire attention
try...catch
est le risque d'encapsuler votre rappel de fin dans l'try
instruction comme ceci:Ce gotcha est très facile à faire car votre code devient plus complexe. En tant que tel, il est préférable d'utiliser des domaines ou de renvoyer des erreurs pour éviter (1) les exceptions non interceptées dans le code asynchrone (2) l'exécution de capture catch try que vous ne voulez pas. Dans les langages qui permettent un threading approprié au lieu du style de machine d'événement asynchrone de JavaScript, cela pose moins de problème.
Enfin, dans le cas où une erreur non interceptée se produit dans un endroit qui n'était pas encapsulé dans un domaine ou une instruction try catch, nous pouvons empêcher notre application de se bloquer en utilisant l'
uncaughtException
écouteur (cependant, cela peut mettre l'application dans un état inconnu ):la source
try catch
? Comme j'aimerais étayer cela avec des preuves. Correction également de l'exemple de synchronisation.Vous trouverez ci-dessous un résumé et une sélection de nombreuses sources différentes sur ce sujet, y compris un exemple de code et des citations de certains articles de blog. La liste complète des meilleures pratiques se trouve ici
Meilleures pratiques de gestion des erreurs Node.JS
Number1: Utilisez des promesses pour la gestion des erreurs asynchrones
TL; DR: gestion des erreurs asynchrones dans le style de rappel est probablement le moyen le plus rapide d'enfer (alias la pyramide du destin). Le meilleur cadeau que vous puissiez faire à votre code est d'utiliser à la place une bibliothèque de promesses réputée qui fournit une syntaxe de code bien compacte et familière comme try-catch
Autrement: style de rappel Node.JS, la fonction (erreur, réponse), est un moyen prometteur de code non maintenable en raison de la combinaison de la gestion des erreurs avec du code occasionnel, de l'imbrication excessive et des modèles de codage maladroits
Exemple de code - bon
exemple de code anti modèle - gestion des erreurs de style de rappel
Citation du blog: "Nous avons un problème avec les promesses" (extrait du blog pouchdb, classé 11 pour les mots clés "Node Promises")
Number2: utilisez uniquement l'objet Error intégré
TL; DR: Il est assez courant de voir du code qui génère des erreurs sous forme de chaîne ou de type personnalisé - cela complique la logique de gestion des erreurs et l'interopérabilité entre les modules. Que vous rejetiez une promesse, leviez une exception ou émettiez une erreur, l'utilisation de l'objet d'erreur intégré Node.JS augmente l'uniformité et empêche la perte d'informations sur les erreurs
Sinon: lors de l'exécution d'un module, le fait de savoir quel type d'erreurs en retour est incertain - rend beaucoup plus difficile de raisonner sur l'exception à venir et de la gérer. Même utile, l'utilisation de types personnalisés pour décrire des erreurs peut entraîner la perte d'informations sur les erreurs critiques comme la trace de la pile!
Exemple de code - bien faire
exemple de code anti motif
Citation du blog: "Une chaîne n'est pas une erreur" (D'après le blog devthought, classé 6 pour les mots clés "Node.JS error object")
Numéro3: Distinguer les erreurs opérationnelles des erreurs de programmation
TL; DR: les erreurs d'opération (par exemple, l'API a reçu une entrée non valide) se réfèrent à des cas connus où l'impact de l'erreur est entièrement compris et peut être géré de manière réfléchie. D'autre part, une erreur de programmeur (par exemple, essayer de lire une variable non définie) fait référence à des échecs de code inconnus qui dictent de redémarrer l'application avec grâce.
Sinon: vous pouvez toujours redémarrer l'application lorsqu'une erreur apparaît, mais pourquoi laisser ~ 5000 utilisateurs en ligne en raison d'une erreur mineure et prédite (erreur opérationnelle)? l'inverse n'est pas non plus idéal - garder l'application en place lorsqu'un problème inconnu (erreur de programmation) s'est produit peut entraîner un comportement imprévu. Différencier les deux permet d'agir avec tact et d'appliquer une approche équilibrée en fonction du contexte donné
Exemple de code - bien faire
exemple de code - marquer une erreur comme opérationnelle (fiable)
Citation de blog : "Sinon, vous risquez l'état" (D'après le blog débogable, classé 3 pour les mots clés "Node.JS uncaught exception")
Number4: Gérer les erreurs de manière centralisée, mais pas dans le middleware
TL; DR: la logique de gestion des erreurs, comme le courrier à l'administrateur et la journalisation, doit être encapsulée dans un objet dédié et centralisé que tous les points finaux (par exemple, middleware Express, tâches cron, tests unitaires) appellent lorsqu'une erreur survient.
Sinon: le fait de ne pas traiter les erreurs au sein d'un même emplacement entraînera une duplication de code et probablement des erreurs mal gérées.
Exemple de code - un flux d'erreur typique
Citation du blog: "Parfois, les niveaux inférieurs ne peuvent rien faire d'utile sauf propager l'erreur à leur appelant" (D'après le blog Joyent, classé 1 pour les mots clés "Gestion des erreurs Node.JS")
Number5: documenter les erreurs de l'API à l'aide de Swagger
TL; DR: laissez vos appelants API savoir quelles erreurs peuvent survenir en retour afin de pouvoir les traiter de manière réfléchie sans se bloquer. Cela se fait généralement avec les cadres de documentation de l'API REST comme Swagger
Sinon: un client API peut décider de planter et de redémarrer uniquement parce qu'il a reçu une erreur qu'il ne pouvait pas comprendre. Remarque: l'appelant de votre API peut être vous (très typique dans un environnement de microservices)
Citation du blog: "Vous devez dire à vos appelants quelles erreurs peuvent se produire" (tiré du blog Joyent, classé 1 pour les mots clés "Journalisation Node.JS")
Number6: Arrêtez le processus gracieusement quand un étranger vient en ville
TL; DR: Lorsqu'une erreur inconnue se produit (une erreur de développeur, voir la meilleure pratique numéro 3) - il y a une incertitude sur la salubrité de l'application. Une pratique courante suggère de redémarrer le processus avec précaution à l'aide d'un outil de «redémarrage» comme Forever et PM2
Sinon: lorsqu'une exception inconnue est interceptée, un objet peut être dans un état défectueux (par exemple, un émetteur d'événements qui est utilisé globalement et ne déclenche plus d'événements en raison d'une défaillance interne) et toutes les demandes futures peuvent échouer ou se comporter de manière folle
Exemple de code - décider s'il faut planter
Citation du blog: "Il existe trois écoles de pensée sur la gestion des erreurs" (tiré du blog jsrecipes)
Number7: Utilisez un enregistreur mature pour augmenter la visibilité des erreurs
TL; DR: un ensemble d'outils de journalisation matures comme Winston, Bunyan ou Log4J, accélérera la découverte et la compréhension des erreurs. Oubliez donc console.log.
Sinon: le survol via console.logs ou manuellement via un fichier texte en désordre sans outils d'interrogation ou une visionneuse de journal décente peut vous occuper au travail jusqu'à tard
Exemple de code - Enregistreur Winston en action
Citation du blog: "Permet d'identifier quelques exigences (pour un enregistreur):" (Sur le blog strongblog)
Number8: Découvrez les erreurs et les temps d'arrêt en utilisant les produits APM
TL; DR: les produits de surveillance et de performance (aka APM) évaluent de manière proactive votre base de code ou votre API afin qu'ils puissent mettre en évidence automatiquement les erreurs, les plantages et les ralentissements qui vous manquaient
Sinon: vous pourriez consacrer beaucoup d'efforts à mesurer les performances et les temps d'arrêt de l'API, vous ne saurez probablement jamais quelles sont vos parties de code les plus lentes dans le scénario du monde réel et comment elles affectent l'UX
Citation du blog: "Segments de produits APM" (Extrait du blog Yoni Goldberg)
Ce qui précède est une version abrégée - voir ici plus de bonnes pratiques et d'exemples
la source
Vous pouvez intercepter des exceptions non capturées, mais son utilisation est limitée. Voir http://debuggable.com/posts/node-js-dealing-with-uncaught-exceptions:4c933d54-1428-443c-928d-4e1ecbdd56cb
monit
,forever
ouupstart
peut être utilisé pour redémarrer le processus de nœud lorsqu'il se bloque. Vous pouvez espérer un arrêt gracieux (par exemple, enregistrer toutes les données en mémoire dans un gestionnaire d'exceptions non capturé).la source
Error
rend la valeur de retour polymorphe qui brouille inutilement la sémantique de la fonction. De plus, la plongée par 0 est déjà traitée en JavaScript en donnantInfinity
,-Infinity
ouNaN
, les valeurs oùtypeof === 'number'
. Ils peuvent être vérifiés avec!isFinite(value)
. En général, je recommanderais de ne jamais renvoyer une erreur d'une fonction. Mieux en termes de lisibilité et de maintenance du code pour lancer ou renvoyer une valeur spéciale non polymorphe avec une sémantique cohérente.Les domaines nodejs sont le moyen le plus à jour de gérer les erreurs dans nodejs. Les domaines peuvent capturer à la fois des événements d'erreur / autres ainsi que des objets traditionnellement levés. Les domaines fournissent également des fonctionnalités pour gérer les rappels avec une erreur transmise comme premier argument via la méthode d'interception.
Comme avec la gestion normale des erreurs de style try / catch, il est généralement préférable de lever 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 consiste à appeler domain.run avec une fonction de bloc de code isolé.
En code synchrone, ce qui précède est suffisant - lorsqu'une erreur se produit, vous pouvez soit la laisser passer, soit l'attraper et la gérer, en rétablissant toutes les données dont vous avez besoin.
Lorsque l'erreur se produit dans un rappel asynchrone, vous devez soit être en mesure de gérer entièrement la restauration des données (état partagé, données externes comme les bases de données, etc.). OU vous devez définir quelque chose pour indiquer qu'une exception s'est produite - chaque fois que vous vous souciez de cet indicateur, vous devez attendre la fin du rappel.
Une partie de ce code ci-dessus est moche, mais vous pouvez créer des modèles pour le rendre plus joli, par exemple:
MISE À JOUR (2013-09):
Ci-dessus, j'utilise un futur qui implique la sémantique des fibres , qui vous permet d'attendre les futures en ligne. Cela vous permet en fait d'utiliser des blocs try-catch traditionnels pour tout - ce que je trouve être la meilleure façon de procéder. Cependant, vous ne pouvez pas toujours le faire (c'est-à-dire dans le navigateur) ...
Il existe également des futurs qui ne nécessitent pas de sémantique de fibres (qui fonctionnent ensuite avec du JavaScript normal pour navigateur). Ceux-ci peuvent être appelés contrats à terme, promesses ou différés (je ferai référence aux contrats à terme à partir d'ici). Les bibliothèques de futures JavaScript simples permettent de propager les erreurs entre les futures. Seules certaines de ces bibliothèques permettent de gérer correctement tout avenir jeté, alors méfiez-vous.
Un exemple:
Cela imite un try-catch normal, même si les pièces sont asynchrones. Il imprimerait:
Notez qu'il n'imprime pas «3» car une exception a été levée qui interrompt ce flux.
Jetez un œil aux promesses de Bluebird:
Notez que je n'ai pas trouvé beaucoup d'autres bibliothèques autres que celles-ci qui gèrent correctement les exceptions levées. Le différé de jQuery, par exemple, ne le fait pas - le gestionnaire «fail» ne verrait jamais l'exception levée par un gestionnaire «then», qui à mon avis est un facteur de rupture.
la source
J'ai récemment écrit à ce sujet sur http://snmaynard.com/2012/12/21/node-error-handling/ . Une nouvelle fonctionnalité du nœud dans la version 0.8 est les domaines et vous permet de combiner toutes les formes de gestion des erreurs en un seul formulaire de gestion plus facile. Vous pouvez en lire plus dans mon article.
Vous pouvez également utiliser quelque chose comme Bugsnag pour suivre vos exceptions non capturées et être averti par e-mail, sur le chat ou faire créer un ticket pour une exception non capturée (je suis le co-fondateur de Bugsnag).
la source
Je voudrais juste ajouter que la bibliothèque Step.js vous aide à gérer les exceptions en la passant toujours à la fonction de l'étape suivante. Par conséquent, vous pouvez avoir comme dernière étape une fonction qui vérifie les erreurs dans l'une des étapes précédentes. Cette approche peut simplifier considérablement la gestion des erreurs.
Voici une citation de la page github:
De plus, vous pouvez utiliser Step pour contrôler l'exécution des scripts afin d'avoir une section de nettoyage comme dernière étape. Par exemple, si vous souhaitez écrire un script de génération dans Node et indiquer le temps qu'il a fallu pour écrire, la dernière étape peut le faire (plutôt que d'essayer de déterrer le dernier rappel).
la source
Une instance où l'utilisation d'un try-catch peut être appropriée est lors de l'utilisation d'une boucle forEach. Il est synchrone mais en même temps, vous ne pouvez pas simplement utiliser une instruction de retour dans la portée interne. Au lieu de cela, une approche try and catch peut être utilisée pour renvoyer un objet Error dans la portée appropriée. Considérer:
Il s'agit d'une combinaison des approches décrites par @balupton ci-dessus.
la source
Après avoir lu ce post il y a quelque temps, je me demandais s'il était sûr d'utiliser des domaines pour la gestion des exceptions au niveau de l'api / fonction. Je voulais les utiliser pour simplifier le code de gestion des exceptions dans chaque fonction asynchrone que j'ai écrite. Ma préoccupation était que l'utilisation d'un nouveau domaine pour chaque fonction entraînerait une surcharge importante. Mes devoirs semblent indiquer que les frais généraux sont minimes et que les performances sont en fait meilleures avec les domaines qu'avec try catch dans certaines situations.
http://www.lighthouselogic.com/#/using-a-new-domain-for-each-async-function-in-node/
la source
La détection des erreurs a été très bien discutée ici, mais il convient de se rappeler de déconnecter les erreurs quelque part afin de pouvoir les afficher et corriger les problèmes.
Bunyan est un cadre de journalisation populaire pour NodeJS - il supprime l'écriture dans un tas d'endroits de sortie différents, ce qui le rend utile pour le débogage local, tant que vous évitez console.log. Dans le gestionnaire d'erreurs de votre domaine, vous pouvez cracher l'erreur dans un fichier journal.
Cela peut prendre du temps si vous avez beaucoup d'erreurs et / ou de serveurs à vérifier, il pourrait donc être utile d'examiner un outil comme Raygun (clause de non-responsabilité, je travaille chez Raygun) pour regrouper les erreurs - ou les utiliser les deux ensemble. Si vous avez décidé d'utiliser Raygun comme outil, il est également assez facile à configurer
Croisée avec l'utilisation d'un outil comme PM2 ou pour toujours, votre application devrait pouvoir planter, déconnecter ce qui s'est passé et redémarrer sans aucun problème majeur.
la source
Si vous souhaitez utiliser les services dans Ubuntu (Upstart): Node as a service in Ubuntu 11.04 with upstart, monit and forever.js
la source