Node.js En İyi Uygulama Özel Durum İşleme


755

Birkaç gün önce node.js'yi denemeye başladım. Programımda işlenmeyen bir istisna olduğunda Düğüm'ün sonlandırıldığını fark ettim. Bu, işlenmeyen özel durumlar oluştuğunda ve İşçi İşareti isteğinin alınabileceği durumlarda yalnızca İşçi İş Parçasının öldüğü normal sunucu kapsayıcısından farklıdır. Bu birkaç soruyu gündeme getiriyor:

  • Buna process.on('uncaughtException')karşı korunmanın tek etkili yolu mu?
  • Will process.on('uncaughtException')yanı asenkron işlemlerin yürütülmesi sırasında işlenmeyen istisna yakalamak?
  • Yakalanmamış istisnalar durumunda kaldırabileceğim bir modül var mı (e-posta göndermek veya bir dosyaya yazmak gibi)?

Düğüm.js'de yakalanmayan istisnaları ele almak için en iyi uygulamaları gösterecek herhangi bir işaretçi / makaleyi takdir ediyorum


11
yakalanmamış istisnalar olmamalıdır. Eğer çöktüğünde tüm uygulamanızı yeniden başlatan bir program kullanıyorlarsa (nodemon, sonsuza dek, danışman)
Raynos

116
Eşzamansız kodunuzun her parçasını içine koymazsanız ve yakalanmamış istisnalar her zaman gerçekleşebilir try .. catchve bunun tüm kütüphaneleriniz
Dan

13
+1 Dan Başlangıçta ben tüm "libs " tüm "iş parçacığı giriş noktaları" denemek / yakalar kod sarmak gerekir gibi, tüm libs biraz abartı olduğunu düşündüm . Ancak daha dikkatli bir şekilde düşünmek, herhangi bir lib'in , kodunuz tarafından yakalanamayan derin bir yere gömülmüş bir setTimeoutveya daha fazla setIntervalşeye sahip olabilir.
Eugene Beresovsky

8
@EugeneBeresovksy Dan haklı ama uncaughtExceptions oluştuğunda tek güvenli seçeneğin uygulamayı yeniden başlatmak olduğu gerçeğini değiştirmez. Başka bir deyişle, uygulamanız kilitlendi ve bu konuda yapabileceğiniz veya yapmanız gereken hiçbir şey yok. Yapıcı bir şey yapmak istiyorsanız, yeni ve hala deneysel, v0.8 etki alanı özelliğini uygulayın, böylece kilitlemeyi günlüğe kaydedebilir ve istemcinize 5xx yanıtı gönderebilirsiniz.
ostergaard

1
@Dan Tüm geri arama işlevlerini denemek bile .. catch, yakalama hatalarını garanti etmez. Gerekli bir modülün kendi ikili dosyalarını kullanması durumunda nezaketsizce çökebilirler. Bunu phantomjs-node ile yakaladım, yakalanması imkansız olan hatalarda başarısız oldum (gerekli ikili dosyalar üzerinde bir tür işlem denetimi yapmadıkça, ama bunu hiç takip etmedim).
Trindaz

Yanıtlar:


737

Güncelleme: Joyent artık kendi rehberine sahip . Aşağıdaki bilgiler daha çok özetlenmiştir:

Güvenle "fırlatma" hataları

İdeal olarak, yakalanmamış hatalardan mümkün olduğunca kaçınmak istiyoruz, hatayı tam anlamıyla atmak yerine, kod mimarimize bağlı olarak aşağıdaki yöntemlerden birini kullanarak hatayı güvenli bir şekilde "atabiliriz":

  • Eşzamanlı kod için bir hata oluşursa hatayı döndürün:

    // 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)
    }
  • Geri arama tabanlı (yani. Asenkron) kodu için geri arama ilk argümandır errbir hata olursa, errbir hata sonra olmaz ise hata olduğunu errolduğunu null. Diğer tüm argümanlar şu argümanı takip eder 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)
        }
    })
  • İçin olaylı hata yerine hatayı atma, herhangi bir yerde ortaya çıkabilir kodu, yangın errorolayı yerine :

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

Güvenle "yakalama" hataları

Bazen de, güvenli bir şekilde yakalamazsak, yakalanmamış bir istisna ve uygulamamızın potansiyel bir çökmesine yol açabilecek bir yere bir hata atan kod hala olabilir. Kod mimarimize bağlı olarak, yakalamak için aşağıdaki yöntemlerden birini kullanabiliriz:

  • Hatanın nerede oluştuğunu bildiğimizde, bu bölümü bir node.js etki alanına sarabiliriz

    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
    })
  • Hatanın nerede meydana geldiğini senkronize kod olarak bilirsek ve herhangi bir nedenle alan adlarını (belki de eski düğüm sürümü) kullanamazsak, try catch deyimini kullanabiliriz:

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

    Ancak, try...catcheşzamansız olarak atılan bir hata yakalanmayacağından, eşzamansız kodda kullanılmamaya dikkat edin :

    try {
        setTimeout(function(){
            var err = new Error('example')
            throw err
        }, 1000)
    }
    catch (err) {
        // Example error won't be caught here... crashing our app
        // hence the need for domains
    }

    try..catchEşzamansız kodla birlikte çalışmak istiyorsanız , Düğüm 7.4 veya üstünü çalıştırırken async/awaiteşzamansız işlevlerinizi yazmak için yerel olarak kullanabilirsiniz .

    Dikkat edilmesi gereken başka bir şey de try...catch, tamamlanma geri aramanızı aşağıdaki trygibi ifadenin içine sarma riskidir :

    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
    }

    Kodunuz daha karmaşık hale geldiğinden bu gotcha'nın yapılması çok kolaydır. Bu nedenle, (1) eşzamansız kodda yakalanmamış istisnaları (2) istemediğiniz denemeyi yakalama yürütmesini önlemek için etki alanlarını kullanmak veya hataları döndürmek en iyisidir. JavaScript'in eşzamansız olay makinesi stili yerine uygun iş parçacığına izin veren dillerde, bu daha az sorun olur.

  • Son olarak, bir etki alanına veya try catch deyimine sarılmamış bir yerde yakalanmamış bir hatanın meydana gelmesi durumunda, uncaughtExceptiondinleyicimizi kullanarak uygulamamızı çökmemesini sağlayabiliriz (ancak bunu yaptığınızda uygulamayı bilinmeyen bir duruma getirebiliriz) ):

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

5
Teşekkürler Raynos, güncellendi. Kötülüğünü açıklayan bir kaynağınız var try catchmı? Bunu kanıtlarla desteklemeyi isterdim. Ayrıca senkronizasyon örneği düzeltildi.
balupton

2
Bu cevap artık geçerli değil. Alanlar bu sorunu çözüyor (node.js tarafından önerilir)
Gabriel Llamas

5
@balupton Hata işleme için hatalar atılmalıdır. Kesinlikle kaçınılmalıdır. Onlar hakkında uygulamanın ya da başka bir şey yürütülmesini kıran hiçbir şey yoktur. Java ve diğer birçok modern dil, istisnalar için mükemmel bir desteğe sahiptir. Buradaki misonform yazıların bazılarını okuduktan sonraki tek sonucum insanların onları çok iyi anlamadığıdır Ve bu yüzden onlardan korkuyorlar. Korku Belirsiz Şüphe. Bu tartışma, en az 20 yıl önce istisnalar lehine kesin olarak kararlaştırıldı.
enl8enmentnow

22
Şimdi alanlar io.js tarafından kullanımdan kaldırıldı : " Bu modül kullanımdan kaldırılıyor. Yeni bir API sonlandırıldıktan sonra bu modül tamamen kullanımdan kaldırılacak ... Alanların sağlaması gereken işlevselliğe sahip olması gereken kullanıcılar şu an için ona güvenebilirler, ancak Gelecekte farklı bir çözüme geçmeyi beklemeliyiz. "
Timothy Gu

5
Domain api artık uygun bulunmamaktadır ? Yedek bir API'den bahsediyorlar - kimse bunun ne zaman çıkacağını ve neye benzeyeceğini biliyor?
UpTheCreek

95

Aşağıda, kod örneği ve seçilen blog yayınlarından alıntılar dahil olmak üzere bu konuyla ilgili birçok farklı kaynaktan bir özetleme ve küratörlük yer almaktadır. En iyi uygulamaların tam listesini burada bulabilirsiniz


Node.JS hata işlemenin en iyi uygulamaları


Sayı1: Zaman uyumsuz hata işleme için vaatler kullanın

TL; DR: Geri çağırma stilinde zaman uyumsuz hataları ele almak muhtemelen en hızlı cehennem yoludur (kıyamet piramidi). Kodunuza verebileceğiniz en iyi hediye, bunun yerine try-catch gibi çok kompakt ve tanıdık kod sözdizimi sağlayan saygın bir söz kitaplığı kullanmaktır

Aksi takdirde: Node.JS geri çağırma stili, işlev (err, yanıt), hata işleme ile sıradan kod, aşırı yuvalama ve garip kodlama kalıplarının karışımı nedeniyle sürdürülemez koda ulaşmanın umut verici bir yoludur

Kod örneği - iyi

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

kod örneği anti desen - geri arama stili hata işleme

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

Blog alıntısı: " Vaatlerle ilgili bir sorunumuz var" (Blog pouchdb'den "Düğüm Vaatleri" anahtar kelimeleri için 11. sırada yer aldı)

“… Ve aslında, geri aramalar daha uğursuz bir şey yapıyor: bizi programlama dillerinde genellikle verdiğimiz bir şeydir. . bunun için ulaşmak ve orada değil kadar, o ne kadar ihtiyacım farkında değiller sözlerin bütün mesele bize asenk gittiğinde Kaybettiğimiz dil temellerini geri vermektir. dönüşünü atmak ve yığın Ama onlardan yararlanmak için vaatlerin nasıl doğru kullanılacağını bilmek zorundayım. "


Sayı2: Yalnızca yerleşik Hata nesnesini kullanın

TL; DR: Hataları dize veya özel tür olarak atan kodu görmek oldukça yaygındır - bu hata işleme mantığını ve modüller arasındaki birlikte çalışabilirliği zorlaştırır. Node.JS yerleşik Error nesnesini kullanarak bir vaadi reddetmek, istisna atmak veya hata yaymak fark etmez tekdüzeliği artırır ve hata bilgilerinin kaybını önler

Aksi takdirde: Bazı modülleri yürütürken, hangi tür hataların geleceğinden emin olmamanız - gelen istisna hakkında mantık yürütmeyi ve onu ele almayı çok daha zorlaştırır. Buna değer olsa da, hataları tanımlamak için özel türler kullanmak yığın izlemesi gibi kritik hata bilgilerinin kaybolmasına neden olabilir!

Kod örneği - doğru yapmak

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

kod örneği anti desen

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

Blog alıntısı: "Bir dize bir hata değildir" (Blogdan, “Node.JS hata nesnesi” anahtar kelimeleri için 6. sırada yer aldı)

"… Hata yerine dize iletmek, modüller arasında birlikte çalışabilirliğin azalmasına neden olur. Hata denetimlerinin örneğini gerçekleştirebilecek veya hata hakkında daha fazla bilgi edinmek isteyen API'lerle sözleşmeleri keser . Göreceğimiz gibi hata nesnelerinin çok yapıcıya iletilen mesajı tutmanın yanı sıra modern JavaScript motorlarındaki ilginç özellikler .. "


Number3: Operasyonel ve programcı hatalarını ayırt etme

TL; DR: İşlem hataları (ör., API geçersiz bir girdi aldı), hata etkisinin tam olarak anlaşıldığı ve dikkatli bir şekilde ele alınabildiği bilinen durumları ifade eder. Öte yandan, programcı hatası (örn. Tanımsız değişkeni okumaya çalışmak) uygulamayı zarif bir şekilde yeniden başlatmayı dikte eden bilinmeyen kod hatalarını ifade eder

Aksi takdirde: Bir hata göründüğünde uygulamayı her zaman yeniden başlatabilirsiniz, ancak neden küçük ve tahmin edilen bir hata (işletim hatası) nedeniyle ~ 5000 çevrimiçi kullanıcıyı devre dışı bırakıyorsunuz? bunun tersi de ideal değildir - bilinmeyen bir sorun (programcı hatası) oluştuğunda uygulamayı yukarıda tutmak beklenmedik davranışlara yol açabilir. İkisini ayırmak taktiksel davranmayı ve verilen bağlama göre dengeli bir yaklaşım uygulamayı sağlar

Kod örneği - doğru yapmak

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

kod örneği - bir hatayı operasyonel (güvenilir) olarak işaretleme

//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 Alıntı : "Aksi takdirde devlet risk" (Hata ayıklanabilir blog, "Node.JS yakalanmamış istisna" anahtar kelimeler için 3 sırada yer)

… JavaScript'te atmanın nasıl çalıştığının doğası gereği, referans sızıntısı yapmadan veya başka türlü tanımlanmamış kırılgan bir durum oluşturmadan“ kaldığınız yerden güvenli bir şekilde almak ”için neredeyse hiçbir yol yoktur. bir atılmış hata sürecini kapatmaya olduğunu . tabii ki, normal bir web sunucusuna, birçok bağlantıları açmak olabilir ve bir hata başkası tarafından tetiklenen çünkü aniden bu aşağı kapatmaya makul değildir. daha iyi bir yaklaşım olduğunu diğerlerinin normal zamanlarında bitmesine izin verirken hatayı tetikleyen isteğe bir hata yanıtı gönderin ve bu çalışandaki yeni istekleri dinlemeyi bırakın "


Sayı4: Merkezi yazılımın içinden, ancak ara katman yazılımı içinde olmayan hataları işleme

TL; DR: Postaya yönetici ve günlük kaydı gibi hata işleme mantığı, bir hata geldiğinde tüm uç noktaların (örn. Express ara katman yazılımı, cron işleri, birim sınama) çağırdığı özel ve merkezi bir nesnede kapsüllenmelidir.

Aksi takdirde: Hataların tek bir yerde ele alınmaması, kod çoğaltılmasına ve muhtemelen yanlış işlenen hatalara neden olur

Kod örneği - tipik bir hata akışı

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

Blog alıntısı: "Bazen daha düşük düzeyler, hatayı arayanlarına yaymak dışında yararlı bir şey yapamaz" (Joyent blogundan, “Node.JS hata işleme” anahtar kelimeleri için 1 sırada yer aldı)

"… Aynı hatayı yığının çeşitli seviyelerinde halledebilirsiniz. Bu, daha düşük seviyeler hatayı arayanlara yaymak dışında yararlı bir şey yapamazsa, hatayı arayana yayar vb. yalnızca en üst düzey arayan uygun işlemin ne olduğunu bilir, bu işlemi yeniden denemek, kullanıcıya bir hata bildirmek veya başka bir şey rapor etmek anlamına gelir, ancak bu, tüm hataları tek bir üst seviyeye bildirmeye çalışmanız gerektiği anlamına gelmez. geri arama, çünkü bu geri aramanın kendisi hatanın hangi bağlamda oluştuğunu bilemez "


Number5: Swagger kullanarak belge API'si hataları

TL; DR: API arayanlarınıza hangi hataların karşılığında gelebileceğini bildirin, böylece bunları çökmeden düşünceli bir şekilde halledebilirler. Bu genellikle Swagger gibi REST API dokümantasyon çerçeveleri ile yapılır

Aksi takdirde: Bir API istemcisi yalnızca anlayamadığı bir hata aldığından çökmeye ve yeniden başlatmaya karar verebilir. Not: API'nizin arayanı siz olabilirsiniz (bir mikro hizmet ortamında çok tipiktir)

Blog alıntısı: "Arayanlara hangi hataların olabileceğini söylemelisiniz" (Joyent blogundan “Node.JS logging” anahtar kelimeleri için 1 sırada yer aldı)

… Hataları nasıl ele alacağımızdan bahsettik, ancak yeni bir işlev yazarken, işlevinizi çağıran koda nasıl hata iletiyorsunuz? … Hangi hataların meydana gelebileceğini bilmiyor veya ne anlama geldiğini bilmiyorsanız, programınız kaza dışında doğru olamaz. Dolayısıyla, yeni bir işlev yazıyorsanız, arayanlarınıza hangi hataların olabileceğini ve ne yaptıklarını söylemelisiniz


Sayı6: Bir yabancı kasabaya geldiğinde süreci nazikçe kapat

TL; DR: Bilinmeyen bir hata oluştuğunda (bir geliştirici hatası, bkz. En iyi uygulama numarası # 3) - uygulamanın sağlıklılığı konusunda belirsizlik var. Yaygın bir uygulama, Forever ve PM2 gibi bir 'yeniden başlatma' aracı kullanarak sürecin dikkatli bir şekilde yeniden başlatılmasını önerir.

Aksi takdirde: Bilmediğiniz bir istisna yakalandığında, bazı nesneler hatalı bir durumda olabilir (örneğin, küresel olarak kullanılan ve bazı dahili hatalardan dolayı olayları tetiklemeyen bir olay gönderici) ve gelecekteki tüm istekler başarısız olabilir veya çılgınca davranabilir

Kod örneği - kilitlenip kilitlenmeyeceğine karar verme

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

Blog alıntı: "Hata işleme hakkında üç düşünce okulu vardır" (Blog jsrecipes)

… Hata işleme ile ilgili başlıca üç düşünce okulu vardır: 1. Uygulamanın çökmesine izin verin ve yeniden başlatın. 2. Tüm olası hataları halledin ve asla kilitlenmeyin. 3. İkisi arasında dengeli yaklaşım


Number7: Hataların görünürlüğünü artırmak için olgun bir günlükçü kullanın

TL; DR: Winston, Bunyan veya Log4J gibi bir dizi olgun günlük oluşturma aracı hata bulma ve anlama işlemlerini hızlandıracak. Bu yüzden console.log'u unutun.

Aksi takdirde: console.logs üzerinden veya sorgulama araçları veya iyi bir günlük görüntüleyici olmadan dağınık metin dosyası aracılığıyla el ile gezinme sizi geç saatlere kadar meşgul tutabilir

Kod örneği - Winston günlükçüsü iş başında

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

Blog alıntısı: "Bir günlükçü için birkaç gereksinimi tanımlayalım:" (Blogdan strongblog'dan)

… Birkaç gereksinimi tanımlayalım (bir günlükçü için): 1. Her günlük satırının zaman damgası. Bu oldukça açıklayıcıdır - her günlük girişinin ne zaman gerçekleştiğini söyleyebilmelisiniz. 2. Kayıt formatı, makineler ve insanlar tarafından kolayca sindirilebilir olmalıdır. 3. Birden fazla yapılandırılabilir hedef akışına izin verir. Örneğin, izleme günlüklerini bir dosyaya yazıyor olabilirsiniz, ancak bir hatayla karşılaştığınızda, aynı dosyaya yazın, ardından hata dosyasına yazın ve aynı anda e-posta gönderin…


Number8: APM ürünlerini kullanarak hataları ve kesinti süresini keşfedin

TL; DR: İzleme ve performans ürünleri (diğer adıyla APM) kod tabanınızı veya API'nızı proaktif olarak ölçer, böylece eksik olduğunuz hataları, çökmeleri ve yavaşladığınız parçaları otomatik olarak vurgulayabilir

Aksi takdirde: API performansını ve duruş sürelerini ölçmek için büyük çaba harcayabilirsiniz, muhtemelen gerçek dünya senaryosu altında en yavaş kod parçalarınızın hangileri olduğunu ve bunların UX'i nasıl etkilediğini asla bilemezsiniz.

Blog teklifi: "APM ürünleri segmentleri" (Yoni Goldberg blogundan)

"… APM ürünleri 3 ana bölümden oluşur: 1. Web sitesi veya API izleme - HTTP istekleri aracılığıyla çalışma süresini ve performansı sürekli olarak izleyen harici hizmetler. Birkaç dakika içinde kurulabilir. Aşağıda birkaç seçili yarışmacı bulunmaktadır: Pingdom, Uptime Robot ve New Relic 2 Kod enstrümantasyonu - yavaş kod tespiti, istisna istatistikleri, performans izleme ve daha birçok özellikten yararlanmak için uygulama içerisine bir ajan katmak isteyen ürünler ailesi.Aşağıdaki birkaç yarışmacı yarışmacıdır: New Relic, App Dynamics 3. Operasyonel zeka panosu -bu ürün yelpazesi, ops ekibini, uygulama performansında kolayca en üst düzeyde kalmanıza yardımcı olan metrikler ve küratörlü içerikle kolaylaştırmaya odaklanmıştır. Bu genellikle birden fazla bilgi kaynağının (uygulama günlükleri, DB günlükleri, sunucu günlüğü, vb.) Ve açık gösterge tablosu tasarım çalışmalarının toplanmasını içerir. Aşağıda birkaç yarışmacı seçilmiştir: Datadog, Splunk "


Yukarıdakiler kısaltılmış bir versiyondur - burada daha fazla en iyi uygulama ve örneklere bakın


30

Yakalanmayan istisnaları yakalayabilirsiniz, ancak kullanımı sınırlıdır. Bkz. Http://debuggable.com/posts/node-js-dealing-with-uncaught-exceptions:4c933d54-1428-443c-928d-4e1ecbdd56cb

monit, foreverYa da upstartbu çöker düğüm işlemini yeniden başlatmak için kullanılabilir. Zarif bir kapatma umduğunuzda en iyisidir (örn. Tüm bellek içi verileri yakalanmamış istisna işleyicisine kaydedin).


4
+1 Bağlantı faydalı, teşekkürler. Hala node.js bağlamında en iyi uygulamayı ve "zarif yeniden başlatma" anlamını arıyorum
momo

Bu bağlamda "zarif yeniden başlatma" anlayışım aslında nponeccop'un önerdiği şey olacaktır: işlemin ölmesine izin verin ve ilk başta çalıştıran her şeyin yeniden başlatılmasına izin verin.
Ilkka

Bu bağlantı için çok teşekkürler! Gerçekten kullanışlı!
SatheeshJM

Bu harika bir cevap. Ancak ilk örneğinizde bir Hata döndürmeme katılmıyorum. An Errordöndürmek, işlevin anlamını gereksiz yere karıştıran dönüş değerini polimorfik hale getirir. Ayrıca, 0 ile dalış önceden vererek JavaScript işlenir Infinity, -Infinityya da NaN, bu değerler burada typeof === 'number'. İle kontrol edilebilirler !isFinite(value). Genel olarak asla bir işlev bir hata döndürme öneriyoruz. Tutarlı semantikle özel bir polimorfik olmayan değer atmak veya döndürmek için kod okunabilirliği ve bakımı açısından daha iyi.
wprl


13

nodejs alan adları , nodejs'deki hataları ele almanın en güncel yoludur. Etki alanları, geleneksel olarak atılan nesnelerin yanı sıra hata / diğer olayları da yakalayabilir. Etki alanları ayrıca, kesme yöntemi aracılığıyla ilk argüman olarak iletilen bir hata ile geri çağrıları işleme işlevselliği de sağlar.

Normal try / catch-tarzı hata işlemede olduğu gibi, genellikle hatalar oluştuğunda atmak ve hataların kodun geri kalanını etkilemesini önlemek istediğiniz alanları engellemek en iyisidir. Bu alanları "engellemenin" yolu, yalıtılmış kod bloğu işlevi olan domain.run'u çağırmaktır.

Eşzamanlı kodda, yukarıdakiler yeterlidir - bir hata oluştuğunda ya atılmasına izin verirsiniz ya da onu yakalayıp oraya işleyerek geri döndürmeniz gereken verileri geri alırsınız.

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

Hata zaman uyumsuz bir geri çağrıda gerçekleştiğinde, verilerin geri alınmasını (paylaşılan durum, veritabanları gibi harici veriler, vb.) Tamamen işleyebilmeniz gerekir. VEYA bir istisna olduğunu gösteren bir şey ayarlamanız gerekir - bu bayrağı önemsediğiniz her durumda geri aramanın tamamlanmasını beklemeniz gerekir.

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
  }

})});

Yukarıdaki kodlardan bazıları çirkin, ancak daha güzel hale getirmek için kendiniz için desenler oluşturabilirsiniz, örneğin:

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

GÜNCELLEME (2013-09):

Yukarıda, vadeli işlemlerde in-line beklemenize izin veren lif semantiği anlamına gelen bir gelecek kullanıyorum . Bu aslında her şey için geleneksel try-catch bloklarını kullanmanıza izin veriyor - bu da gitmek için en iyi yol olduğunu düşünüyorum. Ancak, bunu her zaman yapamazsınız (yani tarayıcıda) ...

Fiber semantiği gerektirmeyen gelecekler de vardır (bunlar normal, tarayıcı JavaScript ile çalışır). Bunlara vadeli işlemler, vaatler veya ertelemeler denebilir (bundan sonra gelecekleri ifade edeceğim). Düz-eski-JavaScript vadeli işlem kütüphaneleri, hataların vadeli işlemler arasında yayılmasını sağlar. Bu kütüphanelerin sadece bir kısmı atılan herhangi bir geleceğin doğru bir şekilde ele alınmasına izin verir, bu yüzden dikkatli olun.

Bir örnek:

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

Parçalar eşzamansız olsa da bu normal bir yakalamayı taklit eder. Yazdırır:

1
2
handler

Bu akışı kesintiye uğratan bir istisna atıldığından '3' yazdırmaz.

Mavi kuş vaatlerine bir göz atın:

Atılan istisnaları uygun şekilde işleyen bunlardan başka pek çok kütüphane bulamadığımı unutmayın. Örneğin, jQuery ertelendi - "başarısız" işleyicisi asla bir 'sonra' işleyicisi atılır istisna alamıyorum, bence bir anlaşma kırıcı olduğunu.


Javascript'teki uygun vaat spesifikasyonu Promises / A + olarak bilinir. Burada uygulamaların bir listesini görebilirsiniz: github.com/promises-aplus/promises-spec/blob/master/… . Çıplak Promises / A + 'nın pratikte kullanılamaz olduğuna dikkat edin - Promises / A +, kütüphanelerin kendilerini çözmesi için hala birçok pratik sorun bırakıyor. Bununla birlikte, gösterdiğiniz hata yayılımı, deterministik yürütme sırası ve yığın taşmasından güvenlik gibi kesinlikle önemli şeyler garanti edilir.
Esailija


11

Bu konuyla ilgili olarak yakın zamanda http://snmaynard.com/2012/12/21/node-error-handling/ adresinde yazdım . Sürüm 0.8'deki düğümün yeni bir özelliği etki alanlarıdır ve tüm hata işleme biçimlerini tek bir kolay yönetme biçiminde birleştirmenize olanak tanır. Onları mesajımda okuyabilirsiniz.

Yakalanmayan istisnalarınızı takip etmek ve e-posta, sohbet odası aracılığıyla bilgilendirilmek veya yakalanmayan bir istisna için bir bilet oluşturmak için Bugsnag gibi bir şey de kullanabilirsiniz (Ben Bugsnag'ın kurucu ortağıyım).


2
Alan modülü artık resmi olarak kullanımdan kaldırıldı. nodejs.org/api/domain.html
MattSidor

3

Sadece Step.js kitaplığının , her zaman bir sonraki adım işlevine geçirerek istisnaları işlemenize yardımcı olduğunu eklemek istiyorum . Bu nedenle, son adım olarak, önceki adımların herhangi birindeki hataları kontrol eden bir işleve sahip olabilirsiniz. Bu yaklaşım hata işlemenizi büyük ölçüde basitleştirebilir.

Aşağıda github sayfasından bir alıntı var:

atılan istisnalar yakalanır ve bir sonraki işleve ilk argüman olarak iletilir. Geri arama işlevlerini iç içe yerleştirmediğiniz sürece, ana işlevleriniz bu sırada yakalanmamış istisnalar olmasını önler. Bu, uzun süre çalışan node.JS sunucuları için çok önemlidir, çünkü yakalanmamış tek bir istisna tüm sunucuyu düşürebilir.

Ayrıca, son adım olarak bir temizleme bölümüne sahip olacak komut dosyalarının yürütülmesini denetlemek için Adım'ı kullanabilirsiniz. Örneğin, Düğümde bir derleme komut dosyası yazmak ve yazma işleminin ne kadar sürdüğünü bildirmek istiyorsanız, son adım bunu yapabilir (son geri aramayı kazmak yerine).


3

Try-catch kullanmanın uygun olabileceği örneklerden biri forEach döngüsü kullanmaktır. Eşzamanlıdır, ancak aynı zamanda yalnızca iç kapsamda bir return ifadesi kullanamazsınız. Bunun yerine bir Error nesnesini uygun kapsamda döndürmek için bir dene ve yakala yaklaşımı kullanılabilir. Düşünmek:

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

Yukarıda @ balupton tarafından tarif edilen yaklaşımların bir kombinasyonudur.


Hatalar atmak yerine, bazı geliştiriciler , hata bilinen bir olasılık olduğunda, bir Tamam veya Hata döndürmek için Sonuç kavramını Rust'dan kullanmanızı önerir . Bu, arızaları beklenmedik hatalardan ayrı tutar. Bunun bir JS uygulaması r-sonucudur .
joeytwiddle

Uygulama genelinde bir tasarım kararı. Bence hata geri dönüş konseptiniz kabaca eşdeğerdir ve başlamak için basittir (ekstra bağımlılık yok), ancak daha az açık ( Sonuç , hataların ne zaman ele alınması gerektiğinin acı verici bir şekilde farkında olmasını sağlar) ve bir yığın olduğunda bu durumlarda daha az verimli gereksiz yere inşa edilmiş.
joeytwiddle

1

Bir süre önce bu yazıyı okuduktan sonra, bir api / işlev düzeyinde istisna işleme için alan adlarını kullanmanın güvenli olup olmadığını merak ediyordum. Onları yazdığım her zaman uyumsuz işlevde istisna işleme kodunu basitleştirmek için kullanmak istedim. Benim endişem, her işlev için yeni bir etki alanı kullanmanın önemli ek yük getireceğiydi. Ödevim, ek yükün minimum düzeyde olduğunu ve alan adlarında performansın bazı durumlarda denemekten daha iyi olduğunu gösteriyor.

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


1

Hataları yakalamak burada çok iyi tartışılmıştır, ancak hataları görüntüleyip bir şeyler düzeltebilmeniz için hataları bir yere kaydetmeyi hatırlamakta fayda var.

Bunyan, NodeJS için popüler bir günlük kaydı çerçevesidir - console.log'dan uzak kaldığınız sürece yerel hata ayıklama için kullanışlı hale getiren bir dizi farklı çıkış yerine yazmayı destekler. Alan adınızın hata işleyicisinde hatayı bir günlük dosyasına gönderebilirsiniz.

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

Kontrol etmek için çok fazla hata ve / veya sunucunuz varsa bu zaman alabilir, bu nedenle hataları birlikte gruplandırmak veya her ikisini birlikte kullanmak için Raygun (sorumluluk reddi, Raygun'da çalışıyorum) gibi bir araca bakmaya değer olabilir. Raygun'u bir araç olarak kullanmaya karar verdiyseniz, kurulumu da oldukça kolaydır

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

PM2 veya sonsuza dek bir araç kullanarak geçtiğinizde, uygulamanız kilitlenebilmeli, olanları kapatabilmeli ve önemli bir sorun olmadan yeniden başlayabilmelidir.


Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.