Global Observability & Error Reporting
Branches: feat/global-observability (commits merged directly to develop)
Date: March 2026
Overview
Implemented a comprehensive, always-on error observability layer for the entire app. This includes a GlobalProviderObserver that intercepts Riverpod state errors, expanded service-level try-catch blocks that report errors to Supabase, and an opt-in model that lets users control whether automatic crash reports are sent.
This replaced ad-hoc print() statements and silent catch blocks with a unified reporting pipeline: every error flows through LogService → ErrorNotifier → Supabase app_issues table.
Why It Was Built
Without centralized observability, silent errors in services went unnoticed until users complained. The app_issues table existed but was only populated by the manual error dialog. Automatic background reporting was needed to surface issues in production without requiring user interaction on every error.
What Changed
core/observers/provider_observer.dart (new)
Added GlobalProviderObserver extends ProviderObserver:
class GlobalProviderObserver extends ProviderObserver {
@override
void didAddProvider(ProviderBase provider, Object? value, ProviderContainer container) { ... }
@override
void providerDidFail(ProviderBase provider, Object error, StackTrace stackTrace, ProviderContainer container) {
LogService.instance.log('Provider failed: ${provider.name}', error, stackTrace);
IssueReportService().reportIssue(
errorMessage: '[Provider] ${provider.name}: $error',
stackTrace: stackTrace.toString(),
);
}
}Wired in main.dart:
final container = ProviderContainer(observers: [GlobalProviderObserver()]);Any Riverpod provider that throws during its build or async computation is automatically reported.
Service-Level Reporting
The following services were updated to include LogService.instance.reportError(e, stack) in their catch blocks:
| Service | What gets reported |
|---|---|
auth_service.dart | Login, registration, session refresh failures |
gamification_service.dart | XP update and badge award errors |
crew_service.dart | Crew creation, join, leave failures |
feedback_service.dart | Feedback submission errors |
pr_service.dart | PR dashboard data fetch failures |
review_service.dart | Review creation and fetch errors |
ticket_service.dart | Ticket purchase and wallet sync failures |
main.dart — Global Error Handlers
FlutterError.onError = (details) {
container.read(errorNotifierProvider.notifier).reportError(
details.exception, details.stack,
);
};
PlatformDispatcher.instance.onError = (error, stack) {
container.read(errorNotifierProvider.notifier).reportError(error, stack);
return true;
};Both handlers forward to ErrorNotifier, which checks the user’s crash reporting preference before sending to Supabase.
error_notifier.dart — Automatic Proactive Reporting
ErrorNotifier.reportError() now:
- Updates the UI state (shows the error dialog if visible)
- Calls
IssueReportService().reportIssue()automatically in the background
The IssueReportService.reportIssue() checks SharedPreferences['feature_crashReporting'] before sending, respecting the user’s opt-in choice.
Opt-In Privacy Model (feat/privacy commits)
Changed crash reporting from always-on to opt-in:
- Default:
false(no reports sent without consent) - Exception: developer accounts (e.g.,
elia-c@outlook.it) default totrue - Toggle in Settings → Privacy → Segnalazione Errori
Architecture Notes
The reporting pipeline has two paths:
User sees error dialog → taps "Invia Report"
→ IssueReportService.reportIssue(force: true) ← always sends
Any error in FlutterError / PlatformDispatcher / Provider failure
→ ErrorNotifier.reportError()
→ IssueReportService.reportIssue() ← checks preference first
The force: true flag on manual submissions bypasses the preference check, because the user explicitly clicked “send”. Automatic background reports are gated by the privacy setting.
Database
Reports are stored in Supabase:
| Column | Type | Description |
|---|---|---|
id | uuid | Primary key |
user_id | uuid | Authenticated user (nullable for unauthenticated crashes) |
error_message | text | Cleaned error string |
stack_trace | text | Full stack trace |
user_description | text | User-provided context (manual reports only) |
device_info | jsonb | Platform, OS version, browser/device model |
app_version | text | 1.0.0+1 format |
status | text | new, in_progress, resolved |
created_at | timestamptz | Auto-set |