네트워크 오류 로깅 (NEL)

네트워크 오류 로깅 (NEL)은 출처에서 클라이언트 측 네트워크 오류를 수집하는 메커니즘입니다.

NEL HTTP 응답 헤더를 사용하여 브라우저에 네트워크 오류를 수집하도록 지시한 다음 Reporting API와 통합하여 서버에 오류를 보고합니다.

기존 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 부울 선택사항입니다. 현재 출처 호스트의 모든 하위 도메인에 엔드포인트 그룹을 사용 설정하는 불리언입니다. 생략되거나 'true' 이외의 값이면 하위 도메인이 엔드포인트에 보고되지 않습니다.

group 이름은 문자열을 엔드포인트와 연결하는 데 사용되는 고유한 이름입니다. Reporting API와 통합된 다른 위치에서 이 이름을 사용하여 특정 엔드포인트 그룹을 참조합니다.

max-age 필드도 필요하며 브라우저가 엔드포인트를 사용하고 엔드포인트에 오류를 보고하는 데 걸리는 시간을 지정합니다.

endpoints 필드는 장애 조치 및 부하 분산 기능을 제공하는 배열입니다. 페일오버 및 부하 분산 섹션을 참고하세요. 그룹이 endpoints에 여러 개의 수집기를 나열하더라도 브라우저는 엔드포인트 하나만 선택한다는 점에 유의해야 합니다. 한 번에 여러 서버로 보고서를 전송하려면 백엔드에서 보고서를 전달해야 합니다.

브라우저는 어떻게 보고서를 전송하나요?

브라우저는 주기적으로 보고서를 일괄 처리하여 구성한 보고 엔드포인트로 전송합니다.

보고서를 전송하기 위해 브라우저는 Content-Type: application/reports+json 및 캡처된 경고/오류 배열이 포함된 본문이 있는 POST 요청을 보냅니다.

브라우저는 언제 보고서를 전송하나요?

보고서는 앱 외부에서 전송되므로 브라우저가 보고서가 서버로 전송되는 시점을 제어합니다.

브라우저는 가장 적절한 시점에 대기열에 추가된 보고서를 전송하려고 시도합니다. 이는 개발자에게 적시에 의견을 제공하기 위해 준비되는 즉시 전송될 수 있지만, 브라우저가 더 높은 우선순위의 작업을 처리하느라 바쁘거나 사용자가 느린 네트워크 또는 혼잡한 네트워크를 사용 중인 경우 전송이 지연될 수도 있습니다. 또한 사용자가 자주 방문하는 경우 브라우저는 특정 출처에 관한 보고서를 먼저 전송하는 것을 우선시할 수 있습니다.

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개를 구성합니다. 그러나 보고는 상당한 트래픽을 생성할 수 있으므로 사양에는 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은 출처에서 선택할 수 있으므로 헤더를 한 번만 전송하면 됩니다. NELReport-To는 모두 향후 동일한 출처의 요청에 적용되며 수집기를 설정하는 데 사용된 max_age 값에 따라 오류를 계속 수집합니다.

헤더 값은 max_agereport_to 필드가 포함된 JSON 객체여야 합니다. 후자를 사용하여 네트워크 오류 수집기의 그룹 이름을 참조합니다.

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

하위 리소스

: example.comfoobar.com/cat.gif를 로드하고 해당 리소스를 로드할 수 없는 경우:

  • foobar.com의 NEL 수집기에 알림이 전송됨
  • example.com의 NEL 수집기에 알림이 전송되지 않음

일반적으로 NEL은 클라이언트에서 방금 생성된 서버 측 로그를 재현합니다.

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

추가 자료