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

אריק בידלמן
מוד נאלפס
מוד נאלפ

מבוא

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

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

סקירה כללית של ה-Reporting API הקודם

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

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

אם כתובת ה-URL של נקודת הקצה נמצאת במקור שונה מהאתר, נקודת הקצה (endpoint) צריכה לתמוך בבקשות קדם-הפעלה של 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 מחרוזת אפשרות. אם לא מציינים שם של group, שם נקודת הקצה מקבל את השם 'ברירת מחדל'.
max_age מספר נדרש. מספר שלם לא שלילי שמגדיר את משך החיים של נקודת הקצה בשניות. הערך 0 יגרום להסרת הקבוצה של נקודות הקצה ממטמון הדיווח של סוכן המשתמש.
endpoints מערך<Object> נדרש. מערך אובייקטים של JSON שמציינים את כתובת ה-URL בפועל של אוסף הדוחות.
include_subdomains boolean אפשרות. ערך בוליאני שמפעיל את קבוצת נקודות הקצה לכל תת-הדומיינים של מארח המקור הנוכחי. אם הוא יושמט או כל דבר אחר מלבד 'true', תת-הדומיינים לא ידווחו בנקודת הקצה.

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

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

השדה endpoints הוא מערך שמספק תכונות של יתירות כשל ושל איזון עומסים. למידע נוסף, יש לעיין בקטע בנושא מעבר אוטומטי ואיזון עומסים. חשוב לציין שהדפדפן יבחר רק נקודת קצה אחת, גם אם בקבוצה רשומים כמה אוספים ב-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 לכל קבוצה. עם זאת, מכיוון שהדיווח יכול ליצור תעבורת נתונים טובה, המפרט כולל תכונות של יתירות כשל (failover) ואיזון עומסים בהשראת רשומת ה-SRV של ה-DNS.

הדפדפן יעשה כמיטב יכולתו כדי לשלוח דוח לנקודת קצה אחת לכל היותר בקבוצה. אפשר להקצות נקודות קצה (endpoints) weight כדי לחלק את העומס, וכל נקודת קצה מקבלת חלק מסוים מתנועת הדיווח. אפשר גם להקצות נקודות קצה (endpoints) 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.

שרת לדוגמה

בהמשך מוצגת דוגמה לשרת צמתים שמשתמש ב-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}`);
});

קריאה נוספת