はじめに
ネットワーク エラー ロギング(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 |
Array<Object> | 必須 。レポート コレクタの実際の URL を指定する JSON オブジェクトの配列。 |
include_subdomains |
ブール値 | 省略可。現在のオリジンのホストのすべてのサブドメインに対してエンドポイント グループを有効にするブール値。省略した場合、または「true」以外の値の場合、サブドメインはエンドポイントに報告されません。 |
group 名は、文字列をエンドポイントに関連付けるために使用される一意の名前です。Reporting API と統合する他の場所でこの名前を使用して、特定のエンドポイント グループを参照します。
max-age フィールドも必須で、ブラウザがエンドポイントを使用してエラーを報告する期間を指定します。
endpoints フィールドは、フェイルオーバー機能とロード バランシング機能を提供する配列です。フェイルオーバーとロード バランシングのセクションをご覧ください。グループの endpoints に複数のコレクタがリストされている場合でも、ブラウザは 1 つのエンドポイントのみを選択します 。レポートを複数のサーバーに同時に送信する場合は、バックエンドでレポートを転送する必要があります。
ブラウザはどのようにレポートを送信しますか?
ブラウザはレポートを定期的にバッチ処理し、構成したレポート エンドポイントに送信します。
レポートを送信するために、ブラウザは Content-Type: application/reports+json と、キャプチャされた警告/エラーの配列を含む本文を含む POST リクエストを発行します。
ブラウザはいつレポートを送信しますか?
レポートはアプリからアウトオブバンドで配信されます。つまり、レポートをサーバーに送信するタイミングはブラウザ が制御します。
ブラウザは、キューに登録されたレポートを最適なタイミングで配信しようとします。準備ができ次第(デベロッパーにタイムリーなフィードバックを提供するため)配信されることもありますが、ブラウザは優先度の高い処理でビジー状態の場合や、ユーザーが低速または輻輳したネットワークを使用している場合は、配信を遅らせることもあります。 ユーザーが頻繁にアクセスしている場合、ブラウザは特定のオリジンに関するレポートの送信を優先する場合があります。
Reporting API を使用する場合、パフォーマンスに関する懸念(アプリとのネットワーク競合など)はほとんどありません。ブラウザがキューに登録されたレポートを送信するタイミングを制御する方法はありません。
複数のエンドポイントを構成する
1 つのレスポンスで、複数の 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"
}]
}
または、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's の 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}`);
});