Files
btpxpress-frontend/services/monitoringService.ts
dahoud a8825a058b Fix: Corriger toutes les erreurs de build du frontend
- Correction des erreurs TypeScript dans userService.ts et workflowTester.ts
- Ajout des propriétés manquantes aux objets User mockés
- Conversion des dates de string vers objets Date
- Correction des appels asynchrones et des types incompatibles
- Ajout de dynamic rendering pour résoudre les erreurs useSearchParams
- Enveloppement de useSearchParams dans Suspense boundary
- Configuration de force-dynamic au niveau du layout principal

Build réussi: 126 pages générées avec succès

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-18 13:23:08 +00:00

278 lines
6.8 KiB
TypeScript

/**
* Service de monitoring avancé côté client
*/
export interface PerformanceMetric {
name: string;
value: number;
timestamp: number;
tags?: Record<string, string>;
}
export interface ErrorMetric {
error: string;
stack?: string;
url: string;
userAgent: string;
timestamp: number;
userId?: string;
}
export interface UserAction {
action: string;
component: string;
timestamp: number;
duration?: number;
metadata?: Record<string, any>;
}
export class MonitoringService {
private static metrics: PerformanceMetric[] = [];
private static errors: ErrorMetric[] = [];
private static userActions: UserAction[] = [];
private static isEnabled = true;
/**
* Initialise le service de monitoring
*/
static init(): void {
if (typeof window === 'undefined') return;
// Monitoring des erreurs JavaScript
window.addEventListener('error', (event) => {
this.recordError({
error: event.message,
stack: event.error?.stack,
url: window.location.href,
userAgent: navigator.userAgent,
timestamp: Date.now()
});
});
// Monitoring des promesses rejetées
window.addEventListener('unhandledrejection', (event) => {
this.recordError({
error: `Unhandled Promise Rejection: ${event.reason}`,
url: window.location.href,
userAgent: navigator.userAgent,
timestamp: Date.now()
});
});
// Monitoring des performances de navigation
if ('performance' in window) {
window.addEventListener('load', () => {
setTimeout(() => {
this.recordNavigationMetrics();
}, 0);
});
}
// Envoi périodique des métriques
setInterval(() => {
this.sendMetrics();
}, 60000); // Toutes les minutes
}
/**
* Enregistre une métrique de performance
*/
static recordMetric(name: string, value: number, tags?: Record<string, string>): void {
if (!this.isEnabled) return;
this.metrics.push({
name,
value,
timestamp: Date.now(),
tags
});
// Limiter le nombre de métriques en mémoire
if (this.metrics.length > 1000) {
this.metrics = this.metrics.slice(-500);
}
}
/**
* Enregistre une erreur
*/
static recordError(error: ErrorMetric): void {
if (!this.isEnabled) return;
this.errors.push(error);
// Limiter le nombre d'erreurs en mémoire
if (this.errors.length > 100) {
this.errors = this.errors.slice(-50);
}
}
/**
* Enregistre une action utilisateur
*/
static recordUserAction(action: string, component: string, metadata?: Record<string, any>): void {
if (!this.isEnabled) return;
this.userActions.push({
action,
component,
timestamp: Date.now(),
metadata
});
// Limiter le nombre d'actions en mémoire
if (this.userActions.length > 500) {
this.userActions = this.userActions.slice(-250);
}
}
/**
* Mesure le temps d'exécution d'une fonction
*/
static async measureTime<T>(
name: string,
fn: () => Promise<T>,
tags?: Record<string, string>
): Promise<T> {
const start = performance.now();
try {
const result = await fn();
const duration = performance.now() - start;
this.recordMetric(`${name}.duration`, duration, tags);
this.recordMetric(`${name}.success`, 1, tags);
return result;
} catch (error) {
const duration = performance.now() - start;
this.recordMetric(`${name}.duration`, duration, tags);
this.recordMetric(`${name}.error`, 1, tags);
throw error;
}
}
/**
* Enregistre les métriques de navigation
*/
private static recordNavigationMetrics(): void {
if (!('performance' in window) || !window.performance.timing) return;
const timing = window.performance.timing;
const navigation = window.performance.navigation;
// Temps de chargement de la page
const pageLoadTime = timing.loadEventEnd - timing.navigationStart;
this.recordMetric('page.load_time', pageLoadTime, {
url: window.location.pathname
});
// Temps de réponse du serveur
const serverResponseTime = timing.responseEnd - timing.requestStart;
this.recordMetric('page.server_response_time', serverResponseTime);
// Temps de rendu DOM
const domRenderTime = timing.domContentLoadedEventEnd - timing.domLoading;
this.recordMetric('page.dom_render_time', domRenderTime);
// Type de navigation
const navigationType = navigation.type === 0 ? 'navigate' :
navigation.type === 1 ? 'reload' :
navigation.type === 2 ? 'back_forward' : 'unknown';
this.recordMetric('page.navigation_type', 1, {
type: navigationType
});
}
/**
* Envoie les métriques au serveur
*/
private static async sendMetrics(): Promise<void> {
if (this.metrics.length === 0 && this.errors.length === 0 && this.userActions.length === 0) {
return;
}
try {
const payload = {
metrics: [...this.metrics],
errors: [...this.errors],
userActions: [...this.userActions],
timestamp: Date.now(),
url: window.location.href,
userAgent: navigator.userAgent
};
// Envoyer au serveur (endpoint à implémenter)
await fetch('/api/monitoring', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(payload)
});
// Vider les buffers après envoi réussi
this.metrics = [];
this.errors = [];
this.userActions = [];
} catch (error) {
console.warn('Échec de l\'envoi des métriques:', error);
}
}
/**
* Obtient les statistiques actuelles
*/
static getStats(): {
metricsCount: number;
errorsCount: number;
userActionsCount: number;
isEnabled: boolean;
} {
return {
metricsCount: this.metrics.length,
errorsCount: this.errors.length,
userActionsCount: this.userActions.length,
isEnabled: this.isEnabled
};
}
/**
* Active ou désactive le monitoring
*/
static setEnabled(enabled: boolean): void {
this.isEnabled = enabled;
}
/**
* Vide tous les buffers
*/
static clear(): void {
this.metrics = [];
this.errors = [];
this.userActions = [];
}
}
// Initialisation automatique
if (typeof window !== 'undefined') {
MonitoringService.init();
}
// Hook React pour le monitoring des composants
export function useMonitoring(componentName: string) {
const recordAction = (action: string, metadata?: Record<string, any>) => {
MonitoringService.recordUserAction(action, componentName, metadata);
};
const measureAsync = async <T>(
name: string,
fn: () => Promise<T>
): Promise<T> => {
return MonitoringService.measureTime(`${componentName}.${name}`, fn);
};
return {
recordAction,
measureAsync
};
}