Next.js: كيفية اكتشاف الأخطاء ومعالجتها للإبلاغ عن السجلات على جانب الخادم

تم إنشاؤها على ٢ مايو ٢٠١٧  ·  74تعليقات  ·  مصدر: vercel/next.js

مرحبا،
أنا في موقف نريد إرسال أخطاء فيه ، على جانب الخادم والعميل ، إلى أداة Sentry.

يستخدم تطبيقنا Express كخادم مخصص. في الأساس ، ننشئ تطبيقًا سريعًا ، ونطبق بعض البرامج الوسيطة ولكن نفوض كل الوظائف الحقيقية إلى مقبض next.js:

  const app = nextJs({ dev: process.env.NODE_ENV !== 'production' });
  const handler = routes.getRequestHandler(app);
  const expressApp = express();

  ...
  ...

  expressApp.use(morgan('combined', { stream: logger.stream }));
  expressApp.use(statsdMiddleware);

  // Add security
  expressApp.use(helmet());

  // Sentry handler
  expressApp.use(sentry.requestHandler());

  // Load locale and translation messages
  expressApp.use(i18n);

  // Next.js handler
  expressApp.use(handler);

  // Sentry error handler.
  // MUST be placed before any other express error handler !!!
  expressApp.use(sentry.errorHandler());

باستخدام هذا الأسلوب ، يتحكم next.js في عملية العرض ويتم التقاط أي خطأ بواسطة next.js والطريقة الوحيدة التي يجب معالجتها هي تجاوز ملف الصفحة _error.js .

ضمن هذا الملف _error.js ، أحتاج إلى طريقة عالمية للإبلاغ عن الأخطاء إلى Sentry. يوجد حاليًا مكتبتان ( raven للعقدة و raven-js por javascript). تكمن المشكلة في أنه لا يمكنني استيراد كلاهما لأن raven يعمل مع SSR ولكنه يفشل عندما يبني webpack الحزمة ، ويفشل أيضًا raven-js بسبب تبعية XMLHTTPRequest أيضًا.

هل هناك طريقة لإخطاري بخطأ next.js على الخادم؟

story feature request

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

فلدي الحب _ unortodox_ الحلول

function installErrorHandler(app) {
  const _renderErrorToHTML = app.renderErrorToHTML.bind(app)
  const errorHandler = rollbar.errorHandler()

  app.renderErrorToHTML = (err, req, res, pathname, query) => {
    if (err) {
      errorHandler(err, req, res, () => {})
    }

    return _renderErrorToHTML(err, req, res, pathname, query)
  }

  return app
}
// ¯\_(ツ)_/¯

ال 74 كومينتر

لتسجيل الأخطاء من جانب العميل ، قمنا بما يلي:
https://gist.github.com/jgautheron/044b88307d934d486f59ae87c5a5a5a0

يرسل بشكل أساسي أخطاء إلى الخادم ، والتي يتم طباعتها في النهاية على stdout وتم التقاطها بواسطة برنامج تشغيل التسجيل Docker الخاص بنا.
لقد اكتشفنا أخطاء SSR بنجاح باستخدام react-guard دون الحاجة إلى تجاوز ملفات Core Next.

أنا أيضا أواجه هذه المشكلة. أتساءل: هل سيكون مجرد إرجاع الوعد المرفوض من handleRequest كافياً؟ أي تغيير الرمز هنا ليكون:

handleRequest (req, res, parsedUrl) {
  // .....snip....

   return this.run(req, res, parsedUrl)
     .catch((err) => {
       if (!this.quiet) console.error(err)
       res.statusCode = 500
       res.end(STATUS_CODES[500])

       // rethrow error to create new, rejected promise 
       throw err;
     })
}

ثم في كود المستخدم:

const app = nextJs({ dev: process.env.NODE_ENV !== 'production' });
const nextJsHandler = app.getRequestHandler();
const expressApp = express();

app.prepare().then(() => {
   // invoke express middlewares
   // ...

   // time to run next
   expressApp.use(function(req, res, next) {
     nextJsHandler(req, res).catch(e => {
       // use rejected promise to forward error to next express middleware
       next(e)
     })
   });

   // Use standard express error middlewares to handle the error from next
   // this makes it easy to log to sentry etc.
})

arunodarauchg هل تعتقد ان التغيير I المقترحة أعلاه مباشرة العمل؟ إذا كان الأمر كذلك ، يسعدني تقديم PR

توافق على إعادة طرح الخطأ حتى نتمكن من التلاعب به.
تحتاج أيضًا إلى إعادة طرح renderToHTML أيضًا ...

أنا أيضًا في موقف نريد إرسال أخطاء فيه ، على جانب الخادم والعميل ، إلى خدمة مشابهة لخدمة Sentry.

أعتقد أن الميزة الأكثر قيمة لهذه الخدمات / الأدوات هي الإبلاغ عن المشكلات غير المتوقعة ، والتي في تجربتي هي أخطاء لم يتم اكتشافها في البرية (أي من جانب العميل). لسوء الحظ ، كما تم التأكيد سابقًا في المشكلة ذات الصلة رقم 2334 ، فإن معالجات جانب العميل في Next.js يحتفظون بهذه الأخطاء لأنفسهم ، مع عدم وجود طريقة ممكنة لتمريرها إلى Sentry من هذه الأداة الأخرى.

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

فيما يلي نموذج لصفحة توضح المشكلة:

import React from 'react';

// Works well in Node 8, but crashes in Chrome<56, Firefox<48, Edge<15, Safari<10, any IE…
const whoops = 'Phew, I made it to the client-side…'.padEnd(80);

export default () => <pre>{whoops}</pre>;

يمكن عرض الكود أعلاه بشكل مثالي من جانب الخادم وتسليمه إلى العميل ، فقط ليصبح فلاش محتوى غير مرغوب فيه قبل أن يتم استبدال الصفحة بأكملها من جانب العميل برسالة مخيفة "حدث خطأ غير متوقع" في معظم المتصفحات ، دون أي طريقة ممكنة للإبلاغ عن الخطأ إلى Sentry (أو أي خدمة / أداة أخرى).

هذا "البلع خطأ" كما يمنع أي ضغط من مستوى onerror معالج، والتي معظم الخطأ من جانب العميل أدوات الإبلاغ هوك على (أو أكثر حداثة، ولكن ليس على نطاق واسع، onunhandledrejection ، والتي قد تكون أكثر ملاءمة بالنظر إلى طبيعة المتزامن من جانب العميل الشفرة).

حل محتمل من جانب العميل

بقدر ما أستطيع أن أقول ، يتم ابتلاع هذا النوع من أخطاء العميل قبل التفاعل في كتلة try / catch في عميل / index.js Next.js حيث Component يتم إعادة تعيين ErrorComponent (حاليًا الأسطر 67-72 ).

أعزائي مؤلفي Next.js والمشرفين عليه ، من أجل التحكم في ما يتم تقديمه ، ما الذي تعتقد أنه سيكون مقبولًا / ممكنًا من بين الأفكار التالية:

  • إدخال خطاف في كتلة catch في client / index.js للتعامل مع هذا النوع من الأخطاء؟
  • نقل الخطأ إلى معالج رفض خطأ / غير معالج ، إذا تم اكتشاف أي خطأ؟
  • توفير خيار لإعادة طرح الخطأ؟
  • توفير خيار لعدم عرض مكون الخطأ؟
  • توفير خيار لعرض مكون خطأ مخصص؟

إدخال خطاف في كتلة الصيد هذه في client / index.js للتعامل مع هذا النوع من الخطأ؟
نقل الخطأ إلى معالج رفض خطأ / غير معالج ، إذا تم اكتشاف أي خطأ؟

هذا شيء تحدثنا عنه داخليًا. وسنتحدث قريبًا.

أنا أستخدم Sentry.io للإبلاغ عن الأخطاء والحل الذي نطبقه هو:

1- تكوين raven-node من جانب الخادم
2- قم بتكوين ravenjs من جانب العميل (قمنا بذلك على _document .

من خلال هاتين الخطوتين ، نكتشف أي استثناءات لم تتم معالجتها على كل من العميل والخادم.

3- أنشئ صفحة _error . يتم عرض أي خطأ بمجرد معالجة nextjs للطلب (بغض النظر عما إذا كان العميل أو الخادم) يتم عرض هذه الصفحة. في طريقة getInitialProps لصفحة الخطأ _ قمنا بإبلاغ الحارس بالخطأ.

طريقة تحديد كيفية التحميل إذا تم حل استيراد raven-node أو ravenjs ديناميكيًا اعتمادًا على ما إذا كنا في العميل const Raven = require('raven-js'); أو جانب الخادم const Raven = require('raven'); .
لاحظ أننا قمنا بتكوين حزمة الويب على عدم تجميع الوحدة النمطية raven (جانب الخادم الأول) مع تحديث next.config.js بـ:

const webpack = require('webpack');

module.exports = {
  // Do not show the X-Powered-By header in the responses
  poweredByHeader: false,
  webpack: (config) => {
    config.plugins.push(new webpack.IgnorePlugin(/^raven$/));
    return config;
  },
};

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

2- قم بتكوين ravenjs من جانب العميل (قمنا بذلك في _document.

هل يمكنك أن تريني كيف قمت بتكوين raven-js على _document.js ؟ إنه لا يعمل بالنسبة لي ، عندما يحدث أي خطأ لا يحدث شيء على الحارس.

هل أحتاج إلى إرسال جميع الأخطاء يدويًا على صفحة _error.js إلى الحارس؟

// _document constructor
constructor(props) {
    super(props);
    Raven
      .config('...')
      .install();
}

لا يزال Next.js ينتج أخطاء إلى console.error على الخادم طالما أنك لم تضبطه على الوضع الصامت .

باستخدام Sentry ، يمكنك تمكين autoBreadcrumbs لالتقاط هذا الناتج ، ثم التقاط رسالتك يدويًا. سيكون العنوان أقل وصفيًا ، لكنه سيظل يحتوي على تتبع المكدس الكامل.

مثال على التنفيذ:

const express = require('express');
const nextjs = require('next');
const Raven = require('raven');

const dev = process.env.NODE_ENV !== 'production';

// Must configure Raven before doing anything else with it
if (!dev) {
  Raven.config('__DSN__', {
    autoBreadcrumbs: true,
    captureUnhandledRejections: true,
  }).install();
}

const app = nextjs({ dev });
const handle = app.getRequestHandler();

const captureMessage = (req, res) => () => {
  if (res.statusCode > 200) {
    Raven.captureMessage(`Next.js Server Side Error: ${res.statusCode}`, {
      req,
      res,
    });
  }
};

app
  .prepare()
  .then(() => {
    const server = express();

    if (!dev) {
      server.use((req, res, next) => {
        res.on('close', captureMessage(req, res));
        res.on('finish', captureMessage(req, res));
        next();
      });
    }

    [...]

    server.get('/', (req, res) => {
      return app.render(req, res, '/home', req.query)
    })

    server.get('*', (req, res) => {
      return handle(req, res)
    })

    server.listen('3000', (err) => {
      if (err) throw err
      console.log(`> Ready on http://localhost:${port}`)
    })
  })
  .catch(ex => {
    console.error(ex.stack);
    process.exit(1);
  });

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

بالطبع ، سيكون من الأفضل أن يقوم Next.js بتمرير هذه الأخطاء إلى Express ، حتى نتمكن من استخدام التكامل Sentry / Express خارج الصندوق.

tusgavomelo آسف على الرد المتأخر.

لدينا التحديث. في تطبيقنا ، لدينا ملف مساعد مع طريقة مسؤولة للحصول على مثيل Raven مع مراعاة ما إذا كنا على جانب العميل أو الخادم.

let clientInstance;
let serverInstance;

const getRavenInstance = (key, config) => {
  const clientSide = typeof window !== 'undefined';

  if (clientSide) {
    if (!clientInstance) {
      const Raven = require('raven-js');  // eslint-disable-line global-require
      Raven.config(key, config).install();
      clientInstance = Raven;
    }

    return clientInstance;
  }

  if (!serverInstance) {
    // NOTE: raven (for node) is not bundled by webpack (see rules in next.config.js).
    const RavenNode = require('raven'); // eslint-disable-line global-require
    RavenNode.config(key, config).install();
    serverInstance = RavenNode;
  }
  return serverInstance;
};

يعمل هذا الرمز على جانب الخادم (عند وصول الطلب) وجانب العميل. ما فعلناه هو تكوين حزمة الويب (ملف next.config.js ) لتجنب تجميع الحزمة raven .

acanimal ، هل يمكنك تقديم مثال عملي؟ يبدو أنني لا أحصل على تتبع المكدس الكامل؟ أو ربما يمكنك نشر الخاص بك _error.js ؟

أفعل شيئًا مثل RavenInstance.captureException(err) في _error.js ، لكن لا يمكنني رؤية نوع الأخطاء التي حدثت وأين؟

export default class Error extends React.Component {
  static getInitialProps({ res, err }) {
    const RavenInstance = getRavenInstance('__SENTRY__')
    if (!(err instanceof Error)) {
      err = new Error(err && err.message)
    }
    RavenInstance.captureException(err)
    // const statusCode = res ? res.statusCode : err ? err.statusCode : null;

    return { }
  }

  render() {
    return (
      <div>
        <p>An error occurred on server</p>
      </div>
    )
  }
}

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

تفاصيل الأخطاء متاحة أيضًا على تيار stderr.
process.stderr.write = error => yourErrorLog(error);

فلدي الحب _ unortodox_ الحلول

function installErrorHandler(app) {
  const _renderErrorToHTML = app.renderErrorToHTML.bind(app)
  const errorHandler = rollbar.errorHandler()

  app.renderErrorToHTML = (err, req, res, pathname, query) => {
    if (err) {
      errorHandler(err, req, res, () => {})
    }

    return _renderErrorToHTML(err, req, res, pathname, query)
  }

  return app
}
// ¯\_(ツ)_/¯

نسخة مضغوطة من كيفية الحصول على Raven الصحيح للعقدة والمتصفح بدون تهيئة Webpack المخصصة. مستوحى من تعليق acanimal

// package.json
"browser": {
    "raven": "raven-js"
}

// getRaven.js
const Raven = require('raven')

if (process.env.NODE_ENV === 'production') {
  Raven.config('YOUR_SENTRY_DSN').install()
}

module.exports = Raven

جوهر

تلخيص سريع لأي شخص يحقق في هذه المشكلة.

// pages/_error.js
import Raven from 'raven';
...
static async getInitialProps({ store, err, isServer }) {
   if (isServer && err) {
      // https://github.com/zeit/next.js/issues/1852
      // eslint-disable-next-line global-require
      const Raven = require('raven');

      Raven.captureException(err);
    }
    ...
// next.config.js
config.plugins.push(new webpack.IgnorePlugin(/^raven$/));

بفضل تعليق acanimal .

قم بتثبيت كل من raven (تجاهل مع webpack كما هو مقترح في التعليقات السابقة) و raven-js ، ثم أنشئ مساعدًا لإنشاء مثيل Raven المتماثل ، على سبيل المثال lib/raven.js

import Raven from 'raven-js';

// https://gist.github.com/impressiver/5092952
const clientIgnores = {
  ignoreErrors: [
    'top.GLOBALS',
    'originalCreateNotification',
    'canvas.contentDocument',
    'MyApp_RemoveAllHighlights',
    'http://tt.epicplay.com',
    "Can't find variable: ZiteReader",
    'jigsaw is not defined',
    'ComboSearch is not defined',
    'http://loading.retry.widdit.com/',
    'atomicFindClose',
    'fb_xd_fragment',
    'bmi_SafeAddOnload',
    'EBCallBackMessageReceived',
    'conduitPage',
    'Script error.',
  ],
  ignoreUrls: [
    // Facebook flakiness
    /graph\.facebook\.com/i,
    // Facebook blocked
    /connect\.facebook\.net\/en_US\/all\.js/i,
    // Woopra flakiness
    /eatdifferent\.com\.woopra-ns\.com/i,
    /static\.woopra\.com\/js\/woopra\.js/i,
    // Chrome extensions
    /extensions\//i,
    /^chrome:\/\//i,
    // Other plugins
    /127\.0\.0\.1:4001\/isrunning/i, // Cacaoweb
    /webappstoolbarba\.texthelp\.com\//i,
    /metrics\.itunes\.apple\.com\.edgesuite\.net\//i,
  ],
};

const options = {
  autoBreadcrumbs: true,
  captureUnhandledRejections: true,
};

let IsomorphicRaven = null;

if (process.browser === true) {
  IsomorphicRaven = Raven;
  IsomorphicRaven.config(SENTRY_PUBLIC_DSN, {
    ...clientIgnores,
    ...options,
  }).install();
} else {
  // https://arunoda.me/blog/ssr-and-server-only-modules
  IsomorphicRaven = eval("require('raven')");
  IsomorphicRaven.config(
    SENTRY_DSN,
    options,
  ).install();
}

export default IsomorphicRaven;

ثم يمكنك استخدامه في pages/_error.js الخاص بك وسيعمل على جانب الخادم والعميل.

import NextError from 'next/error';
import IsomorphicRaven from 'lib/raven';

class MyError extends NextError {
  static getInitialProps = async (context) => {
    if (context.err) {
      IsomorphicRaven.captureException(context.err);
    }
    const errorInitialProps = await NextError.getInitialProps(context);
    return errorInitialProps;
  };
}

export default MyError;

ها هي العلاقات العامة الخاصة بي من أجل برنامج Rollbar sourcemap wepback الإضافي https://github.com/thredup/rollbar-sourcemap-webpack-plugin/pull/56 مع دعم Next.js :)

tusgavomelo ، هل يمكنك توضيح كيفية الاستفادة من الخطأ الموجود في الدفق؟
"process.stderr.write = خطأ => yourErrorLog (خطأ)؛"

أين يجب أن نكتب هذا السطر من التعليمات البرمجية ، حتى يتم تسجيل الخطأ في وحدة تحكم Node؟

هذا هو جانب العميل فقط.
Rollbar.error('some error')

https://docs.rollbar.com/docs/javascript

@ teekey99 هل لديك حل مشابه لـ @sentry/browser ؟ ربما التحديث مع مثال الحارس؟

sheerun لقد كنت أستخدم raven و raven-js حتى الآن. أدرك أنه من المحتمل أن يتم إهمال هذه الميزات نظرًا لإضافة جميع الميزات الجديدة الآن إلى @sentry/node و @sentry/browser . الشيء هو أنني لم أستخدم هذه المكتبات الجديدة في مشاريعي حتى الآن ، لكنني سأحاول النظر فيها. إذا كان لدي مثال عملي سأعود به.

تم تحديث مثال مع الحارس مؤخرًا.

timneutkens أرى أنه لا يدعم أخطاء من جانب الخادم حيث تتم تهيئة Sentry داخل التطبيق حيث فات الأوان للقبض على أخطاء الخادم. ربما يستخدم الحل المناسب @sentry/node مكان ما

sheerun أنا أواجه نفس المشكلة. استخدام @sentry/node ليس بالأمر السهل. يقترح الملف التمهيدي لمثال Sentry هذا استخدام خادم مخصص ، وهو موجود بالفعل في التطبيق الذي أعمل عليه. لالتقاط الاستثناءات في خادم Express.js المخصص الخاص بنا ، تحتاج إلى إدراج خطأ برنامج وسيط لمعالج خطأ Sentry كأول خطأ في معالجة البرامج الوسيطة . إذا أدخلت معالج خطأ Sentry مباشرةً بعد المعالج التالي ، فهذا يعني أن الخطأ قد ابتلعه بالفعل من قبل تلك النقطة ولا يراه Sentry.

لقد كنت أستخدم @sentry/browser في getInitialProps من _error.js ويبدو أنه يعمل من جانب العميل والخادم. لا أعرف ما إذا كان من المفترض أن يكون لدى @sentry/browser دعم من جانب الخادم ، لكني أحصل على الأحداث إلى Sentry.

على الرغم من أنني أتصل أيضًا بـ Sentry.init() عبر @sentry/node في ملف إدخال خادم Express مخصص ، لذلك ربما يكون هذا هو وضع بعض الحالات العالمية التي يستخدمها SSR.

إليك خلاصة الإعداد الذي أستخدمه: https://gist.github.com/mcdougal/7bf001417c3dc4b579da224b12776691

مثير للاهتمام!

هناك بالتأكيد نوع من الحالة العالمية تحدث هنا (وهو أمر مخيف بعض الشيء ، وربما هش وغير مرغوب فيه). تطبيق التغييرات في _error.js :

  • __بدون__ تكوين Sentry في الخادم المخصص:

    • عند التشغيل في وضع التطوير أو الإنتاج (على سبيل المثال ، مع إنشاء تطبيقي) ، لا يتم إبلاغ Sentry بأي شيء عند حدوث خطأ من جانب الخادم ، ولكني أحصل على خطأ ReferenceError: XMLHttpRequest is not defined في سجلات الخادم

  • __بعد التكوين__ Sentry في الخادم المخصص:

    • يتم الإبلاغ عن استثناءات جانب الخادم الخاصة بي ، لكنني أيضًا أحصل على Error: Sentry syntheticException غريب مسجل في Sentry

سيكون من الجيد أن نفهم ما هو الحل الرسمي. يبدو أن مستندات Next's توصي باستخدام مكون <App> مخصص الآن ، وهذا ما يفعله مثال "with Sentry" ، لكن هذا يعمل فقط من جانب العميل.

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

مثال مع الحارس لا يعمل معي إماtimneutkens. يؤدي تشغيل المشروع واختباره باستخدام DSN الفعلي الخاص بي إلى تقديم 400 استجابة ولا يصل أي شيء إلى الحارس (الإصدار 7 من واجهة برمجة التطبيقات).

{"error":"Bad data reconstructing object (JSONDecodeError, Expecting value: line 1 column 1 (char 0))"}

mcdougal لم يعمل معي. لم يكن أمرًا رائعًا أن يكون لديك إعداد عالمي من جانب الخادم بحيث يصل بطريقة ما إلى العميل ، ولكن حتى مع هذا الاختراق ، سأحصل على رد 301 من الحارس.

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

أنا أستخدم @sentry/browser بنفس الطريقة التي يوصى بها في مثال with-sentry ، ويبدو أنه يرسل أخطاء من كل من الخادم والعميل إلى الحارس الخاص بي. ليس لدي أي تكوين خاص ، فقط نفس الكود مثل المثال.

Jauny هل أنت متأكد من أنه يرسل من الخادم؟ كيف اختبرت ذلك؟ يبدو أن الملف التمهيدي للمثال يشير إلى أنه لن يعمل على الخادم.

timrogers نعم بالفعل

يبدو أن المثال الحالي with-sentry لا يمسك بالأخطاء التي تم إلقاؤها في getInitialProps ، فقط تلك الموجودة في شجرة تقديم التطبيق .. في حالتي معظم الأخطاء من getInitialProps.

sheerun هل يمكنك تجربة حل mcdougal أعلاه؟ إنه ليس مثاليًا (أخطاء حراسة اصطناعية غريبة) ولكن لدي انطباع بأنه يحتوي على كل الأخطاء وأود أن أعرف ما إذا كان هذا غير صحيح. إذا كان هذا صحيحًا ، فمن المحتمل أن يحتاج مثال with-sentry إلى التحديث حتى يتمكن Next.js من تقديم المشورة بشأن كيفية القيام بذلك بشكل أفضل (من الأفضل جعله لا يتم تخطي معالج خطأ الحارس server.js؟).

يبدو أنه يعمل مع مسألتين رئيسيتين:

  1. ربما لأنه يتم تجميع التعليمات البرمجية غير المتزامنة من جانب الخادم بشكل غير ضروري في رمز المُجدد ، غالبًا ما يتم اقتطاع تكديس جانب الخادم في الإنتاج في Internal / process / next_tick.js ولا يظهر أي سبب للخطأ على الإطلاق.
  2. لن تعمل معالجة الأخطاء إذا ظهرت أخطاء قبل استدعاء معالج الطلب التالي (على سبيل المثال في رمز الخادم المخصص)

في الواقع ، بعد إجراء مزيد من الاختبارات ، لا يتم تشغيل getInitialProps من خطأ مخصص لسبب ما في الإنتاج عند حدوث خطأ على سبيل المثال داخل getInitialProps المخصص لتطبيق _app.

نعم ، لقد تلقيت بالتأكيد بعض السلوكيات الغريبة بعد الجري مع محاولتي لبضعة أيام. يبدو أن المشاكل الرئيسية التي نواجهها هي:

  1. استيراد @sentry/browser لـ CSR و @sentry/node لـ SSR
  2. العثور على مكان لوضع معالجة الأخطاء التي يتم تشغيلها دائمًا لكل من CSR و SSR

لقد كنت أفكر في محاولة استخدام الواردات البطيئة والحالة العالمية لحل #1 ، شيء من هذا القبيل

const sentryInitialized = false;

# Inside some trigger point
const Sentry = ssr ? eval(`require('@sentry/node')`) : eval(`require('@sentry/browser')`);
if (!sentryInitialized) {
  Sentry.init({dsn: SENTRY_DSN});
}
Sentry.captureException(err);

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

بقدر مشكلة #2 ، يبدو أن الاحتمالات هي:

  1. _app.componentDidCatch ، والذي لا يتم إطلاقه من أجل SSR
  2. _error.getInitialProps والذي به العديد من المشاكل
  3. ...شيء آخر؟ هل هناك أي شيء يمكننا القيام به لـ SSR في وظيفة معالج الخادم التالي؟

شعوري الغريزي هو أنه ستكون هناك طريقة للقيام بذلك على الخادم عن طريق حقن معالج خطأ Sentry قبل Next ، لكنني لم أجربه بعد.

في الوقت الحالي ، لا يوفر Next أي خطافات لمساعدتك في القيام بذلك. إذا وجدنا أن هذا يعمل ، فيمكننا إضافتهم 👌

هناك خطر من أن هذا لن ينجح لأن Next يصطاد مبكرًا للغاية ، على الرغم من ذلك.

mcdougal Ah & #!٪ كنت سعيدًا جدًا عندما ربما يكون الحل الخاص بك جيدًا بما يكفي للتحقق من تغطية العميل / الخادم الحارس المطلوبة

عفوا عن جهلي الكامل ولكن هل نحتاج فقط بعد ذلك للسماح لنا بتعطيل معالج الأخطاء الخاص به بشكل مشروط بحيث يصبح معالج أخطاء الحارس nodejs هو الأول؟ بعض العلامات في next.config.js تسمى "disableErrorHandler"؟

Enalmada لا أعتقد أنك تريد تعطيل معالج الخطأ التالي لأنه سيكون هو الذي يعرض صفحة خطأ لطيفة. أنت فقط تريد إدراج برمجيات وسيطة أخرى قبله. أعتقد أن هذا سينجح ، لكني بحاجة إلى تجربته.

حتى مع هذا الإصلاح ، ما زلت لا أشعر أن معالجة الأخطاء من جانب العميل تعمل كما أتمنى :(

هذه المشكلة برمتها مخزية وهي حقًا مانع لتشغيل Next بأمان في الإنتاج.

لمعلوماتك ، أستورد في كل مكان @sentry/node وأضع ما يلي في next.config.js:

        if (!isServer) {
          config.resolve.alias['@sentry/node$'] = '@sentry/browser'
        }

والتي يمكن أن تكون أفضل من eval من mcdougal

فيما يلي ملاحظاتي الإضافية حول مكون _app المخصص ومعالجة الأخطاء:

  1. يُستخدم _app.js لجميع الصفحات ، بما في ذلك _error.js أو صفحات مثل 404 ، لذا فأنت تريد حقًا التأكد من عدم ظهور أي خطأ عند تمرير ctx.err إليها ..
  2. لا تنس استدعاء Component.getInitialProps في getInitialProps من التطبيق لأنه سيمنع استدعاء getInitialProps من _error.js (أطلق عليه حتى إذا كان ctx.err موجودًا)
class MyApp extends App {
  static async getInitialProps (appContext) {
    const { Component, ctx } = appContext
    if (ctx.err) {
      if (Component.getInitialProps) {
        pageProps = await Component.getInitialProps(ctx)
      }
      return { error: true, pageProps }
    }
    // here code that can throw an error, and then:
    if (Component.getInitialProps) {
      pageProps = await Component.getInitialProps(ctx)
    }
    return { pageProps }
  }
  render() {
    if (this.props.error) return super.render()
    // rest of code that can throw an error
  }
}

حتى الآن أجد أن إعداد الإبلاغ عن الأخطاء بشكل صحيح في next.js إجراء هش للغاية :(

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

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

هل هناك تحديث لهذه القضية؟ ما جربته حتى الآن هو ما يلي: لقد نقلت كل شفرة التتبع الخاصة بنا إلى _app.js

constructor(args: any) {
        super(args)
        Sentry.init({
            dsn: 'blah',
            environment: 'local',
        })
        Sentry.configureScope(scope => {
            scope.setTag('errorOrigin', isServer ? 'SSR' : 'Client')
        })
    }
    static async getInitialProps({ Component, router, ctx }: any) {
        let pageProps = {}

        try {
            if (Component.getInitialProps) {
                pageProps = await Component.getInitialProps(ctx)
            }
        } catch (error) {
            // console.log('we caught an error')
            console.log(error)
            Sentry.captureException(error)
            throw error
        }

        return { pageProps }
    }

إلى جانب الإضافة next.config.js من sheerun وتهيئة الحارس في server.js أيضًا if (!isServer) { config.resolve.alias['@sentry/node$'] = '@sentry/browser' }
يبدو أن هذا يتتبع جميع الأخطاء من جانب العميل ، ولكن على جانب الخادم يبدو أنه يتتبع الخطأ الأول الذي يحدث بعد إعادة تشغيل الخادم. لا يتم تعقب الأخطاء اللاحقة على الخادم بالرغم من ذلك. مع هذا النهج ، ليس لدي أي أخطاء اصطناعية في السجل ، ولكن هناك أخطاء حقيقية فقط.

لا يزال هذا يبدو مخادعًا تمامًا بالنسبة لي وبما أن التتبع من جانب الخادم يعمل فقط في المرة الأولى فإنه لا يزال غير قابل للاستخدام.

لقد أضفت أيضًا هذا الجزء من مثال with-Sentry

    componentDidCatch(error: any, errorInfo: any) {
        // if (process.env.FIAAS_NAMESPACE !== undefined) {
        Sentry.configureScope(scope => {
            Object.keys(errorInfo).forEach(key => {
                scope.setExtra(key, errorInfo[key])
            })
        })
        Sentry.captureException(error)
        console.log('componentDidCatch')

        // This is needed to render errors correctly in development / production
        super.componentDidCatch(error, errorInfo)
        // }
    }

لكني لست متأكدًا تمامًا مما إذا كانت هناك حاجة إلى ذلك

في حالتي مع يعمل بدون مشاكل. كما يجب عليك عدم بدء الحارس
_app.js المنشئ ولكن خارج هذه الفئة تمامًا

يوم الأربعاء 21 نوفمبر 2018 الساعة 2:53 مساءً كتب abraxxas [email protected] :

هل هناك تحديث لهذه القضية؟ ما جربته حتى الآن هو ما يلي:
نقل كل شفرة التتبع الخاصة بنا إلى _app.js

"
المُنشئ (args: any) {
سوبر (أرغس)
Sentry.init ({
dsn: "بلاه" ،
البيئة: "محلية" ،
})
Sentry.configureScope (النطاق => {
range.setTag ('errorOrigin' ، isServer؟ 'SSR': 'Client')
})
}

getInitialProps غير متزامن ثابت ({Component، router، ctx}: أي) {
دع pageProps = {}

try {
    if (Component.getInitialProps) {
        pageProps = await Component.getInitialProps(ctx)
    }
} catch (error) {
    // console.log('we caught an error')
    console.log(error)
    Sentry.captureException(error)
    throw error
}

return { pageProps }

}

"

إلى جانب الإضافة next.config.js منsheerun
https://github.com/sheerun وتهيئة الحارس في server.js أيضًا إذا
(! isServer) {config.resolve.alias ['@ sentry / node $'] = '@ sentry / browser'}
يبدو أن هذا يتتبع جميع الأخطاء من جانب العميل ، ولكن من جانب الخادم
يبدو أنه يتعقب الخطأ الأول الذي يحدث بعد إعادة تشغيل
الخادم. لا يتم تعقب الأخطاء اللاحقة على الخادم بالرغم من ذلك. مع هذا
النهج ليس لدي أي أخطاء SyntheticErrors في السجل ، ولكن أخطاء حقيقية فقط.

لا يزال هذا يبدو مخادعًا جدًا بالنسبة لي وبما أن التتبع من جانب الخادم هو
تعمل فقط في المرة الأولى التي لا تزال غير قابلة للاستخدام.

-
أنت تتلقى هذا لأنه تم ذكرك.
قم بالرد على هذا البريد الإلكتروني مباشرة ، وقم بعرضه على GitHub
https://github.com/zeit/next.js/issues/1852#issuecomment-440668980 ، أو كتم الصوت
الخيط
https://github.com/notifications/unsubscribe-auth/AAR2DeIhoOj6PdWRA2VqiEZyrO5Jui8vks5uxVrHgaJpZM4NOQlp
.

حاولت إخراجها بالفعل وما زلت نفس السلوك. sheerun ، هل يمكنك نشر فكرة بسيطة عن الإعداد الخاص بك؟ لقد حاولت إعداده باستخدام المقتطفات التي قدمتها ولا يمكنني تشغيلها. يبدو الأمر برمته معقدًا للغاية بالنسبة لما كنت أتوقع أن يكون مهمة بسيطة نوعًا ما: (هل تقوم أيضًا بتهيئة الحارس على الخادم أم فقط في _app.js خارج الفصل الدراسي؟

سأقوم بتحديث مثال الحارس الرسمي ولكنني أخشى أن يتم رفضه باعتباره "معقدًا للغاية" يمكنني محاولة نشر شيء ما على الرغم من أنني سأجد الوقت ..

sheerun حتى محاولة التحديث إلى المثال الرسمي ستكون ذات قيمة كبيرة. أشعر أنه سيتم دمجها إذا كان حقًا هو الحد الأدنى من التعقيد الضروري لتشغيل ssr الحارس بدون SyntheticErrors أو تسجيل خطأ الخادم الأول الذي يحدث فقط. ثم يمكننا أن نذهب من هناك لاكتشاف طرق لتحسينها أو الدفع من أجل تحسينات nextjs الأساسية أو دعم متماثل الحراسة.

والآن بعد أن أصبح لدينا مثال عملي معقد بالضرورة ، ما هي الخطوات التالية لتحسين الوضع:

الشيء الوحيد الذي يحتاجه المرء في next.js هو القدرة على إضافة برمجية وسيطة مخصصة في next.config.js وشيء مثل next.browser.js لتكوين البرنامج المساعد العالمي (isomorphic) (يتم استخدام next.config.js من بين أشياء أخرى لتهيئة webpack ، مما يعني أن الأشياء الأخرى المحددة في هذا الملف لا يمكن استخدامها في كود التطبيق ، لأنها قد تتسبب في تبعية دائرية).

بالنسبة لـ next.browser.js يمكن لـ next.js تحديد التكوين مثل مصمم التطبيق أو مكونات المستند. بهذه الطريقة يمكنني تنفيذ تكامل الحارس بالكامل كمكوِّن إضافي.

تحرير: لا أعرف ما إذا كان هناك تذكرة لهذا ، ولكن أعتقد أن timneutkens يقوم بالفعل باستخراج الخادم التالي في حزم منفصلة. أعتقد أن أفضل واجهة للمكوِّن الإضافي ستكون مثل:

module.exports = {
  server: server => {
    server.use(Sentry.Handlers.errorHandler())
  }
}

لقد نفذت اقتراح واجهة برمجة التطبيقات هذه في # 6922

يسمح بإضافة أدوات تزيين إلى كود الخادم المخصص لأنه يتم تغيير العقد إلى عقد لا يستدعي تلقائيًا .listen() لذلك يسمح لـ next.js بتزيينه بشكل أكبر قبل إنشاء مثيل له.

كنت أواجه الكثير من المتاعب عند استخدام المثال with-sentry ، لذلك فتحت العلاقات العامة لمثال أكثر بساطة. ليس بها كل الأجراس والصفارات ، لكنها كانت تعمل معي.

https://github.com/zeit/next.js/pull/7119

جرب هذا بالعرف _document.js :

import React from "react";
import Document, {
  Html,
  Head,
  Main,
  NextScript,
} from "next/document";
import { NodeClient } from "@sentry/node";

const { default: getConfig } = require("next/config");
const { publicRuntimeConfig: { sentryDSN } } = getConfig();

let sentry = null;
if (sentryDSN) {
  sentry = new NodeClient({ dsn: sentryDSN });
}

export default class MyDocument extends Document {
  static async getInitialProps(ctx) {
    const initialProps = await Document.getInitialProps(ctx);
    if (ctx.err && sentry) sentry.captureException(ctx.err);
    return { ...initialProps };
  }

  render() {
    return (
      <Html>
        <Head />
        <body>
          <Main />
          <NextScript />
        </body>
      </Html>
    );
  }
}

وهذا في مخصص _app.js :

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

const { default: getConfig } = require("next/config");
const { publicRuntimeConfig: { sentryDSN } } = getConfig();

if (sentryDSN) {
  Sentry.init({ dsn: sentryDSN });
}

يساعدني.

Tbh أنا في حيرة من أمري بشأن الأماكن الصحيحة للقبض على الأخطاء لـ next.js في الوقت الحالي. هناك _app.tsx و _error.tsx و _document.tsx ، وهناك بعض التعليقات التي تشير إلى أنه يجب ضبط بعض الأشياء في _error.tsx (مثل: https://github.com/zeit/next.js/pull/5727/files # r235981700) ولكن الأمثلة الحالية تستخدم إما _app.tsx أو _app و _document.

لقد جربت كلا الاتجاهين الآن ويبدو أن بعض الأخطاء التي تحدث أثناء SSR لم يتم اكتشافها بالنسبة لي. أي الأماكن هي الآن هي الأماكن الصحيحة للتحقق من وجود أخطاء؟ يبدو أن الأمر الأكثر منطقية بالنسبة لي هو التحقق من مكون componentDidCatch لـ _app و getInitialProps للخطأ حيث يتم تقديم هذا الخطأ في حالات الأخطاء. أنا مرتبك قليلاً بشأن الجزء الكامل process.on في _document

هل يمكن لشخص لديه معرفة أكثر عمقًا أن يحل هذا السؤال؟

abraxxas process.on في _document.js يكتشف الأخطاء التي تحدث على الخادم. كخلاصة ،

  • _document.js _ جانب الخادم فقط_ ويستخدم لتغيير ترميز المستند المعروض من جانب الخادم الأولي.
  • _app.js _ جانب العميل فقط_ ويستخدم لتهيئة الصفحات.

لذلك ، عندما يحدث خطأ على الخادم ، يجب أن يؤدي إلى إرسال خطأ إلى Sentry عبر process.on ثم يقوم العميل بعرض صفحة الخطأ الافتراضية أو _error.js .

نأمل أن يساعد هذا: https://leerob.io/blog/configuring-sentry-for-nextjs-apps/

_app.js ليس من جانب العميل فقط ، ولكن componentDidCatch

timneutkens حسنًا ، هذا تم تحديث المستندات منذ آخر مرة بحثت فيها. هل يمكن أن تشرح كيف أن _app.js ليس من جانب العميل فقط بمزيد من التفصيل؟

timneutkens ، هل يمكنك أن تشرح أين ولماذا

timneutkens حسنًا ، هذا تم تحديث المستندات منذ آخر مرة بحثت فيها. هل يمكن أن تشرح كيف أن _app.js ليس من جانب العميل فقط بمزيد من التفصيل؟

مرحبا ليروب!
لقد قمت للتو بنشر سؤال حول طلب الدمج الخاص بمثالك:
https://github.com/zeit/next.js/pull/7360#issuecomment -514318899

إضافة إلى هذا السؤال ... حاولت أيضًا إلقاء خطأ في العرض () على جانب الخادم (لقد بدأت الحالة مع liftErrorInRender: صحيح ، لذا فإن التصيير الأول ، أي جانب الخادم ، سيؤدي إلى خطأ بالفعل) ، و لم يتم التقاط هذا الخطأ أيضًا بواسطة Sentry.

هل اختبرت ذلك في تطبيقك ، وهل يتم تسجيل الأخطاء من جانب الخادم حقًا؟ أنا أستخدم التالي 9.

إذا كان الأمر كذلك بالفعل ، ولم يتم تسجيل سوى أخطاء جانب العميل ، فلن يكون هناك أيضًا سبب لتجاوز ملف _document.js.

شكرا مقدما على أي مساعدة!

timneutkens حسنًا ، هذا تم تحديث المستندات منذ آخر مرة بحثت فيها. هل يمكن أن تشرح كيف أن _app.js ليس من جانب العميل فقط بمزيد من التفصيل؟

للإجابة على سؤالك ، _app هو المكان الذي يتم فيه تعريف مكون التطبيق لتهيئة الصفحات. سنقوم بتجاوزه في حالات مثل الحاجة إلى تخطيط مستمر بين تغييرات الصفحة (تستخدم جميع الصفحات نفس التطبيق _app) ، أو للاستفادة من الإعادة. لذلك ، حتى التصيير الأول ، الذي يحدث على جانب الخادم ، سيعرض محتوى _app (ليس جانب العميل فقط).

إضافة إلى هذا السؤال ... حاولت أيضًا إلقاء خطأ في العرض () على جانب الخادم (لقد بدأت الحالة مع liftErrorInRender: صحيح ، لذا فإن التصيير الأول ، أي جانب الخادم ، سيؤدي إلى خطأ بالفعل) ، و لم يتم التقاط هذا الخطأ أيضًا بواسطة Sentry.

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

إضافة إلى هذا السؤال ... حاولت أيضًا إلقاء خطأ في العرض () على جانب الخادم (لقد بدأت الحالة مع liftErrorInRender: صحيح ، لذا فإن التصيير الأول ، أي جانب الخادم ، سيؤدي إلى خطأ بالفعل) ، و لم يتم التقاط هذا الخطأ أيضًا بواسطة Sentry.

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

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

ما رأيك في ذلك؟ شكرا!

إضافة إلى هذا السؤال ... حاولت أيضًا إلقاء خطأ في العرض () على جانب الخادم (لقد بدأت الحالة مع liftErrorInRender: صحيح ، لذا فإن التصيير الأول ، أي جانب الخادم ، سيؤدي إلى خطأ بالفعل) ، و لم يتم التقاط هذا الخطأ أيضًا بواسطة Sentry.

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

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

ما رأيك في ذلك؟ شكرا!

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

قد يكون الأشخاص في هذا الموضوع مهتمين بـ https://github.com/zeit/next.js/pull/8684 والأخطاء ذات الصلة. يحتوي على 12 اختبارًا مختلفًا للاستثناءات التي لم تتم معالجتها والتي يمكنك اللعب بها لفهم الاستثناءات التي يعالجها Next.js نيابةً عنك وما لا يعالجها.

أي أخبار بخصوص هذا الموضوع؟

الحل الخاص بي هو استخدام redux حفظ خطأ البيانات في جلب العقدة في state ثم في componentDidMount من _app.js افعل شيئًا (تنبيه للمستخدم أو خطأ في النشر مطلوب إلى الخلفية).
الكود موجود في الخطأ التالي - antd-scaffold_server .

========> دولة

import {
  SERVER_ERROR,
  CLEAR_SERVER_ERROR
} from '../../constants/ActionTypes';

const initialState = {
  errorType: []
};

const serverError = (state = initialState, { type, payload }) => {
  switch (type) {
    case SERVER_ERROR: {
      const { errorType } = state;
      errorType.includes(payload) ? null : errorType.push(payload);
      return {
        ...state,
        errorType
      };
    }
    case CLEAR_SERVER_ERROR: {
      return initialState;
    }
    default:
      return state;
  }
};

export default serverError;

=======> عمل

import {
  SERVER_ERROR
} from '../../constants/ActionTypes';

export default () => next => action => {
  if (!process.browser && action.type.includes('FAIL')) {
    next({
      type: SERVER_ERROR,
      payload: action.type 
    });
  }
  return next(action);
};

=======> _app.js

...
componentDidMount() {
    const { store: { getState, dispatch } } = this.props;
    const { errorType } = getState().serverError;
    if (errorType.length > 0) {
      Promise.all(
        errorType.map(type => message.error(`Node Error, Code:${type}`))
      );
      dispatch(clearServerError());
    }
  }
...

فلدي الحب _ unortodox_ الحلول

function installErrorHandler(app) {
  const _renderErrorToHTML = app.renderErrorToHTML.bind(app)
  const errorHandler = rollbar.errorHandler()

  app.renderErrorToHTML = (err, req, res, pathname, query) => {
    if (err) {
      errorHandler(err, req, res, () => {})
    }

    return _renderErrorToHTML(err, req, res, pathname, query)
  }

  return app
}
// ¯\_(ツ)_/¯

لا يمكن تطبيق هذه الطريقة على خطأ 404 لأن وسيطة الخطأ ستكون فارغة عند الخطأ 404.

لقد قمت بحل هذا لعميل عقدة Elastic APM (https://www.npmjs.com/package/elastic-apm-node)

لأي شخص مهتم:

في next.config.js :

webpack: (config, { isServer, webpack }) => {
      if (!isServer) {
        config.node = {
          dgram: 'empty',
          fs: 'empty',
          net: 'empty',
          tls: 'empty',
          child_process: 'empty',
        };

        // ignore apm (might use in nextjs code but dont want it in client bundles)
        config.plugins.push(
          new webpack.IgnorePlugin(/^(elastic-apm-node)$/),
        );
      }

      return config;
} 

ثم في _error.js render func ، يمكنك استدعاء خطأ التقاط يدويًا:

function Error({ statusCode, message, err }) {

const serverSide = typeof window === 'undefined';

  // only run this on server side as APM only works on server
  if (serverSide) {
    // my apm instance (imports elastic-apm-node and returns  captureError)
    const { captureError } = require('../src/apm'); 

    if (err) {
      captureError(err);
    } else {
      captureError(`Message: ${message}, Status Code: ${statusCode}`);
    }
  }

}

مرحبا جميعا! لقد أطلقنا للتو مكتبة NPM لهندسة النمط السريع في Next.js دون إضافة خادم Express. قد يكون من المفيد التعامل مع أخطاء الخادم الخاص بك التحديات! تحقق من ذلك إذا كنت مهتمًا. https://github.com/oslabs-beta/connext-js

نجح أي شخص في اصطياد (ومعالجة) الاستثناءات التي تحدث في getServerSideProps ؟

نجح أي شخص في اصطياد (ومعالجة) الاستثناءات التي تحدث في getServerSideProps ؟

من غير الواضح ، كنت تعتقد أن إطار العمل سيوفر طريقة اصطلاحية لتسجيل الأخطاء لكل من العميل والخادم 🤷‍♂️

نجح أي شخص في اصطياد (ومعالجة) الاستثناءات التي تحدث في getServerSideProps ؟

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

نعم ، لقد نجح شيء كهذا بالنسبة لي:

أنشئ أولاً برمجية وسيطة للتعامل مع تقارير الأعطال. لقد قمت بلف Sentry داخل فئة CrashReporter لأن إرجاع Sentry لن يعمل (على سبيل المثال ، req.getCrashReporter = () => Sentry ).

// crash-reporter.js

const Sentry = require("@sentry/node");

class CrashReporter {
  constructor(){
    Sentry.init({ dsn: process.env.SENTRY_DSN });
  }
  captureException(ex){
    return Sentry.captureException(ex);
  }
}

function crashReporterMiddleware(req, res, next) {
  req.getCrashReporter = () => new CrashReporter();
  next();
}

module.exports = crashReporterMiddleware;

بعد ذلك ، بالطبع ، قم بتحميل البرامج الوسيطة في تطبيقك قبل تعيين معالج الطلب Next.js:

// server.js

const crashReporterMiddleware = require("./middleware/crash-reporter")

...

app.use(crashReporterMiddleware);

...

setHandler((req, res) => {
  return handle(req, res);
});

ثم أينما كنت تتصل بـ getServerSideProps :

// _error.js

export async function getServerSideProps({req, res, err}) {
  const statusCode = res ? res.statusCode : err ? err.statusCode : 404;
  const crashReporter = req.getCrashReporter();
  const eventId = crashReporter.captureException(err);
  req.session.eventId = eventId;
  return {
    props: { statusCode, eventId }
  }
}
هل كانت هذه الصفحة مفيدة؟
0 / 5 - 0 التقييمات