はじめに
ネットワーク エラー ロギング(NEL)は、送信元からクライアント側のネットワーク エラーを収集するためのメカニズムです。
NEL
HTTP レスポンス ヘッダーを使用してネットワーク エラーを収集するようブラウザに指示し、Reporting API と統合してサーバーにエラーを報告します。
以前の Reporting API の概要
以前の Report-To
ヘッダー
以前の Reporting API を使用するには、Report-To
HTTP レスポンス ヘッダーを設定する必要があります。この値は、ブラウザからエラーを報告するエンドポイント グループを表すオブジェクトです。
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
)。
この例では、メインページとともにこのレスポンス ヘッダーを送信すると、ブラウザが生成した警告を max_age
秒間エンドポイント https://analytics.provider.com/browser-errors
に報告するようにブラウザが構成されます。ページから送信された後続の HTTP リクエスト(画像やスクリプトなど)はすべて無視されることに注意してください。構成はメインページのレスポンス中に設定されます。
ヘッダー フィールドの説明
各エンドポイント構成には、group
名、max_age
、endpoints
配列が含まれます。include_subdomains
フィールドを使用して、エラーを報告するときにサブドメインを考慮するかどうかを選択することもできます。
項目 | タイプ | 説明 |
---|---|---|
group |
文字列 | (省略可)group 名が指定されていない場合、エンドポイントには「default」という名前が付けられます。 |
max_age |
数値 | 必須。エンドポイントの存続期間を秒単位で定義する正の整数。値を「0」にした場合、エンドポイント グループがユーザー エージェントのレポート キャッシュから削除されます。 |
endpoints |
配列<オブジェクト> | 必須。レポートコレクタの実際の URL を指定する JSON オブジェクトの配列。 |
include_subdomains |
boolean | (省略可)現在のオリジンのホストのすべてのサブドメインに対してエンドポイント グループを有効にするブール値。省略した場合や「true」以外の場合、サブドメインはエンドポイントに報告されません。 |
group
名は、文字列をエンドポイントに関連付けるために使用される一意の名前です。Reporting API と統合されている他の場所で、この名前を使用して特定のエンドポイント グループを参照します。
max-age
フィールドも必須で、ブラウザがエンドポイントを使用してエラーを報告する期間を指定します。
endpoints
フィールドは、フェイルオーバーとロード バランシングの機能を提供する配列です。フェイルオーバーとロード バランシングのセクションをご覧ください。グループが endpoints
に複数のコレクタをリストしている場合でも、ブラウザは 1 つのエンドポイントしか選択しないことに注意してください。複数のサーバーにレポートを一度に送信する場合は、バックエンドでレポートを転送する必要があります。
ブラウザがレポートを送信する仕組み
ブラウザはレポートを定期的にバッチ処理し、構成したレポート エンドポイントに送信します。
レポートを送信するため、ブラウザは Content-Type: application/reports+json
とキャプチャされた警告/エラーの配列を含む本文を含む POST
リクエストを発行します。
ブラウザがレポートを送信するタイミング
レポートは帯域外でアプリから配信されるため、レポートがサーバーに送信されるタイミングはブラウザによって制御されます。
ブラウザは、キュー内のレポートを適切なタイミングで配信しようとします。(デベロッパーにタイムリーなフィードバックを提供するために)準備が整い次第、配信を遅らせることもありますが、優先度の高い作業の処理にブラウザがビジー状態の場合や、その時点でユーザーが低速または混雑しているネットワーク上にいる場合にも、配信が遅れることがあります。ユーザーが頻繁にアクセスする場合、ブラウザは特定のオリジンに関するレポートを最初に送信することもできます。
Reporting API を使用する際に、パフォーマンスの問題がほとんど、またはまったく発生しない(アプリとのネットワーク競合など)。また、キューに登録されたレポートをブラウザから送信するタイミングを制御する方法もありません。
複数のエンドポイントの構成
複数の Report-To
ヘッダーを送信することで、1 つのレスポンスで複数のエンドポイントを一度に構成できます。
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"
}]
}
または、それらを 1 つの 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 に送信します。
フェイルオーバーとロード バランシング
ほとんどの場合、グループごとに 1 つの URL コレクタを構成します。ただし、レポートでは大量のトラフィックが生成されるため、この仕様には DNS SRV レコードにヒントを得たフェイルオーバーとロード バランシングの機能が含まれています。
ブラウザは、グループ内の最大 1 つのエンドポイントにレポートを配信するよう最善を尽くします。エンドポイントには、負荷を分散するために 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 は送信元のオプトインであるため、ヘッダーを送信する必要があるのは 1 回だけです。NEL
と Report-To
はどちらも同じ送信元への今後のリクエストに適用され、コレクタの設定に使用された max_age
値に従って引き続きエラーを収集します。
ヘッダー値は、max_age
フィールドと report_to
フィールドを含む JSON オブジェクトである必要があります。後者を使用して、ネットワーク エラー コレクタのグループ名を参照します。
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 を使用するノードサーバーの例を以下に示します。ネットワーク エラーの報告を設定する方法と、結果をキャプチャする専用のハンドラを作成します。
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}`);
});