רישום שגיאות ברשת (NEL)

מבוא

Network Error Logging (NEL) הוא מנגנון לאיסוף שגיאות רשת בצד הלקוח ממקור.

היא משתמשת בכותרת התגובה NEL של ה-HTTP כדי להנחות את הדפדפן לאסוף שגיאות רשת, ואז משתלבת עם Reporting API כדי לדווח על השגיאות לשרת.

סקירה כללית של הגרסה הקודמת של Reporting API

כדי להשתמש בגרסה הקודמת של Reporting API, צריך להגדיר כותרת תגובת HTTP Report-To. הערך שלו הוא אובייקט שמתאר קבוצה של נקודות קצה לדפדפן כדי לדווח על שגיאות:

Report-To:
{
    "max_age": 10886400,
    "endpoints": [{
    "url": "https://analytics.provider.com/browser-errors"
    }]
}

אם כתובת ה-URL של נקודת הקצה נמצאת במקור אחר מזה של האתר, נקודת הקצה צריכה לתמוך בבקשות קדם-הפעלה של CORS. (למשל, Access-Control-Allow-Origin: *; Access-Control-Allow-Methods: GET,PUT,POST,DELETE,OPTIONS; Access-Control-Allow-Headers: Content-Type, Authorization, Content-Length, X-Requested-With).

בדוגמה, שליחת כותרת התגובה הזו עם הדף הראשי מגדירה את הדפדפן לדווח על אזהרות שנוצרו על ידי הדפדפן לנקודת הקצה https://analytics.provider.com/browser-errors למשך max_age שניות. חשוב לציין שהמערכת מתעלמת מכל בקשות ה-HTTP הבאות שנשלחות על ידי הדף (לתמונות, לסקריפטים וכו'). ההגדרות האישיות מוגדרות במהלך התגובה של הדף הראשי.

הסבר על שדות הכותרת

כל הגדרה של נקודת קצה מכילה את המערך group, max_age ו-endpoints. בעזרת השדה include_subdomains אפשר גם לבחור אם להביא בחשבון תת-דומיינים כשמדווחים על שגיאות.

שדה סוג תיאור
group string זה שינוי אופציונלי. אם לא מציינים שם של group, נקודת הקצה תקבל את השם 'default'.
max_age number נדרש. מספר שלם לא שלילי שמגדיר את משך החיים של נקודת הקצה בשניות. הערך '0' יגרום להסרה של הקבוצה של נקודות הקצה ממטמון הדיווח של סוכן המשתמש.
endpoints מערך<אובייקט> נדרש. מערך אובייקטים של JSON שמציינים את כתובת ה-URL בפועל של אוסף הדוחות.
include_subdomains boolean זה שינוי אופציונלי. ערך בוליאני שמפעיל את הקבוצה של נקודות הקצה בכל תתי-הדומיין של המארח של המקור הנוכחי. אם לא כוללים את הפרמטר או כל דבר פרט ל-'true', תת-הדומיינים לא ידווחו לנקודת הקצה.

השם group הוא שם ייחודי המשמש לשיוך מחרוזת לנקודת קצה (endpoint). כדאי להשתמש בשם הזה במקומות אחרים שמשתלבים עם Reporting API כדי להתייחס לקבוצה ספציפית של נקודות קצה.

גם השדה max-age הוא שדה חובה, והוא מציין למשך כמה זמן הדפדפן צריך להשתמש בנקודת הקצה ולדווח עליה על שגיאות.

השדה endpoints הוא מערך שמספק תכונות של יתירות כשל ואיזון עומסים. מידע נוסף זמין בקטע מעבר לגיבוי ואיזון עומסים. חשוב לציין שהדפדפן יבחר רק נקודת קצה אחת, גם אם בקבוצה יש מספר אספנים ב-endpoints. אם רוצים לשלוח דוח לכמה שרתים בבת אחת, הקצה העורפי צריך להעביר את הדוחות.

איך הדפדפן שולח דוחות?

הדפדפן מקבץ מדי פעם דוחות ושולח אותם לנקודות הקצה לדיווח שאתם הגדרתם.

כדי לשלוח דוחות, הדפדפן מנפיק בקשת POST עם Content-Type: application/reports+json וגוף שמכיל את מערך האזהרות/השגיאות שתועד.

מתי הדפדפן שולח דוחות?

דוחות נשלחים מחוץ למסגרת האפליקציה שלכם. כלומר, הדפדפן קובע מתי דוחות יישלחו לשרתים שלכם.

הדפדפן מנסה לספק דוחות בתור בזמן המתאים ביותר. זה יכול להיות ברגע שהוא יהיה מוכן (כדי לתת משוב למפתח בזמן), אבל הדפדפן יכול גם לעכב את השליחה אם הוא עסוק בעבודה בעדיפות גבוהה יותר, או אם המשתמש נמצא ברשת איטית או עמוסה באותו זמן. יכול להיות שהדפדפן גם ייתן עדיפות לשליחת דוחות על מקור מסוים קודם, אם המשתמש הוא מבקר בתדירות גבוהה.

כשמשתמשים ב-Reporting API, אין פגיעה בביצועים (למשל, התנגשות בין הרשת לאפליקציה). אין גם דרך לקבוע מתי הדפדפן ישלח דוחות בתור.

הגדרה של כמה נקודות קצה

תגובה יחידה יכולה להגדיר מספר נקודות קצה בבת אחת על ידי שליחת מספר כותרות Report-To:

Report-To: {
             "group": "default",
             "max_age": 10886400,
             "endpoints": [{
               "url": "https://example.com/browser-reports"
             }]
           }
Report-To: {
             "group": "network-errors-endpoint",
             "max_age": 10886400,
             "endpoints": [{
               "url": "https://example.com/network-errors"
             }]
           }

או על ידי שילובם לכותרת HTTP אחת:

Report-To: {
             "group": "network-errors-endpoint",
             "max_age": 10886400,
             "endpoints": [{
               "url": "https://example.com/network-errors"
             }]
           },
           {
             "max_age": 10886400,
             "endpoints": [{
               "url": "https://example.com/browser-errors"
             }]
           }

אחרי ששולחים את הכותרת Report-To, הדפדפן שומר במטמון את נקודות הקצה בהתאם לערכי max_age שלהן, ושולח את כל האזהרות/השגיאות הבעייתיות במסוף לכתובות ה-URL.

מעבר לגיבוי ואיזון עומסים

ברוב המקרים צריך להגדיר אוסף אחד של כתובות URL לכל קבוצה. עם זאת, מכיוון שהדיווח יכול ליצור כמות גדולה של תעבורת נתונים, המפרט כולל תכונות של יתירות כשל ואיזון עומסים בהשראת רשומת ה-SRV של ה-DNS.

הדפדפן יעשה כמיטב יכולתו כדי לשלוח דוח לנקודת קצה (endpoint) אחת לכל היותר בקבוצה. אפשר להקצות לנקודות קצה (endpoint) weight כדי לפזר את העומס, כך שכל נקודת קצה מקבלת חלק מסוים מהתנועה המדווחת. אפשר גם להקצות לנקודות קצה (endpoint) priority כדי להגדיר אוספים חלופיים.

התכונה 'אוספים חלופיים' פועלת רק אם ההעלאות נכשלות לאוספים הראשיים.

דוגמה: יצירת אוסף חלופות ב-https://backup.com/reports:

Report-To: {
             "group": "endpoint-1",
             "max_age": 10886400,
             "endpoints": [
               {"url": "https://example.com/reports", "priority": 1},
               {"url": "https://backup.com/reports", "priority": 2}
             ]
           }

הגדרת רישום של שגיאות רשת

הגדרה

כדי להשתמש ב-NEL, צריך להגדיר את הכותרת Report-To עם אוסף איסוף שמשתמש בקבוצה בעלת שם:

Report-To: {
    ...
  }, {
    "group": "network-errors",
    "max_age": 2592000,
    "endpoints": [{
      "url": "https://analytics.provider.com/networkerrors"
    }]
  }

בשלב הבא, צריך לשלוח את כותרת התשובה NEL כדי להתחיל לאסוף שגיאות. בגלל ש-NEL מביעים הסכמה לשימוש במקור, צריך לשלוח את הכותרת רק פעם אחת. גם NEL וגם Report-To יחולו על בקשות עתידיות לאותו מקור וימשיכו לאסוף שגיאות בהתאם לערך max_age ששימש להגדרת הגבייה.

ערך הכותרת צריך להיות אובייקט JSON שמכיל את השדות max_age ו-report_to. משתמשים בפורמט השני כדי לציין את שם הקבוצה של אוסף השגיאות ברשת:

GET /index.html HTTP/1.1
NEL: {"report_to": "network-errors", "max_age": 2592000}

משאבי משנה

דוגמה: אם example.com טוען את foobar.com/cat.gif והמשאב הזה לא נטען:

  • נשלחה הודעה למוסף NEL של foobar.com
  • לא נשלחת הודעה לגבי אוסף ה-NEL של example.com

כלל אצבע הוא ש-NEL משחזר יומנים בצד השרת, שנוצרו רק על ידי הלקוח.

מכיוון של-example.com אין גישה ליומני השרת של foobar.com, ולכן אין לו גישה לדוחות ה-NEL שלו.

ניפוי באגים בהגדרות דוח

אם לא מופיעים דוחות בשרת, עברו אל chrome://net-export/. הדף הזה עוזר לוודא שדברים מוגדרים כראוי ושהדוחות נשלחים כראוי.

מה לגבי ReportingObserver?

ReportingObserver הוא מנגנון דיווח קשור אך שונה. הוא מבוסס על קריאות JavaScript. הוא לא מתאים לרישום שגיאות רשת ביומן כי אי אפשר לפענח שגיאות רשת באמצעות JavaScript.

שרת לדוגמה

לפניכם דוגמה לשרת Node שמשתמש ב-Express. הקורס מסביר איך להגדיר דיווח על שגיאות ברשת, ויוצר handler ייעודי לתיעוד התוצאה.

const express = require('express');

const app = express();
app.use(
  express.json({
    type: ['application/json', 'application/reports+json'],
  }),
);
app.use(express.urlencoded());

app.get('/', (request, response) => {
  // Note: report_to and not report-to for NEL.
  response.set('NEL', `{"report_to": "network-errors", "max_age": 2592000}`);

  // The Report-To header tells the browser where to send network errors.
  // The default group (first example below) captures interventions and
  // deprecation reports. Other groups, like the network-error group, are referenced by their "group" name.
  response.set(
    'Report-To',
    `{
    "max_age": 2592000,
    "endpoints": [{
      "url": "https://reporting-observer-api-demo.glitch.me/reports"
    }],
  }, {
    "group": "network-errors",
    "max_age": 2592000,
    "endpoints": [{
      "url": "https://reporting-observer-api-demo.glitch.me/network-reports"
    }]
  }`,
  );

  response.sendFile('./index.html');
});

function echoReports(request, response) {
  // Record report in server logs or otherwise process results.
  for (const report of request.body) {
    console.log(report.body);
  }
  response.send(request.body);
}

app.post('/network-reports', (request, response) => {
  console.log(`${request.body.length} Network error reports:`);
  echoReports(request, response);
});

const listener = app.listen(process.env.PORT, () => {
  console.log(`Your app is listening on port ${listener.address().port}`);
});

קריאה נוספת