Sentry-javascript: Kesalahan Pemuatan dan Pengambilan Asinkron

Dibuat pada 18 Des 2013  ·  36Komentar  ·  Sumber: getsentry/sentry-javascript

Saya ingin dapat memuat raven.js secara asinkron, tetapi masih dapat menangkap kesalahan saat skrip sedang dimuat. (Sesuatu seperti bagaimana Google Analytics menangani peristiwa dengan menyimpannya dalam variabel hingga pustaka dimuat).

Inilah yang saya miliki sejauh ini: https://Gist.github.com/karolisdzeja/8010574

Tetapi melakukannya dengan cara itu akan menghilangkan beberapa informasi dan detail yang biasanya diberikan oleh Raven. Apakah ada cara untuk menyimpan informasi kesalahan lengkap yang mirip dengan ini?

Feature

Komentar yang paling membantu

Berikut cuplikan yang kami gunakan untuk mengantre panggilan ke metode Raven secara transparan: https://Gist.github.com/Kl0tl/ed0a9e74462a2294f4c8842f5389d8ea.

Mock tentu saja dapat ditingkatkan tetapi kita tidak perlu mereplikasi lebih banyak fungsi. Object.defineProperty memungkinkan kita untuk menghubungkan tepat setelah Raven menempel pada jendela tetapi mungkin acara pemuatan skrip sudah cukup. Akan menyenangkan untuk memiliki cara resmi untuk mengaktifkan ini.

Semua 36 komentar

+1

+1

+1

+1

Semua orang yang mengomentari ini – apa yang salah dengan solusi dari @karolisdzeja?

Pada akhirnya, saya tidak yakin bagaimana kita dapat menambahkan fitur ke sumber Raven.js yang seharusnya berfungsi ketika sumber Raven.js tidak ada di halaman. Saya pikir ini pada akhirnya akan selalu menjadi solusi khusus; paling-paling kami dapat menambahkan "cara" ke dokumen kami.

@benvinegar solusinya terlihat baik-baik saja, tetapi akan lebih baik jika ini didukung dan didokumentasikan secara resmi. Saya lebih senang hanya harus mempercayai tim Penjaga daripada mengevaluasi inti acak.

Sebenarnya, solusi yang jauh lebih baik adalah seperti kode JS SDK Twitter: https://dev.twitter.com/web/javascript/loading

Siapkan antrian fungsi pada pemuatan halaman yang kemudian dikonsumsi ketika js eksternal telah dimuat, menggantikan objek proxy. Dan pastikan semua panggilan API melalui sesuatu seperti panggilan .ready() ke proxy.

Ini memastikan bahwa panggilan apa pun dapat diantrekan sebelum js dimuat, bukan hanya captureMessage, tanpa harus mem-proksi setiap fungsi satu per satu.

Saya ingin dapat memuat raven.js secara asinkron/ditangguhkan dan tidak perlu khawatir.

Masalah lain dengan intinya: itu clobbers window.onerror dan memperkenalkan beberapa variabel global yang tidak terkendali.

Itu juga tidak menggunakan fungsi traceKitWindowOnError berfitur lengkap yang digunakan raven.js saat memuat.

Saya sedikit mengulang Intisari di atas: https://Gist.github.com/oroce/ec3786ba7eff59963842220c3ffc56b4

Tidak ada variabel bocor. Saya menyimpan window.onerror handler, tetapi jangan ragu untuk menggunakan window.addEventListener('error', fn) .

Bantuan terbesar saat ini adalah memiliki traceKitWindowOnError sebagai fungsi yang diekspor dari Raven. Karena itulah fungsi yang dipanggil ketika terjadi kesalahan: https://github.com/getsentry/raven-js/blob/master/dist/raven.js#L2074

Saya tahu kami tidak akan memiliki stacktrace yang sangat tepat, tetapi kami akan memiliki sesuatu yang lebih baik daripada yang kami miliki sekarang.

Ada kerugian lain dengan melakukan ini:

  • Dengan mengandalkan window.onerror untuk menangkap kesalahan sebelum Raven dimuat, stacktraces tidak tersedia untuk setiap browser

    • selain tidak memiliki jejak tumpukan, ini akan mempengaruhi pengelompokan secara negatif

    • inilah mengapa install() mencoba/menangkap instrumentasi

  • Jejak sintetis tidak akan berfungsi (semuanya akan muncul sebagai berasal dari kode ini)
  • Tidak ada koleksi remah roti

Jadi, Anda memperdagangkan kinerja yang berpotensi lebih baik untuk laporan kesalahan kualitas yang lebih rendah. Jika eksekusi async penting, sebaiknya Anda menggabungkan Raven dengan kode Anda sendiri sehingga disajikan bersama.

@benvinegar Anda sepenuhnya benar. Dalam aplikasi yang tidak publik (alias google tidak akan mencapai halaman) cara klasik (memblokir) gagak benar-benar baik-baik saja tetapi segera setelah Anda memiliki situs yang menghadap publik di mana poin wawasan halaman google penting, kita perlu mengoptimalkan bagaimana kami memuat kode pihak ketiga (ini adalah harga yang bersedia kami bayar untuk ux, kecepatan, dan posisi hasil pencarian yang lebih baik).

Selain itu, menggabungkan gagak ke dalam bundel kami adalah solusi, tetapi segera setelah Anda mengoptimalkan kode frontend Anda dengan optimasi paro atas, seperti membagi bundel Anda menjadi beberapa yang menggunakan alat seperti factor-bundle atau Anda memasukkan beberapa bundel untuk mendapatkan lebih banyak kecepatan, solusi di atas bisa menjadi solusi yang lebih baik, tapi saya terbuka untuk saran.

Mereka semua memiliki trade-off, jadi akan sangat bagus jika kami dapat mendokumentasikan semua strategi yang tersedia, jadi tergantung pada aplikasi web tertentu, kami akan menerapkan strategi yang berbeda.
Dengan strategi async, kita harus memuat skrip setelah acara onload , alih-alih memuat async saja, untuk mencegah pemblokiran acara onload , rendering.

/**
 * Setup Js error lazy tracking
 * - Pros: doesn't block rendering, onload event
 * - Cons: lower quality error reports for lazy errors
 *
 * <strong i="9">@author</strong> vinhlh
 *
 * <strong i="10">@param</strong>  {object} window
 * <strong i="11">@param</strong>  {object} labJs
 * <strong i="12">@param</strong>  {string} ravenCdn
 * <strong i="13">@param</strong>  {string} sentryDsn
 */
(function(window, labJs, ravenCdn, sentryDsn) {
  var errors = [];
  var oldOnError = window.onerror;

  window.onerror = function() {
    errors.push(arguments);
    oldOnError && oldOnError.apply(this, arguments);
  };
  window.addEventListener('load', function() {
    labJs
      .script(ravenCdn)
      .wait(function() {
        window.onerror = oldOnError;
        Raven.config(sentryDsn).install();
        errors.forEach(function(args) {
          window.onerror.apply(this, args);
        });
      });
  });
})(window, $LAB, 'raven-js-3.8.1/dist/raven.js', 'https://[email protected]/9');

Saya pikir kami mungkin hanya akan mendokumentasikan cuplikan async, seperti yang disediakan di atas, tetapi menyebutkan bahwa itu datang dengan pengorbanan.

Hanya satu komentar lainnya. Pengorbanan tersebut mungkin tampak dapat diterima, tetapi saya berurusan dengan banyak tiket dukungan dari pengguna tentang kesalahan fidelitas rendah yang mereka alami yang (secara keliru) diyakini berasal dari Raven.js. Ketakutan saya adalah jika saya mendorong orang untuk menggunakan pendekatan async, saya akan memiliki semakin banyak orang yang bertanya kepada saya "mengapa tidak ada jejak tumpukan" dan keluhan lain ketika itu karena pendekatan ini memiliki kesetiaan yang lebih rendah. Saya bersedia menerima itu, tetapi ini adalah pil yang sulit untuk ditelan. 😓

@benvinegar Saya benar-benar mengerti dari mana Anda berasal, saya tahu apa yang saya lakukan jadi saya tidak mengharapkan stacktrace ketika seharusnya tidak ada. Untuk pendatang baru, yang kurang berpengalaman atau hanya pengguna dengan usecase yang berbeda pasti bisa memimpin.

@oroce – ya, ini 100% bukan masalah dengan orang-orang di utas ini, tetapi orang-orang yang mungkin mengejar strategi ini tanpa memahami peringatan dengan benar (misalnya hanya menyalin/menempel).

Saya akan membiarkan masalah ini tetap terbuka, dengan rencana untuk menambahkan cuplikan ke dokumen Install – dan saya akan menaruh banyak peringatan di semua tempat.

Sekali lagi terima kasih atas partisipasi Anda di sini / meyakinkan saya untuk melakukan ini.

Berikut cuplikan yang kami gunakan untuk mengantre panggilan ke metode Raven secara transparan: https://Gist.github.com/Kl0tl/ed0a9e74462a2294f4c8842f5389d8ea.

Mock tentu saja dapat ditingkatkan tetapi kita tidak perlu mereplikasi lebih banyak fungsi. Object.defineProperty memungkinkan kita untuk menghubungkan tepat setelah Raven menempel pada jendela tetapi mungkin acara pemuatan skrip sudah cukup. Akan menyenangkan untuk memiliki cara resmi untuk mengaktifkan ini.

Hai teman-teman, hanya ingin tahu apakah ada yang salah dengan cara Raygun melakukannya secara tidak sinkron?
Saya tidak yakin, tetapi tampaknya menangani kasus Edge dengan baik? Saya mungkin salah meskipun :)

@Kl0tl sangat bagus, terima kasih

Ini sangat sederhana menggunakan impor dinamis . Masih dalam tahap 3 tetapi didukung oleh webpack.

Kami hanya menggunakan janji sebagai antrian. Setelah terisi semua panggilan balik dieksekusi secara berurutan.

const RavenPromise = import('raven-js'); // async load raven bundle

// initial setup
RavenPromise.then(Raven => {
    Raven.config('url-to-sentry', options).install();
}):

// exported log function
export const logMessage = (level, logger, text) => {
    RavenPromise.then(Raven => {
        Raven.captureMessage(text, {level, logger});
    });
};

Sama halnya dengan @zanona, saya juga lebih suka memiliki kode pelacakan sederhana seperti yang dimiliki Raygun atau Google Analytics . Berikut adalah contoh untuk analytics.js :

<script async src="https://www.google-analytics.com/analytics.js"></script>
<script>
    window.ga = window.ga || function () {
        (ga.q = ga.q || []).push(arguments)
    }
    ga.l = +new Date

    ga('create', 'UA-XXXXX-Y', 'auto')
    ga('send', 'pageview')
</script>

@benvinegar Apakah hal seperti ini mungkin terjadi dengan Raven.js? Mungkin di masa depan?

@kireerik itu pasti akan diimplementasikan (kemungkinan besar sebagai cara dokumentasi), tetapi saya tidak dapat memberi Anda perkiraan tanggal yang akurat sekarang.

@kamilogorek Kedengarannya bagus (saya tidak suka solusi sebagai solusi). Tidak masalah!

Bagi siapa pun yang tertarik, saya telah memasang inti dari cara lain untuk memuat ravenjs secara tidak sinkron:
https://Gist.github.com/MaxMilton/e2338b02b7381fc7bef2ccd96f434201

Ini didasarkan pada kode oleh @oroce tetapi dengan perbedaan utama adalah saya menggunakan tag <script async src'='..."> di kepala dokumen untuk kinerja yang lebih baik (browser dapat menjadwalkan pengambilan sumber daya lebih awal) + Saya tidak repot membungkusnya sebuah IIFE dan tweak kecil lainnya.

@MaxMilton Bagus! Saya telah membuat rasa saya sendiri berdasarkan Anda:

<script async src="https://cdn.ravenjs.com/3.22.1/raven.min.js" crossorigin="anonymous" id="raven"></script>
<script>
    (function (sentryDataSourceName) {
        var raven = document.getElementById('raven')
        , isNotLoaded = true
        , errors = []
        raven.onreadystatechange = raven.onload = function () {
            if (isNotLoaded) {
                Raven.config(sentryDataSourceName).install()
                isNotLoaded = !isNotLoaded
                errors.forEach(function (error) {
                    Raven.captureException(error[4] || new Error(error[0]), {
                        extra: {
                            file: error[1]
                            , line: error[2]
                            , col: error[3]
                        }
                    })
                })
            }
        }
        window.onerror = function (message, source, lineNumber, colmnNumber, error) {
            if (isNotLoaded)
                errors.push([message, source, lineNumber, colmnNumber, error])
        }
    })('https://<key>@sentry.io/<project>')
</script>

Saya juga punya beberapa pertanyaan:

  • Apakah itu diperlukan untuk menentukan crossorigin atribut pada script tag?
  • Apakah cukup untuk melewati objek kesalahan saja daripada solusi lain ?

Bagaimana menurut anda? Apa pendapat penulis (@kamilogorek) tentang ini?

@kireerik ketika Anda meletakkan crossorigin="anonymous" pada skrip, ini memungkinkan Anda untuk sepenuhnya menangkap kesalahan (dari skrip eksternal itu) dengan acara window.onerror . Ini juga mencegah browser mengirim kredensial dengan permintaan pengambilan, yang biasanya Anda inginkan dengan sumber daya pihak ketiga. Referensi MDN 1 , referensi MDN 2 .

Anda bisa melewati kesalahan dan itu akan berfungsi _sebagian besar_ waktu. Peringatan karena browser lama (misalnya Firefox sebelum versi 31) tidak meneruskan properti columnNo atau Error Object ke acara window.onerror . Jadi jika Anda ingin kompatibilitas yang sangat baik maka Anda perlu melakukan sedikit ekstra. referensi MDN .

EDIT: Kiat bonus: ternyata ketika Anda memasukkan crossorigin tanpa nilai apa pun, itu diperlakukan sama dengan crossorigin="anonymous" .

FYI, saya telah memperbarui Intisari saya

  • banyak komentar untuk menjelaskan apa yang sebenarnya terjadi
  • pembersihan besar + gunakan nama variabel deskriptif (selalu bonus yang bagus :wink: )
  • bungkus di IIFE untuk tidak mencemari namespace global
  • perbaiki params yang salah diteruskan ke item array kesalahan

Lihat intinya jika Anda ingin memahami semuanya ATAU jika Anda lebih suka salin+tempel cepat, inilah versi yang diperkecil:

<!-- Sentry JS error tracking -->
<script async src="https://cdn.ravenjs.com/3.22.0/raven.min.js" crossorigin id="raven"></script>
<script>
(function(b,e,c,d){b.onerror=function(a,b,d,f,g){c||e.push([a,b,d,f,g])};b.onunhandledrejection=function(a){c||e.push([a.reason.reason||a.reason.message,a.type,JSON.stringify(a.reason)])};d.onreadystatechange=d.onload=function(){c||(c=!0,
Raven.config("___PUBLIC_DSN___").install(),
b.onunhandledrejection=function(a){Raven.captureException(Error(a.reason.reason||a.reason.message),{extra:{type:a.type,reason:JSON.stringify(a.reason)}})},e.forEach(function(a){Raven.captureException(a[4]||Error(a[0]),{extra:{file:a[1],line:a[2],col:a[3]}})}))}})(window,[],!1,document.getElementById("raven"));
</script>

<link rel="preconnect" href="https://sentry.io">

Ganti ___PUBLIC_DSN___ dengan DSN Anda dan tempel ini di suatu tempat di kepala dekat tag </head> penutup Anda. Atau jika Anda seorang hipster yang tidak menggunakan tag <head> dan <body> lagi, cukup tempel di dekat bagian atas setelah sumber daya penting/aplikasi (misalnya CSS). Idealnya itu harus sebelum JavaScript lain sehingga Anda dapat menangkap kesalahan dari skrip yang dimuat setelah kode ini.

Dalam tes cepat saya tidak ada masalah jadi saya tidak melihat alasan untuk tidak menggunakan ini pada versi sinkron default.

Jika ada yang punya ide untuk pendekatan yang lebih baik, saya ingin mendengarnya.

Sunting: Maaf telah mengedit komentar ini berkali-kali. Ini pada tingkat yang stabil sekarang. Sangat menyenangkan membawanya ke keadaan ini! :senyum:

Setelah perpustakaan penjaga dimuat, kualitas pelaporan kesalahan sama persis dengan memuatnya disinkronkan? (Saya berasumsi begitu, hanya memeriksa)

Juga dalam dokumen yang mungkin ingin Anda tambahkan, Anda tidak dapat menggunakan Raven sampai lib dimuat, mungkin menyediakan fungsi panggilan balik dalam opsi sehingga Anda dapat mengatur konteks pengguna dll?

setuju dengan @dalyr95 . fungsi panggilan balik akan berguna. mungkin acara pemuatan gagak khusus.

Saya memiliki permintaan serupa dengan @ dayr95. Saat ini satu-satunya cara untuk memanggil setUserContext() adalah dengan memodifikasi cuplikan loader yang tidak sebersih dapat meneruskan panggilan balik pada objek konfigurasi utama.

Halo, terima kasih telah melaporkan masalah ini.

Kami sedang dalam proses mengerjakan rilis besar berikutnya dari SDK kami.
Karena itu, kami harus menunda pengerjaan versi saat ini (kecuali bug utama atau keamanan).
Kami akan mencoba untuk mendapatkan kembali semua laporan sesegera mungkin, jadi harap bersabar.

Terima kasih atas pengertian Anda,
Bersulang!

Solusi saya adalah menambahkan 'undefined'!=k.setup&&k.setup(); segera setelah panggilan ke .install() , kemudian saya menambahkan fungsi yang disebut setup ke SENTRY_SDK dengan kode init posting saya.

Dengan pemuat async, saya dapat mengatur konteks pengguna dan info lainnya dengan meneruskannya sebagai argumen kedua ke Raven.config :

<script>
    Raven.config("https://<mydsn>@sentry.io/<projectid>", 
      {"release":"0.3.1",
       "environment":"dev",
       "user": {"id":"7b031fa0-32ff-46fe-b94b-e6bc201c3c5f",
                "organisation-id":"b1a50c41-b85e-4c50-9cec-c55ff36cf6d1"}}).install();
</script>

Saya pikir semuanya sudah ada untuk ini, itu hanya bisa didokumentasikan dengan lebih baik.

@danielcompton hanya bisa mendapatkan informasi pengguna melalui backend api ?

Apakah halaman ini membantu?
0 / 5 - 0 peringkat