การบันทึกข้อผิดพลาดเกี่ยวกับเครือข่าย (NEL)

บทนำ

Network Error Logging (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 ปลายทางอยู่บนต้นทางที่ต่างจากเว็บไซต์ ปลายทางควรรองรับคำขอ Preflight ของ 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" จะทำให้ระบบนำกลุ่มปลายทางออกจากแคชการรายงานของ User Agent
endpoints Array<Object> ต้องระบุ อาร์เรย์ของออบเจ็กต์ JSON ที่ระบุ URL จริงของเครื่องมือรวบรวมรายงาน
include_subdomains บูลีน ไม่บังคับ บูลีนที่เปิดใช้กลุ่มปลายทางสำหรับโดเมนย่อยทั้งหมดของโฮสต์ต้นทางปัจจุบัน หากละเว้นหรือตั้งค่าเป็นอย่างอื่นที่ไม่ใช่ "จริง" ระบบจะไม่รายงานโดเมนย่อยไปยังปลายทาง

ชื่อ group เป็นชื่อที่ไม่ซ้ำกันซึ่งใช้เพื่อเชื่อมโยงสตริงกับปลายทาง ใช้ชื่อนี้ในที่อื่นๆ ที่ผสานรวมกับ Reporting API เพื่ออ้างอิงกลุ่มปลายทางที่เฉพาะเจาะจง

นอกจากนี้ คุณยังต้องระบุช่อง max-age และระบุระยะเวลาที่เบราว์เซอร์ควรใช้ปลายทางและรายงานข้อผิดพลาดไปยังปลายทาง

ฟิลด์ endpoints เป็นอาร์เรย์ที่ใช้เพื่อให้บริการฟีเจอร์เฟลโอเวอร์และการจัดสรรภาระงาน ดูส่วนเฟลโอเวอร์และการจัดสรรภาระงาน โปรดทราบว่าเบราว์เซอร์จะเลือกปลายทางเพียงรายการเดียว แม้ว่ารายการโปรดจะแสดงรายการ Collector หลายรายการใน 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 1 รายการต่อกลุ่ม อย่างไรก็ตาม เนื่องจากการรายงานสามารถสร้างการเข้าชมได้เป็นจำนวนมาก ข้อกำหนดจึงรวมฟีเจอร์เฟลโอเวอร์และการจัดสรรภาระงานที่ได้รับแรงบันดาลใจจากระเบียน SRV ของ DNS

เบราว์เซอร์จะพยายามอย่างเต็มที่เพื่อส่งรายงานไปยังปลายทางอย่างมากที่สุด 1 แห่งในกลุ่ม คุณสามารถกำหนด weight ให้กับปลายทางเพื่อกระจายภาระงาน โดยปลายทางแต่ละรายการจะได้รับส่วนหนึ่งของการเข้าชมการรายงานที่ระบุ นอกจากนี้ คุณยังกำหนด priority ให้กับปลายทางเพื่อตั้งค่าเครื่องมือรวบรวมข้อมูลสำรองได้ด้วย

ระบบจะลองใช้ Collector สำรองเมื่ออัปโหลดไปยัง Collector หลักไม่สำเร็จเท่านั้น

ตัวอย่าง: สร้าง Collector สำรองที่ 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 ด้วย Collector ที่ใช้กลุ่มที่มีชื่อ ดังนี้

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 โดยจะแสดงวิธีกำหนดค่าการรายงานข้อผิดพลาดเกี่ยวกับเครือข่าย และสร้างเครื่องมือจัดการเฉพาะเพื่อบันทึกผลลัพธ์

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

อ่านเพิ่มเติม