import 'package:flutter/foundation.dart'; import 'package:package_info_plus/package_info_plus.dart'; import 'package:sentry_flutter/sentry_flutter.dart'; /// Sentry service for error tracking and performance monitoring. /// /// This service handles: /// - Initializing Sentry SDK /// - Capturing exceptions and errors /// - Capturing custom messages /// - Setting user context after login /// - Performance monitoring /// /// Usage: /// ```dart /// // Initialize in main.dart /// await SentryService.init( /// dsn: 'your-sentry-dsn', /// appRunner: () => runApp(MyApp()), /// ); /// /// // Capture exception /// SentryService.captureException(error, stackTrace: stackTrace); /// /// // Capture message /// SentryService.captureMessage('User performed action X'); /// /// // Set user context after login /// SentryService.setUser(userId: '123', email: 'user@example.com'); /// ``` class SentryService { SentryService._(); /// Sentry DSN - Replace with your actual DSN from Sentry dashboard static const String _dsn = 'https://2c5893508a29e5ea750b64d5ee31aeef@o4509632266436608.ingest.us.sentry.io/4510464530972672'; /// Initialize Sentry SDK /// /// Must be called before runApp() in main.dart. /// Wraps the app with Sentry error boundary. /// /// [dsn] - Optional DSN override (uses default if not provided) /// [appRunner] - The function that runs the app (typically runApp(MyApp())) /// [environment] - Environment name (e.g., 'development', 'production') static Future init({ String? dsn, required Future Function() appRunner, String? environment, }) async { // Get package info for release version final packageInfo = await PackageInfo.fromPlatform(); final release = 'partner@${packageInfo.version}+${packageInfo.buildNumber}'; await SentryFlutter.init( (options) { options ..dsn = dsn ?? _dsn // Release version: worker@1.0.1+29 ..release = release // Environment configuration ..environment = environment ?? (kReleaseMode ? 'production' : 'development') // Performance monitoring ..tracesSampleRate = kReleaseMode ? 0.2 : 1.0 // 20% in prod, 100% in dev ..profilesSampleRate = kReleaseMode ? 0.2 : 1.0 // Enable automatic instrumentation ..enableAutoPerformanceTracing = true // Capture failed requests ..captureFailedRequests = true // Debug mode settings ..debug = kDebugMode // Add app-specific tags ..beforeSend = (event, hint) { // Filter out certain errors if needed // Return null to drop the event return event; }; }, appRunner: appRunner, ); debugPrint('🔴 Sentry initialized (release: $release, enabled: ${!kDebugMode})'); } /// Capture an exception with optional stack trace /// /// [exception] - The exception to capture /// [stackTrace] - Optional stack trace /// [hint] - Optional hint with additional context static Future captureException( dynamic exception, { StackTrace? stackTrace, Hint? hint, }) async { try { await Sentry.captureException( exception, stackTrace: stackTrace, hint: hint, ); debugPrint('🔴 Sentry: Exception captured - ${exception.runtimeType}'); } catch (e) { debugPrint('🔴 Sentry error: Failed to capture exception - $e'); } } /// Capture a custom message /// /// [message] - The message to capture /// [level] - Severity level (default: info) /// [params] - Optional parameters to include static Future captureMessage( String message, { SentryLevel level = SentryLevel.info, Map? params, }) async { try { await Sentry.captureMessage( message, level: level, withScope: params != null ? (scope) { params.forEach((key, value) { scope.setExtra(key, value); }); } : null, ); debugPrint('🔴 Sentry: Message captured - $message'); } catch (e) { debugPrint('🔴 Sentry error: Failed to capture message - $e'); } } /// Set user context for error tracking /// /// Call this after successful login to associate errors with users. /// /// [userId] - User's unique identifier /// [email] - User's email (optional) /// [username] - User's display name (optional) /// [extras] - Additional user data (optional) static Future setUser({ required String userId, String? email, String? username, Map? extras, }) async { try { await Sentry.configureScope((scope) { scope.setUser(SentryUser( id: userId, email: email, username: username, data: extras, )); }); debugPrint('🔴 Sentry: User set - $userId'); } catch (e) { debugPrint('🔴 Sentry error: Failed to set user - $e'); } } /// Clear user context on logout static Future clearUser() async { try { await Sentry.configureScope((scope) { scope.setUser(null); }); debugPrint('🔴 Sentry: User cleared'); } catch (e) { debugPrint('🔴 Sentry error: Failed to clear user - $e'); } } /// Add a breadcrumb for tracking user actions /// /// Breadcrumbs are used to track the sequence of events leading to an error. /// /// [message] - Description of the action /// [category] - Category of the breadcrumb (e.g., 'navigation', 'ui.click') /// [data] - Additional data (optional) static Future addBreadcrumb({ required String message, String? category, Map? data, SentryLevel level = SentryLevel.info, }) async { try { await Sentry.addBreadcrumb(Breadcrumb( message: message, category: category, data: data, level: level, timestamp: DateTime.now(), )); } catch (e) { debugPrint('🔴 Sentry error: Failed to add breadcrumb - $e'); } } /// Set a tag for filtering in Sentry dashboard /// /// [key] - Tag name /// [value] - Tag value static Future setTag(String key, String value) async { try { await Sentry.configureScope((scope) { scope.setTag(key, value); }); } catch (e) { debugPrint('🔴 Sentry error: Failed to set tag - $e'); } } /// Set extra context data /// /// [key] - Context key /// [value] - Context value (will be serialized) static Future setExtra(String key, dynamic value) async { try { await Sentry.configureScope((scope) { scope.setExtra(key, value); }); } catch (e) { debugPrint('🔴 Sentry error: Failed to set extra - $e'); } } /// Start a performance transaction /// /// [name] - Transaction name /// [operation] - Operation type (e.g., 'http.client', 'ui.load') /// /// Returns the transaction to be finished later. static ISentrySpan? startTransaction(String name, String operation) { try { return Sentry.startTransaction(name, operation); } catch (e) { debugPrint('🔴 Sentry error: Failed to start transaction - $e'); return null; } } }