網路錯誤記錄 (NEL)

Maud Nalpas
Maud Nalpas

網路錯誤記錄 (NEL) 是一種機制,可從來源收集用戶端網路錯誤

這個程式會使用 NEL HTTP 回應標頭,告知瀏覽器收集網路錯誤,然後整合 Reporting API,將錯誤回報給伺服器。

如要使用舊版 Reporting API,您必須設定 Report-To HTTP 回應標頭。其值為物件,可描述瀏覽器用來回報錯誤的端點群組:

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

如果端點網址位於與網站不同的來源,端點應支援 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)。

在這個範例中,傳送此回應標頭與主要網頁時,瀏覽器會在 max_age 秒內將瀏覽器產生的警告回報給端點 https://analytics.provider.com/browser-errors。請注意,系統會忽略網頁發出的所有後續 HTTP 要求 (圖片、指令碼等)。設定會在主頁面回應期間設定。

標頭欄位說明

每個端點設定都包含 group 名稱、max_ageendpoints 陣列。您也可以使用 include_subdomains 欄位,選擇在回報錯誤時是否要考慮子網域。

欄位 類型 說明
group 字串 選用設定。如果未指定 group 名稱,端點會採用預設名稱「default」。
max_age 數字 必填。非負整數,以秒為單位定義端點的生命週期。如果值為「0」,系統會從使用者代理程式回報快取中移除端點群組。
endpoints 陣列<物件> 必填。一組 JSON 物件陣列,用於指定報表收集器的實際網址。
include_subdomains 布林值 選用設定。布林值,可為目前來源主機的所有子網域啟用端點群組。如果省略或不是「true」,則不會將子網域回報至端點。

group 名稱是用於將字串與端點建立關聯的專屬名稱。在與 Reporting API 整合的其他位置使用這個名稱,以便參照特定端點群組。

max-age 欄位也是必要欄位,可指定瀏覽器應使用端點多久,以及向端點回報錯誤。

endpoints 欄位是提供容錯和負載平衡功能的陣列。請參閱「備援和負載平衡」一節。請注意,即使群組在 endpoints 中列出多個收集器,瀏覽器只會選取一個端點。如果您想一次將報表傳送至多個伺服器,後端就必須轉寄報表。

瀏覽器如何傳送報表?

瀏覽器會定期將報表匯出,並傳送至您設定的報表端點。

為了傳送報表,瀏覽器會發出 POST 要求,其中包含 Content-Type: application/reports+json,以及包含所擷取警告/錯誤陣列的主體。

瀏覽器何時會傳送報表?

報表會從應用程式外傳送,也就是說,瀏覽器會控制報表傳送至伺服器的時間。

瀏覽器會嘗試在最適當的時機傳送已排入佇列的報表。這可能會在準備就緒後立即執行 (以便及時向開發人員提供意見回饋),但如果瀏覽器正忙於處理優先順序較高的工作,或是使用者目前使用的是速度緩慢和/或壅塞的網路,則可能會延遲傳送。如果使用者是常客,瀏覽器也可能會優先傳送特定來源的報表。

使用報表 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 值快取端點,並將所有惱人的控制台警告/錯誤傳送至網址。

容錯移轉和負載平衡

您通常會為每個群組設定一個網址收集器。不過,由於報表可能會產生大量流量,因此規格包含備援和負載平衡功能,這些功能的靈感來自 DNS SRV 記錄

瀏覽器會盡力將報表傳送至群組中最多一個端點。您可以為端點指派 weight 來分配負載,每個端點都會接收指定比例的回報流量。您也可以為端點指派 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 是來源的選擇性啟用功能,您只需傳送標頭一次。NELReport-To 都會套用至未來對相同來源的後續要求,並根據用於設定收集器的 max_age 值繼續收集錯誤。

標頭值應為 JSON 物件,其中包含 max_agereport_to 欄位。請使用後者參照網路錯誤收集器的群組名稱:

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

子資源

範例:如果 example.com 載入 foobar.com/cat.gif,但該資源無法載入:

  • foobar.com 的 NEL 收集器會收到通知
  • example.com 的 NEL 收集器「不會」收到通知

一般來說,NEL 會重現剛在用戶端產生的伺服器端記錄。

由於 example.com 無法查看 foobar.com 的伺服器記錄,因此也無法查看 NEL 報表。

偵錯報表設定

如果您沒有在伺服器上看到報表,請前往 chrome://net-export/。這個頁面可用來驗證設定是否正確,以及報表是否正確傳送。

ReportingObserver 又是什麼?

ReportingObserver 是相關但不同的報表機制。這項功能是以 JavaScript 呼叫為基礎。不適合用於網路錯誤記錄,因為網路錯誤無法透過 JavaScript 攔截。

範例伺服器

以下是使用 Express 的 Node 伺服器範例。這會說明如何設定網路錯誤的回報功能,並建立專用處理程序來擷取結果。

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

延伸閱讀