네트워크 오류 로깅 (NEL)

마우드 날파스
Maud Nalpas

소개

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

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

기존 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 string 선택사항입니다. group 이름을 지정하지 않으면 엔드포인트에 'default'라는 이름이 지정됩니다.
max_age 숫자 필수사항: 엔드포인트의 수명을 초 단위로 정의하는 음수가 아닌 정수입니다. 값이 '0'이면 엔드포인트 그룹이 사용자 에이전트의 보고 캐시에서 삭제됩니다.
endpoints 배열<Object> 필수사항: 보고서 수집기의 실제 URL을 지정하는 JSON 객체의 배열입니다.
include_subdomains boolean 선택사항입니다. 현재 출처 호스트의 모든 하위 도메인에 엔드포인트 그룹을 사용 설정하는 불리언입니다. 생략되거나 '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 수집기를 구성합니다. 그러나 보고는 상당한 양의 트래픽을 생성할 수 있으므로 사양에 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 값에 따라 오류를 계속 수집합니다.

헤더 값은 max_agereport_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.comfoobar.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}`);
});

추가 자료