Netzwerkfehler-Logging (NEL)

Einleitung

Das Logging von Netzwerkfehlern (Network Error Logging, NEL) ist ein Mechanismus zum Erfassen clientseitiger Netzwerkfehler aus einer Quelle.

Über den HTTP-Antwortheader NEL wird der Browser angewiesen, Netzwerkfehler zu erfassen. Diese Fehler werden dann in die Reporting API eingebunden, um sie an einen Server zu melden.

Überblick über die alte Reporting API

Wenn Sie die alte Reporting API verwenden möchten, müssen Sie einen Report-To-HTTP-Antwortheader festlegen. Sein Wert ist ein Objekt, das eine Endpunktgruppe beschreibt, an die der Browser Fehler melden kann:

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

Wenn die Endpunkt-URL einen anderen Ursprung hat als Ihre Website, sollte der Endpunkt CORS-Preflight-Anfragen unterstützen. (z.B. 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).

Wenn Sie diesen Antwortheader im Beispiel mit Ihrer Hauptseite senden, wird der Browser so konfiguriert, dass browsergenerierte Warnungen max_age Sekunden lang an den Endpunkt https://analytics.provider.com/browser-errors gesendet werden. Beachten Sie, dass alle nachfolgenden HTTP-Anfragen von der Seite (z. B. Bilder, Skripts usw.) ignoriert werden. Die Konfiguration wird während der Antwort auf die Hauptseite eingerichtet.

Erläuterung der Headerfelder

Jede Endpunktkonfiguration enthält einen group-Namen, eine max_age und ein endpoints-Array. Mit dem Feld include_subdomains können Sie auch festlegen, ob Subdomains beim Melden von Fehlern berücksichtigt werden sollen.

Field Typ Beschreibung
group String Optional. Wenn kein group-Name angegeben ist, erhält der Endpunkt den Namen „default“.
max_age number Erforderlich. Eine nicht negative Ganzzahl, die die Lebensdauer des Endpunkts in Sekunden definiert. Der Wert „0“ führt dazu, dass die Endpunktgruppe aus dem Berichtscache des User-Agents entfernt wird.
endpoints Array<Objekt> Erforderlich. Ein Array von JSON-Objekten, die die tatsächliche URL des Berichts-Collectors angeben.
include_subdomains boolean Optional. Ein boolescher Wert, mit dem die Endpunktgruppe für alle Subdomains des Hosts des aktuellen Ursprungs aktiviert wird. Wenn das Attribut weggelassen wird oder einen anderen Wert als „true“ hat, werden die Subdomains nicht an den Endpunkt gemeldet.

Der Name group ist ein eindeutiger Name, mit dem ein String mit einem Endpunkt verknüpft wird. Verwenden Sie diesen Namen an anderen Stellen, an denen die Reporting API eingebunden wird, um auf eine bestimmte Endpunktgruppe zu verweisen.

Das Feld max-age ist ebenfalls erforderlich und gibt an, wie lange der Browser den Endpunkt verwenden und Fehler an ihn melden soll.

Das Feld endpoints ist ein Array zur Bereitstellung von Failover- und Load-Balancing-Funktionen. Weitere Informationen finden Sie im Abschnitt Failover und Load-Balancing. Beachten Sie, dass der Browser nur einen Endpunkt auswählt, auch wenn die Gruppe mehrere Collectors in endpoints auflistet. Wenn Sie einen Bericht an mehrere Server gleichzeitig senden möchten, muss Ihr Back-End die Berichte weiterleiten.

Wie sendet der Browser Berichte?

Der Browser erstellt regelmäßig Batch-Berichte und sendet sie an die von Ihnen konfigurierten Endpunkte für die Berichterstellung.

Zum Senden von Berichten gibt der Browser eine POST-Anfrage mit Content-Type: application/reports+json und einem Textkörper mit dem Array der erfassten Warnungen/Fehler aus.

Wann sendet der Browser Berichte?

Berichte werden Out-of-Band von Ihrer Anwendung gesendet. Das bedeutet, dass der Browser steuert, wann Berichte an Ihren oder Ihre Server gesendet werden.

Der Browser versucht, Berichte in der Warteschlange zum optimalen Zeitpunkt bereitzustellen. Dies kann geschehen, sobald sie bereit sind (um dem Entwickler zeitnah Feedback zu geben). Der Browser kann die Übermittlung aber auch verzögern, wenn er gerade mit der Verarbeitung von Arbeiten mit höherer Priorität beschäftigt ist oder wenn der Nutzer zu diesem Zeitpunkt ein langsames und/oder überlastetes Netzwerk verwendet. Der Browser kann das Senden von Berichten zu einem bestimmten Ursprung auch zuerst priorisieren, wenn der Nutzer ein häufiger Besucher ist.

Bei der Verwendung der Reporting API gibt es kaum oder gar keine Leistungsbedenken (z.B. Netzwerkkonflikte mit Ihrer App). Außerdem lässt sich nicht steuern, wann der Browser Berichte in der Warteschlange sendet.

Mehrere Endpunkte konfigurieren

Mit einer einzelnen Antwort können durch Senden mehrerer Report-To-Header mehrere Endpunkte gleichzeitig konfiguriert werden:

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

oder durch Kombinieren in einem einzigen HTTP-Header:

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

Nachdem Sie den Report-To-Header gesendet haben, speichert der Browser die Endpunkte gemäß ihren max_age-Werten im Cache und sendet alle diese schädlichen Konsolenwarnungen/-fehler an Ihre URLs.

Failover und Load-Balancing

Meistens konfigurieren Sie einen URL-Collector pro Gruppe. Da die Berichterstellung jedoch viel Traffic generieren kann, enthält die Spezifikation Failover- und Load-Balancing-Funktionen, die vom DNS-SRV-Eintrag inspiriert sind.

Der Browser versucht, einen Bericht an maximal einen Endpunkt in einer Gruppe zu senden. Den Endpunkten kann ein weight zugewiesen werden, um die Last zu verteilen. Dabei erhält jeder Endpunkt einen bestimmten Anteil des Berichtstraffics. Endpunkte kann auch ein priority zugewiesen werden, um Fallback-Collectors einzurichten.

Fallback-Collectors werden nur dann versucht, wenn Uploads in primäre Collectors fehlschlagen.

Beispiel: Erstellen Sie einen Fallback-Collector unter 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}
             ]
           }

Netzwerkfehler-Logging einrichten

Einrichtung

Wenn Sie NEL verwenden möchten, richten Sie den Header Report-To mit einem Collector ein, der eine benannte Gruppe verwendet:

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

Senden Sie als Nächstes den Antwortheader NEL, um mit dem Erfassen der Fehler zu beginnen. Da NEL die Zustimmung für einen Ursprung ist, müssen Sie den Header nur einmal senden. Sowohl NEL als auch Report-To gelten für zukünftige Anfragen an denselben Ursprung und erfassen weiterhin Fehler gemäß dem max_age-Wert, der zum Einrichten des Collectors verwendet wurde.

Der Headerwert sollte ein JSON-Objekt sein, das die Felder max_age und report_to enthält. Verwenden Sie letzteres, um auf den Gruppennamen Ihres Netzwerkfehler-Collectors zu verweisen:

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

Unterressourcen

Beispiel: Wenn example.com foobar.com/cat.gif lädt und diese Ressource nicht geladen werden kann:

  • Der NEL-Collector von foobar.com wird benachrichtigt
  • Der NEL-Collector von example.com wird nicht benachrichtigt

Als Faustregel gilt, dass NEL serverseitige Logs reproduziert, die gerade auf dem Client generiert wurden.

Da example.com keinen Einblick in die Serverlogs von foobar.com hat, hat es auch keinen Einblick in seine NEL-Berichte.

Fehler in Berichtskonfigurationen beheben

Wenn auf Ihrem Server keine Berichte angezeigt werden, rufen Sie chrome://net-export/ auf. Auf dieser Seite können Sie überprüfen, ob alles richtig konfiguriert ist und Berichte ordnungsgemäß gesendet werden.

Was ist mit ReportingObserver?

ReportingObserver ist ein ähnliches Meldeverfahren, aber anders. Es basiert auf JavaScript-Aufrufen. Sie eignet sich nicht für das Logging von Netzwerkfehlern, da Netzwerkfehler nicht über JavaScript abgefangen werden können.

Beispielserver

Unten sehen Sie ein Beispiel für einen Knotenserver, der Express verwendet. Er zeigt, wie die Berichterstellung für Netzwerkfehler konfiguriert wird, und erstellt einen dedizierten Handler, um das Ergebnis zu erfassen.

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

Weitere Informationen