Registrazione degli errori di rete (NEL)

Maud Nalpas
Maud Nalpas

Introduzione

Il logging degli errori di rete (NEL) è un meccanismo per la raccolta di 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 della versione precedente dell'API di reporting

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

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

Se l'URL dell'endpoint risiede su un'origine diversa dal tuo sito, l'endpoint dovrebbe 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 avvisi generati dal browser all'endpoint https://analytics.provider.com/browser-errors per max_age secondi. È importante notare che tutte le successive richieste HTTP effettuate dalla pagina (per immagini, script e così via) vengono ignorate. La configurazione viene impostata durante la risposta della pagina principale.

Spiegazione dei campi intestazione

Ogni configurazione di 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 Campo facoltativo. Se non viene specificato un nome group, all'endpoint verrà assegnato il nome "default".
max_age numero Obbligatorio. Un numero intero non negativo che definisce la durata dell'endpoint in secondi. Se il valore è "0", il gruppo di endpoint viene rimosso dalla cache dei report dello user agent.
endpoints Array<Object> Obbligatorio. Un array di oggetti JSON che specifica l'URL effettivo del raccoglitore di report.
include_subdomains boolean Campo facoltativo. Un valore booleano che abilita il gruppo di endpoint per tutti i sottodomini dell'host dell'origine corrente. Se omesso o in un formato diverso da "true", i sottodomini non vengono segnalati all'endpoint.

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

È obbligatorio anche il campo max-age, che specifica per quanto tempo il browser deve utilizzare l'endpoint e segnala 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 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.

In che modo il browser invia i report?

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

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 che sono stati acquisiti.

Quando il browser invia le segnalazioni?

I report vengono inviati fuori banda dall'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 al momento più opportuno. L'operazione potrebbe essere completata non appena le versioni sono pronte (per fornire un feedback tempestivo allo sviluppatore), ma il browser può anche ritardare la consegna se è impegnato a elaborare attività con priorità più elevata o se l'utente si trova su una rete lenta e/o congestionata in quel momento. Il browser può anche dare la priorità all'invio di report su una determinata origine, se l'utente è un visitatore abituale.

Quando utilizzi l'API di reporting, i problemi relativi alle prestazioni (ad es. conflitto di rete con l'app) sono minimi o nulli. 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"
             }]
           }

o combinandole 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 aver inviato 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

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

Il browser farà del proprio meglio per inviare un report a al massimo un endpoint in un gruppo. È possibile assegnare un weight agli endpoint per distribuire il carico e ogni endpoint riceve una frazione specificata di traffico dei report. Puoi anche assegnare un priority agli endpoint 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 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 raccoglitore 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 è attivato 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 la risorsa non viene caricata:

  • Il raccoglitore NEL di foobar.com è stato avvisato
  • Il raccoglitore NEL di example.com non ha ricevuto una notifica

Come regola generale, 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 visibilità nemmeno sui report NEL.

Debug delle configurazioni dei report

Se non visualizzi i report sul tuo server, vai a chrome://net-export/. Questa pagina è utile per verificare che gli elementi siano configurati correttamente e i report vengano inviati correttamente.

Che cosa succede a ReportingObservationr?

ReportingObserver è un meccanismo di generazione di report correlato, ma diverso. Si basa sulle 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 nodo che utilizza Express. Mostra come configurare i report per gli errori di rete e crea un gestore dedicato per acquisire i risultati.

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