Registrazione degli errori di rete (NEL)

Maud Nalpas
Maud Nalpas

Introduzione

Il logging degli errori di rete (NEL) è un meccanismo per raccogliere errori di rete lato client da un'origine.

Utilizza l'intestazione della risposta HTTP NEL per indicare al browser di raccogliere gli errori di rete, quindi si integra con l'API di reporting per segnalarli a un server.

Panoramica della versione precedente dell'API di reporting

Per utilizzare l'API di reporting precedente, devi impostare un'intestazione di risposta HTTP Report-To. Il suo valore è un oggetto che descrive un gruppo di endpoint a cui il browser deve segnalare gli errori:

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

Se l'URL dell'endpoint si trova su un'origine diversa dal tuo sito, l'endpoint deve supportare le richieste di preflight CORS. (ad es. 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).

Nell'esempio, l'invio di questa intestazione di risposta con la pagina principale configura il browser in modo da segnalare gli avvisi generati dal browser all'endpoint https://analytics.provider.com/browser-errors per max_age secondi. È importante notare che tutte le richieste HTTP successive effettuate dalla pagina (per immagini, script e così via) vengono ignorate. La configurazione viene impostata durante la risposta della pagina principale.

Spiegazione dei campi di intestazione

Ogni configurazione dell'endpoint contiene un array di nomi group, max_age e endpoints. Puoi anche scegliere se prendere in considerazione i sottodomini quando registri gli errori utilizzando il campo include_subdomains.

Campo Tipo Descrizione
group stringa (Facoltativo) Se non viene specificato un nome group, all'endpoint viene assegnato il nome "predefinito".
max_age numero Required. Un numero intero non negativo che definisce la durata dell'endpoint in secondi. Un valore pari a "0" comporta la rimozione del gruppo di endpoint dalla cache dei report dell'agente utente.
endpoints Array<oggetto> Required. Un array di oggetti JSON che specificano l'URL effettivo del tuo raccoglitore di report.
include_subdomains booleano (Facoltativo) Un valore booleano che attiva il gruppo di endpoint per tutti i sottodomini dell'host dell'origine attuale. Se il valore è omesso o è diverso da "true", i sottodomini non vengono segnalati all'endpoint.

Il nome group è un nome univoco utilizzato per associare una stringa a un endpoint. Utilizza questo nome in altri punti che si integrano con l'API di reporting per fare riferimento a un gruppo di endpoint specifico.

È obbligatorio anche il campo max-age e consente di specificare per quanto tempo il browser deve utilizzare l'endpoint e segnalare gli errori.

Il campo endpoints è un array per fornire funzionalità di failover e bilanciamento del carico. Consulta la sezione Failover e bilanciamento del carico. È importante tenere presente che il browser seleziona un solo endpoint, anche se il gruppo elenca diversi collezionisti in endpoints. Se vuoi inviare un report a più server contemporaneamente, il tuo backend dovrà inoltrarli.

In che modo il browser invia i report?

Il browser raggruppa periodicamente i report e li invia agli endpoint per i report che configuri.

Per inviare i report, il browser emette una richiesta POST con Content-Type: application/reports+json e un corpo contenente l'array di avvisi/errori rilevati.

Quando il browser invia i report?

I report vengono pubblicati out-of-band dalla tua app, il che significa che è il browser a controllare quando i report vengono inviati ai tuoi server.

Il browser tenta di inviare i report in coda nel momento più opportuno. Potrebbe avvenire non appena sono pronti (per fornire un feedback tempestivo allo sviluppatore), ma il browser può anche ritardare la pubblicazione se è occupato a elaborare lavori con priorità più elevata o se in quel momento l'utente si trova su una rete lenta e/o congestionata. Il browser potrebbe anche dare la priorità all'invio di report su una determinata origine, se l'utente è un visitatore frequente.

L'utilizzo dell'API Reporting non comporta problemi di prestazioni (ad es. contesa della rete con la tua app). Inoltre, non c'è modo di controllare quando il browser invia report in coda.

Configurazione di più endpoint

Una singola risposta può configurare più endpoint contemporaneamente inviando più intestazioni 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"
             }]
           }

oppure combinandoli in un'unica intestazione 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"
             }]
           }

Dopo che hai inviato l'intestazione Report-To, il browser memorizza nella cache gli endpoint in base ai relativi valori max_age e invia tutti questi avvisi/errori della console ai tuoi URL.

Failover e bilanciamento del carico

La maggior parte delle volte configuri un raccoglitore URL per gruppo. Tuttavia, poiché i report possono generare una buona quantità di traffico, le specifiche includono funzionalità di failover e bilanciamento del carico ispirate al record SRV DNS.

Il browser farà del suo meglio per inviare un report a massimo un endpoint in un gruppo. Agli endpoint può essere assegnato un weight per distribuire il carico, con ciascun endpoint che riceve una frazione specificata del traffico dei report. Puoi anche assegnare un priority agli endpoint per configurare i raccoglitori di riserva.

I raccoglitori di riserva vengono provati solo quando i caricamenti nei raccoglitori principali non riescono.

Esempio: crea un raccoglitore di riserva in 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}
             ]
           }

Configurazione della registrazione degli errori di rete

Configurazione

Per utilizzare NEL, configura l'intestazione Report-To con un raccoltore che utilizza un gruppo denominato:

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

Invia l'intestazione di risposta NEL per iniziare a raccogliere gli errori. Poiché NEL è attivato per un'origine, devi inviare l'intestazione solo una volta. Sia NEL sia Report-To verranno applicati alle richieste future alla stessa origine e continueranno a raccogliere gli errori in base al valore max_age utilizzato per configurare il collector.

Il valore dell'intestazione deve essere un oggetto JSON contenente un campo max_age e report_to. Utilizza quest'ultimo per fare riferimento al nome del gruppo del raccoltore di errori di rete:

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

Risorse secondarie

Esempio: se example.com carica foobar.com/cat.gif e la risorsa non riesce a caricarsi:

  • Il raccoglitore NEL di foobar.com viene informato
  • Il raccoglitore NEL di example.com non riceve notifiche

La regola generale è che NEL riproduce i log lato server, appena generati sul client.

Poiché example.com non ha visibilità sui log del server di foobar.com, non ha nemmeno visibilità sui report NEL.

Configurazioni dei report di debug

Se non visualizzi i report sul server, vai a chrome://net-export/. Questa pagina è utile per verificare che tutto sia configurato correttamente e che i report vengano inviati correttamente.

Che ne dici di ReportingObserver?

ReportingObserver è un meccanismo di segnalazione correlato, ma diverso. Si basa su chiamate JavaScript. Non è adatto per il logging degli errori di rete, poiché gli errori di rete non possono essere intercettati tramite JavaScript.

Server di esempio

Di seguito è riportato un esempio di server Node che utilizza Express. Mostra come configurare i report per gli errori di rete e crea un gestore dedicato per acquisire il risultato.

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

Per approfondire