Węzeł.Obsługa wyjątków w zakresie najlepszych praktyk js
Właśnie zacząłem wypróbowywać node.js kilka dni temu. Zdałem sobie sprawę, że węzeł jest zakończony, gdy mam nieobsługiwany wyjątek w moim programie. Jest to inne niż zwykły kontener serwerowy, na który byłem narażony, gdzie tylko wątek roboczy umiera, gdy występują nieobsługiwane wyjątki i kontener nadal będzie mógł otrzymać żądanie. To rodzi kilka pytań:
- czy
process.on('uncaughtException')
jest jedynym skutecznym sposobem ochrony przed nim? - will
process.on('uncaughtException')
catch the nieobsługiwany wyjątek również podczas wykonywania procesów asynchronicznych? - czy istnieje moduł, który jest już zbudowany (np. wysyłanie wiadomości e-mail lub pisanie do pliku), który mógłbym wykorzystać w przypadku nieprzewidzianych WYJĄTKÓW?
Byłbym wdzięczny za każdy wskaźnik / artykuł, który pokazałby mi wspólne najlepsze praktyki obsługi nieobciążonych WYJĄTKÓW w node.js
10 answers
Aktualizacja: Joyent ma teraz swój własny przewodnik wspomniany w tej odpowiedzi . Poniższe informacje są bardziej podsumowaniem:
Bezpiecznie "rzucać" błędy
W idealnej sytuacji chcielibyśmy jak najwięcej uniknąć nieprzewidzianych błędów, jako takich, zamiast dosłownie rzucać błąd, możemy zamiast tego bezpiecznie "rzucać" błąd za pomocą jednej z następujących metod w zależności od naszej architektury kodu:]}
-
Dla kodu synchronicznego, jeśli wystąpi błąd, zwróć błąd:
// 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) }
-
Dla połączeń zwrotnych (tj. asynchroniczny) kod, pierwszym argumentem wywołania zwrotnego jest
err
, jeśli wystąpi błąderr
jest błędem, jeśli błąd nie wystąpi toerr
jestnull
. Wszelkie inne argumenty następują po argumencieerr
: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) } })
-
Dla kodu, gdzie błąd może się zdarzyć w dowolnym miejscu, zamiast rzucać błąd, odpal
error
Zdarzenie zamiast :// 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)
Bezpiecznie " łapać" błędy
Czasami jednak nadal może istnieć kod, który wyrzuca gdzieś błąd, który może prowadzić do nieużywanego wyjątku i potencjalnej awarii naszej aplikacji, Jeśli nie złapiemy go bezpiecznie. W zależności od naszej architektury kodu możemy użyć jednej z następujących metod, aby go złapać:
-
Kiedy wiemy, gdzie występuje błąd, możemy zawinąć tę sekcję w węzeł .domena 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 })
-
Jeśli wiemy, gdzie występuje błąd jest kod synchroniczny i z jakiegokolwiek powodu nie może używać domen (być może starej wersji node), możemy użyć instrukcji try catch: {]}
W kodzie asynchronicznie wyrzucony błąd nie zostanie wychwycony.]}// 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 { 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 }
Jeśli chcesz pracować z
try..catch
w połączeniu z kodem asynchronicznym, podczas uruchamiania Node 7.4 lub nowszego możesz użyćasync/await
natywnie do pisania funkcji asynchronicznych.Kolejna rzecz, na którą należy uważać o z {[14] } jest ryzyko zawijania zwrotnego zakończenia wewnątrz
try
oświadczenie tak: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 }
To gotcha jest bardzo łatwe do zrobienia, ponieważ twój kod staje się bardziej złożony. W związku z tym najlepiej jest użyć domen lub zwrócić błędy, aby uniknąć (1) nieobciążonych WYJĄTKÓW w kodzie asynchronicznym (2) try catch przechwytywania wykonania, którego nie chcesz. W językach, które pozwalają na prawidłowe threadowanie zamiast asynchronicznego stylu event-machine JavaScript, jest to mniej problem.
-
Wreszcie, w przypadku, gdy błąd wystąpi w miejscu, które nie było zawinięte w domenę lub instrukcję try catch, możemy sprawić, że nasza aplikacja nie ulegnie awarii za pomocą
uncaughtException
listener (jednak w ten sposób może umieścić aplikację w nieznanym stanie ):// 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
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-09-29 13:13:39
Poniżej znajduje się podsumowanie i podsumowanie z wielu różnych źródeł na ten temat, w tym przykład kodu i cytaty z wybranych postów na blogu. Pełna lista najlepszych praktyk znajduje się tutaj
Najlepsze praktyki Node.Obsługa błędów JS
Number1: użyj obietnic do obsługi błędów asynchronicznych
TL; DR: Obsługa błędów asynchronicznych w stylu callback jest prawdopodobnie najszybszą drogą do piekła (a.k.a piramida zagłady). Na najlepszym prezentem, jaki możesz dać swojemu kodowi, jest użycie renomowanej biblioteki promise, która zapewnia znacznie zwartą i znajomą składnię kodu, taką jak try-catch {]}
W Przeciwnym Razie: Węzeł.Js callback style, function (err, response), jest obiecującym sposobem na nie-utrzymanie kodu ze względu na mieszankę obsługi błędów z przypadkowym kodem, nadmierne zagnieżdżanie i niezręczne wzorce kodowania
Przykład kodu-dobry
doWork()
.then(doWork)
.then(doError)
.then(doWork)
.catch(errorHandler)
.then(verify);
Przykład kodu anty pattern-błąd stylu wywołania zwrotnego obsługa
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){
...
});
});
});
});
});
Cytat na blogu: "mamy problem z obietnicami" (Z bloga pouchdb, w rankingu 11 dla słów kluczowych "obietnice węzła")
"...a w rzeczywistości wywołania zwrotne robią coś jeszcze bardziej złowrogiego: pozbawiają nas stosu, co jest czymś, co zwykle uważamy za oczywiste w językach programowania. Pisanie kodu bez stosu jest jak jazda samochodem bez pedału hamulca: nie zdajesz sobie sprawy, jak bardzo go potrzebujesz, dopóki nie sięgniesz po niego i nie jest tam. całym celem obietnic jest zwrócenie nam podstaw językowych, które straciliśmy, gdy zaczęliśmy asynchronizować: return, throw, and the stack. Ale musisz wiedzieć, jak prawidłowo korzystać z obietnic, aby je wykorzystać."
Number2: używaj tylko wbudowanego obiektu błędu
TL; DR: dość często widzi się kod, który wyrzuca błędy jako ciąg znaków lub jako niestandardowy typ – komplikuje to logikę obsługi błędów i interoperacyjność między Moduły. Niezależnie od tego, czy odrzucasz obietnicę, wyrzucasz wyjątek, czy emitujesz błąd-używając węzła.JS wbudowany obiekt błędu zwiększa jednolitość i zapobiega utracie informacji o błędzie
W Przeciwnym Razie: podczas wykonywania jakiegoś modułu, niepewność, jaki typ błędów przychodzi w zamian-znacznie utrudnia rozumowanie o nadchodzącym wyjątku i obsługę go. Nawet warto, używanie niestandardowych typów do opisywania błędów może prowadzić do utraty krytycznych informacji o błędach, takich jak stos trace!
Przykład kodu-doing it right
//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?"));
Przykład kodu anty pattern
//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?");
Cytat z bloga: "ciąg nie jest błędem" (Z bloga devthought, w rankingu 6 za słowa kluczowe " węzeł.Js error object")
"...przekazywanie ciągu znaków zamiast błędu skutkuje zmniejszoną interoperacyjnością między modułami. Łamie umowy z API, które mogą wykonywać instanceof kontroli błędów, lub które chcą wiedzieć więcej o błąd . Obiekty błędów, jak zobaczymy, mają bardzo ciekawe właściwości we współczesnych silnikach JavaScript poza utrzymaniem komunikatu przekazanego do konstruktora.."
Number3: rozróżnienie błędów operacyjnych i programistycznych
TL; DR: błędy operacyjne (np. API otrzymało nieprawidłowe dane wejściowe) odnoszą się do znanych przypadków, w których wpływ błędu jest w pełni zrozumiały i może być obsługiwany w sposób przemyślany. Z drugiej Strony błąd programisty (np. próba odczytania undefined zmienna) odnosi się do nieznanych błędów kodu, które nakazują ponowne uruchomienie aplikacji
W Przeciwnym Razie: zawsze możesz ponownie uruchomić aplikację, gdy pojawi się błąd, ale po co pozwalać ~5000 użytkownikom online z powodu niewielkiego i przewidywanego błędu (błędu operacyjnego)? przeciwieństwo nie jest również idealne – utrzymanie aplikacji, gdy wystąpił nieznany problem (błąd programisty), może prowadzić do nieprzewidywalnego zachowania. Rozróżnienie tych dwóch pozwala na taktyczne działanie i zastosowanie zrównoważone podejście oparte na danym kontekście
Przykład kodu-doing it right
//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?"));
Przykład kodu-oznaczenie błędu jako operacyjny (zaufany)
//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);
});
Cytat na blogu: "inaczej ryzykujesz Państwo" (Z bloga debugable, w rankingu 3 dla słów kluczowych " węzeł.Js uncaught exception")
"...ze względu na to, jak throw działa w JavaScript, prawie nigdy nie ma sposobu, aby bezpiecznie " zacząć od miejsca, w którym skończyłeś", bez wycieków odniesień, czy tworzenia innego rodzaju nieokreślonego stanu kruchości. Najbezpieczniejszym sposobem odpowiedzi na wyrzucony błąd jest zamknięcie procesu . Oczywiście na normalnym serwerze internetowym możesz mieć wiele otwartych połączeń i nie jest rozsądne, aby je nagle wyłączyć, ponieważ błąd został wywołany przez kogoś innego. Lepszym podejściem jest wysłanie odpowiedzi błędu na żądanie, które wywołało błąd, pozwalając innym zakończyć w swoim normalnym czasie i zatrzymać słuchanie nowych próśb u tego pracownika "
Number4: obsługuje błędy centralnie, ale nie wewnątrz middleware
TL; DR: logika obsługi błędów, taka jak Poczta do administratora i logowanie, powinna być zamknięta w dedykowanym i scentralizowanym obiekcie, który wszystkie punkty końcowe (np.
W Przeciwnym Razie: Nie obsługa błędów w jednym miejscu doprowadzi do duplikacji kodu i prawdopodobnie do błędów, które są obsługiwane niewłaściwie
Przykład kodu-typowy przepływ błędów
//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);
});
});
Cytat z bloga: "czasami niższe poziomy nie mogą zrobić nic użytecznego, poza propagowaniem błędu u rozmówcy" (Z bloga Joyent, w rankingu 1 za słowa kluczowe " węzeł.Obsługa błędów JS")
" ... możesz skończyć obsługi tego samego błędu na kilku poziomach stosu. Dzieje się tak, gdy niższe poziomy nie mogą zrobić nic użytecznego, z wyjątkiem propagacji błędu do ich rozmówcy, który propaguje błąd do rozmówcy, i tak dalej. Często tylko rozmówca najwyższego poziomu wie, jaka jest odpowiednia odpowiedź, czy to ponowna próba operacji, zgłoszenie błędu użytkownikowi, czy coś innego. Ale to nie znaczy, że należy próbować zgłaszać wszystkie błędy do jednego najwyższego poziomu callback, ponieważ to callback sam nie może wiedzieć, w jakim kontekście wystąpił błąd "
Number5: Document API errors using Swagger
TL; DR: niech Twoi rozmówcy API wiedzą, które błędy mogą przyjść w zamian, aby mogli sobie z nimi poradzić bez awarii. Zwykle odbywa się to za pomocą frameworków dokumentacji REST API, takich jak Swagger
W Przeciwnym Razie: klient API może zdecydować się na awarię i ponowne uruchomienie tylko dlatego, że otrzymał z powrotem błąd, którego nie mógł zrozumieć. Uwaga: wywołującym API może być ty (bardzo typowy w środowisku mikrousług)
Cytat z bloga: " musisz powiedzieć swoim rozmówcom jakie błędy może się zdarzyć" (Z bloga Joyent, w rankingu 1 za słowa kluczowe " węzeł.Js logging")
...rozmawialiśmy o tym, jak radzić sobie z błędami, ale kiedy piszesz nową funkcję, jak dostarczasz błędy do kodu, który wywołał twoją funkcję? ... Jeśli nie wiesz, jakie błędy mogą się zdarzyć lub nie wiesz, co one oznaczają, to twój program nie może być poprawny, chyba że przez przypadek. Więc jeśli piszesz nową funkcję, musisz powiedzieć swoim rozmówcom, jakie błędy mogą się zdarzyć i jakie są mea
Numer 6: Zamknij proces z wdziękiem, gdy obcy przychodzi do miasta
TL; DR: gdy wystąpi nieznany błąd (błąd programisty, patrz najlepsza praktyka numer # 3) - istnieje niepewność co do zdrowotności aplikacji. Powszechnie stosowana praktyka sugeruje ostrożne ponowne uruchomienie procesu za pomocą narzędzia "restarter", takiego jak Forever i PM2
W Przeciwnym Razie: gdy przechwycony zostanie nieznany wyjątek, jakiś obiekt może być w stanie wadliwym (np. Zdarzenie emiter, który jest używany globalnie i nie uruchamia już zdarzeń z powodu jakiejś wewnętrznej awarii) i wszystkie przyszłe żądania mogą zawieść lub zachowywać się szalenie
Przykład kodu-decydowanie o awarii
//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;
}
Cytat z bloga: " istnieją trzy szkoły myślenia o obsłudze błędów" (Z bloga jsrecipes)
... istnieją przede wszystkim trzy szkoły myślenia o obsłudze błędów: 1. Pozwól aplikacji się zawiesić i uruchom ją ponownie. 2. Obsługa wszystkich możliwych błędów i nigdy się nie Rozbij. 3. Zrównoważone podejście między tymi dwoma
Number7: użyj dojrzałego rejestratora, aby zwiększyć widoczność błędów
TL; DR: zestaw dojrzałych narzędzi rejestrujących, takich jak Winston, Bunyan lub Log4J, przyspieszy wykrywanie i zrozumienie błędów. Więc zapomnij o konsoli.log.
W Przeciwnym Razie: dzienniki lub ręcznie za pomocą niechlujnego pliku tekstowego bez narzędzi do zapytań lub przyzwoitej przeglądarki dziennika mogą Cię zająć w pracy do późna
Przykład kodu-Winston logger w akcji
//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' });
Cytat z bloga: "pozwala określić kilka wymagań (dla loggera):" (Z bloga strongblog)
...pozwala określić kilka wymagań (dla REJESTRATORA): 1. Znacznik czasu każdej linii dziennika. Ten jest dość oczywisty – powinieneś być w stanie stwierdzić, kiedy wystąpił każdy wpis w dzienniku. 2. Format logowania powinien być łatwo przyswajalny zarówno przez ludzi, jak i maszyny. 3. Pozwala na wielokrotne konfigurowalne strumienie docelowe. Na przykład możesz zapisywać dzienniki śledzenia do jednego pliku, ale gdy wystąpi błąd, Zapisz do tego samego pliku, a następnie do pliku błędu i wyślij e-mail w tym samym czasie...
Number8: wykrywanie błędów i przestojów przy użyciu produktów APM
TL; DR: produkty do monitorowania i wydajności (aka APM) proaktywnie sprawdzają twoją bazę kodową lub API, dzięki czemu mogą automatycznie wyróżniać błędy, awarie i wolne części, które były brak
W Przeciwnym Razie: możesz poświęcić wiele wysiłku na mierzenie wydajności API i przestojów, prawdopodobnie nigdy nie będziesz świadomy, które są Twoje najwolniejsze części kodu w rzeczywistym scenariuszu i jak wpływają one na UX
Cytat z bloga: " segmenty produktów APM" (Z bloga Yoni Goldberg)
"...produkty APM stanowią 3 główne segmenty: 1. Monitorowanie witryny lub API- zewnętrzne usługi, które stale monitorują czas pracy i wydajność poprzez żądania HTTP. Można go skonfigurować w kilka minut. Oto kilka wybranych pretendentów: Pingdom, uptime Robot i New Relic 2. Oprzyrządowanie kodowe-rodzina produktów, które wymagają osadzenia agenta w aplikacji, aby skorzystać z funkcji powolnego wykrywania kodu,statystyk WYJĄTKÓW, monitorowania wydajności i wielu innych. Oto kilka wybranych kandydatów: New Relic, App Dynamics 3. Operational intelligence dashboard- Ta linia produktów koncentruje się na ułatwianie zespołu ops dzięki metrykom i wyselekcjonowanym treściom, które pomagają w łatwym utrzymaniu wydajności aplikacji. Zwykle wiąże się to z agregacją wielu źródeł informacji (Dzienniki aplikacji, dzienniki DB, dzienniki serwerów itp.) i pracami projektowymi z góry. Poniżej znajduje się kilka wybranych pretendentów: Datadog, Splunk "
powyższa wersja jest skrócona- zobacz tutaj więcej najlepszych praktyk i przykładów
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-04-18 20:21:04
Można wyłapywać nieprzewidziane wyjątki, ale mają ograniczone zastosowanie. Zobacz też http://debuggable.com/posts/node-js-dealing-with-uncaught-exceptions:4c933d54-1428-443c-928d-4e1ecbdd56cb
monit
, forever
lub upstart
może być użyty do ponownego uruchomienia procesu węzła, gdy ulega awarii. Najlepsze jest pełne wdzięku zamknięcie (np. zapisz wszystkie dane w pamięci w nieobciążonym programie obsługi wyjątków).
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-09-10 22:13:56
Domeny Nodejs to najbardziej aktualny sposób obsługi błędów w nodejs. Domeny mogą przechwytywać zarówno błędy/inne zdarzenia, jak i tradycyjnie rzucane obiekty. Domeny zapewniają również funkcjonalność obsługi wywołań zwrotnych z błędem przekazywanym jako pierwszy argument za pomocą metody intercept.
Podobnie jak w przypadku normalnej obsługi błędów w stylu try/catch, zwykle najlepiej jest wyrzucać błędy, gdy występują, i blokować obszary, w których chcesz odizolować błędy od wpływu na resztę kod. Sposobem na "zablokowanie" tych obszarów jest wywołanie domeny.Uruchom z funkcją jako blok izolowanego kodu.
W kodzie synchronicznym, powyższe wystarczy - gdy wystąpi błąd, pozwalasz mu zostać wyrzuconym, lub łapiesz go i obsługujesz tam, przywracając wszystkie dane, które musisz przywrócić.
try {
//something
} catch(e) {
// handle data reversion
// probably log too
}
Gdy błąd występuje w asynchronicznym wywołaniu zwrotnym, musisz być w stanie w pełni obsłużyć wycofanie danych (stan współdzielony, dane zewnętrzne, takie jak bazy danych, itp.). Albo musisz Ustaw coś, aby wskazać, że wystąpił wyjątek-jeśli kiedykolwiek zależy ci na tej fladze, musisz poczekać na zakończenie połączenia zwrotnego.
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
}
})});
Część z tego powyższego kodu jest brzydka, ale można tworzyć szablony dla siebie, aby było ładniej, np:
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
});
Aktualizacja (2013-09):
Powyżej używam przyszłości, która implikuje semantykę włókien , która pozwala czekać na futures in-line. Pozwala to na korzystanie z tradycyjnego try-catch bloki na Wszystko - co uważam za najlepszą drogę. Jednak nie zawsze można to zrobić(np. w przeglądarce)...
Istnieją również futures, które nie wymagają semantyki włókien (które następnie działają z normalnym, przeglądarkowym JavaScript). Mogą one być nazywane futures, obietnice, lub deferreds(będę tylko odnosić się do futures od teraz). Biblioteki futures Plain-old-JavaScript pozwalają propagować błędy między futuresami. Tylko niektóre z tych bibliotek pozwalają na dowolną przyszłość prawidłowo obsługiwane, więc uważaj.
Przykład:
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()
To imituje normalny chwyt próbny, mimo że elementy są asynchroniczne. Wydrukuje:
1
2
handler
Zauważ, że nie wyświetla "3", ponieważ został wyrzucony wyjątek, który przerywa ten przepływ.
Spójrz na obietnice bluebird:Zauważ, że nie znalazłem wielu innych bibliotek poza tymi, które poprawnie obsługują rzucane wyjątki. jQuery odroczony, na przykład, nie - "fail" handler nigdy nie dostać wyjątek rzucony a' then ' handler, który moim zdaniem jest deal breaker.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-07-02 22:36:36
Pisałam o tym ostatnio na http://snmaynard.com/2012/12/21/node-error-handling / . nowością w node w wersji 0.8 są domeny i pozwalają połączyć wszystkie formy obsługi błędów w jeden łatwiejszy formularz zarządzania. Możecie o nich przeczytać w moim poście.
Możesz również użyć czegoś w rodzaju Bugsnag, aby śledzić swoje nieobciążone wyjątki i być powiadamianym przez e-mail, czatroom lub mieć bilet utworzony dla nieobciążonego wyjątku(jestem współzałożycielem Bugsnag).
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-01-10 00:16:11
Jednym z przykładów, w którym użycie try-catch może być właściwe, jest użycie pętli forEach. Jest synchroniczny, ale jednocześnie nie można po prostu użyć instrukcji return w wewnętrznym zakresie. Zamiast tego można użyć metody try and catch, aby zwrócić obiekt błędu w odpowiednim zakresie. Consider:
function processArray() {
try {
[1, 2, 3].forEach(function() { throw new Error('exception'); });
} catch (e) {
return e;
}
}
Jest to kombinacja podejść opisanych przez @balupton powyżej.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-09-11 14:26:39
Chciałbym tylko dodać, że Krok.biblioteka js pomaga obsługiwać wyjątki, przekazując je zawsze do funkcji następnego kroku. Dlatego możesz mieć jako ostatni krok funkcję, która sprawdza błędy w którymkolwiek z poprzednich kroków. Takie podejście może znacznie uprościć obsługę błędów.
Poniżej cytat ze strony github:
Wszelkie wyjątki rzucone są przechwytywane i przekazywane jako pierwszy argument do następna funkcja. Dopóki nie zagnieżdżesz się funkcje inline Twoje główne funkcje zapobiegają temu, że nigdy nie będzie żadnych nieobciążonych wyjątki. Jest to bardzo ważne w przypadku długich węzłów.Serwery JS ponieważ pojedynczy wyjątek może doprowadzić do awarii całego serwera.
Ponadto możesz użyć Step do kontrolowania wykonywania skryptów, aby mieć sekcję czyszczenia jako ostatni krok. Na przykład, jeśli chcesz napisać skrypt kompilacji w Node i zgłosić, jak długo trwało pisanie, ostatni krok może to zrobić (zamiast próbować kopać out the last callback).
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-06-12 17:47:14
Po przeczytaniu tego postu jakiś czas temu zastanawiałem się, czy używanie domen do obsługi wyjątków na poziomie api / funkcji jest bezpieczne. Chciałem ich użyć do uproszczenia kodu obsługi wyjątków w każdej funkcji asynchronicznej, którą napisałem. Moim zmartwieniem było to, że użycie nowej domeny dla każdej funkcji wprowadziłoby znaczny narzut. Moja praca domowa wydaje się wskazywać, że jest minimalny narzut i że wydajność jest rzeczywiście lepsza z domen niż z try catch w niektórych sytuacje.
Http://www.lighthouselogic.com/#/using-a-new-domain-for-each-async-function-in-node/
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-09-10 04:15:57
Jeśli chcesz korzystać z usług w Ubuntu(Upstart): Node jako usługa w Ubuntu 11.04 z upstart, monit i forever.js
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-03-19 13:22:53
Wyłapywanie błędów zostało tutaj bardzo dobrze omówione, ale warto pamiętać, aby gdzieś je wylogować, aby móc je przeglądać i naprawiać.
Bunyan jest popularnym frameworkiem logowania dla NodeJS - wspiera zapisywanie do kilku różnych miejsc wyjściowych, co czyni go użytecznym do lokalnego debugowania, o ile unikasz konsoli.log. W programie obsługi błędów Twojej domeny możesz wypluć błąd do pliku dziennika.
var log = bunyan.createLogger({
name: 'myapp',
streams: [
{
level: 'error',
path: '/var/tmp/myapp-error.log' // log ERROR to this file
}
]
});
To może być czasochłonne, jeśli masz wiele błędy i / lub serwery do sprawdzenia, więc może warto zajrzeć do narzędzia takiego jak Raygun (zastrzeżenie, pracuję w Raygun), aby grupować błędy razem - lub używać ich razem.
Jeśli zdecydowałeś się użyć Raygun jako narzędzia, to jest to dość łatwe do skonfigurowania zbyt
var raygunClient = new raygun.Client().init({ apiKey: 'your API key' });
raygunClient.send(theError);
Skrzyżowane z użyciem narzędzia takiego jak PM2 lub forever, Twoja aplikacja powinna być w stanie się zawiesić, wylogować, co się stało i ponownie uruchomić bez większych problemów.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-09-29 19:51:58