Core Error Tracking
FASQ provides a comprehensive error tracking system that captures rich context when queries fail, making it easy to diagnose production issues and integrate with external error reporting services like Sentry or Crashlytics.
Overview
Section titled “Overview”When a query fails, FASQ automatically captures detailed context about the failure, including:
- Query Key: Which query failed
- Retry Count: How many retries were attempted
- Network Status: Whether the device was online
- Stale Time: Cache configuration at time of failure
- Sanitized Options: Safe query configuration (PII removed)
- Error & Stack Trace: The actual error that occurred
This context is then delivered to registered error reporters, allowing you to send detailed error reports to external services.
Quick Start
Section titled “Quick Start”1. Implement an Error Reporter
Section titled “1. Implement an Error Reporter”Create a reporter that implements FasqErrorReporter:
import 'package:fasq/fasq.dart';import 'package:sentry_flutter/sentry_flutter.dart';
class SentryErrorReporter implements FasqErrorReporter { @override void report(FasqErrorContext context) { Sentry.captureException( context.error, stackTrace: context.stackTrace, hint: Hint.withMap({ 'queryKey': context.queryKey.join('/'), 'retryCount': context.retryCount, 'networkStatus': context.networkStatus ? 'online' : 'offline', 'staleTimeMs': context.staleTime.inMilliseconds, 'sanitizedOptions': context.sanitizedQueryOptions, }), ); }}2. Register the Reporter
Section titled “2. Register the Reporter”Register your reporter with the QueryClient:
final client = QueryClient();client.addErrorReporter(SentryErrorReporter());That’s it! All query failures will now be automatically reported to Sentry with full context.
Error Context
Section titled “Error Context”The FasqErrorContext class contains all information about a query failure:
Fields
Section titled “Fields”| Field | Type | Description |
|---|---|---|
queryKey | List<Object> | The query key that failed (as a list for hierarchical keys) |
retryCount | int | Number of retry attempts made before failure |
staleTime | Duration | How long data is considered fresh |
networkStatus | bool | true if online, false if offline |
error | Object | The error that occurred |
stackTrace | StackTrace | Stack trace associated with the error |
sanitizedQueryOptions | Map<String, dynamic> | Safe query options (PII removed) |
Creating Error Context
Section titled “Creating Error Context”Error context is automatically created when queries fail. You typically don’t need to create it manually, but if you do:
final context = FasqErrorContext.fromQueryFailure( query, options, error, stackTrace,);PII Sanitization
Section titled “PII Sanitization”FASQ automatically sanitizes query options to prevent sensitive data from leaking into error reports. The sanitization follows a strict allowlist approach:
Included (Safe Fields)
Section titled “Included (Safe Fields)”enabled,refetchOnMount,isSecure- boolean flagsstaleTime,cacheTime,maxAge- duration values (in milliseconds)performance- sanitized performance options (excluding callbacks)
Excluded (Sensitive Fields)
Section titled “Excluded (Sensitive Fields)”meta- may contain user-specific messagesonSuccess/onError- callbacks may contain closures with sensitive datacircuitBreaker/circuitBreakerScope- internal implementation detailsperformance.dataTransformer- callback may contain sensitive logic
Example
Section titled “Example”// Original QueryOptionsfinal options = QueryOptions( staleTime: Duration(minutes: 5), meta: QueryMeta( successMessage: 'User John Doe logged in', // User-specific ), onError: (error) { // Callback with potential sensitive logic logToSecureSystem(error); },);
// In error context, sanitizedOptions will contain:// {// 'staleTime': 300000,// 'enabled': true,// 'refetchOnMount': false,// 'isSecure': false// // meta and onError are excluded// }Error Reporter Interface
Section titled “Error Reporter Interface”Implementing a Reporter
Section titled “Implementing a Reporter”Implement the FasqErrorReporter interface:
abstract class FasqErrorReporter { void report(FasqErrorContext context);}Example: Crashlytics Integration
Section titled “Example: Crashlytics Integration”import 'package:firebase_crashlytics/firebase_crashlytics.dart';
class CrashlyticsErrorReporter implements FasqErrorReporter { @override void report(FasqErrorContext context) { FirebaseCrashlytics.instance.recordError( context.error, context.stackTrace, reason: 'FASQ Query Error', information: [ 'Query Key: ${context.queryKey.join("/")}', 'Retry Count: ${context.retryCount}', 'Network Status: ${context.networkStatus ? "online" : "offline"}', 'Stale Time: ${context.staleTime.inMilliseconds}ms', ], ); }}Example: Custom Logging
Section titled “Example: Custom Logging”class CustomErrorReporter implements FasqErrorReporter { final void Function(FasqErrorContext) onError;
CustomErrorReporter(this.onError);
@override void report(FasqErrorContext context) { onError(context); }}
// Usageclient.addErrorReporter(CustomErrorReporter((context) { // Send to your custom analytics service analytics.track('query_error', { 'query_key': context.queryKey.join('/'), 'error_type': context.error.runtimeType.toString(), 'retry_count': context.retryCount, });}));Managing Reporters
Section titled “Managing Reporters”Adding Reporters
Section titled “Adding Reporters”final client = QueryClient();client.addErrorReporter(SentryErrorReporter());client.addErrorReporter(CrashlyticsErrorReporter());You can register multiple reporters - all will receive error notifications.
Removing Reporters
Section titled “Removing Reporters”final reporter = SentryErrorReporter();client.addErrorReporter(reporter);
// Later, remove itclient.removeErrorReporter(reporter);Reporter Failure Handling
Section titled “Reporter Failure Handling”If a reporter throws an exception, it’s automatically caught and logged (via FasqLogger if available) to prevent:
- Breaking the application
- Preventing other reporters from executing
This ensures that one faulty reporter doesn’t break your entire error reporting pipeline.
Enhanced Logging
Section titled “Enhanced Logging”The FasqLogger has been enhanced to support structured error logging with context:
final logger = FasqLogger();client.addObserver(logger);
// When errors occur, logger.logError is called with contextlogger.logError( error, stackTrace, errorContext, // Optional FasqErrorContext);When context is provided, the logger outputs structured data:
Fasq Query Error: message: Fasq Query Error errorType: SocketException errorMessage: Failed host lookup queryKey: [user-profile] retryCount: 2 staleTimeMs: 300000 networkStatus: offline sanitizedQueryOptions: {enabled: true, staleTime: 300000, ...}Best Practices
Section titled “Best Practices”1. Register Reporters Early
Section titled “1. Register Reporters Early”Register error reporters as early as possible in your app lifecycle:
void main() { WidgetsFlutterBinding.ensureInitialized();
final client = QueryClient(); client.addErrorReporter(SentryErrorReporter());
runApp(MyApp());}2. Handle Reporter Errors Gracefully
Section titled “2. Handle Reporter Errors Gracefully”Your reporter implementation should handle errors gracefully:
class RobustErrorReporter implements FasqErrorReporter { @override void report(FasqErrorContext context) { try { // Your reporting logic sendToService(context); } catch (e) { // Log but don't throw - the pipeline handles this print('Error reporter failed: $e'); } }}3. Use Sanitized Options
Section titled “3. Use Sanitized Options”Always use context.sanitizedQueryOptions instead of accessing raw QueryOptions to ensure PII is not leaked:
@overridevoid report(FasqErrorContext context) { // ✅ Good - uses sanitized options final options = context.sanitizedQueryOptions;
// ❌ Bad - may contain sensitive data // final options = query.options;}4. Filter by Query Key
Section titled “4. Filter by Query Key”You can filter which errors to report based on query keys:
class FilteredErrorReporter implements FasqErrorReporter { final Set<String> _ignoredKeys;
FilteredErrorReporter(this._ignoredKeys);
@override void report(FasqErrorContext context) { final key = context.queryKey.join('/'); if (_ignoredKeys.contains(key)) { return; // Skip reporting }
// Report to service sendToService(context); }}Integration Examples
Section titled “Integration Examples”Sentry Integration
Section titled “Sentry Integration”import 'package:fasq/fasq.dart';import 'package:sentry_flutter/sentry_flutter.dart';
class SentryErrorReporter implements FasqErrorReporter { @override void report(FasqErrorContext context) { Sentry.captureException( context.error, stackTrace: context.stackTrace, hint: Hint.withMap({ 'queryKey': context.queryKey.join('/'), 'retryCount': context.retryCount, 'networkStatus': context.networkStatus ? 'online' : 'offline', 'staleTimeMs': context.staleTime.inMilliseconds, 'sanitizedOptions': context.sanitizedQueryOptions, }), ); }}
// In main.dartvoid main() async { await SentryFlutter.init( (options) { options.dsn = 'YOUR_SENTRY_DSN'; }, appRunner: () { final client = QueryClient(); client.addErrorReporter(SentryErrorReporter());
runApp(MyApp()); }, );}Multiple Reporters
Section titled “Multiple Reporters”You can register multiple reporters for different purposes:
final client = QueryClient();
// Production error trackingclient.addErrorReporter(SentryErrorReporter());
// Analyticsclient.addErrorReporter(AnalyticsErrorReporter());
// Custom loggingclient.addErrorReporter(CustomLoggingReporter());API Reference
Section titled “API Reference”FasqErrorContext
Section titled “FasqErrorContext”class FasqErrorContext { final List<Object> queryKey; final int retryCount; final Duration staleTime; final bool networkStatus; final Object error; final StackTrace stackTrace; final Map<String, dynamic> sanitizedQueryOptions;
factory FasqErrorContext.fromQueryFailure( Query query, QueryOptions? options, Object error, StackTrace stackTrace, );}FasqErrorReporter
Section titled “FasqErrorReporter”abstract class FasqErrorReporter { void report(FasqErrorContext context);}QueryClient Methods
Section titled “QueryClient Methods”class QueryClient { void addErrorReporter(FasqErrorReporter reporter); void removeErrorReporter(FasqErrorReporter reporter);}Related Documentation
Section titled “Related Documentation”- Error Handling - Basic error handling patterns
- Logging - Enhanced logging with error context
- Security Features - PII protection and secure queries