Zurück zum Blog

Folgen und abonnieren

Nur auf Englisch verfügbar

Diese Seite ist momentan nur auf Englisch verfügbar. Wir entschuldigen uns für die Unannehmlichkeiten. Bitte besuchen Sie diese Seite später noch einmal.

Logging von Netzwerkfehlern aus Endnutzerperspektive

Hooman Beheshti

VP of Technology, Fastly

Wenn es um die Interaktion von Nutzern mit Websites und Anwendungen geht, kann es eigentlich nie zu viel Transparenz geben. Eine ganzheitliche Sichtweise, die uns über diese Erfahrungen, die Performance und Fehlerbedingungen informiert, wird daher immer nützlich sein. Daher sind alle Mechanismen und Metriken in diesem Zusammenhang extrem wertvoll.

Da wir bei Fastly ein großes, weltweit verteiltes Netzwerk von Servern betreiben, haben wir einen guten Einblick in die Interaktion von Webclients mit unserem Netzwerk aus Serverperspektive. Wir wollen aber einen vollständigen, durchgängigen Überblick über das Geschehen haben. Deshalb sind wir auch große Fans von Mechanismen, die uns ein Bild davon vermitteln, was aus der Sicht des Clients geschieht. Aus diesem Grund sind Browsertechnologien wie Navigation Timing und Resource Timing, die wir für Fastly Insights nutzen, so wichtig. Sie liefern uns Metriken, die uns ein Verständnis der Performance beim Kundenerlebnis vermitteln. Über die relativ neue Spezifikation Server Timing, mit der wir neben den Browsermetriken am Client auch Servermetriken erfassen können, haben wir in einem früheren Blogpost berichtet.

In diesem Blogpost befassen wir uns mit dem Network Error Logging, einer weiteren für die Transparenz wichtigen Spezifikation. Dabei erfahren Sie, wozu es gut ist, wie es funktioniert und wie Sie Fastly nutzen können, um die Daten zu erfassen, die es liefert.

Was bedeutet Network Error Logging?

Wie der Name schon sagt, ist Network Error Logging (kurz NEL) ein vorgeschlagener W3C-Standard für die Erfassung und Sammlung von Netzwerkfehler-/Ausfallereignissen im Browser. Während Navigation Timing, Resource Timing und bis zu einem gewissen Grad auch Server Timing dazu gedacht sind, die Performance der Clients sichtbar zu machen, zielt NEL darauf ab, Einblicke in die Verfügbarkeit zu liefern. 

Mit anderen Worten: NEL dient zur Aufdeckung von Fehlern und zur Bereitstellung eines Reporting-Mechanismus für Situationen, in denen ein Client überhaupt nicht auf eine Webressource zugreifen kann. Ein Server weiß nie, ob ein Client ihn nicht erreichen konnte. Daher können serverseitige Metriken nur bedingt zur Aufdeckung von Problemen beitragen. NEL hilft, diese Lücke zu schließen und bringt uns einem vollständigen, durchgängigen Bild viel näher.

Wie funktioniert NEL?

Kurz gesagt, erkennt NEL Netzwerkfehler und Ausfälle im Browser – während der DNS-, Verbindungs- (TCP und TLS) oder Anwendungsphase (HTTP) einer Anfrage – und meldet sie an einen oder mehrere Reporting Endpoints. Es arbeitet mit der Reporting API zusammen, einer weiteren W3C-Spezifikation, die einen generischen Reporting-Mechanismus für einen Browser definiert, um jede Funktion zu ermöglichen, für die irgendwo Berichte geliefert werden müssen. NEL nur ist einer der möglichen Nutzer der Reporting-API (auch die Content Security Policy etwa nutzt diese API). Es ist die Aufgabe von NEL, Richtlinien für das Logging von Fehlern für Origin-Server zu definieren und Berichte für verschiedene Fehlerzustände zu erstellen. Es ist dann die Aufgabe der Reporting API, diese Berichte an einen oder mehrere Endpoints zu liefern.

Der Mechanismus wird durch zwei Antwort-Header aktiviert:

  • Der etwas seltsam benannte und nahezu reißerische Antwort-Header NEL definiert die NEL-Richtlinie für den Origin-Server, der den Header sendet.

  • Der Header Report-to definiert eine Gruppe von einem oder mehreren Endpoints für die Zustellung von Berichten. Der Header NEL verweist dann über den hier angegebenen Gruppennamen auf eine Endpoint-Gruppe, an die Berichte gesendet werden sollen.

Der Header von NEL ist ein JSON-Objekt und sieht in seiner einfachsten Form wie folgt aus:

NEL: {"report_to": "network-errors", "max_age": 2592000}

Das Element report_to ist erforderlich und gibt an, wohin die Berichte gehen (in diesem Beispiel ist „network-errors“ der Name einer im Header Report-to definierten Endpoint-Gruppe, die wir gleich sehen werden). Das Element max_age ist ebenfalls erforderlich und legt in Sekunden fest, wie lange diese Richtlinie im Browser gültig ist. Bei einem max_age von 0 wird die NEL-Richtlinie vom Client entfernt (hierbei handelt es sich um den einzigen Umstand, unter dem das Element report_to weggelassen werden kann).

Zusätzlich zu report_to und max_age gibt es noch einige andere optionale Elemente, die ein NEL-Header enthalten kann:

  • Das Element include_subdomains ist ein boolescher Wert, der angibt, ob diese Richtlinie für diesen Origin-Server und alle seine Subdomains gilt. Standardmäßig ist sie false.

  • Das Element failure_fraction definiert eine Sampling Rate für das Reporting von Fehlern und ist praktisch, wenn nicht jeder einzelne Fehler aufgezeichnet werden soll. Der Wert liegt zwischen 0,0 und 1,0 (beide Grenzwerte eingeschlossen). Der Standardwert ist 1,0.

  • Ebenso erlaubt NEL das Reporting von erfolgreichen Transaktionen. Das Element success_fraction definiert die Sampling Rate für das Reporting von Erfolgen. Auch sie kann zwischen 0,0 und 1,0 liegen, ist aber standardmäßig auf 0,0 festgelegt. Die Aufzeichnung von Erfolgs- und Misserfolgsbedingungen ist eine gute Möglichkeit, Misserfolge prozentual zu messen.

Nehmen wir zum Beispiel an, dass Antworten von https://example.com einen NEL-Header haben, der folgendermaßen aussieht:

NEL: {"report_to": "network-errors", "max_age": 2592000, "include_subdomains": true, "success_fraction": 0.01, "failure_fraction": 0.05}

Durch diesen Header werden eine NEL-Richtlinie im Browser registriert, die 30 Tage lang gültig ist, Fehler für example.com und seine Subdomains erfasst und 1 % der Erfolge und 5 % der Misserfolge gemeldet. Die Berichte werden an die Endpoint-Gruppe „network-errors“ gesendet, die im Header Report-to definiert ist.

Der Header Report-to ist ebenfalls ein JSON-Objekt und definiert eine Gruppe von einem oder mehreren Endpoints für das Reporting. Es handelt sich um eine Gruppe, da Failover- und Loadbalancing-Funktionen in die Reporting-API integriert sind. In der einfachsten Form sieht er wie folgt aus:

Report-to: {"group": "network-errors", "max_age": 2592000, "endpoints": [{"url": "https://nel.example.com/report"}]}

Das Element group gibt einen Namen für die Gruppe von einem oder mehreren definierten Endpoints an. Er ist eigentlich optional und standardmäßig auf „default“ eingestellt. Es ist aber wahrscheinlich sinnvoll, einen aussagekräftigen Namen zu verwenden. Beachten Sie, dass der Header NEL auf diesen Gruppennamen verweist. Wie im Header NEL ist max_age erforderlich und definiert die Lebensdauer dieser Gruppe in Sekunden. Ein max_age von 0 entfernt diese Gruppe vom Client. Außerdem gibt es ein optionales boolesches Element include_subdomains, das genau dasselbe bedeutet wie im Header NEL. Wenn Sie möchten, dass Subdomain-Fehler gemeldet werden, muss dies in beiden Headern enthalten sein. 

Das Element endpoints ist ein Array von JSON-Objekten, die jeden Endpoint innerhalb der Gruppe definieren. Jeder in diesem Array angegebene Endpoint kann bis zu drei Elemente haben:

  • Das Element endpoints[].url ist erforderlich und definiert die URL, an die die Meldungen gesendet werden sollen.

  • Das Element endpoints[].priority ist eine optionale nicht-negative Ganzzahl, die das Failover-Verhalten definiert. Der Standardwert ist 1. Bei der Übermittlung einer Meldung, wird zuerst der Endpoint mit der niedrigsten Priorität in der Gruppe versucht. Wenn die Übermittlung fehlschlägt, wird die Meldung an den Endpoint mit der nächsthöheren Priorität gesendet.

  • Das Element endpoints[].weight ist ebenfalls eine optionale nicht-negative Ganzzahl und definiert das Loadbalancing-Verhalten. Der Standardwert ist auch hier 1. Die Endpoints in einer Gruppe (vorausgesetzt, sie haben dieselbe Priorität) werden anhand ihrer Gewichtung per Loadbalancing ausgeglichen.

Betrachten wir also diesen Antwort-Header von https://example.com:

Report-to: {"group":"network-errors","max_age":2592000,"include_subdomains":true,"endpoints":[{"url":"https://nel1.example.com/report","priority":1,"weight":1},{"url":"https://nel2.example.com/report","priority":1,"weight":3},{"url":"https://nel3.example.com/report","priority":2}]}

Dieser Header definiert eine Gruppe mit dem Namen „network-errors“, die 30 Tage lang gültig ist und Berichte für Subdomains von example.com enthält. Die Meldungen werden beim Loadbalancing auf https://nel1.example.com/report und https://nel2.example.com/report verteilt, wobei 25 % an den ersten und 75 % an den zweiten Endpoint gehen. Wenn einer dieser Endpoints ausfällt, erhält der andere 100 %. Wenn beide ausfallen, gehen alle Meldungen an https://nel3.example.com/report.

Damit wird hoffentlich klarer, wie die Header NEL und Report-to zusammenarbeiten, um Fehlerzustände zu erfassen und zu melden: Der Header NEL definiert die Richtlinie und der Header Report-to definiert, wohin die Meldungen gesendet werden.

Jede einzelne Meldung ist ein JSON-Objekt. Die Zustellung erfolgt durch Senden eines Arrays mit einer oder mehreren Meldungen als Body einer HTTP POST-Anfrage an einen Reporting-Endpoint. Ein wichtiger Aspekt des Reportings ist, dass die Meldungen nicht unbedingt in Echtzeit gesendet werden. Der Client kann sie in eine Warteschlange stellen und in Batches senden. Schauen wir uns ein Beispiel für eine Meldung an, um dies etwas näher zu erläutern:

[
  {
    "age": 666,
    "body": {
      "elapsed_time": 37,
      "method": "GET",
      "phase": "connection",
      "protocol": "http/1.1",
      "referrer": "https://www.example.com/",
      "sampling_fraction": 1,
      "server_ip": "1.2.3.4",
      "status_code": 0,
      "type": "tcp.reset"
    },
    "type": "network-error",
    "url": "https://www.example.com/image.png",
    "user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36"
  }
]

Die Spezifikationen gehen auf all diese Bereiche ein. Aber ich möchte hier ein paar davon genauer beleuchten. 

Wie bereits besprochen, meldet NEL DNS-, Verbindungs- oder Anwendungsfehler. Dies wird durch den Wert phase im body der Meldung angezeigt. In diesem Beispiel handelt es sich um einen Verbindungsfehler. NEL definiert eine Reihe von Fehlertypen. In diesem Fall ist der Fehlertyp (type) tcp.reset. Dies bedeutet, dass die TCP-Verbindung während des Abrufs von https://www.example.com/image.pngzurückgesetzt wurde.

Da Meldungen möglicherweise in eine Warteschlange gestellt und in Batches gesendet werden, können age, elapsed_time und ein wenig Mathematik helfen, den Zeitpunkt des Auftretens von Fehlern zu berechnen. Das Element age der Meldung gibt die Zeitspanne zwischen dem Auftreten des Fehlers und dem Versenden der Meldung an. In unserem Beispiel wurde die Meldung mehr als 11 Minuten nach dem Auftreten des Fehlers gesendet (dies kann noch viel höher sein!). Hätten wir “age”: 0 angegeben, hätte dies bedeutet, dass die Meldung sofort nach der Fehlererkennung gesendet wurde. elapsed_time gibt an, wie viel Zeit zwischen dem Beginn der Anfrage und dem Zeitpunkt verstrichen ist, an dem der Browser entschieden hat, dass es sich um einen Fehler handelt (oder wann er aufgegeben hat). In diesem Fall dauerte es 37 Sekunden zwischen dem Zeitpunkt, an dem der Client die Anfrage an https://www.example.com/image.png stellte und dem Zeitpunkt, an dem die TCP-Verbindung zurückgesetzt wurde. Die Meldungen enthalten absichtlich keinen Zeitstempel, um der Zeitverschiebung und der Tatsache Rechnung zu tragen, dass nicht alle Uhren der Clients korrekt gehen. Wenn Sie jedoch diese beiden Zahlen berücksichtigen und einen Zeitstempel am Endpoint des Reportings für den Eingang der Meldungen loggen, können Sie mit ziemlicher Sicherheit sagen, wann dieser Fehler beim Client tatsächlich aufgetreten ist.

Fastly bei der Bereitstellung von NEL

Wir haben uns mit den Grundlagen von NEL befasst und damit, wie hilfreich Einblicke in Client-Ausfälle und Fehlerzustände sein können. Gehen wir jetzt aber einen Schritt weiter, um zu sehen, wie wir diese Mechanismen mit der Edge-Cloud-Plattform von Fastly in der Praxis nutzen können.

Das Hinzufügen von Antwort-Headern zur Erstellung einer NEL-Richtlinie und von Reporting Endpoints ist mit VCL recht einfach:

Aber das ist noch nicht alles! Ähnlich wie bei beacon termination können wir mit Fastly einen Endpoint für das Reporting ohne Origin-Server zum Erfassen und Loggen von NEL-Meldungen erstellen. Dazu gehört zwar etwas mehr als nur zwei Zeilen Code, aber es ist trotzdem verhältnismäßig einfach.

Da die NEL-Meldungen vom Browser als POST-Bodies gesendet werden, stellt der Client zunächst eine CORS-preflight-Anfrage, um zu prüfen, ob er POST-Anfragen an den Endpoint senden kann. Zunächst müssen wir also sicherstellen, dass der Endpoint diese Preflights richtig verarbeiten kann. Das bedeutet, dass die richtigen Access-Control-*Header in den Antworten enthalten sein müssen:


Hier brechen wir in vcl_recv jede Anfrage an /report ab und erzeugen in vcl_error eine synthetische 204 (No Content) mit den entsprechenden CORS Response Headern. Damit werden die Preflight-OPTIONS-Anfrage und die Meldungen selbst abgewickelt, da wir auf die POST-Anfragen für die Zustellung der Berichte ebenfalls mit einer 204 antworten können. 

Jetzt muss die Meldung nur noch an einem Logging Endpoint gespeichert werden, wenn sie über einen POST-Body gesendet wird. Wir können die VCL Variable req.postbody für den Inhalt der Meldung verwenden. Da wir aber die Kontrolle darüber haben, was wir in VCL loggen, können wir auch zusätzliche Informationen protokollieren, die uns später bei der Analyse helfen. Wir haben bereits darüber gesprochen, wie der Zeitstempel des Empfangs hilfreich sein kann, aber es gibt noch eine Reihe anderer VCL Variablen, die sich ebenfalls als nützlich erweisen könnten. Hier ein Beispiel, bei dem wir die NEL-Meldung, den Zeitstempel ihres Eintreffens und einige Geo-IP-Informationen über den Client in vcl_log in einem GCS Bucket loggen:

In diesem Fall ist „reports_bucket“ der Name eines im Fastly Service konfigurierten GCS Logging Endpoints. Wir haben jede Logzeile als einzelnes JSON Objekt erstellt. Das bedeutet, dass jede Datei in GCS im durch Zeilenumbrüche getrennten JSON Format vorliegt, was beim späteren Export zur Analyse hilfreich ist (beachten Sie, dass wir GCS als Beispiel gewählt haben; Sie können die Meldungen an jeden beliebigen Logging Endpoint senden).

Es wäre viel einfacher, direkt zu loggen, beispielsweise in BigQuery von Google, sodass wir die Daten sofort analysieren könnten, sobald sie geloggt sind. Das Problem dabei ist der Body der Meldungen, denn da sie in Batches gesendet werden können, kann eine einzige Anfrage mehrere Meldungen enthalten. Da VCL das JSON nicht verarbeiten kann, um die einzelnen Meldungen zu extrahieren, könnte eine einzelne Tabellenzelle mehrere Meldungen enthalten, wenn wir den gesamten Bericht an BigQuery senden würden. Dies würde wiederum die Queries erschweren. Stattdessen können wir den gesamten Body an GCS senden und dann Cloud Functions verwenden, um jede Datei zu verarbeiten und die einzelnen Meldungen zu extrahieren, bevor wir sie in BigQuery einfügen, eine Zeile pro Meldung.

Diese beiden letzten Schritte könnten wir uns sparen, wenn wir in der Lage wären, die Anfrage-Bodies zu lesen und zu verarbeiten. Zufälligerweise ist dies eine der vielen Möglichkeiten, die Compute für uns bereithält. In einem zukünftigen Blogpost werden wir darüber sprechen, wie wir Compute nutzen können, um das Logging von JSON Bodies viel einfacher und effizienter zu gestalten.

Was wir aus der Nutzung von NEL lernen können

NEL scheint noch nicht sehr weit verbreitet zu sein. Die Unterstützung von Browsern ist nicht sehr verbreitet, und wenn man sich die Daten aus dem HTTP Archive ansieht (das bis vor kurzem Aufzeichnungen über Anfragen von mehr als 5,4 Millionen eindeutigen Hostnamen im Internet enthielt), dann enthalten nur etwa 1,7 % der Hosts NEL-Header in ihren Antworten. Aber das Potenzial von NEL ist groß, und da wir große Fans von großen Dingen sind, haben wir im Rahmen von Fastly Insights damit experimentiert. Wir haben einige praktische Dinge gelernt, die ich Ihnen nicht vorenthalten möchte:

  • Die Zustellung von Meldungen wird in den Devtools von Chrome nicht als Anfrage angezeigt. Wenn Sie also sehen möchten, welche Anfragen vom Client gesendet werden (oder eine Fehlersuche durchführen möchten, wenn dies nicht der Fall ist), müssen Sie kreativ werden. Dazu möchte ich Ihnen Wireshark ans Herz legen, das vielleicht beste Tool zur Fehlersuche seit Menschengedenken.

  • Chrome zeigt Ihnen jedoch sowohl registrierte NEL-Richtlinien als auch Reporting Endpoints über chrome://net-export an. Der Prozess ist etwas aufwändiger als die alte chrome://net-internals-Schnittstelle, bei der alles an einem Ort zu finden war, aber wenn Sie sich einmal durchgearbeitet haben, sollten Sie alles NEL-Bezogene sehen.

  • Wenn Sie mehrere Richtlinien haben, die für einen einzigen Origin-Server gelten, gewinnt die spezifischere Richtlinie. Wenn Sie also eine Richtlinie für https://example.com mit include_subdomains und eine andere Richtlinie für https://www.example.com haben, wird der Browser bei einem Fehler für eine Ressource auf https://www.example.com die Richtlinie für https://www.example.com verwenden. Es kann sinnvoll sein, die NEL-Header und Reporting Endpoints in Ihrer Anwendung konsistent zu halten, um Überraschungen zu vermeiden. Wenn Sie jedoch Flexibilität über Subdomains hinweg wünschen, gibt es eine Möglichkeit, dies zu erreichen.

  • Die Reporting Endpoints selbst können unter eine NEL-Richtlinie fallen. So kann NEL Sie informieren, wenn es Probleme bei der Fehlerzustellung gibt.

  • Aus Sicherheitsgründen handhabt NEL Fehler bei Origin-Servern, deren IP-Adresse sich ändert, separat. Dies kann häufig vorkommen, wenn Sie mehrere IP-Adressen für einen Origin-Server verwenden (z. B. mit Round-Robin-DNS), und gelegentlich, wenn Sie große Content Delivery Networks verwenden, die dynamisches Routing einsetzen, um Nutzer an einen Edge-Standort zu bringen. In solchen Situationen verlieren die Fehlermeldungen ein wenig an Granularität. Ich möchte hier nicht zu sehr ins Detail gehen, aber wenn Sie sich mit NEL befassen wollen und dies relevant erscheint, ist dieser Abschnitt der Spezifikation eine Lektüre wert.

  • Die Spezifikationen sowohl für NEL als auch für die Reporting-API entwickeln sich weiter. Die jüngste Entwicklerversion für NEL hat sich seit der letzten offiziell veröffentlichten Version etwas verändert und wird, wenn sie übernommen wird, die Möglichkeit bieten, auch Anfrage- und Antwort-Header als Teil eines NEL-Berichts zu erfassen. Die neueste Entwicklerversion der Reporting-API sieht ebenfalls anders aus als die zuletzt veröffentlichte Version. Der Header Report-to wurde umbenannt und als strukturierter Header neu definiert, was keine schlechte Idee ist. Wahrscheinlich gibt es auch noch andere Änderungen. Wenn das alles interessant war und Sie noch wach sind und lesen, sollten Sie die Entwicklung der Spezifikationen im Auge behalten.

  • Seien Sie vorsichtig, wenn Sie versuchen, Fehler an dieselbe Stelle zu melden, die Sie auch auf Fehler überwachen. Hier kann der Failover-Mechanismus der Reporting-API hilfreich sein.


Vom Konzept her bin ich ein großer Fan des Potenzials von NEL. Es bietet die Möglichkeit, den Zustand von Anwendungen und Netzwerken aus der Sicht des Clients zu betrachten. Ich bin auch ein großer Fan davon, Anwendungen mithilfe der Edge-Funktionen von Fastly zu erstellen, und ein Endpoint für das Reporting ohne Origin-Server ist ein perfektes Beispiel dafür. Wenn auch Sie diese Mechanismen nützlich finden und damit experimentieren, melden Sie sich bei uns. Und wenn Sie Ihre Erkenntnisse und Erfahrungen in Blogposts und Präsentationen teilen möchten, wäre das großartig.