Journalisation des erreurs réseau (NEL)

Introduction

La journalisation des erreurs réseau (NEL, Network Error Logging) est un mécanisme permettant de collecter les erreurs réseau côté client à partir d'une origine.

Elle utilise l'en-tête de réponse HTTP NEL pour indiquer au navigateur de collecter les erreurs réseau, puis s'intègre à l'API Reporting pour signaler les erreurs à un serveur.

Présentation de l'ancienne API Reporting

Pour utiliser l'ancienne API Reporting, vous devez définir un en-tête de réponse HTTP Report-To. Sa valeur est un objet qui décrit un groupe de points de terminaison auquel le navigateur doit signaler les erreurs :

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

Si l'URL de votre point de terminaison se trouve sur une origine différente de celle de votre site, le point de terminaison doit être compatible avec les requêtes préliminaires CORS. (par exemple, 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).

Dans l'exemple, l'envoi de cet en-tête de réponse avec la configuration de votre page principale configure le navigateur pour qu'il signale les avertissements générés par le navigateur au point de terminaison https://analytics.provider.com/browser-errors pendant max_age secondes. Il est important de noter que toutes les requêtes HTTP ultérieures effectuées par la page (pour les images, les scripts, etc.) sont ignorées. La configuration est effectuée lors de la réponse de la page principale.

Explication des champs d'en-tête

Chaque configuration de point de terminaison contient un nom group, un max_age et un tableau endpoints. Vous pouvez également choisir de prendre en compte les sous-domaines lors du signalement des erreurs à l'aide du champ include_subdomains.

Champ Type Description
group chaîne Facultatif. Si aucun nom group n'est spécifié, le point de terminaison reçoit le nom "default".
max_age nombre Obligatoire. Entier non négatif qui définit la durée de vie du point de terminaison en secondes. La valeur "0" entraîne la suppression du groupe de points de terminaison du cache de rapports de l'agent utilisateur.
endpoints Tableau<Objet> Obligatoire. Tableau d'objets JSON qui spécifient l'URL réelle de votre collecteur de rapports.
include_subdomains booléen Facultatif. Booléen qui active le groupe de points de terminaison pour tous les sous-domaines de l'hôte de l'origine actuelle. S'il est omis ou si sa valeur est différente de "true", les sous-domaines ne sont pas signalés au point de terminaison.

Le nom group est un nom unique utilisé pour associer une chaîne à un point de terminaison. Utilisez ce nom dans d'autres emplacements qui s'intègrent à l'API Reporting pour faire référence à un groupe de points de terminaison spécifique.

Le champ max-age est également obligatoire et spécifie la durée pendant laquelle le navigateur doit utiliser le point de terminaison et lui signaler les erreurs.

Le champ endpoints est un tableau qui fournit des fonctionnalités de basculement et d'équilibrage de charge. Consultez la section Basculement et équilibrage de charge. Il est important de noter que le navigateur ne sélectionne qu'un seul point de terminaison, même si le groupe répertorie plusieurs collecteurs dans endpoints. Si vous souhaitez envoyer un rapport à plusieurs serveurs à la fois, votre backend devra transférer les rapports.

Comment le navigateur envoie-t-il des rapports ?

Le navigateur regroupe périodiquement les rapports et les envoie aux points de terminaison de rapports que vous configurez.

Pour envoyer des rapports, le navigateur émet une requête POST avec Content-Type: application/reports+json et un corps contenant le tableau d'avertissements/d'erreurs qui ont été capturés.

Quand le navigateur envoie-t-il des rapports ?

Les rapports sont fournis hors bande à partir de votre application, ce qui signifie que le navigateur contrôle le moment où les rapports sont envoyés à vos serveurs.

Le navigateur tente de fournir les rapports en file d'attente au moment le plus opportun. Cela peut se produire dès qu'ils sont prêts (afin de fournir des commentaires rapides au développeur), mais le navigateur peut également retarder la livraison s'il est occupé à traiter des tâches de priorité plus élevée ou si l'utilisateur se trouve sur un réseau lent et/ou encombré au moment de l'envoi. Le navigateur peut également donner la priorité à l'envoi de rapports concernant une origine particulière si l'utilisateur est un visiteur fréquent.

L'utilisation de l'API Reporting ne pose que peu ou pas de problème de performances (par exemple, conflit réseau avec votre application). Il n'existe pas non plus de moyen de contrôler le moment où le navigateur envoie les rapports en file d'attente.

Configurer plusieurs points de terminaison

Une seule réponse peut configurer plusieurs points de terminaison à la fois en envoyant plusieurs en-têtes 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"
             }]
           }

ou en les combinant dans un seul en-tête 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"
             }]
           }

Une fois que vous avez envoyé l'en-tête Report-To, le navigateur met en cache les points de terminaison en fonction de leurs valeurs max_age et envoie tous ces avertissements/erreurs de console à vos URL.

Basculement et équilibrage de charge

La plupart du temps, vous configurez un collecteur d'URL par groupe. Toutefois, comme les rapports peuvent générer un trafic important, la spécification inclut des fonctionnalités de basculement et d'équilibrage de charge inspirées de l'enregistrement DNS SRV.

Le navigateur fera de son mieux pour fournir un rapport à au maximum un point de terminaison dans un groupe. Les points de terminaison peuvent se voir attribuer un weight pour répartir la charge, chaque point de terminaison recevant une fraction spécifiée du trafic de rapports. Les points de terminaison peuvent également se voir attribuer une priority pour configurer des collecteurs de secours.

Les collecteurs de secours ne sont essayés que lorsque les importations vers les collecteurs principaux échouent.

Exemple : Créez un collecteur de secours sur 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}
             ]
           }

Configurer la journalisation des erreurs réseau

Configuration

Pour utiliser la journalisation des erreurs réseau, configurez l'en-tête Report-To avec un collecteur qui utilise un groupe nommé :

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

Ensuite, envoyez l'en-tête de réponse NEL pour commencer à collecter les erreurs. Comme la journalisation des erreurs réseau est activée pour une origine, vous n'avez besoin d'envoyer l'en-tête qu'une seule fois. NEL et Report-To s'appliqueront aux futures requêtes adressées à la même origine et continueront de collecter les erreurs en fonction de la valeur max_age utilisée pour configurer le collecteur.

La valeur de l'en-tête doit être un objet JSON contenant un champ max_age et un champ report_to. Utilisez ce dernier pour faire référence au nom de groupe de votre collecteur d'erreurs réseau :

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

Sous-ressources

Exemple : Si example.com charge foobar.com/cat.gif et que cette ressource ne se charge pas :

  • Le collecteur de journalisation des erreurs réseau de foobar.com est averti.
  • Le collecteur de journalisation des erreurs réseau de example.com n'est pas averti.

En règle générale, la journalisation des erreurs réseau reproduit les journaux côté serveur, générés sur le client.

Comme example.com n'a aucune visibilité sur les journaux de serveur de foobar.com, il n'a pas non plus de visibilité sur ses rapports de journalisation des erreurs réseau.

Déboguer les configurations de rapports

Si vous ne voyez pas de rapports s'afficher sur votre serveur, accédez à chrome://net-export/. Cette page est utile pour vérifier que les éléments sont correctement configurés et que les rapports sont envoyés correctement.

Qu'en est-il de ReportingObserver ?

ReportingObserver est un mécanisme de création de rapports associé, mais différent. Il est basé sur des appels JavaScript. Il n'est pas adapté à la journalisation des erreurs réseau, car les erreurs réseau ne peuvent pas être interceptées via JavaScript.

Exemple de serveur

Vous trouverez ci-dessous un exemple de serveur Node qui utilise Express. Il montre comment configurer la création de rapports pour les erreurs réseau et crée un gestionnaire dédié pour capturer le résultat.

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

Documentation complémentaire