LogRock: test via la journalisation
Depuis plus de 2 ans, nous travaillons sur notre projet
Cleverbrush . Il s'agit d'un logiciel pour travailler avec des graphiques vectoriels. Travailler avec un éditeur graphique implique un grand nombre de cas d'utilisation d'application. Nous essayons de gagner du temps et de l'argent, nous optimisons donc tout, y compris les tests. Couvrir les cas de test avec chaque option est trop cher et irrationnel, d'autant plus qu'il est impossible de couvrir toutes les options.
Pendant le développement, un module pour les applications React JS a été créé -
LogRock (github) .
Ce module vous permet d'organiser des applications de journalisation modernes. Sur la base des journaux, nous effectuons des tests. Dans cet article, je vais vous expliquer les subtilités de l'utilisation de ce module et comment organiser les tests via la journalisation.
Quel est le problème?
Si vous comparez le programme avec un organisme vivant, un bogue est une maladie. La cause de cette "maladie" peut être un certain nombre de facteurs, y compris l'environnement d'un utilisateur particulier. Cela est particulièrement vrai si nous envisageons une plate-forme Web. Parfois, une relation causale est très complexe et le bogue détecté lors des tests est le résultat d'un certain nombre d'événements.
Comme pour les maladies humaines, personne n'expliquera mieux ses symptômes que le patient, aucun testeur ne pourra dire ce qui s'est passé, mieux que le programme lui-même.
Que faire?
Pour comprendre ce qui se passe, nous avons besoin d'une liste d'actions que l'utilisateur a effectuées dans notre application.
Pour que notre programme lui-même puisse nous dire ce qu'il «fait mal», nous prendrons le module
LogRock (github) et l'associerons à ElasticSearch, LogStash et Kibana.
ElasticSearch est un puissant moteur de recherche en texte intégral. Vous pouvez regarder le didacticiel ElasticSearch
ici .
LogStash est un système de collecte de journaux provenant de diverses sources, qui peut envoyer des journaux, y compris à ElasticSearch.
Kibana est une interface Web pour ElasticSearch avec de nombreux modules complémentaires.
Comment ça marche?
En cas d'erreur (ou juste sur demande), l'application envoie des journaux au serveur où ils sont enregistrés dans un fichier. Logstash enregistre de manière incrémentielle les données dans ElasticSearch - dans la base de données. L'utilisateur se connecte à Kibana et voit les journaux enregistrés.
Il ressemble à un Kibana bien réglé. Il affiche les données d'ElasticSearch. Kibana peut afficher des données sous forme de tableaux, graphiques, cartes, etc., ce qui est très pratique pour analyser et comprendre ce qui se passe avec notre application.
Dans cet article, je ne discuterai PAS de la configuration d'ElasticStack!Création d'un système de journalisation
Par exemple, nous allons intégrer le système de journalisation dans une application JS d'une page écrite en React. Peu importe le cadre sur lequel votre application sera écrite. Je vais essayer de décrire l'approche de la construction d'un système de journal lui-même.
1. Client
1.0 LogRock. L'installation
Lien vers LogRockPour installer, vous devez effectuer:
npm install logrock yarn add logrock
1.1 LogRock. Configuration de l'application
Pour commencer, encapsulez notre application dans un composant
import { LoggerContainer } from "logrock"; <LoggerContainer> <App /> </LoggerContainer>
LoggerContainer est un composant qui répond à vos erreurs d'application et forme une pile.
Une pile est un objet contenant des informations sur le système d'exploitation de l'utilisateur, le navigateur, le bouton de la souris ou du clavier sur lequel vous avez appuyé et, bien sûr, le sous-tableau des actions, où toutes les actions de l'utilisateur qu'il a effectuées dans notre système sont enregistrées.
LoggerContainer a un certain nombre de paramètres, considérez certains d'entre eux
<LoggerContainer active={true|false} limit={20} onError={stack => { sendToServer(stack); }} > <App /> </LoggerContainer>
actif - activer ou désactiver l'enregistreur
limit - définit une limite sur le nombre d'actions récentes enregistrées par l'utilisateur. Si l'utilisateur effectue 21 actions, la première de ce tableau sera automatiquement supprimée. Ainsi, nous aurons les 20 dernières actions qui ont précédé l'erreur.
onError - le rappel qui est appelé lorsqu'une erreur se produit. L'objet Stack y entre, dans lequel sont stockées toutes les informations sur l'environnement, les actions de l'utilisateur, etc. C'est à partir de ce rappel que nous devons envoyer ces données à ElasticSearch ou au backend, ou les enregistrer dans un fichier pour une analyse et une surveillance supplémentaires.
1.2 LogRock. Journalisation
Afin d'effectuer une journalisation de haute qualité des actions des utilisateurs, nous devrons couvrir notre code avec des appels de journal.
Le module LogRock est livré avec un enregistreur associé à un LoggerContainer
Supposons que nous ayons un composant
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} />; }
Afin de le couvrir correctement avec un journal, nous devons modifier la méthode de basculement
function toggle() { let state = toggleState === "off" ? "on" : "off"; logger.info(`React.Toggle|Toggle component changed state ${state}`); setToggleState(state); }
Nous avons ajouté un enregistreur dans lequel les informations sont divisées en 2 parties. React.Toggle nous montre que cette action s'est produite au niveau de React, le composant Toggle, puis nous avons une explication verbale de l'action et de l'état actuel qui est entré dans ce composant. Une telle séparation en niveaux n'est pas nécessaire, mais avec cette approche, il sera plus clair où exactement notre code a été exécuté.
On peut également utiliser la méthode «componentDidCatch», introduite dans React version 16, en cas d'erreur.
2. Interaction avec le serveur
Prenons l'exemple suivant.
Supposons que nous ayons une méthode qui collecte des données utilisateur à partir d'un backend. La méthode est asynchrone, une partie de la logique est cachée dans le backend. Comment enregistrer correctement ce code?
Tout d'abord, comme nous avons une application cliente, toutes les requêtes envoyées au serveur passeront par une seule session utilisateur, sans recharger la page. Afin d'associer des actions sur le client à des actions sur le serveur, nous devons créer un SessionID global et l'ajouter à l'en-tête de chaque demande au serveur. Sur le serveur, nous pouvons utiliser n'importe quel enregistreur qui couvrira notre logique comme un exemple du frontend, et en cas d'erreur envoyer ces données avec le sessionID attaché à Elastic vers la plaque Backend.
1. Nous générons SessionID sur le client
window.SESSION_ID = `sessionid-${Math.random().toString(36).substr(3, 9)}`;
2. Nous devons définir le SessionID pour toutes les demandes au serveur. Si nous utilisons des bibliothèques pour les requêtes, c'est très simple en déclarant un SessionID pour toutes les requêtes.
let fetch = axios.create({...}); fetch.defaults.headers.common.sessionId = window.SESSION_ID;
3. Dans LoggerContainer, il y a un champ spécial pour SessionID
<LoggerContainer active={true|false} sessionID={window.SESSION_ID} limit={20} onError={stack => { sendToServer(stack); }} > <App /> </LoggerContainer>
4. La demande elle-même (sur le client) ressemblera à ceci:
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}`); });
Comment tout cela fonctionnera: nous enregistrons un journal, avant la demande sur le client. Selon notre code, nous voyons que le chargement des données depuis le serveur va maintenant commencer. Nous avons joint un SessionID à la demande. Si notre backend est couvert de journaux avec l'ajout de ce SessionID et que la demande échoue, nous pouvons voir ce qui s'est passé sur le backend.
Ainsi, nous surveillons l'ensemble du cycle de notre application, non seulement sur le client, mais aussi sur le backend.
3. Le testeur
Travailler avec un testeur mérite une description séparée du processus.
Puisque nous avons une startup, nous n'avons pas d'exigences formelles et parfois tout n'est pas logique au travail.
Si le testeur ne comprend pas le comportement, c'est un cas qui doit au moins être pris en considération. De plus, souvent, un testeur ne peut tout simplement pas répéter deux fois une situation. Étant donné que les étapes conduisant à un comportement incorrect peuvent être nombreuses et non triviales. De plus, toutes les erreurs n'entraînent pas des conséquences critiques, telles que l'exception. Certains d'entre eux ne peuvent que modifier le comportement de l'application, mais ne peuvent pas être interprétés par le système comme une erreur. À ces fins, lors du transfert, vous pouvez ajouter un bouton dans l'en-tête de l'application pour forcer l'envoi de journaux. Le testeur constate que quelque chose ne fonctionne pas correctement, clique sur le bouton et envoie la pile avec des actions à ElasticSearch.
Si, néanmoins, une erreur critique s'est produite, il faut bloquer l'interface pour que le testeur ne clique plus et n'atteigne pas une impasse.
À ces fins, nous affichons l'écran bleu de la mort.
Nous voyons en haut le texte avec Pile de cette erreur critique, et ci-dessous - les actions qui l'ont précédé. Nous obtenons également l'ID d'erreur, il suffit que le testeur le sélectionne et le joigne au ticket. Plus tard, cette erreur peut être facilement trouvée dans Kibana par cet ID.
À ces fins, LoggerContainer a ses propres propriétés.
<LoggerContainer active={true|false} limit={20} bsodActive={true} bsod={BSOD} onError={stack => { sendToServer(stack); }} > <App /> </LoggerContainer>
bsodActive - active / désactive BSOD (la désactivation de BSOD s'applique au code de production)
bsod est un composant. Par défaut, cela ressemble à la capture d'écran ci-dessus.
Pour afficher un bouton dans un UI LoggerContainer, nous pouvons utiliser en contexte
context.logger.onError(context.logger.getStackData());
4. LogRock. Interaction utilisateur
Vous pouvez générer des journaux sur la console ou les montrer à l'utilisateur, pour cela, vous devez utiliser la méthode stdout:
<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 est une méthode chargée d'afficher les messages.
Pour que le message devienne important, il suffit de transmettre le deuxième paramètre vrai à l'enregistreur. Ainsi, ce message peut être affiché à l'utilisateur dans une fenêtre pop-up, par exemple, si le chargement des données a échoué, nous pouvons afficher un message d'erreur.
logger.log('Something was wrong', true);
Journalisation avancée
Si vous utilisez Redux, ou des solutions similaires avec un magasin, vous pouvez mettre l'enregistreur dans le middleware pour traiter vos actions, ainsi, toutes les actions importantes passeront par notre système.
Pour une journalisation efficace, vous pouvez encapsuler vos données dans un objet proxy et placer des enregistreurs sur toutes les actions avec l'objet.
Pour couvrir les méthodes tierces avec la journalisation (méthodes de bibliothèque, méthodes de code héritées), vous pouvez utiliser des décorateurs - «@».
Astuces
Connectez les applications, y compris en production, car mieux que les vrais utilisateurs, aucun testeur ne trouvera de goulots d'étranglement.
N'oubliez pas d'indiquer la collecte des journaux dans le contrat de licence.
NE PAS enregistrer les mots de passe, les coordonnées bancaires et autres informations personnelles!La redondance des journaux est également mauvaise, rendez les signatures aussi claires que possible.
Alternatives
Comme approches alternatives, je souligne:
- Rollbar est hautement personnalisable. Vous permet d'enregistrer 500 000 erreurs pour 150 $ par mois. Je recommande de l'utiliser si vous développez une application à partir de zéro.
- Sentry est plus facile à intégrer, mais moins personnalisable. Vous permet d'enregistrer 1 million d'événements pour 200 $ par mois.
Les deux services vous permettent de faire presque la même chose et de vous intégrer dans le backend.
Et ensuite
La journalisation n'est pas seulement la recherche d'erreurs, elle surveille également les actions des utilisateurs, la collecte de données. La journalisation peut être un bon complément aux tests de Google Analytics et de l'expérience utilisateur.
Conclusions
Lorsque vous sortez l'application, la vie ne fait que commencer pour lui. Soyez responsable de votre idée, obtenez des commentaires, surveillez les journaux et améliorez-les. Écrivez des logiciels de haute qualité et prospérez :)
PS Si vous souhaitez aider au développement de modules pour Angular, Vue, etc. Je serai heureux de tirer des demandes -
ici .