Memantau kesalahan JavaScript dengan window.onerror

Bahan, terjemahan yang kami terbitkan hari ini, didedikasikan untuk menangani kesalahan JS menggunakan window.onerror . Ini adalah acara browser khusus yang dipicu ketika terjadi kesalahan yang tidak tertangkap. Di sini kita akan berbicara tentang cara untuk menangkap kesalahan menggunakan onerror peristiwa onerror , dan cara mengirim informasi tentang mereka ke server pengembang situs web. Handler ini dapat digunakan sebagai dasar sistem Anda sendiri untuk mengumpulkan dan menganalisis informasi kesalahan. Selain itu, ini adalah salah satu mekanisme paling penting yang digunakan dalam perpustakaan berorientasi kesalahan, seperti gagak .

gambar

Mendengarkan acara window.onerror


Anda dapat mendengarkan acara onerror dengan menetapkan window.onerror fungsi yang memainkan peran penangan kesalahan:

 window.onerror = function(msg, url, lineNo, columnNo, error) { // ...   ... return false; } 

Fungsi ini dipanggil ketika kesalahan terjadi, argumen berikut diteruskan ke sana:

  • msg - pesan kesalahan. Sebagai contoh, Uncaught ReferenceError: foo is not defined .
  • url - alamat skrip atau dokumen tempat kesalahan terjadi. Misalnya, /dist/app.js .
  • lineNo - nomor baris tempat kesalahan terjadi (jika didukung).
  • columnNo - nomor kolom baris (jika didukung).
  • error - objek error (jika didukung).

Empat argumen pertama memberi tahu pengembang skrip mana, di baris mana, dan di kolom mana baris ini terjadi kesalahan. Argumen terakhir, objek bertipe Error , mungkin adalah argumen yang paling penting. Mari kita bicarakan.

Objek kesalahan dan properti Error.prototype.stack


Pada pandangan pertama, objek Error tidak ada yang istimewa. Ini berisi tiga properti standar - message , fileName , dan lineNumber . Data ini, mengingat informasi yang diteruskan ke window.onerror event handler, dapat dianggap berlebihan.

Nilai sebenarnya dalam kasus ini adalah properti Error.prototype.stack non-standar. Properti ini memberikan akses ke tumpukan panggilan (tumpukan kesalahan), memungkinkan Anda untuk mengetahui apa yang terjadi dalam program pada saat kesalahan terjadi, yang mana panggilan fungsi mendahului kemunculannya. Pelacakan tumpukan panggilan dapat menjadi bagian penting dari proses debugging. Dan, terlepas dari kenyataan bahwa properti stack tidak standar, itu tersedia di semua browser modern.

Inilah yang terlihat seperti properti stack objek kesalahan di Chrome 46.

 "Error: foobar\n    at new bar (<anonymous>:241:11)\n    at foo (<anonymous>:245:5)\n at <anonymous>:250:5\n    at <anonymous>:251:3\n at <anonymous>:267:4\n at callFunction (<anonymous>:229:33)\n    at <anonymous>:239:23\n at <anonymous>:240:3\n at Object.InjectedScript.\_evaluateOn (<anonymous>:875:140)\n    at Object.InjectedScript.\_evaluateAndWrap (<anonymous>:808:34)" 

Di depan kami adalah string yang tidak diformat. Ketika konten properti ini disajikan dalam bentuk ini, tidak nyaman untuk bekerja dengannya. Begini cara yang sama akan terlihat setelah pemformatan.

 Error: foobar at new bar (<anonymous>:241:11) at foo (<anonymous>:245:5) at callFunction (<anonymous>:229:33) at Object.InjectedScript._evaluateOn (<anonymous>:875:140) at Object.InjectedScript._evaluateAndWrap (<anonymous>:808:34) 

Sekarang, setelah memformat, tumpukan kesalahan terlihat jauh lebih jelas, segera menjadi jelas mengapa properti stack sangat penting ketika men-debug kesalahan.

Namun, di sini semuanya tidak berjalan lancar. Properti stack tidak distandarisasi, tetapi diimplementasikan secara berbeda di browser yang berbeda. Di sini, misalnya, adalah seperti apa tumpukan kesalahan terlihat di Internet Explorer 11.

 Error: foobar at bar (Unknown script code:2:5) at foo (Unknown script code:6:5) at Anonymous function (Unknown script code:11:5) at Anonymous function (Unknown script code:10:2) at Anonymous function (Unknown script code:1:73) 

Anda dapat melihat, dibandingkan dengan contoh sebelumnya, bahwa di sini tidak hanya format yang berbeda untuk mewakili bingkai tumpukan digunakan, tetapi juga bahwa ada lebih sedikit data untuk setiap bingkai. Misalnya, Chrome mengidentifikasi contoh penggunaan kata kunci new dan memberikan informasi lebih rinci tentang acara lain (khususnya, tentang panggilan fungsi. _evaluateOn dan. _evaluateAndWrap ). Pada saat yang sama, di sini kami membandingkan hanya apa yang diberikan IE dan Chrome. Browser lain menggunakan pendekatan mereka sendiri untuk menampilkan data tentang stack dan untuk memilih informasi yang termasuk dalam data ini.

Agar semua ini terlihat seragam, Anda dapat menggunakan alat pihak ketiga. Sebagai contoh, gagak-js menggunakan TraceKit untuk ini. Stacktrace.js dan beberapa proyek lainnya memiliki tujuan yang sama.

Fitur window.onerror didukung oleh berbagai browser


Peristiwa windows.onerror telah ada di browser selama beberapa waktu. Secara khusus, dapat ditemukan di IE6 dan Firefox 2. Masalahnya di sini adalah bahwa semua browser menerapkan windows.onerror dengan cara yang berbeda. Misalnya, ini menyangkut jumlah dan struktur argumen yang diteruskan ke penangan acara ini.

Berikut adalah tabel yang onerror argumen yang diteruskan ke penangan onerror di peramban utama.
Browser
pesan
url
lineTidak
colNo
errorObj
Firefox
Ada
Ada
Ada
Ada
Ada
Chrome
Ada
Ada
Ada
Ada
Ada
Edge
Ada
Ada
Ada
Ada
Ada
IE 11
Ada
Ada
Ada
Ada
Ada
IE10
Ada
Ada
Ada
Ada
Tidak
IE 9.8
Ada
Ada
Ada
Tidak
Tidak
Safari 10 ke atas
Ada
Ada
Ada
Ada
Ada
Safari 9
Ada
Ada
Ada
Ada
Tidak
Browser Android 4.4
Ada
Ada
Ada
Ada
Tidak

Mungkin tidak mengherankan, Internet Explorer 8, 9, dan 10 memiliki dukungan terbatas untuk satu onerror . Namun, mungkin tampak tidak biasa bahwa dukungan browser Safari untuk objek kesalahan hanya muncul di versi 10, dirilis pada 2016. Selain itu, ada perangkat seluler lawas yang menggunakan browser Android standar, yang juga tidak mendukung objek kesalahan. Dalam versi modern Android, browser ini telah digantikan oleh Chrome Mobile.

Jika tidak ada objek kesalahan yang kami miliki, maka tidak ada data tentang jejak stack. Ini berarti bahwa browser yang tidak mendukung objek kesalahan tidak memberikan informasi tumpukan dalam skrip standar untuk menggunakan penangan onerror . Dan ini, seperti yang telah kami katakan, sangat penting.

Pengembangan polyfill untuk window.onerror menggunakan try / catch construct


Untuk mendapatkan informasi tentang tumpukan di browser yang tidak mendukung melewatkan objek onerror ke penangan onerror , Anda dapat menggunakan trik berikut. Anda bisa membungkus kode dalam konstruk try/catch - try/catch dan tangkap sendiri kesalahannya. Objek kesalahan yang dihasilkan akan berisi, di semua browser modern, yang kita butuhkan adalah properti stack .
Lihatlah kode metode helper invoke() , yang memanggil metode yang diberikan objek, memberikan array argumen.

 function invoke(obj, method, args) { return obj[method].apply(this,args); } 

Inilah cara menggunakannya.

 invoke(Math, 'max', [1,2]); //  2 

Berikut ini adalah invoke() sama invoke() , tetapi sekarang pemanggilan metode dibungkus try/catch , yang memungkinkan Anda untuk menangkap kemungkinan kesalahan.

 function invoke(obj, method, args) { try {   return obj[method].apply(this,args); } catch(e) {   captureError(e);//      throw e;//      } } invoke(Math,'highest',[1,2]); //  ,     Math.highest 

Tentu saja, sangat mahal untuk menambahkan struktur seperti itu secara manual ke semua tempat yang mungkin membutuhkannya. Tugas ini dapat disederhanakan dengan membuat fungsi penolong universal.

 function wrapErrors(fn) { //      if(!fn.__wrapped__) {   fn.__wrapped__ = function() {     try{       return fn.apply(this,arguments);     }catch(e){       captureError(e);//          throw e;//          }   }; } return fn.__wrapped__; } var invoke = wrapErrors(function(obj, method, args) { returnobj[method].apply(this,args); }); invoke(Math,'highest',[1,2]);//,   Math.highest 

Karena JavaScript menggunakan model eksekusi kode berulir tunggal, pembungkus ini harus digunakan hanya dengan pemanggilan fungsi yang ada di awal tumpukan baru. Tidak perlu membungkus semua panggilan fungsi di dalamnya.

Akibatnya, ternyata fungsi ini harus digunakan di tempat-tempat berikut:

  • Di mana aplikasi dimulai (misalnya, saat menggunakan jQuery, dalam fungsi $(document).ready )
  • Dalam penangan acara (misalnya, di addEventListener atau dalam konstruksi formulir $.fn.click )
  • Dalam panggilan balik yang dipanggil oleh acara penghitung waktu (misalnya, setTimeout atau requestAnimationFrame )

Berikut adalah contoh menggunakan fungsi wrapErrors .

 $(wrapErrors(function () {//    doSynchronousStuff1();//     setTimeout(wrapErrors(function () {   doSynchronousStuff2();//      })); $('.foo').click(wrapErrors(function () {   doSynchronousStuff3();//     })); })); 

Konstruksi seperti itu dapat ditambahkan ke kode sendiri, tetapi ini tugas yang terlalu memakan waktu. Sebagai alternatif yang nyaman dalam situasi seperti itu, Anda dapat mempertimbangkan perpustakaan untuk bekerja dengan kesalahan, yang, misalnya, memiliki mekanisme yang addEventListener dan setTimeout alat untuk addEventListener kesalahan.

Kesalahan transfer ke server


Jadi, sekarang kami memiliki sarana yang kami miliki untuk mencegat informasi kesalahan baik menggunakan windows.onerror atau menggunakan fungsi tambahan berdasarkan try/catch . Kesalahan ini terjadi di sisi klien, dan, setelah intersepsi mereka, kami ingin mengatasinya dan mengambil langkah-langkah untuk menghilangkannya. Untuk melakukan ini, mereka harus ditransfer ke server kami. Untuk melakukan ini, Anda perlu menyiapkan layanan web yang akan menerima informasi tentang kesalahan melalui HTTP, dan kemudian menyimpannya untuk diproses lebih lanjut, misalnya - akan menulis ke file log atau database.

Jika layanan web ini terletak pada domain yang sama dengan aplikasi web, XMLHttpRequest akan cukup. Contoh berikut menunjukkan cara menggunakan fungsi untuk menjalankan kueri AJAX dari jQuery untuk mentransfer data ke server.

 function captureError(ex){ var errorData = {   name:ex.name,// : ReferenceError   message:ex.line,// : x is undefined   url:document.location.href,   stack:ex.stack//   ; ,     ! }; $.post('/logger/js/',{   data:errorData }); } 

Perlu diingat bahwa jika Anda perlu mengirim permintaan lintas-domain untuk mengirim informasi tentang kesalahan ke server, Anda harus berhati-hati mendukung permintaan tersebut.

Ringkasan


Anda telah mempelajari dasar-dasar membuat layanan untuk menangkap kesalahan dan mengirim informasi tentangnya ke server. Secara khusus, di sini kami memeriksa masalah berikut:

  • Fitur acara onerror dan dukungannya di berbagai browser.
  • Menggunakan mekanisme try/catch untuk mendapatkan informasi tentang tumpukan panggilan dalam kasus-kasus di mana onerror tidak mendukung bekerja dengan objek kesalahan.
  • Transfer data kesalahan ke server pengembang.

Setelah mempelajari tentang bagaimana mekanisme di atas bekerja, Anda telah memperoleh pengetahuan dasar yang akan memungkinkan Anda untuk mulai membuat sistem Anda sendiri untuk bekerja dengan kesalahan, mengklarifikasi detail tambahan selama bekerja. Mungkin skenario ini sangat relevan untuk kasus-kasus ketika datang ke aplikasi tertentu di mana, katakanlah, untuk alasan keamanan, itu tidak direncanakan untuk menggunakan perpustakaan pihak ketiga. Jika aplikasi Anda memungkinkan penggunaan kode pihak ketiga, Anda dapat menemukan alat yang tepat untuk memonitor kesalahan JS. Di antara alat-alat tersebut adalah Sentry , Rollbar , TrackJS dan proyek serupa lainnya.

Pembaca yang budiman! Alat apa untuk memantau kesalahan JS yang Anda gunakan?

Source: https://habr.com/ru/post/id413173/


All Articles