node.js नोड.जेएस बेस्ट प्रैक्टिस अपवाद हैंडलिंग




exception-handling serverside-javascript (8)

मैंने कुछ दिनों पहले node.js को आजमाने की कोशिश की। मुझे एहसास हुआ है कि जब भी मेरे प्रोग्राम में एक अनचाहे अपवाद होता है तो नोड समाप्त हो जाता है। यह सामान्य सर्वर कंटेनर से अलग है जिसे मैं खुलासा कर चुका हूं जहां केवल कार्यकर्ता थ्रेड मर जाता है जब अनचाहे अपवाद होते हैं और कंटेनर अभी भी अनुरोध प्राप्त कर पाएगा। इससे कुछ सवाल उठते हैं:

  • process.on('uncaughtException') इसके खिलाफ सुरक्षा करने का एकमात्र प्रभावी तरीका है?
  • process.on('uncaughtException') होगी process.on('uncaughtException') के निष्पादन के दौरान भी अनचाहे अपवाद को पकड़ता है?
  • क्या कोई मॉड्यूल है जो पहले से ही बनाया गया है (जैसे ईमेल भेजना या फ़ाइल में लिखना) कि मैं बेजोड़ अपवादों के मामले में लाभ उठा सकता हूं?

मैं किसी भी पॉइंटर / आलेख की सराहना करता हूं जो मुझे node.js में बेजोड़ अपवादों को संभालने के लिए सामान्य सर्वोत्तम प्रथाओं को दिखाएगा


एक उदाहरण जहां एक आचरण लूप का उपयोग करते समय एक प्रयास-पकड़ का उपयोग करना उपयुक्त हो सकता है। यह तुल्यकालिक है लेकिन साथ ही आप आंतरिक दायरे में वापसी विवरण का उपयोग नहीं कर सकते हैं। इसके बजाय एक प्रयास वस्तु को उचित दायरे में एक त्रुटि ऑब्जेक्ट वापस करने के लिए उपयोग किया जा सकता है। विचार करें:

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

यह ऊपर @balupton द्वारा वर्णित दृष्टिकोण का एक संयोजन है।


मैंने हाल ही में http://snmaynard.com/2012/12/21/node-error-handling/ पर इस बारे में लिखा था। संस्करण 0.8 में नोड की एक नई सुविधा डोमेन है और आपको त्रुटि प्रबंधन के सभी रूपों को एक आसान प्रबंधन फ़ॉर्म में संयोजित करने की अनुमति देती है। आप मेरे पद में उनके बारे में पढ़ सकते हैं।

आप अपने बेजोड़ अपवादों को ट्रैक करने के लिए बग्सगैग जैसे कुछ भी उपयोग कर सकते हैं और ईमेल, चैट रूम के माध्यम से अधिसूचित किए जा सकते हैं या बिना किसी अपवाद के लिए टिकट बनाया गया है (मैं बग्सनाग के सह-संस्थापक हूं)।


नोडजेस डोमेन नोडजेस में त्रुटियों को संभालने का सबसे अद्यतित तरीका है। डोमेन दोनों त्रुटि / अन्य घटनाओं के साथ-साथ परंपरागत रूप से फेंकने वाली वस्तुओं को भी पकड़ सकते हैं। डोमेन इंटरैक्शन विधि के माध्यम से पहले तर्क के रूप में पारित त्रुटि के साथ कॉलबैक को संभालने के लिए कार्यक्षमता भी प्रदान करते हैं।

सामान्य प्रयास / पकड़-शैली त्रुटि प्रबंधन के साथ, आमतौर पर त्रुटियों को फेंकना सबसे अच्छा होता है, और उन क्षेत्रों को अवरुद्ध कर देता है जहां आप शेष कोड को प्रभावित करने से त्रुटियों को अलग करना चाहते हैं। इन क्षेत्रों को "अवरुद्ध" करने का तरीका डोमेन कोड को एक कोड के साथ अलग कोड के ब्लॉक के रूप में कॉल करना है।

सिंक्रोनस कोड में, उपर्युक्त पर्याप्त है - जब कोई त्रुटि होती है तो आप इसे फेंक देते हैं, या आप इसे पकड़ते हैं और वहां संभालते हैं, किसी भी डेटा को वापस करने के लिए आपको वापस लेना पड़ता है।

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

जब त्रुटि एक असीमित कॉलबैक में होती है, तो आपको या तो डेटा के रोलबैक को पूरी तरह से संभालने में सक्षम होना चाहिए (साझा स्थिति, डेटाबेस जैसे बाहरी डेटा इत्यादि)। या आपको यह इंगित करने के लिए कुछ सेट करना होगा कि एक अपवाद हुआ है - जहां भी आप उस ध्वज की परवाह करते हैं, आपको कॉलबैक को पूरा करने की प्रतीक्षा करनी होगी।

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
  }

})});

उपर्युक्त कोड में से कुछ बदसूरत है, लेकिन आप इसे सुंदर बनाने के लिए अपने लिए पैटर्न बना सकते हैं, उदाहरण के लिए:

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

अद्यतन (2013-09):

ऊपर, मैं भविष्य का उपयोग करता हूं जो फाइबर सेमेन्टिक्स का तात्पर्य है, जो आपको ऑनलाइन वायदा पर इंतजार करने की अनुमति देता है। यह वास्तव में आपको सब कुछ के लिए पारंपरिक प्रयास-पकड़ ब्लॉक का उपयोग करने की अनुमति देता है - जो मुझे जाने का सबसे अच्छा तरीका लगता है। हालांकि, आप हमेशा ऐसा नहीं कर सकते (यानी ब्राउज़र में) ...

ऐसे वायदा भी हैं जिन्हें फाइबर सेमेन्टिक्स की आवश्यकता नहीं होती है (जो तब सामान्य, browsery जावास्क्रिप्ट के साथ काम करते हैं)। इन्हें वायदा, वादे, या स्थगित कहा जा सकता है (मैं यहां से वायदा का उल्लेख करूंगा)। सादा-पुराने-जावास्क्रिप्ट वायदा पुस्तकालय वायदा के बीच त्रुटियों को प्रसारित करने की अनुमति देते हैं। इन पुस्तकालयों में से कुछ केवल किसी भी फेंकने वाले भविष्य को सही ढंग से संभालने की अनुमति देते हैं, इसलिए सावधान रहें।

एक उदाहरण:

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

यह एक सामान्य कोशिश-पकड़ की नकल करता है, भले ही टुकड़े असीमित हैं। यह प्रिंट करेगा:

1
2
handler

ध्यान दें कि यह '3' प्रिंट नहीं करता है क्योंकि एक अपवाद फेंक दिया गया है जो उस प्रवाह को बाधित करता है।

ब्लूबर्ड वादे पर एक नज़र डालें:

ध्यान दें कि मुझे इनके अलावा कई अन्य पुस्तकालय नहीं मिला है जो ठीक से अपवादों को व्यवस्थित करते हैं। jQuery की स्थगित, उदाहरण के लिए, नहीं - "असफल" हैंडलर को 'तत्कालीन' हैंडलर को अपवाद नहीं दिया जाएगा, जो मेरी राय में एक सौदा ब्रेकर है।



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/


इस विषय पर कई अलग-अलग स्रोतों से संक्षिप्त सारांश और क्यूरेशन शामिल है जिसमें कोड उदाहरण और चयनित ब्लॉग पोस्ट से उद्धरण शामिल हैं। सर्वोत्तम प्रथाओं की पूरी सूची यहां पाई जा सकती है

नोड.जेएस त्रुटि प्रबंधन का सर्वोत्तम अभ्यास

संख्या 1: एसिंक त्रुटि प्रबंधन के लिए वादे का प्रयोग करें

टीएल; डीआर: कॉलबैक शैली में एसिंक त्रुटियों को संभालना शायद नरक का सबसे तेज़ तरीका है (उर्फ का पिरामिड उर्फ)। सबसे अच्छा उपहार जो आप अपने कोड को दे सकते हैं वह एक प्रतिष्ठित वादा पुस्तकालय का उपयोग कर रहा है जो बहुत कॉम्पैक्ट और परिचित कोड वाक्यविन्यास प्रदान करता है जैसे कि प्रयास

अन्यथा: नोड.जेएस कॉलबैक शैली, फ़ंक्शन (त्रुटि, प्रतिक्रिया), आकस्मिक कोड, अत्यधिक घोंसले और अजीब कोडिंग पैटर्न के साथ त्रुटि प्रबंधन के मिश्रण के कारण अन-रखरखाव कोड का एक आशाजनक तरीका है

कोड उदाहरण - अच्छा

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

कोड उदाहरण विरोधी पैटर्न - कॉलबैक शैली त्रुटि हैंडलिंग

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

ब्लॉग उद्धरण: "हमें वादे के साथ समस्या है" (ब्लॉग पाउचडब से, कीवर्ड "नोड वादा" के लिए 11 वां स्थान पर)

"... और वास्तव में, कॉलबैक कुछ और भी भयावह करते हैं: वे हमें ढेर से वंचित करते हैं, जो कि हम आमतौर पर प्रोग्रामिंग भाषाओं में दी जाती है। स्टैक के बिना कोड लिखना ब्रेक पेडल के बिना कार चलाने जैसा है: आप यह महसूस न करें कि आपको इसकी कितनी बुरी तरह तक आवश्यकता है, जब तक आप इसके लिए नहीं पहुंच जाते हैं और यह वहां नहीं है। वादे का पूरा बिंदु हमें उन भाषा मौलिक सिद्धांतों को वापस देना है जब हम एसिंक गए थे: वापसी, फेंक और ढेर। लेकिन आप उन्हें लाभ उठाने के लिए सही तरीके से वादे का उपयोग कैसे करना है, यह जानना है। "

संख्या 2: केवल अंतर्निहित त्रुटि ऑब्जेक्ट का उपयोग करें

टीएल; डीआर: यह कोड देखने के लिए बहुत आम है जो त्रुटियों को स्ट्रिंग या कस्टम प्रकार के रूप में फेंकता है - यह त्रुटि प्रबंधन तर्क और मॉड्यूल के बीच इंटरऑपरेबिलिटी को जटिल करता है। चाहे आप एक वादे को अस्वीकार करते हैं, अपवाद फेंकते हैं या त्रुटि को उत्सर्जित करते हैं - नोड.जेएस का उपयोग करते हुए अंतर्निहित त्रुटि ऑब्जेक्ट एकरूपता बढ़ाता है और त्रुटि जानकारी के नुकसान को रोकता है

अन्यथा: कुछ मॉड्यूल को निष्पादित करते समय, अनिश्चित होने के कारण किस प्रकार की त्रुटियां बदले में आती हैं - आने वाले अपवाद के बारे में तर्क करना और इसे संभालना बहुत मुश्किल हो जाता है। त्रुटियों का वर्णन करने के लिए कस्टम प्रकारों का उपयोग करने के लायक भी, स्टैक ट्रेस जैसी महत्वपूर्ण त्रुटि जानकारी का नुकसान हो सकता है!

कोड उदाहरण - इसे सही कर रहा है

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

कोड उदाहरण विरोधी पैटर्न

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

ब्लॉग उद्धरण: "एक स्ट्रिंग कोई त्रुटि नहीं है" (ब्लॉग devthought से, कीवर्ड के लिए रैंक 6 "Node.JS त्रुटि ऑब्जेक्ट")

"... मॉड्यूल के बीच कम अंतःक्रियाशीलता में त्रुटि के बजाय एक स्ट्रिंग को पास करना। यह एपीआई के साथ अनुबंध तोड़ता है जो त्रुटि जांच के उदाहरण हो सकता है, या जो त्रुटि के बारे में अधिक जानना चाहता है । त्रुटि ऑब्जेक्ट्स, जैसा कि हम देखेंगे, बहुत कुछ है आधुनिक जावास्क्रिप्ट इंजन में दिलचस्प गुण कन्स्ट्रक्टर को पास संदेश भेजकर .. "

संख्या 3: परिचालन बनाम प्रोग्रामर त्रुटियों में अंतर

टीएल; डीआर: ऑपरेशंस त्रुटियां (जैसे एपीआई को एक अवैध इनपुट प्राप्त हुआ) ज्ञात मामलों का संदर्भ लें जहां त्रुटि प्रभाव पूरी तरह से समझा जाता है और विचारपूर्वक संभाला जा सकता है। दूसरी तरफ, प्रोग्रामर त्रुटि (उदाहरण के लिए अपरिभाषित चर पढ़ने की कोशिश कर रहा है) अज्ञात कोड विफलताओं को संदर्भित करता है जो आवेदन को गर्वपूर्वक पुन: प्रारंभ करने का निर्देश देते हैं

अन्यथा: त्रुटि प्रकट होने पर आप हमेशा एप्लिकेशन को पुनरारंभ कर सकते हैं, लेकिन मामूली और अनुमानित त्रुटि (परिचालन त्रुटि) के कारण ~ 5000 ऑनलाइन उपयोगकर्ताओं को क्यों दे रहे हैं? विपरीत भी आदर्श नहीं है - अज्ञात समस्या (प्रोग्रामर त्रुटि) होने पर एप्लिकेशन को अपरिवर्तित व्यवहार का कारण बन सकता है। दोनों को अलग करने से दिए गए संदर्भ के आधार पर कुशलतापूर्वक कार्य करने और संतुलित दृष्टिकोण लागू करने की अनुमति मिलती है

कोड उदाहरण - इसे सही कर रहा है

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

कोड उदाहरण - एक त्रुटि को परिचालन (विश्वसनीय) के रूप में चिह्नित करना

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

ब्लॉग उद्धरण : "अन्यथा आप राज्य को जोखिम देते हैं" (ब्लॉग से डीबग करने योग्य, कीवर्ड के लिए रैंक 3 "नोड.जेएस बेजोड़ अपवाद")

" ... जावास्क्रिप्ट में फेंकने की प्रकृति की बहुत प्रकृति से, संदर्भों को लीक किए बिना, या किसी अन्य प्रकार के अपरिभाषित भंगुर राज्य को सुरक्षित रूप से" जहां आपने छोड़ा था वहां उठाएं "सुरक्षित रूप से कभी भी कोई रास्ता नहीं है। इसका जवाब देने का सबसे सुरक्षित तरीका एक फेंकने वाली त्रुटि प्रक्रिया को बंद करना है । बेशक, एक सामान्य वेब सर्वर में, आपके पास कई कनेक्शन खुले हो सकते हैं, और उनको अचानक बंद करना उचित नहीं है क्योंकि किसी और द्वारा त्रुटि उत्पन्न हुई है। बेहतर तरीका है त्रुटि को ट्रिगर करने वाले अनुरोध को त्रुटि प्रतिक्रिया भेजें, जबकि दूसरों को अपने सामान्य समय में समाप्त करने दें, और उस कार्यकर्ता में नए अनुरोधों को सुनना बंद करें "

संख्या 4: मध्यवर्ती त्रुटियों के माध्यम से केंद्रीय रूप से त्रुटियों को संभाल लें

टीएल; डीआर: व्यवस्थापक और लॉगिंग के लिए मेल जैसे तर्क को संभालने में त्रुटि एक समर्पित और केंद्रीकृत वस्तु में encapsulated किया जाना चाहिए कि सभी अंत बिंदु (जैसे एक्सप्रेस मिडलवेयर, क्रॉन जॉब्स, यूनिट-टेस्टिंग) जब कोई त्रुटि आती है तो कॉल करें।

अन्यथा: एक ही स्थान के भीतर त्रुटियों को संभालने से कोड डुप्लिकेशंस हो सकता है और शायद त्रुटियों को अनुचित तरीके से संभाला जा सकता है

कोड उदाहरण - एक सामान्य त्रुटि प्रवाह

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

ब्लॉग उद्धरण: "कभी-कभी निचले स्तर उनके कॉलर को त्रुटि प्रसारित करने के अलावा कुछ भी उपयोगी नहीं कर सकते हैं" (ब्लॉग जॉयेंट से, कीवर्ड के लिए रैंक 1 "नोड.जेएस त्रुटि हैंडलिंग")

"... आप स्टैक के कई स्तरों पर एक ही त्रुटि को संभालने का अंत कर सकते हैं। ऐसा तब होता है जब निचले स्तर उनके कॉलर को त्रुटि फैलाने के अलावा कुछ भी उपयोगी नहीं कर सकते हैं, जो इसके कॉलर को त्रुटि का प्रचार करता है, और इसी तरह। अक्सर, केवल शीर्ष-स्तरीय कॉलर जानता है कि उपयुक्त प्रतिक्रिया क्या है, भले ही वह ऑपरेशन को पुनः प्रयास करें, उपयोगकर्ता को एक त्रुटि की रिपोर्ट करें, या कुछ और। लेकिन इसका मतलब यह नहीं है कि आपको सभी त्रुटियों को एक शीर्ष-स्तर पर रिपोर्ट करने का प्रयास करना चाहिए कॉलबैक, क्योंकि कॉलबैक स्वयं नहीं जानता कि त्रुटि किस संदर्भ में हुई "

संख्या 5: स्वैगर का उपयोग कर दस्तावेज़ एपीआई त्रुटियां

टीएल; डीआर: अपने एपीआई कॉलर्स को पता चले कि कौन सी त्रुटियां बदले में आ सकती हैं ताकि वे क्रैश किए बिना इन विचारशील तरीके से संभाल सकें। यह आमतौर पर स्वैगर जैसे आरईएसटी एपीआई दस्तावेज ढांचे के साथ किया जाता है

अन्यथा: एक एपीआई क्लाइंट केवल क्रैश करने और फिर से शुरू करने का निर्णय ले सकता है क्योंकि उसे एक त्रुटि मिली है जिसे वह समझ नहीं सका। नोट: आपके एपीआई का कॉलर आप हो सकता है (माइक्रोस्कोपिस पर्यावरण में बहुत विशिष्ट)

ब्लॉग उद्धरण: "आपको अपने कॉलर्स को बताना होगा कि कौन सी त्रुटियां हो सकती हैं" (ब्लॉग जॉयेंट से, कीवर्ड के लिए रैंक 1 "नोड.जेएस लॉगिंग")

... हमने त्रुटियों को संभालने के तरीके के बारे में बात की है, लेकिन जब आप एक नया फ़ंक्शन लिख रहे हैं, तो आप अपने फ़ंक्शन नामक कोड में त्रुटियों को कैसे वितरित करते हैं? ... यदि आपको नहीं पता कि त्रुटियां क्या हो सकती हैं या नहीं जानते कि उनका क्या मतलब है, तो आपका प्रोग्राम दुर्घटना के अलावा सही नहीं हो सकता है। तो यदि आप एक नया फ़ंक्शन लिख रहे हैं, तो आपको अपने कॉलर्स को यह बताना होगा कि कौन सी त्रुटियां हो सकती हैं और वे क्या हैं

संख्या 6: जब कोई अजनबी शहर में आता है तो प्रक्रिया को सुन्दर तरीके से बंद करें

टीएल; डीआर: जब कोई अज्ञात त्रुटि होती है (एक डेवलपर त्रुटि, सर्वोत्तम अभ्यास संख्या # 3 देखें) - एप्लिकेशन स्वस्थता के बारे में अनिश्चितता है। एक सामान्य अभ्यास हमेशा के लिए एक 'restarter' उपकरण का उपयोग कर प्रक्रिया को पुनरारंभ करने का सुझाव देता है जैसे हमेशा और पीएम 2

अन्यथा: जब एक अपरिचित अपवाद पकड़ा जाता है, तो कुछ वस्तु एक दोषपूर्ण स्थिति में हो सकती है (उदाहरण के लिए एक घटना उत्सर्जक जो वैश्विक रूप से उपयोग किया जाता है और कुछ आंतरिक विफलता के कारण घटनाओं को फायर नहीं कर रहा है) और सभी भावी अनुरोध विफल हो सकते हैं या पागल हो सकते हैं

कोड उदाहरण - यह तय करना कि क्रैश करना है या नहीं

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

ब्लॉग उद्धरण: "त्रुटि प्रबंधन पर विचारों के तीन स्कूल हैं" (ब्लॉग jsrecipes से)

... मुख्य रूप से त्रुटि प्रबंधन पर विचारों के तीन स्कूल हैं: 1. एप्लिकेशन को क्रैश करने दें और इसे पुनरारंभ करें। 2. सभी संभावित त्रुटियों को संभालें और कभी भी क्रैश न करें। 3. दोनों के बीच संतुलित दृष्टिकोण

संख्या 7: त्रुटियों की दृश्यता बढ़ाने के लिए एक परिपक्व लॉगर का उपयोग करें

टीएल; डीआर: विंस्टन, बुनियन या लॉग 4 जे जैसे परिपक्व लॉगिंग टूल का एक सेट, त्रुटि की खोज और समझ को तेज करेगा। तो console.log के बारे में भूल जाओ।

अन्यथा: कंसोल.लॉग के माध्यम से स्किमिंग या मैन्युअल रूप से मैसेज टेक्स्ट फ़ाइल के माध्यम से उपकरण या एक सभ्य लॉग व्यूअर के बिना मैन्युअल रूप से काम पर व्यस्त रह सकता है

कोड उदाहरण - कार्रवाई में विंस्टन लॉगर

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

ब्लॉग उद्धरण: "कुछ आवश्यकताओं (लॉगर के लिए) की पहचान करें:" (ब्लॉग strongblog से)

... आइए कुछ आवश्यकताओं (लॉगर के लिए) की पहचान करें: 1. प्रत्येक लॉग लाइन को समयबद्ध करें। यह एक बहुत ही आत्म व्याख्यात्मक है - आपको यह बताने में सक्षम होना चाहिए कि प्रत्येक लॉग प्रविष्टि कब होती है। 2. लॉगिंग प्रारूप मनुष्यों के साथ-साथ मशीनों द्वारा आसानी से पचाने योग्य होना चाहिए। 3. एकाधिक विन्यास योग्य गंतव्य धाराओं की अनुमति देता है। उदाहरण के लिए, हो सकता है कि आप एक फ़ाइल में ट्रेस लॉग लिख रहे हों लेकिन जब कोई त्रुटि आती है, तो उसी फ़ाइल को लिखें, फिर त्रुटि फ़ाइल में और एक ही समय में एक ईमेल भेजें ...

संख्या 8: एपीएम उत्पादों का उपयोग कर त्रुटियों और डाउनटाइम की खोज करें

टीएल; डीआर: निगरानी और प्रदर्शन उत्पाद (उर्फ एपीएम) सक्रिय रूप से आपके कोडबेस या एपीआई को गेज करें ताकि वे त्रुटियों, क्रैश और धीमे हिस्सों को ऑटो-जादुई रूप से हाइलाइट कर सकें जिन्हें आप याद कर रहे थे

अन्यथा: आप एपीआई प्रदर्शन और डाउनटाइम को मापने के लिए बहुत मेहनत कर सकते हैं, शायद आप कभी भी इस बात से अवगत नहीं होंगे कि असली दुनिया परिदृश्य के तहत आपके सबसे धीमे कोड भाग क्या हैं और ये यूएक्स को कैसे प्रभावित करते हैं

ब्लॉग उद्धरण: "एपीएम उत्पाद खंड" (ब्लॉग योन गोल्डबर्ग से)

"... एपीएम उत्पादों में 3 प्रमुख खंड हैं: 1. वेबसाइट या एपीआई निगरानी - बाहरी सेवाएं जो लगातार HTTP अनुरोधों के माध्यम से अपटाइम और प्रदर्शन की निगरानी करती हैं। कुछ मिनटों में सेटअप किया जा सकता है। निम्नलिखित कुछ चयनित दावेदार हैं: पिंगडम, अपटाइम रोबोट, और न्यू रिलीक 2 कोड इंस्ट्रूमेंटेशन - उत्पाद परिवार जो फीचर धीमी कोड पहचान, अपवाद आंकड़े, प्रदर्शन निगरानी और कई अन्य लोगों के लिए आवेदन के भीतर एक एजेंट को एम्बेड करने की आवश्यकता है। निम्नलिखित कुछ चुनिंदा दावेदार हैं: नई अवशेष, ऐप डायनेमिक्स 3. ऑपरेशनल इंटेलिजेंस डैशबोर्ड - ये लाइन उत्पादों के ओप्स टीम को मेट्रिक्स और क्यूरेटेड सामग्री के साथ सुविधा प्रदान करने पर ध्यान केंद्रित किया जाता है जो आसानी से एप्लिकेशन प्रदर्शन के शीर्ष पर रहने में मदद करता है। इसमें आमतौर पर जानकारी के कई स्रोत (एप्लिकेशन लॉग, डीबी लॉग, सर्वर लॉग इत्यादि) और अपफ्रंट डैशबोर्ड डिज़ाइन को एकत्रित करना शामिल है काम। कुछ चयनित दावेदार निम्नलिखित हैं: डाटाडॉग, स्प्लंक "

उपरोक्त एक संक्षिप्त संस्करण है - यहां और अधिक सर्वोत्तम प्रथाओं और उदाहरण देखें


अपडेट करें: जॉयेंट के पास अब इस उत्तर में उल्लिखित अपनी मार्गदर्शिका है । निम्नलिखित जानकारी सारांश का अधिक है:

सुरक्षित रूप से "फेंकने" त्रुटियां

आदर्श रूप में हम जितना संभव हो सके असुरक्षित त्रुटियों से बचना चाहते हैं, जैसे कि सचमुच त्रुटि को फेंकने के बजाय, हम अपने कोड आर्किटेक्चर के आधार पर निम्न विधियों में से किसी एक का उपयोग करके त्रुटि को "फेंक" सुरक्षित रूप से सुरक्षित कर सकते हैं:

  • सिंक्रोनस कोड के लिए, यदि कोई त्रुटि होती है, तो त्रुटि लौटाएं:

    // 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)
    }
    
  • कॉलबैक-आधारित (यानी एसिंक्रोनस) कोड के लिए, कॉलबैक का पहला तर्क err , अगर कोई त्रुटि त्रुटि होती है तो त्रुटि होती है, अगर कोई त्रुटि नहीं होती है तो त्रुटि null । कोई अन्य तर्क 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)
        }
    })
    
  • eventful कोड के लिए, जहां त्रुटि कहीं भी हो सकती है, त्रुटि को फेंकने के बजाय, 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)
    

सुरक्षित रूप से "पकड़ने" त्रुटियां

कभी-कभी, फिर भी ऐसा कोड हो सकता है जो किसी त्रुटि को कहीं फेंक देता है जो एक अपरिचित अपवाद और हमारे आवेदन की संभावित दुर्घटना का कारण बन सकता है अगर हम इसे सुरक्षित रूप से नहीं पकड़ते हैं। हमारे कोड आर्किटेक्चर के आधार पर हम इसे पकड़ने के लिए निम्न विधियों में से एक का उपयोग कर सकते हैं:

  • जब हम जानते हैं कि त्रुटि कहां हो रही है, तो हम उस अनुभाग को 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
    })
    
  • अगर हम जानते हैं कि त्रुटि कहां हो रही है तो सिंक्रोनस कोड है, और जो भी कारण डोमेन (शायद नोड का पुराना संस्करण) का उपयोग नहीं कर सकता है, हम कोशिश पकड़ने का कथन का उपयोग कर सकते हैं:

    // 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...catchtry...catch एसिंक्रोनस कोड में पकड़ें, क्योंकि एक असीमित रूप से फेंक दिया गया त्रुटि पकड़ा नहीं जाएगा:

    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...catch बारे में सावधान रहना एक और बात है try...catch का try जैसे कि कथन कथन के अंदर अपना पूरा करने का कॉलबैक लपेटने का जोखिम:

    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
    }
    

    यह गोटाचा करना बहुत आसान है क्योंकि आपका कोड अधिक जटिल हो जाता है। इस प्रकार, डोमेन का उपयोग करना या त्रुटियों को वापस करने के लिए सबसे अच्छा है (1) एसिंक्रोनस कोड (2) में अनचाहे अपवादों को पकड़ने के प्रयास को पकड़ने की कोशिश करें जिसे आप नहीं चाहते हैं। ऐसी भाषाओं में जो जावास्क्रिप्ट की एसिंक्रोनस इवेंट-मशीन शैली की बजाय उचित थ्रेडिंग की अनुमति देते हैं, यह एक समस्या से कम है।

  • आखिरकार, ऐसे मामले में जहां किसी डोमेन में लपेटा नहीं गया था या किसी पकड़ने की कथन का प्रयास नहीं किया गया है, तो हम uncaughtException श्रोता का उपयोग करके हमारे एप्लिकेशन को क्रैश नहीं कर सकते हैं (हालांकि ऐसा करने से एप्लिकेशन को अज्ञात में रखा जा सकता है राज्य ):

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

I would just like to add that Step.js library helps you handle exceptions by always passing it to the next step function. Therefore you can have as a last step a function that check for any errors in any of the previous steps. This approach can greatly simplify your error handling.

Below is a quote from the github page:

any exceptions thrown are caught and passed as the first argument to the next function. 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).







serverside-javascript