exception-handling - Node.js أفضل الممارسات معالجة الاستثناء




serverside-javascript (9)

كتبت عن هذا مؤخرا في http://snmaynard.com/2012/12/21/node-error-handling/ . ميزة جديدة للعقدة في الإصدار 0.8 هي المجالات وتسمح لك بدمج كافة أشكال معالجة الأخطاء في نموذج إدارة أسهل واحد. يمكنك أن تقرأ عنها في منصبي.

يمكنك أيضًا استخدام شيء ما مثل Bugsnag لتتبع الاستثناءات غير المعلنة وإخطارك عبر البريد الإلكتروني أو غرفة الدردشة أو الحصول على تذكرة تم إنشاؤها لاستثناء غير مألوف (أنا المؤسس المشارك لـ Bugsnag).

لقد بدأت للتو تجربة node.js قبل بضعة أيام. لقد أدركت أنه يتم إنهاء العقدة عندما يكون لدي استثناء غير معالج في برنامجي. يختلف هذا عن حاوية الخادم العادية التي تعرضت لها حيث يموت مؤشر ترابط العامل فقط عند حدوث الاستثناءات غير المعالجة ولا تزال الحاوية قادرة على تلقي الطلب. هذا يثير بعض الأسئلة:

  • هل هي process.on('uncaughtException') الطريقة الفعالة الوحيدة للحماية من ذلك؟
  • هل process.on('uncaughtException') الاستثناء غير process.on('uncaughtException') أثناء تنفيذ العمليات غير المتزامنة أيضًا؟
  • هل هناك وحدة بنيت بالفعل (مثل إرسال البريد الإلكتروني أو الكتابة إلى ملف) يمكنني الاستفادة منها في حالة وجود استثناءات غير مقيدة؟

وسأكون ممتناً لأي مؤشر / مقالة توضح لي أفضل الممارسات الشائعة للتعامل مع الاستثناءات غير المعلنة في node.js


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/


مثيل واحد عند استخدام try-catch قد يكون مناسبًا عند استخدام حلقة forEach. إنه متزامن ولكن في نفس الوقت لا يمكنك فقط استخدام عبارة return في النطاق الداخلي. بدلاً من ذلك ، يمكن استخدام أسلوب المحاولة و الإرجاع لإرجاع كائن خطأ في النطاق المناسب. يعتبر:

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

إنه مزيج من الطرق التي وصفهاbalupton أعلاه.


يمكنك التقاط استثناءات غير معلومة ، ولكنها ذات استخدام محدود. انظر http://debuggable.com/posts/node-js-dealing-with-uncaught-exceptions:4c933d54-1428-443c-928d-4e1ecbdd56cb

يمكن استخدام monit أو forever أو upstart لإعادة تشغيل عملية العقدة عند تعطلها. يعد إيقاف التشغيل الآمن أفضل ما يمكن أن تأمل فيه (على سبيل المثال حفظ كافة البيانات الموجودة في الذاكرة في معالج الاستثناء غير الملاحظ).


أود فقط أن أضيف أن مكتبة Step.js تساعدك على التعامل مع الاستثناءات من خلال تمريرها دائمًا إلى وظيفة الخطوة التالية. 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).


فيما يلي تلخيص وتقدير من العديد من المصادر المختلفة حول هذا الموضوع بما في ذلك مثال التعليمة البرمجية وعلامات الاقتباس من مشاركات المدونة المحددة. يمكن العثور على القائمة الكاملة لأفضل الممارسات هنا

أفضل الممارسات لمعالجة الخطأ Node.JS

Number1: استخدام الوعود لمعالجة الأخطاء غير المتزامن

TL ؛ DR: التعامل مع أخطاء المتزامن في أسلوب رد الاتصال ربما يكون أسرع طريقة للجحيم (ويعرف أيضا باسم هرم الموت). أفضل هدية يمكن أن تعطيها لرمزك تستخدم بدلاً من ذلك مكتبة ذات عهود حسنة السمعة ، والتي توفر تركيبًا مدمجًا للكتابة ومألوفًا مثل try-catch

على خلاف ذلك: نمط رد الاتصال Node.JS ، وظيفة (يخطئ ، استجابة) ، هو طريقة واعدة إلى رمز غير قابل للصيانة بسبب مزيج من معالجة الأخطاء مع الكود غير الرسمي ، التعشيش المفرط وأنماط التشفير المحرج

مثال الكود - جيد

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

اقتباس المدونة: "لدينا مشكلة في الوعود" (من مدونة pouchdb ، في المرتبة 11 للكلمات الرئيسية "Node Promises")

"... وفي الواقع ، فإن الاستدعاءات تقوم بشيء أكثر شراً: فهي تحرمنا من المكدس ، وهو أمر نأخذه عادةً على أنه أمر مسلم به في لغات البرمجة. إن كتابة الكود بدون كومة يشبه قيادة سيارة بدون دواسة الفرامل: لا تدرك مدى سوء ما تحتاج إليه ، حتى تصل إليه وليس هناك ، والهدف الكامل من الوعود هو إعادتنا أساسيات اللغة التي فقدناها عندما ذهبنا إلى وضع المتزامن: العودة ، والرمي ، والمكدس. يجب أن تعرف كيفية استخدام الوعود بشكل صحيح من أجل الاستفادة منها. "

Number2: استخدام كائن Error مضمّن فقط

TL ؛ DR: من الشائع جدًا رؤية التعليمة البرمجية التي تطرح أخطاء كسلسلة أو كنوع مخصص - وهذا يعقد الخطأ في معالجة المنطق والتشغيل البيني بين الوحدات النمطية. سواء قمت برفض الوعد أو رمي الخطأ الاستثنائي أو إصداره - باستخدام كائن خطأ Node.JS المدمج يزيد من التماثل ويمنع فقدان معلومات الخطأ

على خلاف ذلك: عند تنفيذ بعض الوحدات ، فإن عدم التأكد من أنواع الأخطاء التي تأتي في المقابل - يجعل من الصعب جدًا التفكير في الاستثناء القادم ومعالجته. يستحق حتى ، استخدام أنواع مخصصة لوصف الأخطاء قد يؤدي إلى فقدان معلومات خطأ حرجة مثل تتبع المكدس!

المثال رمز - القيام بذلك بشكل صحيح

    //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 خطأ في الكائن")

"... يؤدي تمرير سلسلة بدلاً من خطأ إلى تقليل قابلية التشغيل البيني بين الوحدات النمطية. يؤدي ذلك إلى تقسيم العقود مع واجهات برمجة التطبيقات التي قد تقوم بإجراء عمليات التحقق من الأخطاء ، أو التي تريد معرفة المزيد عن الخطأ . كما أن كائنات الخطأ ، كما سنرى ، خصائص مثيرة للاهتمام في محركات جافا سكريبت الحديثة إلى جانب عقد الرسالة التي تم تمريرها إلى المنشئ .. "

Number3: التمييز بين الأخطاء التشغيلية والمبرمج

TL ؛ DR: أخطاء العمليات (على سبيل المثال API تلقى مدخلات غير صالحة) الرجوع إلى الحالات المعروفة حيث يتم فهم تأثير الخطأ بشكل كامل ويمكن التعامل معها بعناية. من ناحية أخرى ، يشير خطأ مبرمج (على سبيل المثال محاولة قراءة متغير غير معروف) إلى فشل تعليمات برمجية غير معروفة تملي إعادة تشغيل التطبيق بأمان

على خلاف ذلك: يمكنك إعادة تشغيل التطبيق دائمًا عند ظهور خطأ ، ولكن لماذا يترك ~ 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);
});

بلوق اقتباس : "وإلا فإنك تخاطر الدولة" (من بلوق debugable ، في المرتبة 3 للكلمات الرئيسية "Node.JS استثناء لم يمس")

" ... وبطبيعة طبيعة كيفية عمل الرمي في جافا سكريبت ، فليس هناك أي طريقة تقريبًا" لأخذ المكان الذي تركته "بأمان ، دون تسريب المراجع ، أو خلق نوع آخر من الحالات الهشة غير المحددة. خطأ في الإغلاق هو إيقاف العملية ، بالطبع ، في خادم ويب عادي ، قد يكون لديك العديد من الاتصالات المفتوحة ، وليس من المعقول إغلاق هذه الفجأة فجأة لأن خطأ ما قد تم تنفيذه من قبل شخص آخر. إرسال رد خطأ على الطلب الذي تسبب في حدوث الخطأ ، مع السماح للآخرين بالانتهاء في الوقت العادي ، والتوقف عن الاستماع للطلبات الجديدة في هذا العامل "

Number4: تعامل مع الأخطاء مركزيًا ، لكن ليس من خلال البرامج الوسيطة

TL ؛ DR: يجب معالجة خطأ معالجة المنطق مثل البريد إلى المشرف وتسجيل الدخول في كائن مخصص ومركزية أن جميع النقاط النهائية (على سبيل المثال اكسبرس البرمجيات الوسيطة ، كرون وظائف ، اختبار الوحدة) عندما يأتي خطأ.

على خلاف ذلك: عدم التعامل مع الأخطاء في مكان واحد سيؤدي إلى تكرار الشفرة وربما إلى أخطاء يتم التعامل معها بطريقة غير صحيحة

مثال التعليمة البرمجية - تدفق نموذجي

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

اقتباس المدونة: "في بعض الأحيان لا تستطيع المستويات الأقل فعل أي شيء مفيد باستثناء نشر الخطأ إلى المتصل" (من المدونة Joyent ، المصنفة 1 للكلمات الرئيسية "Node.JS معالجة الأخطاء")

"... قد ينتهي بك الأمر إلى معالجة نفس الخطأ على مستويات متعددة من المكدس. يحدث هذا عندما لا تستطيع المستويات الدنيا القيام بأي شيء مفيد باستثناء نشر الخطأ إلى المتصل ، والذي يقوم بنشر الخطأ إلى المتصل الخاص به ، وهكذا. لا يعرف سوى المتصل من المستوى الأعلى ما هي الاستجابة المناسبة ، سواء كان ذلك لإعادة العملية أو الإبلاغ عن خطأ للمستخدم أو أي شيء آخر ، ولكن هذا لا يعني أنه يجب عليك محاولة الإبلاغ عن جميع الأخطاء إلى مستوى أعلى واحد رد الاتصال ، لأن ذلك الاستدعاء نفسه لا يمكنه معرفة السياق الذي حدث فيه الخطأ "

Number5: أخطاء API في المستند باستخدام Swagger

TL ؛ DR: دع متصلين API الخاص بك يعرفون الأخطاء التي قد تأتي في المقابل حتى يتمكنوا من التعامل معها بعناية دون ان تتحطم. ويتم ذلك عادة مع أطر توثيق REST API مثل Swagger

على خلاف ذلك: قد يقرر أحد عملاء واجهة برمجة التطبيقات التعطيل وإعادة التشغيل فقط لأنه تلقى خطأ لم يتمكن من فهمه. ملاحظة: قد يكون المتصل من API الخاص بك (نموذجي للغاية في بيئة microservices)

اقتباس المدونة: "يجب أن تخبر المتصلين بك ما هي الأخطاء التي يمكن أن تحدث" (من مدونة Joyent ، المصنفة 1 للكلمات الرئيسية "Node.JS logging")

... لقد تحدثنا عن كيفية التعامل مع الأخطاء ، ولكن عندما تقوم بكتابة وظيفة جديدة ، كيف تقوم بتسليم الأخطاء إلى الرمز الذي يسمى وظيفتك؟ ... إذا كنت لا تعرف ما هي الأخطاء التي يمكن أن تحدث أو لا تعرف ماذا تعني ، فلا يمكن أن يكون برنامجك صحيحًا إلا بالصدفة. لذا إذا كنت تكتب وظيفة جديدة ، فيجب عليك إخبار المتصلين بك عن الأخطاء التي قد تحدث وماذا يفعلون

رقم 6: أغلق العملية برشاقة عندما يأتي شخص غريب إلى المدينة

TL ؛ DR: عندما يحدث خطأ غير معروف (خطأ مطور ، راجع أفضل ممارسة رقم 3) - هناك عدم يقين حول صحة التطبيق. تقترح ممارسة شائعة إعادة تشغيل العملية بعناية باستخدام أداة 'restarter' مثل Forever و PM2

على خلاف ذلك: عندما يتم اكتشاف استثناء غير مألوف ، قد يكون بعض الكائنات في حالة معيبة (على سبيل المثال ، باعث الحدث الذي يتم استخدامه على مستوى العالم وليس إطلاق الأحداث بعد الآن بسبب بعض الفشل الداخلي) وقد تفشل جميع الطلبات المستقبلية أو تتصرف بشكل جنوني

مثال على الرمز - تحديد ما إذا كان يجب التعطل

//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. نهج متوازن بين الاثنين

Number7: استخدام مسجل ناضج لزيادة رؤية الأخطاء

TL: DR: مجموعة من أدوات التسجيل الناضجة مثل Winston أو Bunyan أو Log4J ، سوف تسرع اكتشاف الأخطاء والفهم. حتى ننسى console.log.

على خلاف ذلك: يمكن أن يساعدك التنقل عبر console.logs أو يدويًا من خلال ملف نصي فوضوي دون الاستعلام عن أدوات أو عارض سجل لائق في العمل حتى وقت متأخر

مثال التعليمات البرمجية - مسجل Winston أثناء العمل

//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. يسمح لعدة تيارات وجهة شكلي. على سبيل المثال ، قد تكون تكتب سجلات التتبع إلى ملف واحد ولكن عند مواجهة خطأ ، الكتابة إلى نفس الملف ، ثم إلى ملف خطأ وإرسال بريد إلكتروني في نفس الوقت ...

Number8: اكتشاف الأخطاء ووقت التوقف عن العمل باستخدام منتجات APM

TL: DR: تقوم منتجات المراقبة والأداء (ويعرف أيضًا باسم APM) بقياس برنامج التعريف الخاص بك أو API بشكل استباقي حتى يتمكنوا من تسليط الضوء تلقائيًا على الأخطاء والتعطيلات والأجزاء البطيئة التي كنت تفتقدها

بخلاف ذلك: قد تقضي جهدًا كبيرًا في قياس أداء واجهة برمجة التطبيقات وأوقات توقف التشغيل ، على الأرجح لن تكون على علم مطلقًا بأبطال أجزاء الشفرة ضمن سيناريو العالم الحقيقي وكيفية تأثيرها على تجربة المستخدم

اقتباس المدونة: "شرائح منتجات APM" (من المدونة Yoni Goldberg)

"... منتجات APM تشكل 3 أجزاء رئيسية: 1. مراقبة الموقع أو API - الخدمات الخارجية التي ترصد باستمرار الجهوزية والأداء عبر طلبات HTTP. يمكن الإعداد في دقائق قليلة. فيما يلي عدد قليل من المتنافسين المختارين: Pingdom و Uptime Robot و New Relic 2 - أدوات القياس - عائلة المنتجات التي تتطلب تضمين وكيل داخل التطبيق للاستفادة من ميزة الكشف البطيء عن الشفرات ، وإحصاءات الاستثناءات ، ومراقبة الأداء وغيرها الكثير.هناك عدد قليل من المتنافسين المختارين: New Relic ، وديناميكيات التطبيقات 3. لوحة معلومات المخابرات التشغيلية - هذه الخطوط وتركز المنتجات على تسهيل فريق العمليات مع المقاييس والمحتوى المنظم الذي يساعد على البقاء على رأس أداء التطبيق بسهولة ، وعادةً ما ينطوي ذلك على تجميع مصادر متعددة للمعلومات (سجلات التطبيق ، سجلات DB ، سجل الخوادم ، إلخ) وتصميم لوحة القيادة مقدمًا فيما يلي عدد قليل من المتنافسين المختارين: Datadog، Splunk "

ما سبق هو نسخة مختصرة - شاهد هنا أفضل الممارسات والأمثلة


تحديث: لدى Joyent الآن دليل خاص بهم مذكور في هذه الإجابة . المعلومات التالية هي أكثر من ملخص:

بأمان "رمي" أخطاء

من الناحية المثالية ، نود تجنب الأخطاء غير المسجلة قدر الإمكان ، على هذا النحو ، بدلاً من رمي الخطأ حرفياً ، يمكننا بدلاً من ذلك "رمي" الخطأ بأمان باستخدام إحدى الطرق التالية وفقًا لبنية التعليمات البرمجية الخاصة بنا:

  • بالنسبة إلى التعليمة البرمجية المتزامنة ، في حالة حدوث خطأ ، قم بإرجاع الخطأ:

    // 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 ، إذا حدث خطأ ما هو الخطأ ، إذا لم يحدث err فإن 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...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 كما يلي:

    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) تنفيذ catch catch catch الذي لا تريده. في اللغات التي تسمح بالترابط الصحيح بدلاً من نمط جهاز الحدث غير المتزامن الخاص بجافا سكريبت ، هذه مشكلة أقل.

  • أخيرًا ، في حالة حدوث خطأ غير معلوم في مكان لم يكن ملفوفًا في نطاق أو بيان تجريب ، يمكننا أن نجعل تطبيقنا لا uncaughtException عن طريق استخدام المستمع 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
    

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

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

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

يمكن أن يستغرق ذلك وقتًا طويلاً إذا كان لديك الكثير من الأخطاء و / أو الخوادم للتحقق من ذلك ، لذا قد يكون من المفيد البحث في أداة مثل Raygun (إخلاء المسؤولية ، أعمل في Raygun) لتجميع الأخطاء معًا - أو استخدامها معًا. إذا قررت استخدام Raygun كأداة ، فمن السهل الإعداد أيضًا

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

عبر استخدام أداة مثل PM2 أو إلى الأبد ، يجب أن يكون تطبيقك قادرًا على التعطّل وتسجيل ما حدث وإعادة التشغيل دون أي مشكلات رئيسية.


في Python3 توجد 4 جمل مختلفة لاستثناء الاستبعاد:

1. raise exception 
2. raise exception (args) 
3. raise
4. raise exception (args) from original_exception

1. رفع الاستثناء مقابل 2. رفع الاستثناء (args)

إذا كنت تستخدم raise exception (args) لرفع استثناء ، فستتم طباعة args عند طباعة كائن الاستثناء - كما هو موضح في المثال أدناه.

  #raise exception (args)
    try:
        raise ValueError("I have raised an Exception")
    except ValueError as exp:
        print ("Error", exp)     # Output -> Error I have raised an Exception 



  #raise execption 
    try:
        raise ValueError
    except ValueError as exp:
        print ("Error", exp)     # Output -> Error 

3.raise

raise بيان دون أي الحجج إعادة يثير الاستثناء الأخير. يفيد ذلك إذا كنت تحتاج إلى تنفيذ بعض الإجراءات بعد التقاط الاستثناء ، ثم ترغب في إعادة رفعها. ولكن إذا لم يكن هناك استثناء من قبل ، raise بيان استثناء TypeError .

def somefunction():
    print("some cleaning")

a=10
b=0 
result=None

try:
    result=a/b
    print(result)

except Exception:            #Output ->
    somefunction()           #some cleaning
    raise                    #Traceback (most recent call last):
                             #File "python", line 8, in <module>
                             #ZeroDivisionError: division by zero

4. رفع استثناء (args) من original_exception

يتم استخدام هذا البيان لإنشاء تسلسل استثناء حيث يمكن أن يحتوي الاستثناء الذي يتم طرحه كاستجابة لاستثناء آخر على تفاصيل الاستثناء الأصلي - كما هو موضح في المثال أدناه.

class MyCustomException(Exception):
pass

a=10
b=0 
reuslt=None
try:
    try:
        result=a/b

    except ZeroDivisionError as exp:
        print("ZeroDivisionError -- ",exp)
        raise MyCustomException("Zero Division ") from exp

except MyCustomException as exp:
        print("MyException",exp)
        print(exp.__cause__)

انتاج:

ZeroDivisionError --  division by zero
MyException Zero Division 
division by zero




node.js exception-handling serverside-javascript