Node.js Best Practice Exception Handling

Ho appena iniziato a provare node.js qualche giorno fa. Mi sono reso conto che il nodo è terminato ogni volta che ho un’eccezione non gestita nel mio programma. Questo è diverso dal normale contenitore del server a cui sono stato esposto dove solo il Thread di lavoro muore quando si verificano eccezioni non gestite e il contenitore potrebbe comunque ricevere la richiesta. Questo solleva alcune domande:

  • process.on('uncaughtException') l’unico modo efficace per proteggersi da esso?
  • process.on('uncaughtException') intercetta anche l’eccezione non gestita durante l’esecuzione di processi asincroni?
  • Esiste un modulo già costruito (come l’invio di e-mail o la scrittura su un file) che potrei sfruttare in caso di eccezioni non rilevate?

Gradirei qualsiasi puntatore / articolo che mi mostrerebbe le migliori pratiche comuni per la gestione delle eccezioni non rilevate in node.js

Aggiornamento: Joyent ha ora la propria guida menzionata in questa risposta . Le seguenti informazioni sono più di un riassunto:

Errori “di lancio” in sicurezza

Idealmente vorremmo evitare il più ansible gli errori non catturati, in quanto tali, invece di lanciare letteralmente l’errore, possiamo invece tranquillamente “lanciare” l’errore usando uno dei seguenti metodi a seconda della nostra architettura di codice:

  • Per il codice sincrono, se si verifica un errore, restituire l’errore:

     // 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) } 
  • Per il codice basato sulla callback (cioè asincrono), il primo argomento della callback è err , se si verifica un errore err è l’errore, se non si verifica un errore, err è null . Qualsiasi altro argomento segue l’argomento 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) } }) 
  • Per il codice eventful , in cui l’errore può verificarsi ovunque, anziché lanciare l’errore, triggersre invece l’evento di 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) 

Errori “catching” in modo sicuro

A volte però, potrebbe ancora esserci un codice che genera un errore da qualche parte che può portare a un’eccezione non rilevata e ad un potenziale crash della nostra applicazione se non la si cattura in modo sicuro. A seconda dell’architettura del codice, è ansible utilizzare uno dei seguenti metodi per catturarlo:

  • Quando sappiamo dove si sta verificando l’errore, possiamo avvolgere quella sezione in un dominio 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 }) 
  • Se sappiamo dove si sta verificando l’errore è il codice sincrono, e per qualsiasi ragione non è ansible utilizzare i domini (forse la vecchia versione del nodo), possiamo usare l’istruzione 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) } 

    Tuttavia, fai attenzione a non usare try...catch codice asincrono, poiché un errore generato in modo asincrono non verrà rilevato:

     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 } 

    Un’altra cosa da fare attenzione con try...catch è il rischio di avvolgere il callback del completamento all’interno dell’istruzione try modo:

     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 } 

    Questo getcha è molto facile da fare in quanto il codice diventa più complesso. Pertanto, è meglio utilizzare i domini o restituire gli errori per evitare (1) eccezioni non rilevate nel codice asincrono (2) l’esecuzione catch catch catching che non si desidera eseguire. Nelle lingue che consentono il threading corretto invece dello stile asincrono di macchina degli eventi di JavaScript, questo è meno di un problema.

  • Infine, nel caso in cui si verifichi un errore non catturato in un luogo non incluso in un dominio o in un’istruzione catch, possiamo fare in modo che la nostra applicazione non si blocchi utilizzando il listener uncaughtException (tuttavia, ciò può mettere l’applicazione in uno sconosciuto stato ):

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

Di seguito è riportato un riepilogo e una curazione da molte fonti diverse su questo argomento, inclusi esempi di codice e citazioni da post di blog selezionati. L’elenco completo delle migliori pratiche può essere trovato qui


Best practice per la gestione degli errori di Node.JS


Numero1: utilizza le promesse per la gestione degli errori asincroni

TL; DR: Gestire gli errori asincroni nello stile di callback è probabilmente il modo più veloce per andare all’inferno (ovvero la piramide del destino). Il miglior regalo che puoi dare al tuo codice sta usando una libreria di promesse che offre una syntax di codice molto compatta e familiare come try-catch

Altrimenti: stile di callback Node.JS, funzione (err, risposta), è un modo promettente per codice non gestibile a causa del mix di gestione degli errori con codice casuale, nidificazione eccessiva e schemi di codifica scomodi

Esempio di codice: buono

 doWork() .then(doWork) .then(doError) .then(doWork) .catch(errorHandler) .then(verify); 

esempio di codice anti modello – gestione degli errori di stile di callback

 getData(someParameter, function(err, result){ if(err != null) //do something like calling the given callback function and pass the error getMoreData(a, function(err, result){ if(err != null) //do something like calling the given callback function and pass the error getMoreData(b, function(c){ getMoreData(d, function(e){ ... }); }); }); }); }); 

Citazione del blog: “Abbiamo un problema con le promesse” (dal blog pouchdb, classificato 11 per le parole chiave “Promesse del nodo”)

“… E infatti, i callback fanno qualcosa di ancora più sinistro: ci privano dello stack, cosa che di solito diamo per scontato nei linguaggi di programmazione: scrivere codice senza stack è molto simile a guidare una macchina senza un pedale del freno: non ti rendi conto di quanto tu ne abbia bisogno, finché non lo raggiungi e non è lì. Il punto centrale delle promesse è di restituirci i fondamenti linguistici che abbiamo perso quando siamo andati in asincrono: ritorno, lancio e pila. sapere come usare correttamente le promesse per trarne vantaggio.


Numero2: utilizza solo l’object Errore incorporato

TL; DR: È piuttosto comune vedere il codice che genera errori come stringa o come tipo personalizzato – questo complica la logica di gestione degli errori e l’interoperabilità tra i moduli. Se si rifiuta una promise, si genera un’eccezione o si emette un errore, utilizzando l’object Errore incorporato di Node.JS aumenta l’uniformità e si evita la perdita delle informazioni di errore

Altrimenti: quando si esegue un modulo, essendo incerto quale tipo di errore viene restituito, è molto più difficile ragionare sull’eccezione che si sta verificando e gestirla. Anche la pena, l’uso di tipi personalizzati per descrivere errori potrebbe portare alla perdita di informazioni critiche sugli errori come la traccia dello stack!

Esempio di codice: farlo bene

  //throwing an Error from typical function, whether sync or async if(!productToAdd) throw new Error("How can I add new product when no value provided?"); //'throwing' an Error from EventEmitter const myEmitter = new MyEmitter(); myEmitter.emit('error', new Error('whoops!')); //'throwing' an Error from a Promise return new promise(function (resolve, reject) { DAL.getProduct(productToAdd.id).then((existingProduct) =>{ if(existingProduct != null) return reject(new Error("Why fooling us and trying to add an existing product?")); 

codice esempio anti modello

 //throwing a String lacks any stack trace information and other important properties if(!productToAdd) throw ("How can I add new product when no value provided?"); 

Citazione del blog: “Una stringa non è un errore” (dal blog devesto, classificato 6 per le parole chiave “object errore Node.JS”)

“… il passaggio di una stringa anziché di un errore comporta un’interoperabilità ridotta tra i moduli, rompendo i contratti con le API che potrebbero eseguire un’istanza di verifiche degli errori o che desiderano saperne di più sull’errore . Gli oggetti di errore, come vedremo, hanno molto proprietà interessanti nei motori JavaScript moderni oltre a contenere il messaggio passato al costruttore .. ”


Numero 3: Distingue tra errori operativi e programmatori

TL; DR: Errori operativi (ad es. API ha ricevuto un input non valido) si riferiscono a casi noti in cui l’impatto dell’errore è completamente compreso e può essere gestito in modo ponderato. D’altra parte, l’errore del programmatore (ad es. Il tentativo di leggere una variabile non definita) si riferisce a errori di codice sconosciuti che impongono di riavviare con garbo l’applicazione

Altrimenti: puoi sempre riavviare l’applicazione quando viene visualizzato un errore, ma perché lasciare ~ 5000 utenti online a causa di un errore minore e previsto (errore operativo)? anche l’opposto non è l’ideale: mantenere l’applicazione quando si è verificato un problema sconosciuto (errore del programmatore) che potrebbe comportare un comportamento imprevedibile. La differenziazione dei due permette di agire con tatto e di applicare un approccio equilibrato basato sul contesto dato

Esempio di codice: farlo bene

  //throwing an Error from typical function, whether sync or async if(!productToAdd) throw new Error("How can I add new product when no value provided?"); //'throwing' an Error from EventEmitter const myEmitter = new MyEmitter(); myEmitter.emit('error', new Error('whoops!')); //'throwing' an Error from a Promise return new promise(function (resolve, reject) { DAL.getProduct(productToAdd.id).then((existingProduct) =>{ if(existingProduct != null) return reject(new Error("Why fooling us and trying to add an existing product?")); 

esempio di codice: contrassegnare un errore come operativo (attendibile)

 //marking an error object as operational var myError = new Error("How can I add new product when no value provided?"); myError.isOperational = true; //or if you're using some centralized error factory (see other examples at the bullet "Use only the built-in Error object") function appError(commonType, description, isOperational) { Error.call(this); Error.captureStackTrace(this); this.commonType = commonType; this.description = description; this.isOperational = isOperational; }; throw new appError(errorManagement.commonErrors.InvalidInput, "Describe here what happened", true); //error handling code within middleware process.on('uncaughtException', function(error) { if(!error.isOperational) process.exit(1); }); 

Blog Quote : “Altrimenti rischi lo stato” (dal blog debugable, classificato 3 per le parole chiave “eccezione Nodo.JS non rilevata”)

… Per la natura stessa di come funziona throw in JavaScript, non c’è quasi mai alcun modo di” riprendere in sicurezza dove hai lasciato “, senza perdere riferimenti o creare qualche altro tipo di fragile stato indefinito. un errore generato è quello di arrestare il processo . Naturalmente, in un normale server Web, potresti avere molte connessioni aperte e non è ragionevole interromperle bruscamente perché un errore è stato triggersto da qualcun altro. inviare una risposta di errore alla richiesta che ha triggersto l’errore, lasciando che gli altri finiscano nel loro tempo normale e interrompere l’ascolto di nuove richieste in quel lavoratore ”


Numero4: gestire gli errori centralmente, tramite ma non nel middleware

TL; DR: la logica di gestione degli errori, come la posta per l’amministrazione e la registrazione, dovrebbe essere incapsulata in un object dedicato e centralizzato che tutti gli endpoint (es. Middleware Express, cron job, unit test) chiamano quando arriva un errore.

Altrimenti: la mancata gestione degli errori all’interno di un singolo luogo porterà alla duplicazione del codice e probabilmente a errori gestiti in modo improprio

Esempio di codice: un tipico stream di errore

 //DAL layer, we don't handle errors here DB.addDocument(newCustomer, (error, result) => { if (error) throw new Error("Great error explanation comes here", other useful parameters) }); //API route code, we catch both sync and async errors and forward to the middleware try { customerService.addNew(req.body).then(function (result) { res.status(200).json(result); }).catch((error) => { next(error) }); } catch (error) { next(error); } //Error handling middleware, we delegate the handling to the centrzlied error handler app.use(function (err, req, res, next) { errorHandler.handleError(err).then((isOperationalError) => { if (!isOperationalError) next(err); }); }); 

Citazione del blog: “A volte i livelli inferiori non possono fare nulla di utile se non propagare l’errore al chiamante” (dal blog Joyent, classificato 1 per le parole chiave “Gestione degli errori di Node.JS”)

“… Si può finire per gestire lo stesso errore a diversi livelli dello stack, questo accade quando livelli più bassi non possono fare nulla di utile tranne propagare l’errore al chiamante, che propaga l’errore al suo chiamante, e così via. solo il chiamante di primo livello sa quale sia la risposta appropriata, sia che si tratti di riprovare l’operazione, di segnalare un errore all’utente o qualcos’altro, ma ciò non significa che dovresti provare a riportare tutti gli errori a un singolo livello principale callback, perché quella stessa callback non può sapere in quale contesto si è verificato l’errore ”


Numero 5: errori dell’API del documento con Swagger

TL; DR: lascia che i tuoi chiamanti API sappiano quali errori potrebbero venire in risposta in modo che possano gestirli in modo ponderato senza arrestarsi. Questo di solito è fatto con framework di documentazione API REST come Swagger

Altrimenti: un client API potrebbe decidere di bloccarsi e riavviarsi solo perché ha ricevuto un errore che non riusciva a capire. Nota: il chiamante della tua API potrebbe essere tu (molto tipico in un ambiente di microservizi)

Citazione del blog: “Devi dire ai tuoi interlocutori quali errori possono accadere” (dal blog Joyent, classificato 1 per le parole chiave “Node.JS logging”)

… Abbiamo parlato di come gestire gli errori, ma quando si scrive una nuova funzione, come si consegnano errori al codice che ha chiamato la propria funzione? … Se non sai quali errori possono accadere o non sai cosa significano, allora il tuo programma non può essere corretto se non per sbaglio. Quindi, se stai scrivendo una nuova funzione, devi dire ai tuoi interlocutori quali errori possono accadere e quali sono le loro intenzioni


Numero 6: Chiudi il processo con grazia quando uno straniero arriva in città

TL; DR: quando si verifica un errore sconosciuto (un errore sviluppatore, vedere il numero di pratica migliore n. 3), vi è incertezza sulla salute dell’applicazione. Una pratica comune suggerisce di riavviare il processo con attenzione utilizzando uno strumento ‘riavvio’ come Forever e PM2

Altrimenti: quando viene rilevata un’eccezione non familiare, alcuni oggetti potrebbero trovarsi in uno stato di errore (ad esempio un emettitore di eventi che viene utilizzato globalmente e che non genera più eventi a causa di un errore interno) e tutte le richieste future potrebbero non funzionare o comportarsi in modo folle

Esempio di codice: decidere se andare in crash

 //deciding whether to crash when an uncaught exception arrives //Assuming developers mark known operational errors with error.isOperational=true, read best practice #3 process.on('uncaughtException', function(error) { errorManagement.handler.handleError(error); if(!errorManagement.handler.isTrustedError(error)) process.exit(1) }); //centralized error handler encapsulates error-handling related logic function errorHandler(){ this.handleError = function (error) { return logger.logError(err).then(sendMailToAdminIfCritical).then(saveInOpsQueueIfCritical).then(determineIfOperationalError); } this.isTrustedError = function(error) { return error.isOperational; } 

Citazione del blog: “Ci sono tre scuole di pensiero sulla gestione degli errori” (dal blog jsrecipes)

… Ci sono principalmente tre scuole di pensiero sulla gestione degli errori: 1. Lasciare l’applicazione in crash e riavviarla. 2. Gestire tutti gli errori possibili e non arrestarsi mai. 3. Approccio equilibrato tra i due


Numero 7: utilizza un registratore maturo per aumentare la visibilità degli errori

TL; DR: una serie di strumenti di registrazione maturi come Winston, Bunyan o Log4J, velocizzerà la scoperta e la comprensione degli errori. Quindi dimentica console.log.

Altrimenti: sfogliare console.logs o manualmente attraverso un file di testo disordinato senza dover eseguire query sugli strumenti o un visualizzatore di registro decente potrebbe tenerti occupato al lavoro fino a tardi

Esempio di codice: logger Winston in azione

 //your centralized logger object var logger = new winston.Logger({ level: 'info', transports: [ new (winston.transports.Console)(), new (winston.transports.File)({ filename: 'somefile.log' }) ] }); //custom code somewhere using the logger logger.log('info', 'Test Log Message with some parameter %s', 'some parameter', { anything: 'This is metadata' }); 

Citazione blog: “Consente di identificare alcuni requisiti (per un logger):” (dal blog strongblog)

… Consente di identificare alcuni requisiti (per un programma di registrazione): 1. Stampa di ciascuna riga di registro. Questo è piuttosto auto-esplicativo: dovresti essere in grado di dire quando si è verificata ogni voce di registro. 2. Il formato di registrazione dovrebbe essere facilmente digeribile dagli esseri umani e dalle macchine. 3. Consente più flussi di destinazione configurabili. Ad esempio, potresti scrivere registri di tracciamento in un file, ma quando si incontra un errore, scrivi nello stesso file, poi nel file di errore e invia un’email allo stesso tempo …


Numero8: scopri errori e tempi di inattività utilizzando i prodotti APM

TL; DR: prodotti di monitoraggio e prestazioni (APM) misurano in modo proattivo il codice base o l’API in modo che possano evidenziare automaticamente errori, arresti anomali e rallentamenti delle parti mancanti

Altrimenti: potresti dedicare grandi sforzi alla misurazione delle prestazioni e dei tempi di inattività dell’API, probabilmente non sarai mai a conoscenza quali sono le parti di codice più lente in uno scenario reale e come queste influiscono sulla UX

Citazione del blog: “Segmenti dei prodotti APM” (dal blog Yoni Goldberg)

“… I prodotti APM costituiscono 3 segmenti principali: 1. Monitoraggio di siti Web o API: servizi esterni che monitorano costantemente uptime e prestazioni tramite richieste HTTP Possono essere impostati in pochi minuti Di seguito sono riportati alcuni contendenti selezionati: Pingdom, Uptime Robot e New Relic 2 Strumentazione del codice – famiglia di prodotti che richiede di incorporare un agente all’interno dell’applicazione per beneficiare del rilevamento di codice lento, statistiche di eccezioni, monitoraggio delle prestazioni e molti altri. Di seguito sono riportati alcuni contendenti selezionati: Nuova reliquia, App Dynamics 3. Cruscotto di intelligence operativa – questa linea I prodotti sono focalizzati sulla facilitazione del team operativo con metriche e contenuti curati che aiutano a rimanere facilmente al top delle prestazioni delle applicazioni. Questo di solito comporta l’aggregazione di più fonti di informazioni (registri delle applicazioni, registri di DB, log dei server, ecc.) e progettazione di dashboard upfront Di seguito sono riportati alcuni contendenti selezionati: Datadog, Splunk ”


Quanto sopra è una versione abbreviata – vedi qui più buone pratiche ed esempi

È ansible rilevare eccezioni non rilevate, ma è di uso limitato. Vedi http://debuggable.com/posts/node-js-dealing-with-uncaught-exceptions:4c933d54-1428-443c-928d-4e1ecbdd56cb

monit , forever o upstart possono essere utilizzati per riavviare il processo del nodo quando si blocca. Un arresto regolare è il migliore che si possa sperare (ad esempio, salvare tutti i dati in memoria nel gestore eccezioni non rilevati).

domini nodejs è il modo più aggiornato di gestire gli errori in nodejs. I domini possono catturare sia errori / altri eventi sia oggetti tradizionalmente lanciati. I domini forniscono anche funzionalità per la gestione dei callback con un errore passato come primo argomento tramite il metodo dell’intercetta.

Come con la normale gestione degli errori try / catch-style, di solito è meglio lanciare errori quando si verificano e bloccare le aree in cui si desidera isolare gli errori dal resto del codice. Il modo per “bloccare” queste aree è chiamare domain.run con una funzione come blocco di codice isolato.

Nel codice sincrono, quanto sopra è sufficiente – quando si verifica un errore o lo si lascia passare, oppure lo si cattura e si gestisce lì, ripristinando tutti i dati che è necessario ripristinare.

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

Quando l’errore si verifica in un callback asincrono, è necessario essere in grado di gestire completamente il rollback dei dati (stato condiviso, dati esterni come database, ecc.). OPPURE devi impostare qualcosa che indichi che è avvenuta un’eccezione – dove mai ti importa di quel flag, devi aspettare il completamento della callback.

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

Alcuni di questi codici sono brutti, ma è ansible creare modelli per renderlo più carino, ad esempio:

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

AGGIORNAMENTO (2013-09):

Sopra, io uso un futuro che implica semantica delle fibre , che ti consente di aspettare i futures in linea. In realtà questo ti permette di usare i tradizionali blocchi try-catch per tutto – che trovo essere il modo migliore per andare. Tuttavia, non puoi sempre farlo (cioè nel browser) …

Ci sono anche i futures che non richiedono la semantica delle fibre (che quindi funziona con il normale JavaScript della cartella). Questi possono essere chiamati futures, promises o deferred (mi riferirò ai futures da qui in poi). Le librerie future di plain-old-JavaScript consentono di propagare gli errori tra i futures. Solo alcune di queste librerie consentono di gestire correttamente qualsiasi futuro generato, quindi fai attenzione.

Un esempio:

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

Questo imita un normale try-catch, anche se i pezzi sono asincroni. Si stampa:

 1 2 handler 

Si noti che non stampa ‘3’ perché è stata generata un’eccezione che interrompe tale stream.

Dai un’occhiata alle promesse di bluebird:

Si noti che non ho trovato molte altre librerie diverse da queste che gestiscono correttamente le eccezioni generate. Ad esempio, il comando differito di jQuery non lo fa: il gestore “fail” non otterrebbe mai l’eccezione generata da un gestore “allora”, che a mio parere è un rompicapo.

Di recente ho scritto su http://snmaynard.com/2012/12/21/node-error-handling/ . Una nuova funzionalità del nodo nella versione 0.8 è domini e consente di combinare tutte le forms di gestione degli errori in un unico modulo di gestione più semplice. Puoi leggere su di loro nel mio post.

Puoi anche usare qualcosa come Bugsnag per tracciare le tue eccezioni non rilevate e ricevere notifiche via email, chat room o avere un ticket creato per un’eccezione non catturata (io sono il co-fondatore di Bugsnag).

Un’istanza in cui è ansible utilizzare un try-catch è quando si utilizza un ciclo forEach. È sincrono ma allo stesso tempo non puoi usare solo un’istruzione return nello scope interno. Invece un approccio try and catch può essere utilizzato per restituire un object Error nello scope appropriato. Prendere in considerazione:

 function processArray() { try { [1, 2, 3].forEach(function() { throw new Error('exception'); }); } catch (e) { return e; } } 

È una combinazione degli approcci descritti sopra da @balupton.

Vorrei solo aggiungere che la libreria Step.js ti aiuta a gestire le eccezioni passando sempre alla funzione del passo successivo. Pertanto è ansible avere come ultima operazione una funzione che controlla eventuali errori in uno qualsiasi dei passaggi precedenti. Questo approccio può semplificare notevolmente la gestione degli errori.

Di seguito è riportata una citazione dalla pagina github:

tutte le eccezioni lanciate vengono catturate e passate come primo argomento alla funzione successiva. As long as you don’t nest callback functions inline your main functions this prevents there from ever being any uncaught exceptions. This is very important for long running node.JS servers since a single uncaught exception can bring the whole server down.

Furthermore, you can use Step to control execution of scripts to have a clean up section as the last step. For example if you want to write a build script in Node and report how long it took to write, the last step can do that (rather than trying to dig out the last callback).

After reading this post some time ago I was wondering if it was safe to use domains for exception handling on an api / function level. I wanted to use them to simplify exception handling code in each async function I wrote. My concern was that using a new domain for each function would introduce significant overhead. My homework seems to indicate that there is minimal overhead and that performance is actually better with domains than with try catch in some situations.

http://www.lighthouselogic.com/#/using-a-new-domain-for-each-async-function-in-node/

If you want use Services in Ubuntu(Upstart): Node as a service in Ubuntu 11.04 with upstart, monit and forever.js

Catching errors has been very well discussed here, but it’s worth remembering to log the errors out somewhere so you can view them and fix stuff up.

​Bunyan is a popular logging framework for NodeJS – it supporst writing out to a bunch of different output places which makes it useful for local debugging, as long as you avoid console.log. ​ In your domain’s error handler you could spit the error out to a log file.

 var log = bunyan.createLogger({ name: 'myapp', streams: [ { level: 'error', path: '/var/tmp/myapp-error.log' // log ERROR to this file } ] }); 

This can get time consuming if you have lots of errors and/or servers to check, so it could be worth looking into a tool like Raygun (disclaimer, I work at Raygun) to group errors together – or use them both together. ​ If you decided to use Raygun as a tool, it’s pretty easy to setup too

 var raygunClient = new raygun.Client().init({ apiKey: 'your API key' }); raygunClient.send(theError); 

​ Crossed with using a tool like PM2 or forever, your app should be able to crash, log out what happened and reboot without any major issues.