LogRock: Testen durch Protokollierung

Logrock

LogRock: Testen durch Protokollierung


Seit über 2 Jahren arbeiten wir an unserem Cleverbrush- Projekt. Dies ist eine Software zum Arbeiten mit Vektorgrafiken. Die Arbeit mit einem grafischen Editor impliziert eine Vielzahl von Anwendungsfällen. Wir versuchen, Geld und Zeit zu sparen, also optimieren wir alles, einschließlich Tests. Das Abdecken der Testfälle mit jeder Option ist zu teuer und irrational, zumal es unmöglich ist, alle Optionen abzudecken.

Während der Entwicklung wurde ein Modul für React JS-Anwendungen erstellt - LogRock (github) .

Mit diesem Modul können Sie moderne Protokollierungsanwendungen organisieren. Basierend auf den Protokollen führen wir Tests durch. In diesem Artikel werde ich Ihnen die Feinheiten der Verwendung dieses Moduls und das Organisieren von Tests durch Protokollierung erläutern.

Was ist das Problem?


Wenn Sie das Programm mit einem lebenden Organismus vergleichen, ist ein Fehler darin eine Krankheit. Die Ursache dieser "Krankheit" kann eine Reihe von Faktoren sein, einschließlich der Umgebung eines bestimmten Benutzers. Dies gilt insbesondere dann, wenn wir eine Webplattform in Betracht ziehen. Manchmal ist ein Kausalzusammenhang sehr komplex, und der beim Testen festgestellte Fehler ist das Ergebnis einer Reihe von Ereignissen.

Wie bei menschlichen Beschwerden wird niemand ihre Symptome besser erklären als der Patient, kein Tester kann besser als das Programm selbst sagen, was passiert ist.

Was tun?


Um zu verstehen, was passiert, benötigen wir eine Liste der Aktionen, die der Benutzer in unserer Anwendung ausgeführt hat.

Damit unser Programm uns sagen kann, was es „weh tut“, nehmen wir das LogRock- Modul (Github) und verknüpfen es mit ElasticSearch, LogStash und Kibana.

Bild

ElasticSearch ist eine leistungsstarke Volltextsuchmaschine. Sie können das ElasticSearch-Tutorial hier ansehen.
LogStash ist ein System zum Sammeln von Protokollen aus verschiedenen Quellen, die Protokolle senden können, einschließlich an ElasticSearch.
Kibana ist eine Webschnittstelle zu ElasticSearch mit vielen Add-Ons.

Wie funktioniert es


Bild

Im Falle eines Fehlers (oder nur bei Bedarf) sendet die Anwendung Protokolle an den Server, wo sie in einer Datei gespeichert werden. Logstash speichert Daten schrittweise in ElasticSearch - in der Datenbank. Der Benutzer meldet sich bei Kibana an und sieht die gespeicherten Protokolle.

Bild

Es sieht aus wie ein gut abgestimmtes Kibana. Es zeigt Daten von ElasticSearch an. Kibana kann Daten in Form von Tabellen, Grafiken, Karten usw. anzeigen. Dies ist sehr praktisch, um zu analysieren und zu verstehen, was mit unserer Anwendung geschieht.

In diesem Artikel werde ich NICHT auf das Einrichten von ElasticStack eingehen!

Protokollierungssystem erstellen


Als Beispiel werden wir das Protokollierungssystem in eine einseitige JS-Anwendung integrieren, die in React geschrieben wurde. Es spielt wirklich keine Rolle, auf welchem ​​Framework Ihre Anwendung geschrieben wird. Ich werde versuchen, den Ansatz des Aufbaus eines Protokollsystems selbst zu beschreiben.

1. Kunde


1.0 LogRock. Installation


Link zu LogRock

Um zu installieren, müssen Sie Folgendes ausführen:

npm install logrock  yarn add logrock 

1.1 LogRock. Anwendungssetup


Wickeln Sie unsere Anwendung zunächst in eine Komponente

 import { LoggerContainer } from "logrock"; <LoggerContainer> <App /> </LoggerContainer> 

LoggerContainer ist eine Komponente, die auf Ihre Anwendungsfehler reagiert und einen Stapel bildet.

Ein Stapel ist ein Objekt mit Informationen über das Betriebssystem des Benutzers, den Browser, welche Maus- oder Tastaturtaste gedrückt wurde, und natürlich das Unterarray Aktionen, in dem alle Benutzeraktionen aufgezeichnet sind, die er in unserem System ausgeführt hat.

LoggerContainer verfügt über eine Reihe von Einstellungen, von denen einige berücksichtigt werden

 <LoggerContainer active={true|false} limit={20} onError={stack => { sendToServer(stack); }} > <App /> </LoggerContainer> 

aktiv - Aktiviert oder deaktiviert den Logger

limit - Legt ein Limit für die Anzahl der zuletzt vom Benutzer gespeicherten Aktionen fest. Wenn der Benutzer 21 Aktionen ausführt, wird die erste in diesem Array automatisch gelöscht. Somit haben wir die letzten 20 Aktionen, die dem Fehler vorausgingen.

onError - Der Rückruf, der aufgerufen wird, wenn ein Fehler auftritt. Darin wird das Stack-Objekt eingefügt, in dem alle Informationen über die Umgebung, Benutzeraktionen usw. gespeichert sind. Aus diesem Rückruf müssen wir diese Daten an ElasticSearch oder das Backend senden oder zur weiteren Analyse und Überwachung in einer Datei speichern.

1.2 LogRock. Protokollierung


Um Benutzeraktionen in hoher Qualität protokollieren zu können, müssen wir unseren Code mit Protokollaufrufen abdecken.

Das LogRock-Modul wird mit einem Logger geliefert, der einem LoggerContainer zugeordnet ist

Angenommen, wir haben eine Komponente

 import React, { useState } from "react"; export default function Toggle(props) { const [toggleState, setToggleState] = useState("off"); function toggle() { setToggleState(toggleState === "off" ? "on" : "off"); } return <div className={`switch ${toggleState}`} onClick={toggle} />; } 

Um es richtig mit einem Protokoll zu bedecken, müssen wir die Umschaltmethode ändern

 function toggle() { let state = toggleState === "off" ? "on" : "off"; logger.info(`React.Toggle|Toggle component changed state ${state}`); setToggleState(state); } 

Wir haben einen Logger hinzugefügt, in dem die Informationen in zwei Teile unterteilt sind. React.Toggle zeigt uns, dass diese Aktion auf der Ebene von React, der Toggle-Komponente, stattgefunden hat, und dann haben wir eine verbale Erklärung der Aktion und des aktuellen Status, der zu dieser Komponente gekommen ist. Eine solche Aufteilung in Ebenen ist nicht erforderlich, aber mit diesem Ansatz wird klarer, wo genau unser Code ausgeführt wurde.

Im Fehlerfall können wir auch die in React Version 16 eingeführte Methode „componentDidCatch“ verwenden.

2. Serverinteraktion


Betrachten Sie das folgende Beispiel.

Angenommen, wir haben eine Methode, die Benutzerdaten aus einem Backend sammelt. Die Methode ist asynchron, ein Teil der Logik ist im Backend versteckt. Wie protokolliere ich diesen Code richtig?

Erstens, da wir eine Client-Anwendung haben, durchlaufen alle an den Server gesendeten Anforderungen eine einzelne Benutzersitzung, ohne die Seite neu zu laden. Um Aktionen auf dem Client mit Aktionen auf dem Server zu verknüpfen, müssen wir eine globale Sitzungs-ID erstellen und diese bei jeder Anforderung an den Server dem Header hinzufügen. Auf dem Server können wir jeden Logger verwenden, der unsere Logik wie ein Beispiel aus dem Frontend abdeckt, und im Fehlerfall diese Daten mit der angehängten Sitzungs-ID an Elastic an die Backend-Platte senden.

1. Wir generieren SessionID auf dem Client

 window.SESSION_ID = `sessionid-${Math.random().toString(36).substr(3, 9)}`; 

2. Wir müssen die SessionID für alle Anfragen an den Server festlegen. Wenn wir Bibliotheken für Abfragen verwenden, ist dies sehr einfach, indem eine SessionID für alle Abfragen deklariert wird.

 let fetch = axios.create({...}); fetch.defaults.headers.common.sessionId = window.SESSION_ID; 

3. In LoggerContainer gibt es ein spezielles Feld für die Sitzungs-ID

 <LoggerContainer active={true|false} sessionID={window.SESSION_ID} limit={20} onError={stack => { sendToServer(stack); }} > <App /> </LoggerContainer> 

4. Die Anfrage selbst (auf dem Client) sieht folgendermaßen aus:

 logger.info(`store.getData|User is ready for loading... User ID is ${id}`); getData('/api/v1/user', { id }) .then(userData => { logger.info(`store.getData|User have already loaded. User count is ${JSON.stringify(userData)}`); }) .catch(err => { logger.error(`store.getData|User loaded fail ${err.message}`); }); 

So funktioniert alles: Wir protokollieren ein Protokoll vor der Anfrage auf dem Client. Gemäß unserem Code sehen wir, dass das Laden von Daten vom Server jetzt beginnt. Wir haben der Anfrage eine SessionID angehängt. Wenn unser Backend mit dem Hinzufügen dieser SessionID mit Protokollen bedeckt ist und die Anforderung fehlschlägt, können wir sehen, was im Backend passiert ist.

So überwachen wir den gesamten Zyklus unserer Anwendung nicht nur auf dem Client, sondern auch im Backend.

3. Der Tester


Die Arbeit mit einem Tester verdient eine separate Beschreibung des Prozesses.

Da wir ein Startup haben, haben wir keine formalen Anforderungen und manchmal ist bei der Arbeit nicht alles logisch.

Wenn der Tester das Verhalten nicht versteht, muss dies zumindest berücksichtigt werden. Außerdem kann ein Tester häufig eine Situation einfach nicht zweimal wiederholen. Da die Schritte, die zu falschem Verhalten führen, zahlreich und nicht trivial sein können. Darüber hinaus führen nicht alle Fehler zu kritischen Konsequenzen wie Ausnahmen. Einige von ihnen können nur das Verhalten der Anwendung ändern, werden jedoch vom System nicht als Fehler interpretiert. Zu diesem Zweck können Sie beim Staging eine Schaltfläche im Anwendungsheader hinzufügen, um das Senden von Protokollen zu erzwingen. Der Tester stellt fest, dass etwas nicht richtig funktioniert, klickt auf die Schaltfläche und sendet den Stapel mit Aktionen an ElasticSearch.

Bild


Wenn dennoch ein kritischer Fehler aufgetreten ist, müssen wir die Schnittstelle blockieren, damit der Tester nicht weiter klickt und nicht in eine Sackgasse gerät.

Zu diesem Zweck zeigen wir den blauen Bildschirm des Todes an.

Wir sehen oben den Text mit dem Stapel dieses kritischen Fehlers und unten die Aktionen, die ihm vorangegangen sind. Wir erhalten auch die Fehler-ID. Der Tester kann sie auswählen und an das Ticket anhängen. Später kann dieser Fehler in Kibana anhand dieser ID leicht gefunden werden.

Bild

Zu diesem Zweck verfügt LoggerContainer über eigene Eigenschaften.

 <LoggerContainer active={true|false} limit={20} bsodActive={true} bsod={BSOD} onError={stack => { sendToServer(stack); }} > <App /> </LoggerContainer> 

bsodActive - aktiviert / deaktiviert BSOD (das Deaktivieren von BSOD gilt für Produktionscode)

bsod ist eine Komponente. Standardmäßig sieht es wie im obigen Screenshot aus.

Um eine Schaltfläche in einem UI LoggerContainer anzuzeigen, können wir sie im Kontext verwenden

 context.logger.onError(context.logger.getStackData()); 

4. LogRock. Benutzerinteraktion


Sie können Protokolle an die Konsole ausgeben oder dem Benutzer anzeigen. Dazu müssen Sie die Methode stdout verwenden:

 <LoggerContainer active={true|false} limit={20} bsodActive={true} bsod={BSOD} onError={stack => { sendToServer(stack); }} stdout={(level, message, important) => { console[level](message); if (important) { alert(message); } }} > <App /> </LoggerContainer> 

stdout ist eine Methode, die für die Anzeige von Nachrichten verantwortlich ist.

Damit die Nachricht wichtig wird, reicht es aus, den zweiten Parameter true an den Logger zu übergeben. So kann diese Nachricht dem Benutzer in einem Popup-Fenster angezeigt werden. Wenn beispielsweise das Laden der Daten fehlgeschlagen ist, können wir eine Fehlermeldung anzeigen.

 logger.log('Something was wrong', true); 

Erweiterte Protokollierung


Wenn Sie Redux oder ähnliche Lösungen mit einem Geschäft verwenden, können Sie einen Logger in die Middleware einbinden, der Ihre Aktionen verarbeitet. Dadurch werden alle wichtigen Aktionen durch unser System geleitet.

Für eine effektive Protokollierung können Sie Ihre Daten in ein Proxy-Objekt einschließen und alle Aktionen mit dem Objekt protokollieren.

Um Methoden von Drittanbietern mit der Protokollierung abzudecken (Bibliotheksmethoden, Legacy-Codemethoden), können Sie Dekoratoren verwenden - "@".

Tipps


Protokollieren Sie Anwendungen, auch in der Produktion, da kein Tester Engpässe feststellen kann, da er besser als echte Benutzer ist.

Vergessen Sie nicht, die Sammlung der Protokolle in der Lizenzvereinbarung anzugeben.

Protokollieren Sie KEINE Passwörter, Bankdaten und andere persönliche Informationen!

Die Redundanz der Protokolle ist ebenfalls schlecht. Machen Sie die Signaturen so klar wie möglich.

Alternativen


Als alternative Ansätze hebe ich Folgendes hervor:

  • Überrollbügel ist sehr anpassbar. Ermöglicht das Protokollieren von 500.000 Fehlern für 150 US-Dollar pro Monat. Ich empfehle es zu verwenden, wenn Sie eine Anwendung von Grund auf neu entwickeln.
  • Sentry ist einfacher zu integrieren, aber weniger anpassbar. Ermöglicht das Protokollieren von 1 Million Ereignissen für 200 US-Dollar pro Monat.

Mit beiden Diensten können Sie fast dasselbe tun und sich in das Backend integrieren.

Was weiter


Die Protokollierung ist nicht nur die Suche nach Fehlern, sondern überwacht auch Benutzeraktionen und die Datenerfassung. Die Protokollierung kann eine gute Ergänzung zu Google Analytics- und User Experience-Tests sein.

Schlussfolgerungen


Wenn Sie die App veröffentlichen, fängt das Leben für ihn gerade erst an. Seien Sie für Ihre Idee verantwortlich, erhalten Sie Feedback, überwachen Sie Protokolle und verbessern Sie sie. Schreiben Sie hochwertige Software und gedeihen Sie :)

PS Wenn Sie bei der Entwicklung von Modulen für Angular, Vue usw. helfen möchten. Gerne ziehe ich Anfragen - hier .

Source: https://habr.com/ru/post/de453652/


All Articles