252 lines
7.2 KiB
Dart
252 lines
7.2 KiB
Dart
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<void> init({
|
|
String? dsn,
|
|
required Future<void> 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;
|
|
return null;
|
|
};
|
|
},
|
|
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<void> 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<void> captureMessage(
|
|
String message, {
|
|
SentryLevel level = SentryLevel.info,
|
|
Map<String, dynamic>? 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<void> setUser({
|
|
required String userId,
|
|
String? email,
|
|
String? username,
|
|
Map<String, dynamic>? 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<void> 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<void> addBreadcrumb({
|
|
required String message,
|
|
String? category,
|
|
Map<String, dynamic>? 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<void> 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<void> 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;
|
|
}
|
|
}
|
|
}
|