Registrazione degli errori di rete (NEL)

Introduzione

Network Error Logging (NEL) è un meccanismo per raccogliere gli 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 segnalare gli errori a un server.

Panoramica dell'API di reporting precedente

Per utilizzare l'API di reporting legacy, devi impostare un'intestazione della 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 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 che segnali 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 dell'intestazione

Ogni configurazione dell'endpoint contiene un nome group, max_age e un array endpoints. Puoi anche scegliere se considerare i sottodomini quando segnali gli errori utilizzando il campo include_subdomains.

Campo Tipo Descrizione
group stringa Facoltativo. Se non viene specificato un nome group, l'endpoint viene denominato "default".
max_age numero Required. Un numero intero non negativo che definisce la durata dell'endpoint in secondi. Un valore "0" comporta la rimozione del gruppo di endpoint dalla cache dei report dello user agent.
endpoints Array<Object> Required. Un array di oggetti JSON che specificano l'URL effettivo del raccoglitore di report.
include_subdomains booleano Facoltativo. Un valore booleano che attiva il gruppo di endpoint per tutti i sottodomini dell'host dell'origine corrente. Se omesso o se viene specificato un valore 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.

Anche il campo max-age è obbligatorio e specifica per quanto tempo il browser deve utilizzare l'endpoint e segnalare gli errori.

Il campo endpoints è un array che fornisce funzionalità di failover e bilanciamento del carico. Consulta la sezione Failover e bilanciamento del carico. È importante notare che il browser selezionerà un solo endpoint, anche se il gruppo elenca diversi raccoglitori in endpoints. Se vuoi inviare un report a più server contemporaneamente, il backend dovrà inoltrare i report.

Come invia i report il browser?

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

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

Quando il browser invia i report?

I report vengono inviati fuori banda dalla tua app, il che significa che il browser controlla quando i report vengono inviati ai tuoi server.

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

L'utilizzo dell'API Reporting non comporta problemi di rendimento (ad es. contesa di rete con la tua app). Inoltre, non è possibile controllare quando il browser invia i 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"
             }]
           }

o 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"
             }]
           }

Una volta inviata l'intestazione Report-To, il browser memorizza nella cache gli endpoint in base ai relativi valori max_age e invia tutti gli avvisi/errori della console ai tuoi URL.

Failover e bilanciamento del carico

La maggior parte delle volte configurerai un raccoglitore di URL per gruppo. Tuttavia, poiché i report possono generare una grande quantità di traffico, la specifica include funzionalità di failover e bilanciamento del carico ispirate al record SRV DNS.

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

I raccoglitori di riserva vengono provati solo quando i caricamenti nei raccoglitori principali non vanno a buon fine.

Esempio: crea un raccoglitore di riserva all'indirizzo 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 di Network Error Logging

Configurazione

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

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

Successivamente, invia l'intestazione della risposta NEL per iniziare a raccogliere gli errori. Poiché NEL è un'opzione di attivazione per un'origine, devi inviare l'intestazione una sola volta. Sia NEL che Report-To verranno applicati alle richieste future alla stessa origine e continueranno a raccogliere errori in base al valore max_age utilizzato per configurare il raccoglitore.

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 raccoglitore 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 il caricamento di questa risorsa non va a buon fine:

  • Il raccoglitore NEL di foobar.com riceve una notifica
  • L'agente di raccolta NEL di example.com non riceve una notifica

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

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

Debug delle configurazioni dei report

Se non vedi i report sul tuo server, vai alla pagina chrome://net-export/. Questa pagina è utile per verificare che le impostazioni siano configurate correttamente e che i report vengano inviati correttamente.

Che cos'è ReportingObserver?

ReportingObserver è un meccanismo di generazione dei report correlato, ma diverso. Si basa sulle chiamate JavaScript. Non è adatto alla registrazione degli errori di rete, in quanto 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 la generazione di 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