Sentry-javascript: [@ sentry / node] دعم AWS Lambda وحلول Serverless الأخرى

تم إنشاؤها على ٢٨ يوليو ٢٠١٨  ·  77تعليقات  ·  مصدر: getsentry/sentry-javascript

  • @ Sentry / node الإصدار 4.0.0-beta.11
  • أنا أستخدم تطبيق Sentry المستضاف

ما هو السلوك الحالي؟

أنا أستخدم @ sentry / node لالتقاط استثناء في وظيفة AWS lambda.

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

ومع ذلك ، فإنه يقتل العملية عندما يتم استدعاء context.fail() ولا ينتهي الأمر بالاستثناء في Sentry.

يمكنني إجراء حل بديل مثل:

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

ما هو السلوك المتوقع؟

سيكون من الرائع أن أفعل شيئًا مثل:

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

أو شيء يتعامل مع رد الاتصال عالميًا.

التعليق الأكثر فائدة

LinusU سننشئ على الأرجح حزمة محددة بدون خادم لهذا السيناريو. نحتاج فقط إلى تخصيص بعض الوقت ، لأننا في نهاية العام والأمور تزداد ازدحامًا الآن. حسنا ابقي ملصقك!

ال 77 كومينتر

قد يساعد هذا في تخمين https://blog.sentry.io/2018/06/20/how-droplr-uses-sentry-to-debug-serverless (إنه يستخدم إصدار الغراب القديم ، الذي كان له رد اتصال ، لكنني يشير في الغالب إلى علامة callbackWaitsForEmptyEventLoop .

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

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 شكرا لك على المؤشر. سأجربها وأعيد تشغيل ما تعلمته.

kamilogorek أنت اقتراح يعمل. إنني أتطلع إلى طريقة رسمية أكثر.

تضمين التغريدة
في 4.0.0-rc.1 قدمنا ​​وظيفة على العميل تسمى close ، تسميها على النحو التالي:

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 حتى يتم إرسال جميع الطلبات ، وسوف يتم حلها دائمًا (النتيجة = تم الوصول إلى المهلة الخاطئة) ، حتى الوصول إلى المهلة (2000 مللي ثانية في هذا المثال).
هذا هو API الرسمي لدينا.
بينما لا يزال النهج السابق يعمل ، تعمل طريقة close لجميع الحالات.

HazAT لطيف واحد. شكرا على كل العمل الشاق.

في 4.0.3 أسميها هكذا في دالة لامدا الخاصة بي:

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

getDefaultHub () لم يعد متاحًا.

vietbui يطلق عليه getCurrentHub الآن ، حيث كان علينا توحيد واجهة برمجة التطبيقات الخاصة بنا مع حزم SDK الخاصة باللغات الأخرى.

kamilogorek شكرا للتوضيح. هناك مشكلة في نهج getCurrentHub حيث إن النطاق الذي قمت بإعداده بطريقة ما لم ينتهي به المطاف في Sentry.

في النهاية ، اتبعت نهجًا مختلفًا كما اقترحه HazAT لالتقاط الاستثناء في وظائف lambda الخاصة بي:

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

وهو يعمل بشكل مثالي.

هل هذه هي الطريقة الموصى بها للانتظار / إجبار الحارس على إرسال الأحداث؟

albinekb نعم - https://docs.sentry.io/learn/draining/؟platform=browser

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

'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 Thats @ أيضًا في المستندات:

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

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

HazAT هل يمكننا إعادة فتح هذا ، من فضلك؟ أعتقد أنه من المهم أن يكون لديك طريقة للعمل مع هذا على Lambda ، والذي أصبح هدفًا شائعًا بشكل متزايد للانتشار فيه.

هذا يمنعني حاليًا من الترقية إلى أحدث إصدار ...

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

ما هو الأساس المنطقي لإزالة رد الاتصال من captureException ؟

albinekb لا يعمل على الإطلاق إذا قمت بإزالة السطر التالي

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

LinusU ما هو الحل والحارس أو الغراب الحل الذي تستخدمه؟

بالنسبة لي ، يعمل ما يلي بشكل أساسي مع sentry/node @4.3.0 ، لكن يجب أن أجعل lambda تنتظر يدويًا بعض الوقت (في هذه الحالة أضع ثانيتين) حتى يقوم الحارس بما يحتاج إلى القيام به. وأنا لست متأكدًا من سبب وجوب وجوده لأننا ننتظر الحارس لإنهاء طلب captureException . إذا لم تكن لدي فترة الانتظار بعد ذلك ، فلا يبدو أن الحارس يرسل الخطأ.

'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,
  }
};

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

أنا شخصياً أفضل نوعًا من flush() يفي بوعد - يصعب العثور على جانب سلبي. هل تعتقد أن هذا سيحدث؟

ما هو الحل والحارس أو الغراب الذي تستخدمه؟

أنا أستخدم معالج الخطأ السريع التالي:

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

ثم أستخدم scandium لنشر تطبيق Express في Lambda

تحرير: هذا مع Raven "raven": "^2.6.3",

سيكون حلم API شيء من هذا القبيل 😍

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

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

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

يبدو أن ما أريده حقًا هو شيء مثل:

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

من أجل تخطي كل قوائم الانتظار والتخزين المؤقت ...

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

هل من الممكن أن يكون لديك طريقة خاصة ترسل الحدث بأسرع ما يمكن ، وترجع Promise يمكننا await ؟ سيكون ذلك مثاليًا للسيناريوهات بدون خادم!

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 سننشئ على الأرجح حزمة محددة بدون خادم لهذا السيناريو. نحتاج فقط إلى تخصيص بعض الوقت ، لأننا في نهاية العام والأمور تزداد ازدحامًا الآن. حسنا ابقي ملصقك!

سنقوم على الأرجح بإنشاء حزمة محددة بدون خادم لهذا السيناريو

سيكون ذلك مذهلاً! 😍

ههههههههههه

متى بالضبط سأستخدم هذا الحل الأفضل؟ بقدر ما أعلم أنه من غير الممكن معرفة متى سيتم إيقاف تشغيل lambda - بالإضافة إلى أنه يبدو من السخف الانتظار لفترة زمنية تعسفية للإغلاق للسماح للحارس بالقيام بعمله - خاصةً في وظائف lambda ذات الذاكرة العالية / وحدة المعالجة المركزية باهظة الثمن.

(يتحدث عن استنزاف)

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

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

أيضًا LinusU ، أعيد قراءة تعليقك السابق ، وتحديداً هذا الجزء:

هل من الممكن أن يكون لديك طريقة خاصة ترسل الحدث بأسرع ما يمكن ، وتعيد الوعد الذي يمكننا انتظاره؟ سيكون ذلك مثاليًا للسيناريوهات بدون خادم!

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

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

هذا يعني أنك إذا فعلت شيئًا كهذا في AWS Lambda (بافتراض أنك تريد استخدام العميل الافتراضي ، وهو أبسط حالة):

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

يمكنك التأكد من أنه تم إرسالها على الفور ولم يكن هناك وقت / معالجة زائدة.

تضمين التغريدة
هل هذا يعني أن شيئًا كهذا يجب أن يعمل في معالج غير متزامن / انتظار (حيث لا تستخدم رد الاتصال)؟

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';
    }
};

تضمين التغريدة :)

يبدو أن التغييرات التالية ستعمل معي بعد ذلك

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

لأكون صريحًا ، أصبح كل سطر أقبح قليلاً لكني أعتقد أنه من الأفضل تحت الغطاء ...

kamilogorek لم أتمكن من العثور على getCurrentHub() في المستندات على موقع الويب الخاص بك ، فهل تضمن واجهة برمجة التطبيقات هذه عدم كسرها دون حدوث نتوء كبير؟ ❤️

kamilogorek لم أتمكن من العثور على getCurrentHub () في المستندات على موقع الويب الخاص بك ، فهل تضمن واجهة برمجة التطبيقات هذه عدم كسرها دون حدوث نتوء كبير؟ ❤️

نعم ، هذا مضمون. إنه جزء من حزمة @sentry/hub الموصوفة هنا - https://docs.sentry.io/enriching-error-data/scopes/؟platform=browser

نحن نناقش "الاستخدامات المتقدمة" كيندا هنا في هذا الموضوع ولم نصل إلى نقطة توثيقها بعد. سنفعل هذا في النهاية :)

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

تضمين التغريدة
هل هذا يعني أن شيئًا كهذا يجب أن يعمل في معالج غير متزامن / انتظار (حيث لا تستخدم رد الاتصال)؟

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';
    }
};

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

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

لن أرى في الواقع "بعض المعلومات الإضافية" في Sentry.

اقترح شخص ما طريقة بديلة في الجزء العلوي من هذا الموضوع ، وهذا ناجح ، لكن يبدو أنه متسلل (فرض مهلة).

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

تضمين التغريدة

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';
   }
};

هل يمكن تطبيق هذا أيضًا على استثناءات غير معلومة؟ يبدو أن تعديل تكامل Sentry.Integrations.OnUncaughtException هو الطريقة الرسمية للقيام بذلك ولكن التوثيق ضعيف جدًا في الوقت الحالي.

+1 لهذا. على الأقل وجود شيء موثق رسميًا سيكون جيدًا. ينمو Serverless بسرعة اعتبارًا من عام 2019 ، وأريد حقًا رؤية الدعم الرسمي من Sentry عليه. كانت إحدى الأفكار التي قرأتها هنا وأعجبتني حقًا هي الحصول على شيء مثل Sentry.flush() لإرسال جميع الأحداث الموجودة في قائمة الانتظار.

rdsedmundo هل يمكنك توضيح سبب عدم نجاح هذا النهج؟

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

هذا هو نهجنا الرسمي وبشكل أساسي Sentry.flush() .
المرجع: https://docs.sentry.io/error-reporting/configuration/draining/؟

HazAT تأتي مشكلة ذلك عندما تفكر في إعادة استخدام حاوية AWS Lambda. وهو ما يعني في مصطلحات TL ؛ DR أن العملية التي قدمت طلبًا للتو يمكن أن تخدم علامة تجارية جديدة إذا تم إجراؤها في فترة زمنية قصيرة. إذا أغلقت الاتصال بهذا المقتطف الذي قدمته ، وأعيد استخدام الحاوية ، فسوف أحتاج إلى إدارة إنشاء محور جديد للطلب الجديد. أستطيع بسهولة أن أرى هذا يصبح معقدًا. هذا هو السبب في أن await Sentry.flush() البسيط سيكون حلاً أفضل:

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 لست متأكدًا مما إذا كنت قد أسأت فهم شيء ما ولكن إذا فعلت ذلك

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

إنه تمامًا مثل await Sentry.flush فقط الذي تحدد المهلة.

يتم حل الوعد بعد 2000 مللي ثانية بالتأكيد مع false إذا كان لا يزال هناك أشياء في قائمة الانتظار.
وإلا فسيتم حل close مع true إذا تم استنزاف قائمة الانتظار قبل الوصول إلى المهلة.

أم سيتم إعادة استخدام الحاوية قبل حل جميع الوعود؟ (لا أستطيع تخيل ذلك)

HazAT أليس مشكلة أن close(...) سيمنع استخدام العميل مرة أخرى؟ تعيد Lambda استخدام نفس عملية Node بحيث تكون المكالمات شيئًا من هذا القبيل ، والتي أعتقد أنها ستتوقف عن العمل بعد المكالمة الأولى إلى 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()
  • ...

لا ، close لا يتخلص من العميل ، إنه هنا فقط لاستنزاف قائمة انتظار النقل.
أوافق على أن الاسم close في هذا السياق قد يكون مضللًا ولكن على الأقل في JS / Node close لا يفعل أي شيء مع العميل ومن الجيد تمامًا الاستمرار في استخدامه بعد ذلك.

تعديل: إذا كانت هذه هي "المشكلة" فعليًا ، فسأحدِّث المستندات لتوضيح ذلك.

رائع. لكن التوثيق خاطئ إذن:

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

حسنًا ، لقد ناقشنا للتو هذا الأمر داخليًا في الفريق.
لقد كنتم على حق ، وبينما لا تتصرف JavaScript الآن بالطريقة التي وثقناها بها - سنقدم وظيفة flush والتي ستفعل بالضبط ما تتوقعه.

لذا يمكنك الآن استخدام close بدون أي مشاكل (لست متأكدًا مما إذا كنا سنغيره للتخلص من / تعطيل العميل في المستقبل).
ولكن ستكون هناك وظيفة flush موجودة لقائمة الانتظار _ just_ flush .

سوف أقوم بتحديث هذه المشكلة بمجرد هبوط الميزة.

نظرًا لأنني فقدت بعض الشيء في كل هذه التعليقات ، فهل هذه هي الطريقة التي يجب أن يبدو بها معالج الخطأ السريع (محاكاة ذلك الموجود في هذا الريبو)؟

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

يبدو أنه يعمل ولا يفقد بيانات إضافية كما هو الحال في كودrreynier .

أنا شخصياً أشعر بذلك

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

أنظف من:

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

close يقرأ حقًا كما لو أنه سيغلق العميل ...

مثال كامل:

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 لقد جربت ذلك ولسبب ما ، لا يرسل بيانات إضافية مع تتبع المكدس. يرسل أساسا مجرد كومة تتبع. لا توجد معلومات حول المستخدم أو نظام التشغيل أو أي شيء.

آها ، هذا ليس جيدًا على الإطلاق

بينما ننتظر flush ، كحل بديل أكثر موثوقية من كلا الخيارين المذكورين أعلاه ، يمكنك الإبلاغ عن النتيجة وانتظارها ، _ و_ تضمين النطاق ، باستخدام المقتطف أدناه:

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

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

أنا أستخدم كل هذا في الواقع مع وظائف Netlify ، لكن النظرية هي نفسها مع Lambda وما إلى ذلك. لقد كتبت منشورًا يحتوي على التفاصيل الكاملة لكيفية تشغيل هذا ، إذا كان أي شخص مهتمًا: https: // HTptoolkit. tech / blog / netlify-function-error-report-with-sentry /

أنا أستخدم هذا المساعد في جميع حيوانات لامبدا الخاصة بي حاليًا.

@ pimterry أليس هذا هو الحل نفسه الذي اقترحه LinusU ؟ لقد جربته ولا يرسل بيانات إضافية أيضًا.

لقد نجح هذا النهج بالنسبة لي حتى الآنondrowan

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

تضمين التغريدة _ _ _ _ _ _ _ _ _

بدءًا من 4.6.0 ، لم يعد هناك رقص client/hub . يمكنك فقط الاتصال بأي طريقة captureX الخاصة بنا ثم استخدام Sentry.flush() لانتظار الرد بمجرد إرسال كل شيء إلى الخادم. يجب الاحتفاظ بجميع بيانات النطاق / البيانات الإضافية دون أي تفاعل مطور.

فيما يلي مثال للطلبات الناجحة / التي انتهت مهلتها.

image

أتمنى أن يساعد! :)

لطيف!

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

LinusU نأمل نعم ، لكننا غارقون في لغات أخرى SDKs الآن 😅

شكرًا لكم جميعًا على جميع الحلول الممكنة ، TL ؛ دكتور لجميع القادمين إلى هنا

استخدم: await Sentry.flush() لإرسال جميع الطلبات المعلقة ، وقد تم تقديم هذا في 4.6.x .

عند إغلاق هذا ، لا تتردد في فتح إصدار جديد في حالة فقد أي شيء (ولكن هذا الموضوع طويل جدًا بالفعل).

في صحتك 👍 🎉

تضمين التغريدة لمعلوماتك سريعة ، أستخدم Sentry.flush في تطبيقي بدلاً من الحل البديل القديم ولم يتم الإبلاغ عن أي من الأخطاء. أعود حاليًا إلى الحل القديم من طريقة flush المحدّثة .

zeusdeux ، هل هناك أي طريقة يمكنك من خلالها تقديم بعض معلومات التصحيح / حالة إعادة المحاولة لهذا الغرض؟
أنت تتجاوز طريقة captureException التي تضيف الحدث إلى المخزن المؤقت ، ومن ثم يجب عليك await على القيمة المرجعة flush . هل حاولت استخدامه "بالطريقة العادية"؟

kamilogorek أتمنى لو كان لدي معلومات تصحيح الأخطاء ولكن لا يوجد شيء في السجلات. كنت دائمًا أفعل await على التجاوز captureException . بالطريقة المعتادة ، هل تقصد بدون تجاوز captureException ؟

zeusdeux بالضبط ، ما عليك سوى الاتصال بـ Sentry.captureException(error) الأصلي دون أي تجاوز.

لذلك سيكون مساعدك:

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

وفي الكود تسميه:

import * as Sentry from '@sentry/node'

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

kamilogorek سأعطيه ذهاب وإبلاغ. أيضًا ، شكرًا على الإكرامية على beforeSend ^ _ ^

await Sentry.flush(2000);

هو أيضا ~ لا ~ يعمل بالنسبة لي.

tanduong هل يمكنك تقديم حالة repro؟ مجرد القول بأنها لا تعمل ليس مفيدًا جدًا 😅

kamilogorek في الواقع ، اكتشفت ذلك للتو

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

لا تعمل معي إما لأن وظيفة lambda الخاصة بي متصلة بـ VPC.

أؤكد ذلك

await Sentry.flush(2000);

يعمل بالفعل.

راجع للشغل ، فكيف ستتعامل مع لامدا في VPC؟ هل تريد إرفاق بوابة NAT؟ أريد فقط Sentry ولكن ليس الإنترنت العام.

tanduong Sentry موجود على الإنترنت العام ، لذا نعم تحتاج إلى أن يكون لديك بوابة NAT إذا كان lambda الخاص بك يعمل داخل VPC الخاص بك. وإلا فسيتعين عليك استكشاف خيار Sentry المستضاف.

ما الذي يفعله flush(2000) في الواقع؟ كان لدي هذا الرمز يعمل بشكل جيد في الغالب ولكن لدي الآن مكالمتين captureMessage تحدث في نفس الوقت ، تنتهي المهلة في كل مرة!

مسح قائمة الانتظار الداخلية للرسائل عبر السلك

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

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() لا ينتهي حقًا بعد أول مكالمة لـ captureMessage.

لدي ما أعتقد أنه مشكلة مماثلة لـenapupe. إذا اتصلت بـ await client.flush(2000); بالتوازي ، فإن الوعد الأول فقط هو الذي يتم الوفاء به. يمكن أن يحدث هذا في سياقات AWS lambda حيث تتم إعادة استخدام العميل بين عدة مكالمات متزامنة إلى المعالج.

أنا أستخدم كود مثل هذا:

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

لكن عندما أجري مكالمتين لوظيفة لامدا الخاصة بي بتتابع سريع ، أحصل على:

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

يمكنك أن ترى أن الوعد الثاني لم يُحسم أبدًا.

esetnik لقد قدمت مشكلة بهذا الشأن: https://github.com/getsentry/sentry-javascript/issues/2131
الحل البديل الخاص بي هو تدفق للغلاف يتم حله دائمًا (بناءً على المهلة):

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

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

enapupe لقد أضفت ملاحظة حول الحل الخاص بك في # 2131. أعتقد أنه سيؤدي إلى تراجع في الأداء عند التدفق المتزامن.

في حال كان أي شخص لديه أي مشاكل.
هذا يعمل بشكل جميل

تضمين التغريدة
بادئ ذي بدء ... شكرا لتقاسم الحل الخاص بك! :)
هناك رد اتصال واحد لطريقة configScope التي أعتقد أنه من المفترض أن يتم استدعاؤها قبل captureException ولكن لا يتم إجراؤها في نفس "مؤشر الترابط".
ألا يمكن أن يؤدي هذا إلى ظهور ظروف السباق؟

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

نعم ، من الجيد تمامًا القيام بذلك

تحديث: يدعم تطبيق Sentry الآن التقاط الأخطاء تلقائيًا لبيئات Node / Lambda: https://docs.sentry.io/platforms/node/guides/aws-lambda/

أنا أستخدم @ sentry / serverless مثل هذا:

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

});

لا يعمل على لامدا.
في بيئة الاختبار الخاصة بي ، كان يعمل ولكن في حالة وجود الكثير من عمليات الإعدام المتزامنة والحاويات التي يتم إعادة استخدامها ، يتم تسجيل حوالي 10 ٪ فقط من المبلغ الإجمالي.

اي نصيحه؟

امين

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

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

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

أفترض أن المشكلة هي عندما يكون الاستدعاء على حاوية معاد استخدامها.

لقد حاولت أيضا
await Sentry.captureException(error);
و:
await Sentry.flush();
وبدون التنظيف
نفس النتيجة

@ marshall-lee ماذا تنصح؟ هل يجب أن أقوم بإنشاء مشكلة ، فأنا عالق هنا.

@ armando25723 يبدو أن الخادم يستجيب مع 429 (استثناءات كثيرة جدًا) أثناء إرسال هذه الأحداث. نحن نرمي ذلك في حالة تجاوز السيناريوهات المحددة للحصة / المعدل. هل تعلم ما إذا كنت ترسل أخطاء بالتسلسل أو تزيد عن الحصة؟ يمكننا تصحيح الأخطاء بشكل أكبر إذا كنت تعتقد أن هذه أحداث خطأ حقيقية تم إسقاطها وأنت أقل من حدنا البالغ 5 كيلو بايت للطبقة المجانية.

ajjindal جميع المشاريع الأخرى تعمل بشكل جيد مع الحارس. سبيكة المنظمة هي "alegra" ، اسم المشروع هو mail-dispatch-serverless تحت # mail-micros. لقد استخدمنا الحارس لفترة طويلة ، ولكن لأول مرة بدون خادم. هذه ليست فئة مجانية ، لا يمكنني إخبارك بالضبط بالخطة التي نستخدمها ولكنها خطة مدفوعة.
سيكون من الرائع أن تتمكن من مساعدتي في إجراء المزيد من التصحيح.
شكرا على الرد : )

PD: هي خطة الفريق

هل كانت هذه الصفحة مفيدة؟
0 / 5 - 0 التقييمات