Skip to content

feat(logging): add structured logger + Crashlytics integration - #35

Open
Melsaeed276 wants to merge 1 commit into
renewfrom
codex/add-logging-system
Open

feat(logging): add structured logger + Crashlytics integration#35
Melsaeed276 wants to merge 1 commit into
renewfrom
codex/add-logging-system

Conversation

@Melsaeed276

Copy link
Copy Markdown
Owner

Summary

  • add centralized app logging module (AppLogger) with structured levels and context support
  • integrate Firebase Crashlytics for error reporting and breadcrumbs
  • wire global error capture in bootstrap:
    • FlutterError.onError
    • PlatformDispatcher.instance.onError
    • zoned uncaught async errors (runZonedGuarded)
  • add practical auth-flow logs (login/register success/failure)
  • add logger safety fallback so logs work before explicit initialization
  • add unit test for logger fallback behavior

Testing

  • flutter analyze
  • flutter test
  • Other (describe below)
  • flutter test --tags golden
  • dart run tool/design_guard.dart --changed-only --base-ref origin/renew

Design Compliance

  • I reviewed docs/design-system/checklist.md
  • I used design tokens/theme APIs (no hardcoded colors/text styles)
  • I added or updated golden tests for changed UI previews/components
  • I validated EN/AR/TR behavior and RTL layout impact

Notes

  • Crashlytics collection is enabled in release/profile and gracefully disabled in local/test fallback mode.
  • Per your process, PR stays open for your GitHub approval.

Copilot AI review requested due to automatic review settings March 3, 2026 12:42

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a centralized structured logging facility (AppLogger) and wires global error capture + Firebase Crashlytics reporting into app bootstrap, with additional auth-flow instrumentation and supporting test/dependency updates.

Changes:

  • Introduce AppLogger (structured levels, context, Crashlytics breadcrumbs/error reporting) and a unit test for pre-initialization fallback behavior
  • Wire global error capture in bootstrapApp() (FlutterError.onError, PlatformDispatcher.instance.onError, runZonedGuarded)
  • Update auth pages to emit success/failure logs; update timezone service contract + fakes to support coordinate-based timezone resolution

Reviewed changes

Copilot reviewed 13 out of 14 changed files in this pull request and generated 12 comments.

Show a summary per file
File Description
project_code/lib/app/logging/app_logger.dart New centralized logger + Crashlytics integration and fallback behavior
project_code/lib/app/bootstrap.dart Initializes logger and installs global error handlers / zone guard
project_code/lib/features/auth/presentation/login_page.dart Adds logging around phone/Google login flows
project_code/lib/features/auth/presentation/register_page.dart Adds logging around phone/Google registration flows
project_code/lib/features/prayer_times/location/domain/location_services.dart Extends TimezoneService API with coordinate-based resolver
project_code/lib/features/prayer_times/location/data/flutter_timezone_service.dart Implements new TimezoneService method (currently a stub)
project_code/test/app/logging/app_logger_test.dart New unit test covering logger safety before initialization
project_code/test/prayer_times/location/location_setup_page_test.dart Updates fake timezone service to satisfy new interface
project_code/test/prayer_times/location/location_setup_controller_test.dart Updates fake timezone service to satisfy new interface
project_code/test/design/design_golden_test.dart Removes unused app import (keeps design preview golden setup)
project_code/test/design/design_accessibility_test.dart Removes unused app import (keeps semantics/RTL checks)
project_code/pubspec.yaml Adds logger and firebase_crashlytics dependencies
project_code/pubspec.lock Locks new dependency versions and transitives
project_code/macos/Flutter/GeneratedPluginRegistrant.swift Registers Crashlytics plugin for macOS

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +51 to +59
if (crashlyticsEnabled) {
try {
await FirebaseCrashlytics.instance.setCrashlyticsCollectionEnabled(
true,
);
enabled = true;
} catch (_) {
enabled = false;
}

Copilot AI Mar 3, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

initialize() only calls setCrashlyticsCollectionEnabled(true) when crashlyticsEnabled is true, but never explicitly disables collection when it’s false. Since Crashlytics collection can persist between runs, debug/test sessions may still upload reports. Consider always calling setCrashlyticsCollectionEnabled(crashlyticsEnabled) (or explicitly setting false in the else branch) to guarantee the intended behavior.

Suggested change
if (crashlyticsEnabled) {
try {
await FirebaseCrashlytics.instance.setCrashlyticsCollectionEnabled(
true,
);
enabled = true;
} catch (_) {
enabled = false;
}
try {
await FirebaseCrashlytics.instance.setCrashlyticsCollectionEnabled(
crashlyticsEnabled,
);
enabled = crashlyticsEnabled;
} catch (_) {
enabled = false;

Copilot uses AI. Check for mistakes.
Comment on lines +18 to +22
Future<String> resolveTimezoneForCoordinates({
required double latitude,
required double longitude,
}) {
return resolveLocalTimezone();

Copilot AI Mar 3, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

resolveTimezoneForCoordinates ignores latitude/longitude and just returns the device’s local timezone. This is misleading given the API name and will produce incorrect timezones when the user manually selects a location in a different timezone. Either implement a coordinate-based lookup (or rename/remove this API if not supported) so manual locations get the correct timezone.

Copilot uses AI. Check for mistakes.
Comment on lines 79 to 81
setState(() {
_error = 'Login failed unexpectedly.';
});

Copilot AI Mar 3, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This setState runs in the generic catch path without checking mounted. If the page is disposed while the async call is running, this can throw at runtime. Mirror the mounted guard used elsewhere before calling setState in this error path.

Copilot uses AI. Check for mistakes.
Comment on lines 114 to 116
setState(() {
_error = error.message;
});

Copilot AI Mar 3, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This setState runs in the AuthFailure catch path without checking mounted. If the widget is disposed while the Google login is in-flight, this can throw setState() called after dispose(). Add a mounted guard before calling setState here.

Copilot uses AI. Check for mistakes.
Comment on lines 123 to 125
setState(() {
_error = 'Google login failed unexpectedly.';
});

Copilot AI Mar 3, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This setState runs in the generic catch path without checking mounted. If the widget is disposed before the Future completes, this can throw at runtime. Add a mounted guard before calling setState here.

Copilot uses AI. Check for mistakes.
fatal: true,
),
);
return true;

Copilot AI Mar 3, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PlatformDispatcher.instance.onError currently returns true unconditionally, which signals the error was handled and can suppress default error reporting/printing. Consider returning false in debug (or delegating to any previous handler) so developers still see uncaught errors, while returning true only when you explicitly want to swallow them.

Copilot uses AI. Check for mistakes.
Comment on lines 70 to 72
setState(() {
_error = error.message;
});

Copilot AI Mar 3, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This setState runs in the AuthFailure catch path without checking mounted. If the widget is disposed while the login request is in-flight, this can throw setState() called after dispose(). Add a if (!mounted) return; guard (or wrap the setState in a if (mounted) check) before mutating state in error paths too.

Copilot uses AI. Check for mistakes.
AppLogger.instance.warning(
'Register with phone failed',
context: <String, Object?>{'code': error.code.name},
);

Copilot AI Mar 3, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This setState runs in the AuthFailure catch path without checking mounted. If the widget is disposed while the registration request is in-flight, this can throw setState() called after dispose(). Add a mounted guard before calling setState in this error path.

Suggested change
);
);
if (!mounted) {
return;
}

Copilot uses AI. Check for mistakes.
Comment on lines 131 to 133
setState(() {
_error = error.message;
});

Copilot AI Mar 3, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This setState runs in the AuthFailure catch path without checking mounted. If the widget is disposed while the Google registration is in-flight, this can throw setState() called after dispose(). Add a mounted guard before calling setState here.

Copilot uses AI. Check for mistakes.
'Unexpected Google registration failure',
error: error,
stackTrace: stackTrace,
);

Copilot AI Mar 3, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This setState runs in the generic catch path without checking mounted. If the widget is disposed before the Future completes, this can throw at runtime. Add a mounted guard before calling setState here.

Suggested change
);
);
if (!mounted) {
return;
}

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants