fix(MVP-013): Add error correlation with request IDs in logs

This commit is contained in:
senke 2025-12-22 23:13:49 +01:00
parent 26a1f1e624
commit 9e41db9614
4 changed files with 157 additions and 17 deletions

View file

@ -774,7 +774,7 @@
"description": "Backend returns request_id but frontend doesn't log it. Add for debugging.",
"owner": "frontend",
"estimated_hours": 2,
"status": "todo",
"status": "completed",
"priority": 13,
"dependencies": [],
"files_to_modify": [
@ -900,12 +900,12 @@
]
},
"progress_tracking": {
"completed": 12,
"completed": 13,
"in_progress": 0,
"todo": 3,
"todo": 2,
"blocked": 0,
"last_updated": "2025-01-28T01:00:00Z",
"completion_percentage": 80
"last_updated": "2025-01-28T02:00:00Z",
"completion_percentage": 87
},
"validation_checklist": {
"description": "Run these checks after all tasks complete to verify MVP stability",

View file

@ -10,10 +10,10 @@
| Métrique | Valeur |
|----------|--------|
| **Tâches complétées** | 12 / 15 |
| **Tâches complétées** | 13 / 15 |
| **Phase actuelle** | PHASE-3 (Reliability & Polish) |
| **Progression globale** | ████████████░ 80% |
| **Dernière mise à jour** | 2025-01-28 01:00 |
| **Progression globale** | █████████████░ 87% |
| **Dernière mise à jour** | 2025-01-28 02:00 |
### Progression par Phase
@ -21,7 +21,7 @@
|-------|--------|-------------|
| PHASE-1 — Bloquants Critiques | ✅ Terminé | 5/5 |
| PHASE-2 — Alignement API | ✅ Terminé | 5/5 |
| PHASE-3 — Fiabilité & Polish | 🔄 En cours | 2/5 |
| PHASE-3 — Fiabilité & Polish | 🔄 En cours | 3/5 |
| PHASE-3 — Fiabilité | ⚪ En attente | 0/5 |
---
@ -646,14 +646,32 @@ code: z.number()
| **Source** | INT-000013 |
| **Owner** | Frontend |
| **Effort** | ~2h |
| **Statut** | ⬜ À faire |
| **Statut** | ✅ Terminé |
**Problème** : `request_id` du backend non logué côté frontend.
**Fichier** :
- [ ] `apps/web/src/services/api/client.ts`
**Fichiers modifiés** :
- [x] `apps/web/src/services/api/client.ts` → Ajouté logging du request_id dans tous les cas d'erreur
- [x] `apps/web/src/utils/apiErrorHandler.ts` → Ajouté paramètre optionnel pour inclure request_id dans les messages utilisateur
**Action** : Extraire et logger `request_id` des réponses d'erreur.
**Changements effectués** :
- Ajouté logging du `request_id` dans l'interceptor de réponse pour :
- Erreurs générales (avec console.error)
- Erreurs 429 (rate limiting, avec console.warn)
- Erreurs 502/503 (avec console.warn pour retries, console.error après échec)
- Modifié `formatErrorMessage()` pour accepter un paramètre `includeRequestId` :
- Si `true` et en mode développement, inclut le request_id dans le message
- Permet de corréler les erreurs frontend avec les logs backend
**Validation** :
- `npx tsc --noEmit` → ✅ Aucune erreur TypeScript
- Tous les logs d'erreur incluent maintenant le request_id si disponible
- Format des logs : `[API Error] message (Code: X, Request ID: Y)`
**Critères d'acceptation** :
- [x] Logs d'erreur incluent request_id
- [x] Peut corréler les erreurs frontend avec les logs backend
- [x] Request_id optionnellement affiché dans les messages utilisateur (mode dev)
---
@ -1088,10 +1106,40 @@ Frontend :
**Temps passé** : 2h
**Prochaine tâche** : MVP-013 (Add Error Correlation with Request IDs)
**Prochaine tâche** : MVP-014 (Validate CORS Credentials Configuration)
**Notes** : Les erreurs transitoires (502/503) sont maintenant automatiquement retentées avec exponential backoff, améliorant la robustesse de l'application face aux problèmes temporaires de réseau ou de services externes. Le header Retry-After est respecté si présent, permettant au backend de contrôler le timing des retries.
----
## 2025-01-28 (suite 2)
**Tâches travaillées** : MVP-013
**Statut** :
- MVP-013 : ✅ Terminé
**Changements effectués** :
- Modifié `apps/web/src/services/api/client.ts` :
- Ajouté logging du `request_id` dans tous les cas d'erreur :
- Erreurs générales : `console.error` avec request_id, code, message, timestamp, details, context, url, method
- Erreurs 429 (rate limiting) : `console.warn` avec request_id et retry_after
- Erreurs 502/503 : `console.warn` pour chaque retry avec request_id, `console.error` après échec final
- Modifié `apps/web/src/utils/apiErrorHandler.ts` :
- `formatErrorMessage()` accepte maintenant un paramètre optionnel `includeRequestId`
- Si `true` et en mode développement, inclut le request_id dans le message formaté
- Permet de corréler les erreurs affichées à l'utilisateur avec les logs backend
**Validation** :
- `npx tsc --noEmit` → ✅ Aucune erreur TypeScript
- Tous les logs d'erreur incluent maintenant le request_id si disponible
- Format des logs : `[API Error] message (Code: X, Request ID: Y)` avec contexte complet
**Temps passé** : 1h30
**Prochaine tâche** : MVP-014 (Validate CORS Credentials Configuration)
**Notes** : Les erreurs API incluent maintenant le request_id dans les logs, permettant de corréler facilement les erreurs frontend avec les logs backend pour le debugging. Le request_id peut également être affiché dans les messages utilisateur en mode développement.
---
## 📚 Commandes Utiles

View file

@ -210,6 +210,20 @@ apiClient.interceptors.response.use(
const apiError = parseApiError(error);
const retryAfter = apiError.retry_after || 5; // Default 5 secondes
// Log error with request_id for correlation
if (apiError.request_id) {
console.warn(
`[API Rate Limit] ${apiError.message} (Request ID: ${apiError.request_id}, Retry after: ${retryAfter}s)`,
{
code: apiError.code,
request_id: apiError.request_id,
retry_after: retryAfter,
url: originalRequest?.url,
method: originalRequest?.method,
},
);
}
// Si la requête n'a pas encore été retentée, attendre et réessayer
if (originalRequest && !originalRequest._retry && retryAfter > 0) {
originalRequest._retry = true;
@ -239,6 +253,23 @@ apiClient.interceptors.response.use(
// Calculate delay (respect Retry-After header if present, otherwise exponential backoff)
const delay = getRetryDelay(error, retryCount, 1000);
// Log retry attempt with request_id if available
const apiError = parseApiError(error);
if (apiError.request_id) {
console.warn(
`[API Retry] ${status} error, retrying (${retryCount + 1}/${maxRetries}) - Request ID: ${apiError.request_id}`,
{
status,
retry_count: retryCount + 1,
max_retries: maxRetries,
delay_ms: delay,
request_id: apiError.request_id,
url: originalRequest?.url,
method: originalRequest?.method,
},
);
}
// Wait before retrying
return sleep(delay).then(() => {
// Retry the request
@ -248,11 +279,58 @@ apiClient.interceptors.response.use(
// If already retried maxRetries times, reject immediately
const apiError = parseApiError(error);
// Log final error with request_id after all retries failed
if (apiError.request_id) {
console.error(
`[API Error] ${status} error after ${maxRetries} retries - Request ID: ${apiError.request_id}`,
{
code: apiError.code,
message: apiError.message,
request_id: apiError.request_id,
timestamp: apiError.timestamp,
url: originalRequest?.url,
method: originalRequest?.method,
},
);
}
return Promise.reject(apiError);
}
// Parser l'erreur en ApiError standardisé pour les autres codes
const apiError = parseApiError(error);
// Log error with request_id for correlation with backend logs
if (apiError.request_id) {
console.error(
`[API Error] ${apiError.message} (Code: ${apiError.code}, Request ID: ${apiError.request_id})`,
{
code: apiError.code,
message: apiError.message,
request_id: apiError.request_id,
timestamp: apiError.timestamp,
details: apiError.details,
context: apiError.context,
url: originalRequest?.url,
method: originalRequest?.method,
},
);
} else {
// Log without request_id if not available
console.error(
`[API Error] ${apiError.message} (Code: ${apiError.code})`,
{
code: apiError.code,
message: apiError.message,
timestamp: apiError.timestamp,
details: apiError.details,
url: originalRequest?.url,
method: originalRequest?.method,
},
);
}
return Promise.reject(apiError);
},
);

View file

@ -156,18 +156,32 @@ function normalizeApiError(error: any): ApiError {
/**
* Formate un message d'erreur pour l'affichage dans l'UI
* @param error - ApiError
* @param includeRequestId - Si true, inclut le request_id dans le message (pour debugging)
* @returns Message formaté pour l'utilisateur
*/
export function formatErrorMessage(error: ApiError): string {
export function formatErrorMessage(
error: ApiError,
includeRequestId: boolean = false,
): string {
let message = error.message;
// Si l'erreur a des détails de validation, les inclure
if (error.details && Array.isArray(error.details) && error.details.length > 0) {
const detailsMessages = error.details
.map((detail) => `${detail.field}: ${detail.message}`)
.join(', ');
return `${error.message} (${detailsMessages})`;
message = `${error.message} (${detailsMessages})`;
}
return error.message;
// Optionnellement inclure le request_id pour le debugging (en mode développement)
if (includeRequestId && error.request_id) {
const isDev = import.meta.env.DEV;
if (isDev) {
message = `${message} [Request ID: ${error.request_id}]`;
}
}
return message;
}
/**