Netzwerkfehler-Logging (NEL)

Einleitung

Network Error Logging (NEL) ist ein Mechanismus zum Erfassen von clientseitigen Netzwerkfehlern aus einem Ursprung.

Der HTTP-Antwortheader NEL teilt dem Browser mit, dass er Netzwerkfehler erfasst, und kann dann in die Reporting API eingebunden werden, um die Fehler an einen Server zu melden.

Übersicht über die alte Reporting API

Wenn Sie die alte Reporting API verwenden möchten, müssen Sie einen Report-To-HTTP-Antwortheader festlegen. Der 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 sich Ihre Endpunkt-URL an einem anderen Ursprung als Ihre Website befindet, 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 im Beispiel diesen Antwortheader mit Ihrer Hauptseite senden, konfiguriert der Browser den Browser so, dass im Browser generierte Warnungen max_age Sekunden lang an den Endpunkt https://analytics.provider.com/browser-errors gesendet werden. Wichtig: Alle nachfolgenden HTTP-Anfragen der Seite (für Bilder, Skripts usw.) werden ignoriert. Die Konfiguration wird während der Antwort auf die Hauptseite eingerichtet.

Erläuterung der Headerfelder

Jede Endpunktkonfiguration enthält einen group-Namen, 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 „Standard“.
max_age Zahl 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 Report Collectors angeben.
include_subdomains boolean Optional. Ein boolescher Wert, der die Endpunktgruppe für alle Subdomains des Hosts des aktuellen Ursprungs aktiviert. Wenn sie weggelassen oder ein anderer Wert als „true“ festgelegt ist, 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 auch an anderen Stellen, an denen die Reporting API eingebunden ist, 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, das Failover- und Load-Balancing-Features bereitstellt. Weitere Informationen finden Sie im Abschnitt zu 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 fasst Berichte regelmäßig in Batches zusammen und sendet sie an die von Ihnen konfigurierten Endpunkte für die Berichterstellung.

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

Wann sendet der Browser Berichte?

Berichte werden Out-of-Band von Ihrer Anwendung bereitgestellt, d. h. der Browser steuert, wann Berichte an Ihre Server gesendet werden.

Der Browser versucht, Berichte in der Warteschlange zum passenden Zeitpunkt zu erstellen. Dies kann der Fall sein, sobald sie bereit sind, um dem Entwickler zeitnahes Feedback zu geben. Der Browser kann die Zustellung aber auch verzögern, wenn er gerade Arbeit mit höherer Priorität verarbeitet oder der Nutzer sich gerade in einem langsamen und/oder überlasteten Netzwerk befindet. Der Browser kann auch priorisieren, zuerst Berichte über einen bestimmten Ursprung zu senden, wenn der Nutzer regelmäßig die Website besucht.

Bei 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

Für eine einzelne Antwort können mehrere Endpunkte gleichzeitig konfiguriert werden. Dazu werden mehrere Report-To-Header gesendet:

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 in einem einzigen HTTP-Header kombiniert werden:

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 du den Report-To-Header gesendet hast, speichert der Browser die Endpunkte gemäß ihren max_age-Werten im Cache und sendet alle diese schädlichen Warnungen/Fehler an deine 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 SRV-Eintrag des DNS inspiriert sind.

Der Browser versucht dann, einen Bericht an maximal einen Endpunkt in einer Gruppe zu senden. Endpunkte kann eine weight zur Verteilung der Last zugewiesen werden, wobei jeder Endpunkt einen bestimmten Teil des Berichtstraffics erhält. Endpunkte kann auch eine priority zugewiesen werden, um Fallback-Collectors einzurichten.

Fallback-Collectors werden nur ausprobiert, wenn Uploads zu primären 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 der Fehlererfassung zu beginnen. Da NEL für einen Ursprung aktiviert 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 Wert max_age, mit dem der Collector eingerichtet 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:

  • 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, gibt es auch keinen Einblick in die NEL-Berichte.

Fehler in Berichtskonfigurationen beheben

Wenn keine Berichte auf Ihrem Server angezeigt werden, wechseln Sie zu chrome://net-export/. Auf dieser Seite können Sie prüfen, ob alles richtig konfiguriert ist und Berichte korrekt gesendet werden.

Was ist mit ReportingObserver?

ReportingObserver ist ein verwandter, aber unterschiedlicher Meldemechanismus. Sie 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. Sie erfahren, wie Sie die Berichterstellung für Netzwerkfehler konfigurieren, und erstellt einen speziellen 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