Sentry-javascript: [@sentry/node] AWS Lambda dan dukungan solusi Tanpa Server lainnya

Dibuat pada 28 Jul 2018  ·  77Komentar  ·  Sumber: getsentry/sentry-javascript

  • @sentry/node versi 4.0.0-beta.11
  • Saya menggunakan Sentry yang dihosting

Apa perilaku saat ini?

Saya menggunakan @sentry/node untuk menangkap pengecualian pada fungsi lambda AWS.

    .catch(err => {
      Sentry.captureException(err)
      context.fail()
    })

Namun, itu membunuh proses ketika context.fail() dipanggil dan pengecualian tidak berakhir di Sentry.

Saya bisa melakukan solusi seperti:

    .catch(err => {
      Sentry.captureException(err)
      setTimeout(() => context.fail(), 1000)
    })

Apa perilaku yang diharapkan?

Akan lebih baik jika saya bisa melakukan sesuatu seperti:

    .catch(err => {
      Sentry.captureException(err, () => context.fail())
    })

Atau sesuatu yang menangani panggilan balik secara global.

Komentar yang paling membantu

@LinusU kemungkinan besar kami akan membuat paket tanpa server khusus untuk skenario ini. Kami hanya perlu mencari waktu, karena ini adalah akhir tahun dan segalanya menjadi ramai sekarang. Akan membuat Anda tetap diposting!

Semua 77 komentar

Ini mungkin membantu saya kira https://blog.sentry.io/2018/06/20/how-droplr-uses-sentry-to-debug-serverless (menggunakan versi gagak lama, yang memiliki panggilan balik, tapi saya kebanyakan menunjuk ke bendera callbackWaitsForEmptyEventLoop .

Belum ada cara resmi , karena kami masih mencoba berbagai hal dalam versi beta, tetapi dapat dilakukan dengan kode ini:

import { init, getDefaultHub } from '@sentry/node';

init({
  dsn: 'https://my-dsn.com/1337'
});

exports.myHandler = async function(event, context) {
  // your code

  await getDefaultHub().getClient().captureException(error, getDefaultHub().getScope());
  context.fail();
}

@kamilogorek Terima kasih atas penunjuknya. Saya akan mencobanya dan memutar ulang pelajarannya.

@kamilogorek Saran Anda berhasil. Saya menantikan cara yang lebih resmi.

@vietbui
Di 4.0.0-rc.1 kami memperkenalkan fungsi pada klien yang disebut close , Anda menyebutnya seperti ini:

import { getCurrentHub } from '@sentry/node';

getCurrentHub().getClient().close(2000).then(result => {
      if (!result) {
        console.log('We reached the timeout for emptying the request buffer, still exiting now!');
      }
      global.process.exit(1);
})

close akan menunggu sampai semua permintaan terkirim, itu akan selalu diselesaikan (hasil = false timeout tercapai), hingga timeout tercapai (dalam contoh ini 2000ms).
Ini adalah API resmi kami.
Sementara pendekatan sebelumnya akan tetap berfungsi, metode close berfungsi untuk semua kasus.

@HazAT Bagus. Terima kasih untuk semua kerja kerasnya.

Di 4.0.3 saya menyebutnya seperti ini di fungsi lambda saya:

try {
  ...
} catch (err) {
  await getCurrentHub().getClient().captureException(err, getCurrentHub().getScope())
  throw err
}

getDefaultHub() tidak lagi tersedia.

@vietbui sekarang disebut getCurrentHub , karena kami harus menyatukan API kami dengan SDK bahasa lain.

@kamilogorek Terima kasih atas klarifikasinya. Ada masalah dengan pendekatan getCurrentHub karena entah bagaimana cakupan yang saya atur tidak berakhir di Sentry.

Pada akhirnya saya mengambil pendekatan berbeda seperti yang disarankan oleh @HazAT untuk menangkap pengecualian dalam fungsi lambda saya:

try {
  ...
} catch (err) {
  Sentry.captureException(err)
  await new Promise(resolve => Sentry.getCurrentHub().getClient().close(2000).then(resolve))
  throw err
}

Dan itu bekerja dengan sempurna.

Apakah ini cara yang disarankan untuk menunggu/memaksa penjaga untuk mengirim acara?

@albinekb ya – https://docs.sentry.io/learn/draining/?platform=browser

Solusi ini tidak bekerja untuk saya karena suatu alasan. Ini hanya bekerja pertama kali dalam produksi ketika ada waktu mulai dingin dan tidak bekerja setelah itu. di sini adalah contoh kode

'use strict'

const Sentry =  require('@sentry/node')
Sentry.init({
  dsn: 'xxx',
  environment: process.env.STAGE
});

module.exports.createPlaylist = async (event, context, callback) => {
  context.callbackWaitsForEmptyEventLoop = false
  if(!event.body) {
    Sentry.captureException(error)
    await new Promise(resolve => Sentry.getCurrentHub().getClient().close(2000).then(resolve))
    return {
      statusCode: 500,
      headers: { 'Content-Type': 'text/plain' },
      body: 'Missing body parameters'
    }
  }
  return {
    statusCode: 200,
  }
};

@Andriy-Kulak Itu juga dinyatakan dalam dokumen:

After shutdown the client cannot be used any more so make sure to only do that right before you shut down the application.

Jadi saya tidak tahu bagaimana kita bisa menangani ini di lambda di mana kita tidak tahu kapan aplikasi akan dimatikan. Yang terbaik adalah menguras penjaga per permintaan seperti yang kami bisa dengan API lama?

@HazAT bisakah kita membuka kembali ini? Saya pikir penting untuk memiliki cara untuk bekerja dengan ini di Lambda, yang menjadi target yang semakin umum untuk digunakan.

Ini saat ini menghalangi saya untuk meningkatkan ke versi terbaru...

Secara pribadi, saya lebih suka bisa mendapatkan Janji/panggilan balik saat melaporkan kesalahan. Memiliki cara untuk menguras antrian tanpa benar-benar menutupnya sesudahnya akan menjadi hal terbaik berikutnya...

Apa alasan menghapus panggilan balik dari captureException ?

@albinekb tidak berfungsi sama sekali jika saya menghapus baris berikut

await new Promise(resolve => Sentry.getCurrentHub().getClient().close(2000).then(resolve))

@LinusU apa solusi dan solusi penjaga atau gagak yang Anda gunakan?

Bagi saya pada dasarnya yang berikut ini berfungsi dengan sentry/node @4.3.0 , tetapi saya harus membuat lambda secara manual menunggu beberapa waktu (dalam hal ini saya menempatkan 2 detik) agar penjaga melakukan apa yang perlu dilakukan. Yang saya tidak yakin mengapa harus ada di sana karena kami sedang menunggu penjaga untuk menyelesaikan permintaan captureException . Jika saya tidak memiliki masa tunggu setelahnya, penjaga sepertinya tidak mengirim kesalahan.

'use strict'

const Sentry =  require('@sentry/node')
Sentry.init({
  dsn: 'xxx',
  environment: process.env.STAGE
});

module.exports.createPlaylist = async (event, context, callback) => {
  context.callbackWaitsForEmptyEventLoop = false
  if(!event.body) {
    const error = new Error('Missing body parameters in createPlaylist')
    await Sentry.captureException(error)
    await new Promise(resolve => {setTimeout(resolve, 2000)})
    return {
      statusCode: 500,
      headers: { 'Content-Type': 'text/plain' },
      body: 'Missing body parameters'
    }
  }
  return {
    statusCode: 200,
  }
};

Kami juga terlibat dalam hal ini di Lambda. Kami mulai dengan lib baru dan benar-benar tertutup, mempertimbangkan untuk kembali ke Raven. Kami sedang menulis tes sekarang untuk mencoba menutup hub dan kemudian menginisialisasi ulang, yang akan menjadi solusi yang bisa diterapkan jika menahan air. Tapi masih hacky / cenderung menyebabkan masalah di bawah beban.

Secara pribadi saya lebih suka semacam flush() yang mengembalikan janji – sulit untuk menemukan kerugiannya. Pikirkan itu akan pernah terjadi?

apa solusi dan solusi penjaga atau gagak yang Anda gunakan?

Saya menggunakan penangan kesalahan ekspres berikut:

app.use((err: any, req: express.Request, res: express.Response, next: express.NextFunction) => {
  let status = (err.status || err.statusCode || 500) as number

  if (process.env.NODE_ENV === 'test') {
    return next(err)
  }

  if (status < 400 || status >= 500) {
    Raven.captureException(err, () => next(err))
  } else {
    next(err)
  }
})

Saya kemudian menggunakan skandium untuk menyebarkan aplikasi Express ke Lambda

edit: ini dengan Raven "raven": "^2.6.3",

API impian akan menjadi seperti ini 😍

Sentry.captureException(err: Error): Promise<void>

@LinusU https://github.com/getsentry/sentry-javascript/blob/master/packages/core/src/baseclient.ts#L145 -L152

Anda harus menggunakan instance klien secara langsung untuk mendapatkannya. Alasan untuk ini adalah memutuskan bahwa skenario utama adalah jenis perilaku "api dan lupakan", jadi ini bukan metode asinkron. Namun secara internal, kami memiliki API async yang kami gunakan sendiri.

Tampaknya yang sebenarnya saya inginkan adalah sesuatu yang lebih seperti:

const backend = client.getBackend()
const event = await backend.eventFromException(error)
await client.processEvent(event, finalEvent => backend.sendEvent(finalEvent))

Untuk melewati semua antrian dan buffering ...

Saya mendapatkan bahwa desainnya disesuaikan untuk "api dan lupa", dan untuk berjalan di server yang berjalan lama, dan mungkin cukup bagus karena melakukan banyak buffering, dll. Masalahnya adalah ini adalah kebalikannya yang Anda inginkan untuk Lambda, App Engine, dan arsitektur "tanpa server" lainnya, yang menjadi semakin umum.

Apakah mungkin untuk memiliki metode khusus yang mengirimkan acara secepat mungkin, dan mengembalikan Promise yang kita dapat await ? Itu akan sempurna untuk skenario tanpa server!

class Sentry {
  // ...

  async unbufferedCaptureException(err: Error): Promise<void> {
    const backend = this.client.getBackend()
    const event = await backend.eventFromException(error)
    await this.client.processEvent(event, finalEvent => backend.sendEvent(finalEvent))
  }

  // ...
}

@LinusU kemungkinan besar kami akan membuat paket tanpa server khusus untuk skenario ini. Kami hanya perlu mencari waktu, karena ini adalah akhir tahun dan segalanya menjadi ramai sekarang. Akan membuat Anda tetap diposting!

kemungkinan besar kami akan membuat paket tanpa server khusus untuk skenario ini

Itu akan luar biasa! 😍

@mtford90

kapan tepatnya saya akan menggunakan solusi yang lebih baik ini? Sejauh yang saya tahu, tidak mungkin untuk mengetahui kapan lambda akan dimatikan - ditambah lagi tampaknya konyol menunggu jumlah waktu yang sewenang-wenang untuk mematikan untuk memungkinkan penjaga melakukan hal itu - terutama pada fungsi lambda memori/cpu yang mahal.

(berbicara tentang menguras)

Ini dimaksudkan untuk digunakan sebagai hal terakhir sebelum menutup proses server. Timeout pada metode drain adalah waktu maksimal yang kita tunggu sebelum proses shutdown, bukan berarti kita akan selalu menggunakan waktu tersebut. Jika server sepenuhnya responsif, itu akan segera mengirim semua acara yang tersisa.

Tidak ada cara untuk mengetahui hal ini, tetapi ada cara untuk memberi tahu lambda kapan harus dimatikan menggunakan argumen callback handler.

Juga @LinusU , saya membaca kembali komentar Anda sebelumnya, khususnya bagian ini:

Mungkinkah memiliki metode khusus yang mengirimkan acara secepat mungkin, dan mengembalikan Janji yang bisa kita tunggu? Itu akan sempurna untuk skenario tanpa server!

Ini adalah bagaimana kami mengimplementasikan buffer kami. Setiap panggilan captureX pada klien, akan menambahkannya ke buffer, itu benar, tetapi tidak diantrekan dengan cara apa pun, itu langsung dieksekusi dan pola ini hanya digunakan sehingga kami bisa mendapatkan informasi jika semuanya sudah selesai. berhasil dikirim ke Sentry.

https://github.com/getsentry/sentry-javascript/blob/0f0dc37a4276aa2b832da451307bc4cd5413b34d/packages/core/src/requestbuffer.ts#L12 -L18

Ini berarti bahwa jika Anda melakukan sesuatu seperti ini di AWS Lambda (dengan asumsi Anda ingin menggunakan klien default, yang merupakan kasus paling sederhana):

import * as Sentry from '@sentry/browser';

Sentry.init({ dsn: '__YOUR_DSN__' });

exports.handler = (event, context, callback) => {
    try {
      // do something
    catch (err) {
      Sentry.getCurrentHub()
        .getClient()
        .captureException(err)
        .then((status) => {
          // request status
          callback(null, 'Hello from Lambda');
        })
    }
};

Anda dapat yakin bahwa itu dikirim segera dan tidak ada overhead waktu/pemrosesan.

@kamilogorek
Apakah ini berarti sesuatu seperti ini harus berfungsi di penangan async/menunggu (di mana Anda tidak menggunakan panggilan balik)?

import * as Sentry from '@sentry/node';

Sentry.init({ dsn: '__YOUR_DSN__' });

exports.handler = async (event, context) => {
    try {
      // do something

      return 'Hello from Lambda';
    catch (err) {
      await Sentry.getCurrentHub().getClient().captureException(err);
      return 'Hello from Lambda with error';
    }
};

@jviolas benar-benar! :)

Sepertinya perubahan berikut akan berhasil untuk saya ️

-import Raven = require('raven')
+import * as Sentry from '@sentry/node'

 // ...

-Raven.config(config.SENTRY_DSN)
+Sentry.init({ dsn: config.SENTRY_DSN })

 // ...

 app.use((err: any, req: express.Request, res: express.Response, next: express.NextFunction) => {
   let status = (err.status || err.statusCode || 500) as number

   if (process.env.NODE_ENV === 'test') {
     return next(err)
   }

   if (status < 400 || status >= 500) {
-    Raven.captureException(err, () => next(err))
+    Sentry.getCurrentHub().getClient().captureException(err).then(() => next(err))
   } else {
     next(err)
   }
 })

Sejujurnya, setiap baris menjadi sedikit lebih buruk tapi saya rasa itu lebih baik di bawah tenda...

@kamilogorek Saya tidak dapat menemukan getCurrentHub() dalam dokumen di situs web Anda, apakah API ini dijamin tidak akan rusak tanpa hambatan besar? ❤️

@kamilogorek Saya tidak dapat menemukan getCurrentHub() dalam dokumen di situs web Anda, apakah API ini dijamin tidak akan rusak tanpa hambatan besar? ❤️

Ya, itu dijamin. Ini adalah bagian dari paket @sentry/hub yang dijelaskan di sini - https://docs.sentry.io/enriching-error-data/scopes/?platform=browser

Kami sedang mendiskusikan "penggunaan lanjutan" di sini di utas ini dan kami belum sampai pada titik untuk mendokumentasikannya. Kami akan melakukan ini pada akhirnya :)

Jelas apa yang kami lewatkan di sini adalah beberapa dokumentasi dan praktik yang baik dalam kasus penggunaan tingkat lanjut semacam ini. Akan sangat bagus ketika akan didokumentasikan atau bahkan posting blog bisa menjadi awal yang baik.
Jika tidak, SDK baru sangat mudah digunakan dan penyatuannya sangat bagus.

@kamilogorek
Apakah ini berarti sesuatu seperti ini harus berfungsi di penangan async/menunggu (di mana Anda tidak menggunakan panggilan balik)?

import * as Sentry from '@sentry/node';

Sentry.init({ dsn: '__YOUR_DSN__' });

exports.handler = async (event, context) => {
    try {
      // do something

      return 'Hello from Lambda';
    catch (err) {
      await Sentry.getCurrentHub().getClient().captureException(err);
      return 'Hello from Lambda with error';
    }
};

Melakukan sesuatu seperti yang disarankan di atas berhasil, kecuali saya tidak dapat menambahkan konteks tambahan. Misalnya, jika saya melakukan:

Sentry.configureScope(scope => {
   scope.setExtra('someExtraInformation', information);
});
await Sentry.getCurrentHub().getClient().captureException(err);

Saya tidak akan benar-benar melihat 'someExtraInformation' di Sentry.

Seseorang memang menyarankan metode alternatif di bagian atas utas ini, dan itu berhasil, tetapi tampaknya meretas (memaksa batas waktu).

Sentry.configureScope(scope => {
  scope.setExtra('someExtraInformation', information);
});
Sentry.captureException(error);
await new Promise(resolve => Sentry.getCurrentHub().getClient().close(2000).then(resolve));

@kamilogorek @jviolas

import * as Sentry from '@sentry/node';

Sentry.init({ dsn: '__YOUR_DSN__' });

exports.handler = async (event, context) => {
   try {
     // do something

     return 'Hello from Lambda';
   catch (err) {
     await Sentry.getCurrentHub().getClient().captureException(err);
     return 'Hello from Lambda with error';
   }
};

Bisakah ini juga diterapkan ke _uncaught exceptions_ ? Tampaknya memodifikasi integrasi Sentry.Integrations.OnUncaughtException adalah cara resmi untuk melakukannya tetapi dokumentasinya sangat buruk saat ini.

+1 untuk ini. Setidaknya memiliki sesuatu yang didokumentasikan secara resmi akan baik. Tanpa server berkembang pesat pada 2019, saya sangat ingin melihat dukungan resmi dari Sentry di atasnya. Salah satu ide yang saya baca di sini dan sangat saya sukai adalah memiliki sesuatu seperti Sentry.flush() untuk mengirim semua acara yang antri.

@rdsedmundo Bisakah Anda menjelaskan mengapa pendekatan ini tidak berhasil untuk Anda?

import * as Sentry from '@sentry/node';

Sentry.getCurrentHub().getClient().close(2000).then(result => {
      if (!result) {
        console.log('We reached the timeout for emptying the request buffer, still exiting now!');
      }
      global.process.exit(1);
})

Ini adalah pendekatan resmi kami dan pada dasarnya Sentry.flush() .
ref: https://docs.sentry.io/error-reporting/configuration/draining/?platform=javascript

@HazAT Masalahnya muncul ketika Anda memikirkan penggunaan kembali wadah AWS Lambda. Yang dalam istilah TL;DR berarti bahwa proses yang baru saja melayani permintaan dapat melayani permintaan baru jika dibuat dalam waktu singkat. Jika saya menutup koneksi dengan cuplikan yang Anda berikan ini, dan wadahnya digunakan kembali, saya harus berhasil membuat hub baru untuk permintaan baru. Saya dapat dengan mudah melihat ini menjadi rumit. Itu sebabnya await Sentry.flush() sederhana akan menjadi solusi yang lebih baik:

import Sentry from './sentry'; // this calls Sentry.init under the hood

export const handler = async (event, context) => {
  try {
    ...
  } catch (error) {
    Sentry.captureException(error);
    await Sentry.flush(); // could even be called on the finally block

    return formatError(error);
  }
}

@rdsedmundo Saya tidak yakin apakah saya mungkin salah paham tentang sesuatu tetapi jika Anda melakukannya

import Sentry from './sentry'; // this calls Sentry.init under the hood

export const handler = async (event, context) => {
  try {
    ...
  } catch (error) {
    Sentry.captureException(error);
    await Sentry.getCurrentHub().getClient().close(2000);

    return formatError(error);
  }
}

Ini persis seperti await Sentry.flush hanya saja Anda menentukan batas waktu.

Janji diselesaikan setelah 2000 ms pasti dengan false jika masih ada barang dalam antrian.
Jika tidak, close akan diselesaikan dengan true jika antrian telah dikuras sebelum batas waktu tercapai.

Atau akankah wadah itu digunakan kembali sebelum semua janji diselesaikan? (Saya tidak bisa membayangkan itu)

@HazAT bukankah masalah close(...) akan mencegah klien digunakan lagi? Lambda menggunakan kembali proses Node yang sama sehingga panggilannya akan menjadi seperti ini, yang saya kira akan berhenti berfungsi setelah panggilan pertama ke close ?

  • Sentry.init()
  • Sentry.captureException()
  • Sentry.getCurrentHub().getClient().close()
  • Sentry.captureException()
  • Sentry.getCurrentHub().getClient().close()
  • Sentry.captureException()
  • Sentry.getCurrentHub().getClient().close()
  • Sentry.captureException()
  • Sentry.getCurrentHub().getClient().close()
  • ...

Tidak, close tidak membuang klien, itu hanya di sini untuk menguras antrian transportasi.
Saya setuju bahwa nama close dalam konteks ini mungkin menyesatkan tetapi setidaknya di JS/Node close tidak melakukan apa pun dengan klien dan tidak apa-apa untuk tetap menggunakannya sesudahnya.

Sunting: Jika itu benar-benar "masalah" saya akan memperbarui dokumen untuk memperjelasnya.

Dingin. Tetapi dokumentasinya salah:

After shutdown the client cannot be used any more so make sure to only do that right before you shut down the application.

Oke, kita baru saja membahas masalah ini secara internal di dalam tim.
Kalian benar dan sementara JavaScript saat ini tidak berperilaku seperti yang kami dokumentasikan kami akan memperkenalkan fungsi flush yang akan melakukan persis seperti yang Anda harapkan.

Jadi sekarang Anda dapat menggunakan close tanpa masalah (tidak yakin apakah kami akan mengubahnya untuk membuang/menonaktifkan klien di masa mendatang).
Tetapi akan ada fungsi flush yang ada untuk _just_ flush antrian.

Saya akan memperbarui masalah ini setelah fitur tersebut mendarat.

Karena saya agak tersesat dalam semua komentar ini, apakah seperti ini seharusnya penangan kesalahan Express (meniru yang dari repo ini)?

function getStatusCodeFromResponse(error) {
    const statusCode = error.status || error.statusCode || error.status_code || (error.output && error.output.statusCode);
    return statusCode ? parseInt(statusCode, 10) : 500;
}

app.use(async (err, req, res, next) => {
    const status = getStatusCodeFromResponse(err);

    if (status >= 500) {
        Sentry.captureException(err)

        await Sentry.getCurrentHub().getClient().close(2000)
    }

    next(err)
})

Sepertinya berfungsi dan tidak kehilangan data tambahan seperti pada kode @rreynier .

Secara pribadi saya merasakannya

await Sentry.getCurrentHub().getClient().captureException(err)

lebih bersih dari:

Sentry.captureException(err)
await Sentry.getCurrentHub().getClient().close(2000)

close benar-benar berbunyi seperti itu akan menutup klien...

Contoh lengkap:

import * as Sentry from '@sentry/node'

// ...

Sentry.init({ dsn: config.SENTRY_DSN })

// ...

app.use((err: any, req: express.Request, res: express.Response, next: express.NextFunction) => {
  let status = (err.status || err.statusCode || 500) as number

  if (process.env.NODE_ENV === 'test') {
    return next(err)
  }

  if (status < 400 || status >= 500) {
    Sentry.getCurrentHub().getClient().captureException(err).then(() => next(err))
  } else {
    next(err)
  }
})

@LinusU Saya mencobanya dan untuk beberapa alasan, itu tidak mengirim data tambahan bersama dengan jejak tumpukan. Ini pada dasarnya hanya mengirim jejak tumpukan. Tidak ada info tentang pengguna, OS atau apa pun.

Aha, itu tidak bagus sama sekali

Sementara kami menunggu flush , sebagai solusi yang lebih andal daripada kedua opsi di atas, Anda dapat melaporkan dan menunggu hasilnya, _dan_ sertakan cakupannya, menggunakan cuplikan di bawah ini:

const scope = Sentry.getCurrentHub().getScope();
await Sentry.getCurrentHub().getClient().captureException(error, scope);

Saya menggunakan ini, dan tampaknya bekerja dengan andal untuk saya, dengan kesalahan yang dilaporkan termasuk semua yang saya harapkan.

Saya sebenarnya menggunakan semua ini dengan Fungsi Netlify, tetapi teorinya sama dengan Lambda dll. Saya telah menulis sebuah posting dengan detail lengkap tentang cara membuatnya berfungsi, jika ada yang tertarik: https://httptoolkit. tech/blog/netlify-function-error-reporting-with-sentry/

Saya menggunakan pembantu ini di semua lambda saya saat ini.

@pimterry Bukankah ini pada dasarnya solusi yang sama seperti yang disarankan @LinusU ? Saya sudah mencobanya dan tidak mengirim data tambahan juga.

Pendekatan ini berhasil bagi saya sejauh ini @ondrowan

@ondrowan itu sama, tetapi secara manual meraih dan memasukkan ruang lingkup saat ini. Itu seharusnya cukup untuk membuat Anda bekerja pengecualian meskipun saya pikir. Dengan versi sebelumnya, saya mendapatkan acara yang tidak berlabel, dan sekarang dengan perubahan ini, pengecualian saya muncul dengan semua detail ekstra normal.

@vietbui @albinekb @Andriy-Kulak @LinusU @dwelch2344 @jviolas @rreynier @guillaumekh @rdsedmundo @ondrowan @pimterry @zeusdeux tidak yakin siapa yang masih tertarik dengan kasus penggunaan ini, jadi maafkan saya jika saya tidak boleh menelepon Anda.

Mulai 4.6.0 , tidak ada lagi client/hub dansa. Anda cukup memanggil metode captureX kami dan kemudian menggunakan Sentry.flush() untuk menunggu respons setelah semuanya dikirim ke server. Semua ruang lingkup/data tambahan harus dipertahankan tanpa interaksi pengembang.

Berikut ini contoh dengan permintaan yang berhasil/waktu habis.

image

Semoga membantu! :)

Bagus!

Apakah masih ada rencana untuk membuat paket minimal hanya untuk menangkap pengecualian dari Lambda dan solusi tanpa server lainnya? Saya pikir itu masih akan menjadi tambahan yang sangat bagus ❤️

@LinusU semoga ya, tapi kami kebanjiran SDK bahasa lain sekarang

Terima kasih semua untuk semua solusi yang mungkin, tl; dr untuk semua orang yang datang ke sini

Gunakan: await Sentry.flush() untuk mengirim semua permintaan yang tertunda, ini telah diperkenalkan di 4.6.x .

Menutup ini, jangan ragu untuk membuka masalah baru jika ada yang hilang (tetapi utas ini sudah sangat panjang).

Semangat 👍

@kamilogorek Hai! Fyi cepat, saya menggunakan Sentry.flush di aplikasi saya sebagai pengganti solusi lama dan tidak ada kesalahan yang dilaporkan. Saat ini saya kembali ke solusi lama dari metode flush yang diperbarui .

@zeusdeux apakah ada cara Anda bisa memberikan beberapa info debug/kasus repro untuk ini?
Anda mengganti metode captureException yang menambahkan acara ke buffer, dan kemudian Anda harus await pada nilai kembalian flush . Sudahkah Anda mencoba menggunakannya dengan "cara biasa"?

@kamilogorek Saya berharap saya punya info debug tapi tidak ada di log. Saya selalu melakukan await pada captureException yang ditimpa . Dengan cara biasa, maksud Anda tanpa mengesampingkan captureException ?

@zeusdeux tepatnya, panggil saja Sentry.captureException(error) asli kami tanpa penggantian apa pun.

Jadi penolong Anda adalah:

import * as Sentry from '@sentry/node'

export function init({ host, method, lambda, deployment }) {
  const environment = host === process.env.PRODUCTION_URL ? 'production' : host

  Sentry.init({
    dsn: process.env.SENTRY_DSN,
    environment,
    beforeSend(event, hint) {
      if (hint && hint.originalException) {
        // eslint-disable-next-line
        console.log('Error:', hint.originalException);
      }
      return event;
    }
  })

  Sentry.configureScope(scope => {
    scope.setTag('deployment', deployment)
    scope.setTag('lambda', lambda)
    scope.setTag('method', method)
  })
}

dan dalam kode Anda menyebutnya:

import * as Sentry from '@sentry/node'

try {
  // ...
} catch (err) {
  Sentry.captureException(err);
  await Sentry.flush(2000);
  return respondWithError('Something went wrong', 500);
}

@kamilogorek Saya akan mencobanya dan melaporkan kembali. Juga, terima kasih atas tipnya tentang beforeSend ^_^

await Sentry.flush(2000);

juga ~tidak~ bekerja untuk saya.

@tanduong dapatkah Anda memberikan kasus repro? Hanya menyatakan bahwa itu tidak berhasil tidak terlalu membantu

@kamilogorek sebenarnya, saya baru tahu itu

await Sentry.getCurrentHub().getClient().close(2000)

tidak berfungsi untuk saya juga karena fungsi lambda saya dilampirkan ke VPC.

Saya mengkonfirmasi itu

await Sentry.flush(2000);

sebenarnya bekerja.

BTW, jadi bagaimana Anda menangani lambda di VPC? Lampirkan ke gateway NAT? Saya hanya ingin Sentry tetapi bukan internet publik.

@tanduong Sentry ada di internet publik, jadi ya Anda harus memiliki gateway NAT jika lambda Anda berjalan di dalam VPC Anda. Jika tidak, Anda harus menjelajahi opsi Sentry yang dihosting.

Apa yang sebenarnya dilakukan flush(2000) ? Saya memiliki kode ini sebagian besar berfungsi dengan baik tetapi sekarang saya memiliki beberapa panggilan captureMessage yang terjadi secara bersamaan, waktunya habis setiap saat!

Membilas antrean internal pesan melalui kabel

Oke, itu masuk akal. Saya pikir masalah saya adalah bahwa janji ini tidak pernah kembali ketika tidak ada lagi yang harus disiram? Setiap kali saya menjalankan captureException fn saya yang dibungkus secara bersamaan, waktu handler saya habis.

export const captureMessage = async (
  message: string,
  extras?: any,
): Promise<boolean> =>
  new Promise((resolve) => {
    Sentry.withScope(async (scope) => {
      if (typeof extras !== 'undefined') {
        scope.setExtras(extras)
      }
      Sentry.captureMessage(message)
      await Sentry.flush(2000)
      resolve(true)
    })
  })

await Sentry.flush() tidak benar-benar selesai setelah panggilan captureMessage pertama.

Saya memiliki apa yang saya yakini sebagai masalah yang serupa dengan @enapupe. Jika Anda memanggil await client.flush(2000); secara paralel, hanya janji pertama yang diselesaikan. Ini dapat terjadi dalam konteks lambda AWS di mana klien digunakan kembali di antara beberapa panggilan serentak ke handler.

Saya menggunakan kode seperti ini:

 let client = Sentry.getCurrentHub().getClient();
  if (client) {
    // flush the sentry client if it has any events to send
    log('begin flushing sentry client');
    try {
      await client.flush(2000);
    } catch (err) {
      console.error('sentry client flush error:', err);
    }
    log('end flushing sentry client');
  }

Tetapi ketika saya melakukan dua panggilan ke fungsi lambda saya secara berurutan, saya mendapatkan:

  app begin flushing sentry client +2ms
  app begin flushing sentry client +0ms
  app end flushing sentry client +2ms

Anda dapat melihat bahwa janji kedua tidak pernah diselesaikan.

@esetnik Saya telah mengajukan masalah tentang itu: https://github.com/getsentry/sentry-javascript/issues/2131
Solusi saya saat ini adalah wrapper flush fn yang selalu diselesaikan (berdasarkan batas waktu):

const resolveAfter = (ms: number) =>
  new Promise((resolve) => setTimeout(resolve, ms))

const flush = (timeout: number) =>
  Promise.race([resolveAfter(timeout), Sentry.flush(timeout)])

@enapupe Saya menambahkan catatan tentang solusi Anda di #2131. Saya percaya itu akan menyebabkan regresi kinerja pada flush bersamaan.

Jika ada yang mengalami masalah.
Ini bekerja dengan indah

@SarasArya @HazAT
Pertama-tama... Terima kasih telah berbagi solusi Anda! :)
Ada satu panggilan balik dari metode configureScope yang saya kira seharusnya dipanggil sebelum captureException tetapi itu tidak dilakukan di "utas" yang sama.
Tidak bisakah ini mengarah pada munculnya kondisi balapan?

@cibergarri Saya rasa tidak, terlihat sinkron bagi saya, jika Anda memiliki metode async di sana, maka akan ada kondisi balapan.
Pertimbangkan seperti .map of array bahwa hal yang sama terjadi di sini. Jika Anda memiliki masalah membungkus kepala Anda di sekitarnya. Saya harap itu membantu.

Ya, tidak apa-apa untuk melakukan itu

Pembaruan: Sentry sekarang mendukung penangkapan kesalahan otomatis untuk lingkungan Node/Lambda: https://docs.sentry.io/platforms/node/guides/aws-lambda/

Saya menggunakan @sentry/serverless seperti ini:

const Sentry = require("@sentry/serverless");
Sentry.AWSLambda.init({
  dsn: process.env.SENTRY_DSN,
  tracesSampleRate: 1.0,
  environment: appEnv
});

exports.main = Sentry.AWSLambda.wrapHandler(async (event, context) => {
     try{
           //my code
     }catch(error){
          Sentry.captureException(error);
          await Sentry.flush(3000);
     }

});

Ini tidak bekerja pada lambda.
Dalam pengujian saya, itu berfungsi tetapi dalam prod di mana ada banyak eksekusi bersamaan dan wadah digunakan kembali, itu hanya mencatat sekitar 10% dari jumlah total.

Ada saran?

@armado25723

Tolong beri tahu bagaimana ukuran Anda bahwa itu kehilangan peristiwa pengecualian? Apakah Anda memiliki contoh kode tentang bagaimana pengecualian yang hilang itu dilemparkan? Perlu lebih banyak konteks.

const Sentry = require("@sentry/serverless"); // "version": "5.27.3"
Sentry.AWSLambda.init({
  dsn: process.env.SENTRY_DSN,
  tracesSampleRate: 1.0,
  environment: appEnv
});
exports.main = Sentry.AWSLambda.wrapHandler(async (event, context) => {
     try{
           throw new Error('Test Error');
     }catch(error){
          Sentry.captureException(error);
          await Sentry.flush(3000);
     }
});

Apa yang terjadi?
Jika fungsi dipanggil beberapa kali dengan interval pendek antara pemanggilan, acara hanya dicatat sekali.
Jika interval waktu antara pemanggilan lebih besar, semua kejadian dicatat.

Saya berasumsi bahwa masalahnya adalah ketika doa melewati wadah yang digunakan kembali.

saya juga sudah coba
await Sentry.captureException(error);
dan:
await Sentry.flush();
dan tanpa pembilasan
hasil yang sama

@marshall-lee apa yang Anda rekomendasikan? Haruskah saya membuat masalah, saya terjebak di sini.

@ armando25723 Sepertinya server merespons dengan 429 (terlalu banyak pengecualian) saat mengirim acara ini. Kami membuangnya jika terjadi skenario pembatasan kuota/tarif. Apakah Anda tahu jika Anda mengirim kesalahan secara berurutan atau melebihi kuota? Kami dapat melakukan debug lebih lanjut jika menurut Anda ini adalah peristiwa kesalahan nyata yang dijatuhkan dan Anda berada di bawah batas 5k kami untuk tingkat gratis.

@ajjindal semua proyek lain bekerja dengan baik dengan penjaga. Siput organisasi adalah "alegra", nama proyek adalah mail-dispatch-serverless di bawah #mail-micros. Kami telah menggunakan penjaga untuk waktu yang lama, tetapi pertama kali dengan tanpa server. Ini bukan tingkat gratis, saya tidak dapat memberi tahu Anda dengan tepat paket mana yang kami gunakan tetapi ini adalah paket berbayar.
Akan lebih baik jika Anda dapat membantu saya untuk men-debug lebih lanjut.
Terima kasih atas balasannya :)

PD: adalah Rencana Tim

Apakah halaman ini membantu?
0 / 5 - 0 peringkat