/// Logger centralisé pour l'application library logger; import 'package:flutter/foundation.dart'; import '../constants/app_constants.dart'; /// Niveaux de log enum LogLevel { debug, info, warning, error, fatal, } /// Logger centralisé pour toute l'application class AppLogger { // Empêcher l'instanciation AppLogger._(); /// Couleurs ANSI pour les logs en console static const String _reset = '\x1B[0m'; static const String _red = '\x1B[31m'; static const String _green = '\x1B[32m'; static const String _yellow = '\x1B[33m'; static const String _blue = '\x1B[34m'; static const String _magenta = '\x1B[35m'; static const String _cyan = '\x1B[36m'; static const String _white = '\x1B[37m'; /// Log de niveau DEBUG (bleu) static void debug(String message, {String? tag}) { if (AppConstants.enableLogging && kDebugMode) { _log(LogLevel.debug, message, tag: tag, color: _blue); } } /// Log de niveau INFO (vert) static void info(String message, {String? tag}) { if (AppConstants.enableLogging && kDebugMode) { _log(LogLevel.info, message, tag: tag, color: _green); } } /// Log de niveau WARNING (jaune) static void warning(String message, {String? tag}) { if (AppConstants.enableLogging && kDebugMode) { _log(LogLevel.warning, message, tag: tag, color: _yellow); } } /// Log de niveau ERROR (rouge) static void error( String message, { String? tag, dynamic error, StackTrace? stackTrace, }) { if (AppConstants.enableLogging) { _log(LogLevel.error, message, tag: tag, color: _red); if (error != null) { _log(LogLevel.error, 'Error: $error', tag: tag, color: _red); } if (stackTrace != null) { _log(LogLevel.error, 'StackTrace:\n$stackTrace', tag: tag, color: _red); } // TODO: Envoyer à un service de monitoring (Sentry, Firebase Crashlytics) if (AppConstants.enableCrashReporting) { _sendToMonitoring(message, error, stackTrace); } } } /// Log de niveau FATAL (magenta) static void fatal( String message, { String? tag, dynamic error, StackTrace? stackTrace, }) { if (AppConstants.enableLogging) { _log(LogLevel.fatal, message, tag: tag, color: _magenta); if (error != null) { _log(LogLevel.fatal, 'Error: $error', tag: tag, color: _magenta); } if (stackTrace != null) { _log(LogLevel.fatal, 'StackTrace:\n$stackTrace', tag: tag, color: _magenta); } // TODO: Envoyer à un service de monitoring (Sentry, Firebase Crashlytics) if (AppConstants.enableCrashReporting) { _sendToMonitoring(message, error, stackTrace, isFatal: true); } } } /// Log d'une requête HTTP static void httpRequest({ required String method, required String url, Map? headers, dynamic body, }) { if (AppConstants.enableLogging && kDebugMode) { final buffer = StringBuffer(); buffer.writeln('┌─────────────────────────────────────────────────'); buffer.writeln('│ HTTP REQUEST'); buffer.writeln('├─────────────────────────────────────────────────'); buffer.writeln('│ Method: $method'); buffer.writeln('│ URL: $url'); if (headers != null && headers.isNotEmpty) { buffer.writeln('│ Headers:'); headers.forEach((key, value) { buffer.writeln('│ $key: $value'); }); } if (body != null) { buffer.writeln('│ Body: $body'); } buffer.writeln('└─────────────────────────────────────────────────'); _log(LogLevel.debug, buffer.toString(), color: _cyan); } } /// Log d'une réponse HTTP static void httpResponse({ required int statusCode, required String url, Map? headers, dynamic body, Duration? duration, }) { if (AppConstants.enableLogging && kDebugMode) { final buffer = StringBuffer(); buffer.writeln('┌─────────────────────────────────────────────────'); buffer.writeln('│ HTTP RESPONSE'); buffer.writeln('├─────────────────────────────────────────────────'); buffer.writeln('│ Status: $statusCode'); buffer.writeln('│ URL: $url'); if (duration != null) { buffer.writeln('│ Duration: ${duration.inMilliseconds}ms'); } if (headers != null && headers.isNotEmpty) { buffer.writeln('│ Headers:'); headers.forEach((key, value) { buffer.writeln('│ $key: $value'); }); } if (body != null) { buffer.writeln('│ Body: $body'); } buffer.writeln('└─────────────────────────────────────────────────'); final color = statusCode >= 200 && statusCode < 300 ? _green : _red; _log(LogLevel.debug, buffer.toString(), color: color); } } /// Log d'un événement BLoC static void blocEvent(String blocName, String eventName, {dynamic data}) { if (AppConstants.enableLogging && kDebugMode) { final message = data != null ? '[$blocName] Event: $eventName | Data: $data' : '[$blocName] Event: $eventName'; _log(LogLevel.debug, message, color: _cyan); } } /// Log d'un changement d'état BLoC static void blocState(String blocName, String stateName, {dynamic data}) { if (AppConstants.enableLogging && kDebugMode) { final message = data != null ? '[$blocName] State: $stateName | Data: $data' : '[$blocName] State: $stateName'; _log(LogLevel.debug, message, color: _magenta); } } /// Log d'une navigation static void navigation(String from, String to) { if (AppConstants.enableLogging && kDebugMode) { _log(LogLevel.debug, 'Navigation: $from → $to', color: _yellow); } } /// Log d'une action utilisateur static void userAction(String action, {Map? data}) { if (AppConstants.enableLogging && kDebugMode) { final message = data != null ? 'User Action: $action | Data: $data' : 'User Action: $action'; _log(LogLevel.info, message, color: _green); } // TODO: Envoyer à un service d'analytics if (AppConstants.enableAnalytics) { _sendToAnalytics(action, data); } } /// Méthode privée pour logger avec formatage static void _log( LogLevel level, String message, { String? tag, String color = _white, }) { final timestamp = DateTime.now().toIso8601String(); final levelStr = level.name.toUpperCase().padRight(7); final tagStr = tag != null ? '[$tag] ' : ''; if (kDebugMode) { // En mode debug, utiliser les couleurs debugPrint('$color$timestamp | $levelStr | $tagStr$message$_reset'); } else { // En mode release, pas de couleurs debugPrint('$timestamp | $levelStr | $tagStr$message'); } } /// Envoyer les erreurs à un service de monitoring static void _sendToMonitoring( String message, dynamic error, StackTrace? stackTrace, { bool isFatal = false, }) { // TODO: Implémenter l'envoi à Sentry, Firebase Crashlytics, etc. // Exemple avec Sentry: // Sentry.captureException( // error, // stackTrace: stackTrace, // hint: Hint.withMap({'message': message}), // ); } /// Envoyer les événements à un service d'analytics static void _sendToAnalytics(String action, Map? data) { // TODO: Implémenter l'envoi à Firebase Analytics, Mixpanel, etc. // Exemple avec Firebase Analytics: // FirebaseAnalytics.instance.logEvent( // name: action, // parameters: data, // ); } /// Divider pour séparer visuellement les logs static void divider({String? title}) { if (AppConstants.enableLogging && kDebugMode) { if (title != null) { debugPrint('$_cyan═══════════════════════════════════════════════════$_reset'); debugPrint('$_cyan $title$_reset'); debugPrint('$_cyan═══════════════════════════════════════════════════$_reset'); } else { debugPrint('$_cyan═══════════════════════════════════════════════════$_reset'); } } } } /// Extension pour faciliter le logging depuis n'importe où extension LoggerExtension on Object { /// Log debug void logDebug(String message) { AppLogger.debug(message, tag: runtimeType.toString()); } /// Log info void logInfo(String message) { AppLogger.info(message, tag: runtimeType.toString()); } /// Log warning void logWarning(String message) { AppLogger.warning(message, tag: runtimeType.toString()); } /// Log error void logError(String message, {dynamic error, StackTrace? stackTrace}) { AppLogger.error( message, tag: runtimeType.toString(), error: error, stackTrace: stackTrace, ); } }