简介
网络错误日志记录 (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"
}]
}
如果您的端点网址与您的网站位于不同的源,则该端点应支持 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> | 必需。一个 JSON 对象数组,用于指定报告收集器的实际网址。 |
include_subdomains |
布尔值 | 可选。一个布尔值,用于为当前源站主机的所有子网域启用端点组。如果省略此政策或将其设为“true”以外的任何值,系统不会向端点报告子网域。 |
group 名称是用于将字符串与端点相关联的唯一名称。在与 Reporting API 集成的其他位置使用此名称来引用特定端点组。
max-age 字段也是必需的,用于指定浏览器应使用端点并向其报告错误的时长。
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 值缓存端点,并将所有这些令人讨厌的控制台警告/错误发送到您的网址。
故障切换和负载均衡
大多数情况下,您需要为每个组配置一个网址收集器。不过,由于报告可能会产生大量流量,因此该规范包含受 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 是针对来源选择启用的,因此您只需发送一次标头。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 的 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}`);
});
深入阅读
- 使用 Reporting API 监控 Web 应用(关于 Reporting API 的主要博文)
- 从 Reporting API v0 迁移到 v1 的迁移指南
- 规范:旧版 Reporting API (v0)
- 规范:新 Reporting API (v1)