veza/veza-mobile/src/services/monitoring.ts

307 lines
No EOL
7.4 KiB
TypeScript

import * as Sentry from '@sentry/react';
import { BrowserTracing } from '@sentry/tracing';
// Configuration Sentry
const SENTRY_DSN = process.env.REACT_APP_SENTRY_DSN || '';
export class MonitoringService {
private static instance: MonitoringService;
private isInitialized = false;
private constructor() {}
static getInstance(): MonitoringService {
if (!MonitoringService.instance) {
MonitoringService.instance = new MonitoringService();
}
return MonitoringService.instance;
}
// Initialiser Sentry
initializeSentry(): void {
if (this.isInitialized || !SENTRY_DSN) {
return;
}
Sentry.init({
dsn: SENTRY_DSN,
integrations: [
new BrowserTracing({
tracePropagationTargets: ['localhost', 'veza-platform.com'],
}),
],
tracesSampleRate: 1.0,
environment: process.env.NODE_ENV,
release: process.env.REACT_APP_VERSION || '1.0.0',
beforeSend(event) {
// Filtrer les erreurs sensibles
if (event.exception) {
const exception = event.exception.values?.[0];
if (exception?.value?.includes('password') ||
exception?.value?.includes('token') ||
exception?.value?.includes('key')) {
return null;
}
}
return event;
},
});
this.isInitialized = true;
}
// Capturer une erreur
captureException(error: Error, context?: Record<string, any>): void {
if (!this.isInitialized) {
console.error('Sentry not initialized');
return;
}
Sentry.captureException(error, {
extra: context,
});
}
// Capturer un message
captureMessage(message: string, level: Sentry.SeverityLevel = 'info'): void {
if (!this.isInitialized) {
console.log(message);
return;
}
Sentry.captureMessage(message, {
level,
});
}
// Ajouter du contexte utilisateur
setUser(user: {
id: string;
email: string;
username?: string;
}): void {
if (!this.isInitialized) {
return;
}
Sentry.setUser({
id: user.id,
email: user.email,
username: user.username,
});
}
// Ajouter des tags
setTag(key: string, value: string): void {
if (!this.isInitialized) {
return;
}
Sentry.setTag(key, value);
}
// Ajouter du contexte
setContext(name: string, context: Record<string, any>): void {
if (!this.isInitialized) {
return;
}
Sentry.setContext(name, context);
}
// Créer une transaction de performance
startTransaction(name: string, operation: string): Sentry.Transaction {
if (!this.isInitialized) {
return null as any;
}
return Sentry.startTransaction({
name,
op: operation,
});
}
// Ajouter une span à une transaction
addSpan(transaction: Sentry.Transaction, name: string, operation: string): Sentry.Span {
if (!this.isInitialized || !transaction) {
return null as any;
}
return transaction.startChild({
op: operation,
description: name,
});
}
// Mesurer les performances d'une fonction
async measurePerformance<T>(
name: string,
operation: string,
fn: () => Promise<T>
): Promise<T> {
const transaction = this.startTransaction(name, operation);
try {
const result = await fn();
transaction.setStatus('ok');
return result;
} catch (error) {
transaction.setStatus('error');
this.captureException(error as Error);
throw error;
} finally {
transaction.finish();
}
}
// Mesurer les performances d'une fonction synchrone
measurePerformanceSync<T>(
name: string,
operation: string,
fn: () => T
): T {
const transaction = this.startTransaction(name, operation);
try {
const result = fn();
transaction.setStatus('ok');
return result;
} catch (error) {
transaction.setStatus('error');
this.captureException(error as Error);
throw error;
} finally {
transaction.finish();
}
}
// Capturer les métriques de performance
capturePerformanceMetrics(metrics: {
name: string;
value: number;
unit: string;
tags?: Record<string, string>;
}): void {
if (!this.isInitialized) {
return;
}
Sentry.metrics.gauge(metrics.name, metrics.value, {
unit: metrics.unit,
tags: metrics.tags,
});
}
// Capturer les métriques de navigation
captureNavigationMetrics(route: string, duration: number): void {
this.capturePerformanceMetrics({
name: 'navigation.duration',
value: duration,
unit: 'millisecond',
tags: { route },
});
}
// Capturer les métriques d'API
captureAPIMetrics(endpoint: string, duration: number, status: number): void {
this.capturePerformanceMetrics({
name: 'api.request.duration',
value: duration,
unit: 'millisecond',
tags: { endpoint, status: status.toString() },
});
}
// Capturer les métriques de rendu
captureRenderMetrics(component: string, duration: number): void {
this.capturePerformanceMetrics({
name: 'render.duration',
value: duration,
unit: 'millisecond',
tags: { component },
});
}
// Capturer les métriques d'interaction
captureInteractionMetrics(action: string, duration: number): void {
this.capturePerformanceMetrics({
name: 'interaction.duration',
value: duration,
unit: 'millisecond',
tags: { action },
});
}
// Capturer les erreurs de réseau
captureNetworkError(url: string, status: number, message: string): void {
this.captureException(new Error(`Network error: ${status} - ${message}`), {
url,
status,
type: 'network',
});
}
// Capturer les erreurs de validation
captureValidationError(field: string, value: any, rule: string): void {
this.captureException(new Error(`Validation error: ${field} - ${rule}`), {
field,
value: String(value),
rule,
type: 'validation',
});
}
// Capturer les erreurs d'authentification
captureAuthError(action: string, reason: string): void {
this.captureException(new Error(`Authentication error: ${action} - ${reason}`), {
action,
reason,
type: 'authentication',
});
}
// Capturer les erreurs de fonctionnalité
captureFeatureError(feature: string, action: string, error: Error): void {
this.captureException(error, {
feature,
action,
type: 'feature',
});
}
// Nettoyer le contexte
clearContext(): void {
if (!this.isInitialized) {
return;
}
Sentry.setUser(null);
Sentry.setContext('app', {});
}
// Fermer Sentry
close(): void {
if (!this.isInitialized) {
return;
}
Sentry.close();
this.isInitialized = false;
}
}
// Instance singleton
export const monitoringService = MonitoringService.getInstance();
// Hook React pour le monitoring
export const useMonitoring = () => {
return {
captureException: monitoringService.captureException.bind(monitoringService),
captureMessage: monitoringService.captureMessage.bind(monitoringService),
setUser: monitoringService.setUser.bind(monitoringService),
setTag: monitoringService.setTag.bind(monitoringService),
setContext: monitoringService.setContext.bind(monitoringService),
measurePerformance: monitoringService.measurePerformance.bind(monitoringService),
measurePerformanceSync: monitoringService.measurePerformanceSync.bind(monitoringService),
};
};