Skip to content

Commit 938171a

Browse files
committed
Sync original
Up to date goldbergyoni@6529a2b
1 parent 02137b3 commit 938171a

File tree

5 files changed

+184
-9
lines changed

5 files changed

+184
-9
lines changed

README.french.md

+6-2
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ Lire dans une autre langue : [![CN](/assets/flags/CN.png)**CN**](/README.chines
4747
## Table des matières
4848

4949
1. [Structure de projet (5)](#1-structure-de-projet)
50-
2. [Gestion des erreurs (11) ](#2-gestion-des-erreurs)
50+
2. [Gestion des erreurs (12) ](#2-gestion-des-erreurs)
5151
3. [Style du code (12) ](#3-style-du-code)
5252
4. [Tests et pratiques générales de qualité (13) ](#4-tests-et-pratiques-générales-de-qualité)
5353
5. [Pratiques de mise en production (19) ](#5-pratiques-de-mise-en-production)
@@ -699,7 +699,9 @@ Toutes les déclarations ci-dessus renverront false si elles sont utilisées ave
699699

700700
## ![] 5.14. Attribuez un id de transaction à chaque relevé du journal
701701

702-
**TL;PL :** Attribuez le même identifiant, transaction-id : {une valeur}, à chaque entrée du journal à l'intérieur d'une même requête. Ensuite, lors de l'inspection des erreurs dans les journaux, il est facile de conclure ce qui s'est passé avant et après. Malheureusement, cela n'est pas facile à réaliser dans Node en raison de sa nature asynchrone, consultez les exemples de code.
702+
Également connu sous le nom de corrélation id / transit id / tracing id / request id / request context / etc.
703+
704+
**TL;PL :** Attribuez le même identifiant, transaction-id : {une valeur}, à chaque entrée du journal à l'intérieur d'une même requête. Ensuite, lors de l'inspection des erreurs dans les journaux, il est facile de conclure ce qui s'est passé avant et après. Malheureusement, cela n'est pas facile à réaliser dans Node en raison de sa nature asynchrone, consultez les exemples de code. Jusqu'à la version 14 de Node, cela n'était pas facile à réaliser en raison de la nature asynchrone de Node, mais depuis l'arrivée de AsyncLocalStorage, cela est devenu possible et plus facile que jamais. Consultez les exemples de code fournis.
703705

704706
**Autrement :** L'examen d'un journal d'erreurs de production sans le contexte (ce qui s'est passé auparavant) rend le travail de réflexion beaucoup plus difficile et lent.
705707

@@ -1564,6 +1566,8 @@ Thanks goes to these wonderful people who have contributed to this repository!
15641566
<td align="center"><a href="https://github.com/andrewjbarbour"><img src="https://avatars.githubusercontent.com/u/77080074?v=4?s=100" width="100px;" alt=""/><br /><sub><b>andrewjbarbour</b></sub></a><br /><a href="#content-andrewjbarbour" title="Content">🖋</a></td>
15651567
<td align="center"><a href="https://MasujimaRyohei.jp"><img src="https://avatars.githubusercontent.com/u/17163541?v=4?s=100" width="100px;" alt=""/><br /><sub><b>mr</b></sub></a><br /><a href="#content-MasujimaRyohei" title="Content">🖋</a></td>
15661568
<td align="center"><a href="https://github.com/kubanac95"><img src="https://avatars.githubusercontent.com/u/16191931?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Aleksandar</b></sub></a><br /><a href="#content-kubanac95" title="Content">🖋</a></td>
1569+
<td align="center"><a href="http://vincentjonathan.com"><img src="https://avatars.githubusercontent.com/u/32597776?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Owl</b></sub></a><br /><a href="#content-SuspiciousLookingOwl" title="Content">🖋</a></td>
1570+
<td align="center"><a href="https://github.com/yedidyas"><img src="https://avatars.githubusercontent.com/u/36074789?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Yedidya Schwartz</b></sub></a><br /><a href="#content-yedidyas" title="Content">🖋</a> <a href="#example-yedidyas" title="Examples">💡</a></td>
15671571
</tr>
15681572
</table>
15691573

sections/docker/use-cache-for-shorter-build-time.french.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -112,4 +112,4 @@ CMD ["node", "dist/server.js"]
112112

113113
## Useful links
114114

115-
Docker docks: https://docs.docker.com/develop/develop-images/dockerfile_best-practices/#leverage-build-cache
115+
Docker docs: https://docs.docker.com/develop/develop-images/dockerfile_best-practices/#leverage-build-cache

sections/production/assigntransactionid.french.md

+161-2
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,150 @@
66

77
Un journal typique est un registre des entrées de tous les composants et requêtes. Lorsqu'une ligne ou une erreur suspecte est détectée, il devient difficile de faire correspondre d'autres lignes appartenant au même flux spécifique (par exemple, l'utilisateur "John" a essayé d'acheter quelque chose). Cela devient encore plus critique et difficile dans un environnement de micro-services lorsqu'une requête/transaction peut concerner plusieurs ordinateurs. Il convient de remédier à ce problème en attribuant une valeur d'identification de transaction unique à toutes les entrées d'une même requête, de sorte qu'en détectant une ligne, on puisse copier l'identifiant et rechercher toutes les lignes qui ont un identifiant de transaction similaire. Toutefois, la réalisation de cette opération dans Node n'est pas simple, car un seul processus est utilisé pour toutes les requêtes - envisagez d'utiliser une bibliothèque qui peut regrouper les données au niveau de la requête - voir l'exemple de code suivant. Lorsque vous appelez d'autres micro-services, transmettez l'identifiant de la transaction en utilisant une entête HTTP comme "x-transaction-id" pour conserver le même contexte.
88

9-
<br/><br/>
9+
<br/>
10+
11+
### Exemple de code : partage de TransactionId entre les fonctions de requête et entre les services à l'aide de [async-local-storage](https://nodejs.org/api/async_hooks.html#async_hooks_class_asynclocalstorage)
12+
13+
**Qu'est ce que async-local-storage ?** Vous pouvez le considérer comme l'alternative de Node pour le stockage local des threads.
14+
Il s'agit essentiellement d'un stockage pour les flux asynchrones dans Node. Vous pouvez en savoir plus [ici](https://www.freecodecamp.org/news/async-local-storage-nodejs/).
15+
16+
```javascript
17+
const express = require('express');
18+
const { AsyncLocalStorage } = require('async_hooks');
19+
const uuid = require('uuid/v4');
20+
21+
const asyncLocalStorage = new AsyncLocalStorage();
22+
23+
// Définit le TransactionId des requêtes entrantes
24+
const transactionIdMiddleware = (req, res, next) => {
25+
// Le premier argument de asyncLocalStorage.run est l'initialisation de l'état du stockage, le second argument est la fonction qui a accès à ce stockage
26+
asyncLocalStorage.run(new Map(), () => {
27+
// Essaye d'extraire le TransactionId de l'entête de la requête, ou en génére un nouveau s'il n'existe pas
28+
const transactionId = req.headers['transactionId'] || uuid();
29+
30+
// Définit le TransactionId à l'intérieur du stockage
31+
asyncLocalStorage.getStore().set('transactionId', transactionId);
32+
33+
// En appelant next() dans la fonction, nous nous assurons que tous les autres middlewares fonctionnent dans le même contexte AsyncLocalStorage
34+
next();
35+
});
36+
};
37+
38+
const app = express();
39+
app.use(transactionIdMiddleware);
40+
41+
// Définit le TransactionId des requêtes sortantes
42+
app.get('/', (req, res) => {
43+
// Une fois que TransactionId a été initialisé dans le middleware, il est accessible à tout moment pour le flux de requêtes.
44+
const transactionId = asyncLocalStorage.getStore().get('transactionId');
45+
46+
try {
47+
// Ajoute TransactionId comme entête afin de le passer au service suivant
48+
const response = await axios.get('https://externalService.com/api/getAllUsers', headers: {
49+
'x-transaction-id': transactionId
50+
});
51+
} catch (err) {
52+
// L'erreur est transmise au middleware, et il n'est pas nécessaire d'envoyer le TransactionId
53+
next(err);
54+
}
55+
56+
logger.info('externalService a été appelé avec succès avec l\'entête TransactionId');
57+
58+
res.send('OK');
59+
});
60+
61+
// Un middleware de gestion des erreurs appelle le journal
62+
app.use(async (err, req, res, next) => {
63+
await logger.error(err);
64+
});
65+
66+
// Le journal peut désormais ajouter le TransactionId à chaque entrée, de sorte que les entrées d'une même requête aient la même valeur
67+
class logger {
68+
error(err) {
69+
console.error(`${err} ${asyncLocalStorage.getStore().get('transactionId')}`);
70+
}
71+
72+
info(message) {
73+
console.log(`${message} ${asyncLocalStorage.getStore().get('transactionId')}`);
74+
}
75+
}
76+
```
77+
<br/>
78+
79+
<details>
80+
<summary><strong>Exemple de code : utilisation d'une bibliothèque pour simplifier la syntaxe</strong></summary>
81+
82+
Partage du TransactionId entre les fonctions de requête actuelles en utilisant [cls-rtracer](https://www.npmjs.com/package/cls-rtracer) (une bibliothèque basée sur async-local-storage, implémentée pour les middlewares Express & Koa et les plugins Fastify & Hapi)
83+
84+
```javascript
85+
const express = require('express');
86+
const rTracer = require('cls-rtracer');
87+
88+
const app = express();
89+
90+
app.use(rTracer.expressMiddleware());
1091

11-
### Exemple de code : configuration typique d'Express
92+
app.get('/getUserData/{id}', async (req, res, next) => {
93+
try {
94+
const user = await usersRepo.find(req.params.id);
95+
96+
// Le TransactionId est accessible de l'intérieur du journal, il n'est pas nécessaire de l'envoyer
97+
logger.info(`les données de l'utilisateur ${user.id} ont été récupérées avec succès`);
98+
99+
res.json(user);
100+
} catch (err) {
101+
// L'erreur est transmise au middleware
102+
next(err);
103+
}
104+
})
105+
106+
// Un middleware de gestion des erreurs appelle le journal
107+
app.use(async (err, req, res, next) => {
108+
await logger.error(err);
109+
});
110+
111+
// Le journal peut désormais ajouter le TransactionId à chaque entrée, de sorte que les entrées d'une même requête aient la même valeur
112+
class logger {
113+
error(err) {
114+
console.error(`${err} ${rTracer.id()}`);
115+
}
116+
117+
info(message) {
118+
console.log(`${message} ${rTracer.id()}`);
119+
}
120+
}
121+
```
122+
<br/>
123+
124+
Partage le TransactionId entre les micro services
125+
126+
```javascript
127+
// cls-tracer a la capacité de stocker le TransactionId sur les entêtes des requêtes sortantes de votre service, et d'extraire le TransactionId des entêtes des requêtes entrantes, en remplaçant simplement la configuration par défaut du middleware
128+
app.use(rTracer.expressMiddleware({
129+
// Ajoute le TransactionId à l'entête
130+
echoHeader: true,
131+
// Respecte le TransactionId de l'entête
132+
useHeader: true,
133+
// Nom de l'entête TransactionId
134+
headerName: 'x-transaction-id'
135+
}));
136+
137+
const axios = require('axios');
138+
139+
// Maintenant, le service extérieur obtiendra automatiquement le TransactionId actuel comme entête
140+
const response = await axios.get('https://externalService.com/api/getAllUsers');
141+
```
142+
</details>
143+
<br/>
144+
145+
**REMARQUE : l'utilisation de async-local-storage est soumise à deux restrictions :**
146+
1. Il nécessite Node v.14.
147+
2. Il est basé sur une construction de niveau inférieur dans Node appelé async_hooks qui est encore expérimental, donc vous pouvez craindre des problèmes de performance. Même s'ils existent, ils sont très négligeables, mais vous devriez faire vos propres choix.
148+
149+
<br/>
150+
151+
<details>
152+
<summary><strong>Exemple de code - configuration Express typique sans dépendance de async-local-storage</strong></summary>
12153

13154
```javascript
14155
// à la réception d'une nouvelle requête, commencez un nouveau contexte isolé et définissez un identifiant de transaction. L'exemple suivant utilise la bibliothèque npm continuation-local-storage pour isoler les requêtes
@@ -37,3 +178,21 @@ class logger {
37178
}
38179
}
39180
```
181+
</details>
182+
183+
<br/><br/>
184+
185+
### Bon : Journaux avec un TransactionId attribué - peut être utilisé comme filtre pour ne voir qu'un seul flux
186+
![alt text](https://i.ibb.co/YjJwgbN/logs-with-transaction-id.jpg "Journaux avec transaction id")
187+
<br/><br/>
188+
189+
### Mauvais : journaux sans TransactionId - pas de possibilité d'utiliser un filtre et de ne voir qu'un seul flux, vous devez comprendre par vous-même quels journaux sont pertinents entre tous les « bruits » environnants
190+
![alt text](https://i.ibb.co/PFgVNfn/logs-withtout-transaction-id.jpg "Journaux avec transaction id")
191+
192+
<br/><br/>
193+
194+
### Citation de blog : « La notion d'ID de corrélation est simple. C'est une valeur qui est commune à toutes les requêtes, messages et réponses dans une transaction donnée. Avec cette simplification, vous obtenez beaucoup de pouvoir ».
195+
196+
Extrait de [rapid7](https://blog.rapid7.com/2016/12/23/the-value-of-correlation-ids/)
197+
198+
> Dans le passé, lorsque le comportement transactionnel se déroulait dans un seul domaine, dans le cadre de procédures par étapes, le suivi du comportement des requêtes et des réponses était une tâche simple. Cependant, aujourd'hui, une requête vers un domaine particulier peut impliquer une myriade de requêtes asynchrones ultérieures du domaine de départ vers d'autres domaines. Par exemple, vous envoyez une requête à Expedia, mais en coulisse, Expedia transmet votre requête sous forme de message à un gestionnaire de messages. Ce message est ensuite consommé par un hôtel, une compagnie aérienne et une agence de location de voitures qui répondent également de manière asynchrone. La question se pose donc, alors que votre seule requête est transmise à une multitude de consommateurs en cours de traitement, comment pouvons-nous suivre la transaction ? La réponse est : utiliser un identifiant de corrélation.

sections/testingandquality/3-parts-in-name.french.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ describe('Service Produits', () => {
3535
describe('Service Produits', () => {
3636
describe('Ajoute un nouveau produit', () => {
3737
it('Devrait retourner le bon statut', () => {
38-
//hmm, quelle est cette vérification de test ? quels sont le scénario et les attentes ?
38+
//hmm, quelle est cette vérification de test ? quels sont le scénario et les attentes ?
3939
const newProduct = new ProductService().add(...);
4040
expect(newProduct.status).to.equal('validationEnAttente');
4141
});

sections/testingandquality/avoid-global-test-fixture.french.md

+15-3
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,15 @@
1111
### Exemple de code : chaque test agit sur son propre ensemble de données
1212
```javascript
1313
it('Lors de la mise à jour du nom du site, obtenez une confirmation réussie', async () => {
14-
// le test ajoute de nouveaux enregistrements et agit uniquement sur les enregistrements
14+
// Arrange (Préparer) - le test ajoute de nouveaux enregistrements et agit uniquement sur les enregistrements
1515
const siteUnderTest = await SiteService.addSite({
1616
name: 'siteForUpdateTest'
1717
});
18+
19+
// Act (Agir)
1820
const updateNameResult = await SiteService.changeName(siteUnderTest, 'newName');
21+
22+
// Assert (Vérifier)
1923
expect(updateNameResult).to.be(true);
2024
});
2125
```
@@ -28,15 +32,23 @@ before(() => {
2832
// ajouter des données de sites et d'administrateurs à notre base de données. Où sont les données ? à l'extérieur. Sur un json externe ou un framework de migration
2933
await DB.AddSeedDataFromJson('seed.json');
3034
});
35+
3136
it('Lors de la mise à jour du nom du site, obtenez une confirmation réussie', async () => {
32-
// Je sais que le nom du site « Portal » existe - je l'ai vu dans les fichiers de remplissage
37+
// Arrange (Préparer) - Je sais que le nom du site « Portal » existe - je l'ai vu dans les fichiers de remplissage
3338
const siteToUpdate = await SiteService.getSiteByName('Portal');
39+
40+
// Act (Agir)
3441
const updateNameResult = await SiteService.changeName(siteToUpdate, 'newName');
42+
43+
// Assert (Vérifier)
3544
expect(updateNameResult).to.be(true);
3645
});
46+
3747
it('Lorsque vous interrogez par le nom du site, obtenez le bon site', async () => {
38-
// Je sais que le nom de site « Portal » existe - je l'ai vu dans les fichiers de remplissage
48+
// Act (Agir) - Je sais que le nom de site « Portal » existe - je l'ai vu dans les fichiers de remplissage
3949
const siteToCheck = await SiteService.getSiteByName('Portal');
50+
51+
// Assert (Vérifier)
4052
expect(siteToCheck.name).to.be.equal('Portal'); // Échec ! Le test précédent change le nom :[
4153
});
4254
```

0 commit comments

Comments
 (0)