Berikut ini adalah ringkasan dan kurasi dari berbagai sumber tentang topik ini termasuk contoh kode dan kutipan dari posting blog yang dipilih. Daftar lengkap praktik terbaik dapat ditemukan di sini
Praktik terbaik penanganan kesalahan Node.JS
Nomor 1: Gunakan janji untuk penanganan kesalahan async
TL; DR: Menangani kesalahan async dalam gaya panggilan balik mungkin merupakan cara tercepat menuju neraka (alias piramida malapetaka). Hadiah terbaik yang dapat Anda berikan kepada kode Anda adalah menggunakan perpustakaan janji yang memiliki reputasi baik yang menyediakan banyak sintaksis kode yang ringkas dan akrab seperti try-catch
Sebaliknya: gaya panggilan balik Node.JS, fungsi (err, response), adalah cara yang menjanjikan untuk kode yang tidak dapat dipelihara karena campuran penanganan kesalahan dengan kode kasual, pola pengkodean sarang yang berlebihan dan canggung
Contoh kode - bagus
doWork()
.then(doWork)
.then(doError)
.then(doWork)
.catch(errorHandler)
.then(verify);
contoh kode anti pola - penanganan kesalahan gaya panggilan balik
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){
...
});
});
});
});
});
Kutipan blog: "Kami memiliki masalah dengan janji"
(Dari blog pouchdb, peringkat 11 untuk kata kunci "Node Promises")
"... Dan pada kenyataannya, panggilan balik melakukan sesuatu yang bahkan lebih menyeramkan: mereka membuat kita kehilangan tumpukan, yang biasanya kita anggap remeh dalam bahasa pemrograman. Menulis kode tanpa tumpukan sama seperti mengendarai mobil tanpa pedal rem: Anda tidak menyadari betapa Anda sangat membutuhkannya, sampai Anda meraihnya dan itu tidak ada di sana. Inti dari janji-janji itu adalah memberi kita kembali dasar-dasar bahasa yang kita hilangkan ketika kita pergi ke async: kembali, lempar, dan tumpukan. harus tahu cara menggunakan janji dengan benar untuk memanfaatkannya. "
Nomor2: Gunakan hanya objek Galat bawaan
TL; DR: Cukup umum untuk melihat kode yang melempar kesalahan sebagai string atau sebagai tipe khusus - ini mempersulit logika penanganan kesalahan dan interoperabilitas antar modul. Apakah Anda menolak janji, melempar pengecualian atau memancarkan kesalahan - menggunakan objek Galat bawaan Node.JS meningkatkan keseragaman dan mencegah hilangnya informasi kesalahan
Kalau tidak: Ketika menjalankan beberapa modul, menjadi tidak pasti jenis kesalahan apa yang akan kembali - membuatnya lebih sulit untuk alasan tentang pengecualian yang akan datang dan menanganinya. Bahkan layak, menggunakan tipe khusus untuk menggambarkan kesalahan dapat menyebabkan hilangnya informasi kesalahan kritis seperti jejak tumpukan!
Contoh kode - melakukannya dengan benar
//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?"));
contoh kode anti pola
//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?");
Kutipan blog: "Sebuah string bukan kesalahan"
(Dari blog renungkan, peringkat 6 untuk kata kunci "objek kesalahan Node.JS")
"... meneruskan string alih-alih hasil kesalahan mengurangi interoperabilitas antar modul. Ini memutus kontrak dengan API yang mungkin melakukan instance dari pemeriksaan Kesalahan, atau yang ingin tahu lebih banyak tentang kesalahan . Objek kesalahan, seperti yang akan kita lihat, memiliki sangat properti menarik di mesin JavaScript modern selain menahan pesan yang diteruskan ke konstruktor .. "
Nomor 3: Bedakan kesalahan operasional vs programmer
TL; DR: Kesalahan operasi (mis. API menerima input yang tidak valid) merujuk pada kasus yang diketahui di mana dampak kesalahan dipahami sepenuhnya dan dapat ditangani dengan bijaksana. Di sisi lain, kesalahan pemrogram (misalnya mencoba membaca variabel yang tidak terdefinisi) mengacu pada kegagalan kode yang tidak diketahui yang mengharuskan untuk me-restart aplikasi dengan anggun
Jika tidak: Anda selalu dapat memulai ulang aplikasi ketika kesalahan muncul, tetapi mengapa membiarkan ~ 5000 pengguna online down karena kesalahan kecil dan yang diperkirakan (kesalahan operasional)? sebaliknya juga tidak ideal - menjaga aplikasi tetap aktif ketika masalah yang tidak diketahui (kesalahan pemrogram) terjadi dapat menyebabkan perilaku yang tidak terduga. Membedakan keduanya memungkinkan bertindak bijaksana dan menerapkan pendekatan yang seimbang berdasarkan konteks yang diberikan
Contoh kode - melakukannya dengan benar
//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?"));
contoh kode - menandai kesalahan sebagai operasional (tepercaya)
//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);
});
Kutipan Blog : "Kalau tidak, Anda berisiko negara" (Dari blog debugable, peringkat 3 untuk kata kunci "Node.JS pengecualian tanpa tertangkap")
" ... Karena sifat dari cara melempar bekerja dalam JavaScript, hampir tidak pernah ada cara untuk dengan aman" mengambil di mana Anda tinggalkan ", tanpa membocorkan referensi, atau menciptakan semacam negara rapuh yang tidak terdefinisi. Cara paling aman untuk merespons kesalahan yang dilemparkan adalah untuk mematikan proses . Tentu saja, di server web normal, Anda mungkin memiliki banyak koneksi terbuka, dan itu tidak masuk akal untuk secara tiba-tiba mematikannya karena kesalahan dipicu oleh orang lain. Pendekatan yang lebih baik adalah dengan mengirim respons kesalahan ke permintaan yang memicu kesalahan, sambil membiarkan yang lain selesai dalam waktu normal mereka, dan berhenti mendengarkan permintaan baru pada pekerja itu "
Nomor 4: Menangani kesalahan secara terpusat, melalui tetapi tidak di dalam middleware
TL; DR: Logika penanganan kesalahan seperti surat ke admin dan pencatatan harus dienkapsulasi dalam objek khusus dan terpusat yang dipanggil oleh semua titik akhir (mis. Middleware, pekerjaan cron, pengujian unit) ketika ada kesalahan.
Kalau tidak: Tidak menangani kesalahan dalam satu tempat akan menyebabkan duplikasi kode dan mungkin kesalahan yang ditangani dengan tidak tepat
Contoh kode - aliran kesalahan yang khas
//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);
});
});
Kutipan blog: "Kadang-kadang level yang lebih rendah tidak bisa melakukan apa pun kecuali menyebarkan kesalahan ke penelepon mereka" (Dari blog Joyent, peringkat 1 untuk kata kunci "Node.JS error handling")
"... Anda mungkin akhirnya menangani kesalahan yang sama di beberapa level stack. Ini terjadi ketika level yang lebih rendah tidak bisa melakukan apa pun yang berguna kecuali menyebarkan kesalahan ke penelepon mereka, yang menyebarkan kesalahan ke peneleponnya, dan sebagainya. Seringkali, hanya penelepon tingkat atas yang tahu apa tanggapan yang sesuai, apakah itu untuk mencoba kembali operasi, melaporkan kesalahan kepada pengguna, atau sesuatu yang lain.Tapi itu tidak berarti Anda harus mencoba melaporkan semua kesalahan ke tingkat atas tunggal panggilan balik, karena panggilan balik itu sendiri tidak dapat mengetahui dalam konteks apa kesalahan terjadi "
Nomor5: Kesalahan dokumen API menggunakan Swagger
TL; DR: Biarkan penelepon API Anda tahu kesalahan mana yang mungkin terjadi sehingga mereka dapat menangani ini dengan serius tanpa menabrak. Ini biasanya dilakukan dengan kerangka dokumentasi REST API seperti Swagger
Kalau tidak: Klien API mungkin memutuskan untuk mogok dan memulai kembali hanya karena ia menerima kembali kesalahan yang tidak dapat ia pahami. Catatan: penelepon API Anda mungkin Anda (sangat tipikal dalam lingkungan layanan mikro)
Kutipan blog: "Anda harus memberi tahu penelepon Anda kesalahan apa yang bisa terjadi" (Dari blog Joyent, peringkat 1 untuk kata kunci “Node.JS logging”)
... Kami sudah bicara tentang cara menangani kesalahan, tetapi ketika Anda sedang menulis fungsi baru, bagaimana Anda mengirim kesalahan ke kode yang disebut fungsi Anda? ... Jika Anda tidak tahu kesalahan apa yang bisa terjadi atau tidak tahu apa artinya, maka program Anda tidak dapat dikoreksi kecuali karena kecelakaan. Jadi, jika Anda sedang menulis fungsi baru, Anda harus memberi tahu penelepon Anda kesalahan apa yang bisa terjadi dan apa yang mereka lakukan
Nomor 6: Tutup proses dengan anggun ketika orang asing datang ke kota
TL; DR: Ketika terjadi kesalahan yang tidak diketahui (kesalahan pengembang, lihat praktik terbaik nomor 3) - ada ketidakpastian tentang kesehatan aplikasi. Praktik umum menyarankan memulai kembali proses dengan hati-hati menggunakan alat 'restarter' seperti Forever dan PM2
Jika tidak: Ketika pengecualian yang tidak dikenal ditangkap, beberapa objek mungkin berada dalam kondisi rusak (mis. Emitor peristiwa yang digunakan secara global dan tidak memecat peristiwa lagi karena beberapa kegagalan internal) dan semua permintaan di masa depan mungkin gagal atau berperilaku gila-gilaan
Contoh kode - memutuskan apakah akan mogok
//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;
}
Kutipan blog: "Ada tiga aliran pemikiran tentang penanganan kesalahan" (Dari blog jsrecipes)
... Terutama ada tiga aliran pemikiran tentang penanganan kesalahan: 1. Biarkan aplikasi macet dan mulai ulang. 2. Tangani semua kemungkinan kesalahan dan jangan pernah crash. 3. Pendekatan yang seimbang antara keduanya
Nomor7: Gunakan logger dewasa untuk meningkatkan visibilitas kesalahan
TL; DR: Satu set alat logging yang matang seperti Winston, Bunyan atau Log4J, akan mempercepat penemuan dan pemahaman kesalahan. Jadi, lupakan tentang console.log.
Kalau tidak: Melacak melalui console.logs atau secara manual melalui file teks yang berantakan tanpa alat kueri atau penampil log yang baik mungkin membuat Anda sibuk di tempat kerja hingga larut
Contoh kode - Winston logger sedang beraksi
//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' });
Kutipan blog: "Mari mengidentifikasi beberapa persyaratan (untuk logger):" (Dari blog blog kuat)
... Mari kita mengidentifikasi beberapa persyaratan (untuk logger): 1. Cap waktu setiap baris log. Yang ini cukup jelas - Anda harus bisa mengetahui kapan setiap entri log terjadi. 2. Format logging harus mudah dicerna oleh manusia dan juga mesin. 3. Memungkinkan untuk beberapa stream tujuan yang dapat dikonfigurasi. Misalnya, Anda mungkin menulis log jejak ke satu file tetapi ketika kesalahan ditemukan, menulis ke file yang sama, kemudian ke file kesalahan dan mengirim email pada saat yang sama ...
Nomor 8: Temukan kesalahan dan waktu henti menggunakan produk APM
TL; DR: Pemantauan dan produk kinerja (alias APM) secara proaktif mengukur basis kode atau API Anda sehingga mereka dapat secara otomatis menyoroti kesalahan, kerusakan, dan bagian lambat yang Anda lewatkan
Jika tidak: Anda mungkin berusaha keras untuk mengukur kinerja dan waktu henti API, mungkin Anda tidak akan pernah menyadari bagian kode mana yang paling lambat dalam skenario dunia nyata dan bagaimana hal ini mempengaruhi UX
Kutipan blog: "Segmen produk APM" (Dari blog Yoni Goldberg)
"... Produk APM terdiri dari 3 segmen utama: 1. Pemantauan situs web atau API - layanan eksternal yang secara konstan memonitor uptime dan kinerja melalui permintaan HTTP. Dapat diatur dalam beberapa menit. Berikut adalah beberapa pesaing yang dipilih: Pingdom, Uptime Robot, dan New Relic
2 Instrumentasi kode - rangkaian produk yang perlu menyematkan agen di dalam aplikasi untuk memanfaatkan fitur deteksi kode lambat, statistik pengecualian, pemantauan kinerja, dan banyak lagi. Berikut adalah beberapa pesaing terpilih: Relik Baru, Dinamika Aplikasi
3. Dasbor intelijen operasional -lini produk ini difokuskan untuk memfasilitasi tim ops dengan metrik dan konten yang dikuratori yang membantu dengan mudah tetap di atas kinerja aplikasi. Ini biasanya melibatkan pengumpulan berbagai sumber informasi (log aplikasi, log DB, server log, dll) dan pekerjaan desain dashboard dimuka. Berikut adalah beberapa pesaing terpilih: Datadog, Splunk "
Versi di atas adalah versi singkat - lihat di sini lebih banyak praktik dan contoh terbaik