Registro de errores de red (NEL)

Introducción

El registro de errores de red (NEL) es un mecanismo para recopilar errores de red del cliente desde un origen.

Usa el encabezado de respuesta HTTP NEL para indicarle al navegador que recopile errores de red y, luego, se integra con la API de Reporting para informar los errores a un servidor.

Descripción general de la API de Reporting heredada

Para usar la API de Reporting heredada, deberás configurar un encabezado de respuesta HTTP Report-To. Su valor es un objeto que describe un grupo de extremos para que el navegador informe errores a lo siguiente:

Report-To:
{
    "max_age": 10886400,
    "endpoints": [{
    "url": "https://analytics.provider.com/browser-errors"
    }]
}

Si la URL de extremo se encuentra en un origen diferente al de tu sitio, el extremo debería admitir solicitudes de comprobación previa de CORS. (p.ej., 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).

En el ejemplo, el envío de este encabezado de respuesta con tu página principal configura el navegador para informar advertencias generadas por el navegador al extremo https://analytics.provider.com/browser-errors durante max_age segundos. Es importante tener en cuenta que se ignoran todas las solicitudes HTTP posteriores que realiza la página (para imágenes, secuencias de comandos, etcétera). La configuración se define durante la respuesta de la página principal.

Explicación de los campos de encabezado

Cada configuración de extremo contiene un nombre group, max_age y un arreglo endpoints. También puedes elegir si deseas considerar los subdominios cuando se informan errores mediante el campo include_subdomains.

Campo Tipo Descripción
group cadena Opcional. Si no se especifica un nombre group, se asigna el nombre "default" al extremo.
max_age número Obligatorio: Un número entero no negativo que define la vida útil del extremo en segundos. Un valor de "0" hará que se quite el grupo de extremos de la caché de informes del usuario-agente.
endpoints Arreglo<Objeto> Obligatorio: Un array de objetos JSON que especifica la URL real del recopilador de informes.
include_subdomains boolean Opcional. Un valor booleano que habilita el grupo de extremos para todos los subdominios del host del origen actual. Si se omite o si no es “true”, los subdominios no se informan al extremo.

El nombre group es un nombre único que se usa para asociar una cadena con un extremo. Usa este nombre en otros lugares que se integren con la API de informes para hacer referencia a un grupo de extremos específico.

El campo max-age también es obligatorio y especifica por cuánto tiempo el navegador debe usar el extremo y notificarle errores.

El campo endpoints es un array para proporcionar funciones de conmutación por error y balanceo de cargas. Consulta la sección Conmutación por error y balanceo de cargas. Es importante tener en cuenta que el navegador seleccionará solo un extremo, incluso si el grupo enumera varios recopiladores en endpoints. Si quieres enviar un informe a varios servidores a la vez, tu backend deberá reenviar los informes.

¿Cómo envía informes el navegador?

El navegador agrupa en lotes los informes de forma periódica y los envía a los extremos de informes que configuras.

Para enviar informes, el navegador emite una solicitud POST con Content-Type: application/reports+json y un cuerpo que contiene el array de advertencias o errores que se capturaron.

¿Cuándo envía informes el navegador?

Los informes se entregan fuera de banda desde tu app, es decir, el navegador controla cuándo se envían los informes a tus servidores.

El navegador intenta entregar los informes en cola en el momento más oportuno. Puede ser en cuanto esté listo (para proporcionar comentarios oportunos al desarrollador), pero el navegador también puede retrasar la entrega si está ocupado procesando trabajos de mayor prioridad o si el usuario se encuentra en una red lenta o congestionada en ese momento. El navegador también puede priorizar el envío de informes sobre un origen en particular primero, si el usuario es un visitante frecuente.

Hay pocos o ningún problema de rendimiento (p.ej., la contención de red con tu app) cuando se usa la API de informes. Tampoco hay forma de controlar cuándo el navegador envía informes en cola.

Configura varios extremos

Una sola respuesta puede configurar varios extremos a la vez mediante el envío de varios encabezados 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"
             }]
           }

o combinándolos en un solo encabezado 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"
             }]
           }

Una vez que hayas enviado el encabezado Report-To, el navegador almacenará en caché los extremos según sus valores de max_age y enviará todas esas advertencias o errores desagradables de la consola a tus URLs.

Conmutación por error y balanceo de cargas

La mayoría de las veces, configurarás un recopilador de URLs por grupo. Sin embargo, como los informes pueden generar una gran cantidad de tráfico, la especificación incluye funciones de conmutación por error y balanceo de cargas inspiradas en el registro SRV de DNS.

El navegador hará todo lo posible para entregar un informe a un máximo de un extremo de un grupo. Se puede asignar un weight a los extremos para distribuir la carga, y cada extremo recibe una fracción específica del tráfico de informes. También se puede asignar un priority a los extremos para configurar recopiladores de resguardo.

Los recopiladores de resguardo solo se prueban cuando fallan las cargas a recopiladores principales.

Ejemplo: Crea un recopilador de resguardo en 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}
             ]
           }

Configura el registro de errores de red

Configuración

Para usar NEL, configura el encabezado Report-To con un colector que use un grupo con nombre:

Report-To: {
    ...
  }, {
    "group": "network-errors",
    "max_age": 2592000,
    "endpoints": [{
      "url": "https://analytics.provider.com/networkerrors"
    }]
  }

A continuación, envía el encabezado de respuesta NEL para comenzar a recopilar errores. Dado que NEL está habilitado para un origen, solo debes enviar el encabezado una vez. Tanto NEL como Report-To se aplicarán a las solicitudes futuras al mismo origen y seguirán recopilando errores según el valor max_age que se usó para configurar el recopilador.

El valor del encabezado debe ser un objeto JSON que contenga los campos max_age y report_to. Usa esta última para hacer referencia al nombre del grupo de tu recopilador de errores de red:

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

Subrecursos

Ejemplo: Si example.com carga foobar.com/cat.gif y ese recurso no se carga, ejecuta el siguiente comando:

  • Se notifica al recopilador de NEL de foobar.com
  • No se notifica al recopilador de NEL de example.com.

La regla general es que NEL reproduce los registros del servidor, que se acaban de generar en el cliente.

Dado que example.com no tiene visibilidad de los registros del servidor de foobar.com, tampoco tiene visibilidad de sus informes de NEL.

Cómo depurar configuraciones de informes

Si no ves informes que se muestran en tu servidor, ve a chrome://net-export/. Esa página es útil para verificar que los elementos estén configurados correctamente y que los informes se envíen adecuadamente.

¿Qué ocurre con ReportingObserver?

ReportingObserver es un mecanismo relacionado, pero diferente, para enviar informes. Se basa en llamadas de JavaScript. No es adecuado para el registro de errores de red, ya que los errores de red no se pueden interceptar a través de JavaScript.

Servidor de ejemplo

A continuación, se muestra un ejemplo de un servidor de nodos que usa Express. Se muestra cómo configurar informes para errores de red y se crea un controlador dedicado para capturar el resultado.

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

Lecturas adicionales