Skip to content

Commit

Permalink
add logging repository for collecting errors and logs
Browse files Browse the repository at this point in the history
  • Loading branch information
smart7even committed Aug 18, 2024
1 parent 3a03bc8 commit 49c2580
Show file tree
Hide file tree
Showing 10 changed files with 263 additions and 28 deletions.
106 changes: 106 additions & 0 deletions ios/Podfile.lock
Original file line number Diff line number Diff line change
@@ -1,7 +1,75 @@
PODS:
- appmetrica_plugin (2.1.1):
- AppMetricaAnalytics (~> 5.3)
- Flutter
- AppMetricaAdSupport (5.7.0):
- AppMetricaCore (= 5.7.0)
- AppMetricaCoreExtension (= 5.7.0)
- AppMetricaAnalytics (5.7.0):
- AppMetricaAdSupport (= 5.7.0)
- AppMetricaCore (= 5.7.0)
- AppMetricaCrashes (= 5.7.0)
- AppMetricaWebKit (= 5.7.0)
- AppMetricaCore (5.7.0):
- AppMetricaCoreUtils (= 5.7.0)
- AppMetricaEncodingUtils (= 5.7.0)
- AppMetricaFMDB (= 5.7.0)
- AppMetricaHostState (= 5.7.0)
- AppMetricaLog (= 5.7.0)
- AppMetricaNetwork (= 5.7.0)
- AppMetricaPlatform (= 5.7.0)
- AppMetricaProtobuf (= 5.7.0)
- AppMetricaProtobufUtils (= 5.7.0)
- AppMetricaStorageUtils (= 5.7.0)
- AppMetricaCoreExtension (5.7.0):
- AppMetricaCore (= 5.7.0)
- AppMetricaStorageUtils (= 5.7.0)
- AppMetricaCoreUtils (5.7.0):
- AppMetricaLog (= 5.7.0)
- AppMetricaCrashes (5.7.0):
- AppMetricaCore (= 5.7.0)
- AppMetricaCoreExtension (= 5.7.0)
- AppMetricaCoreUtils (= 5.7.0)
- AppMetricaEncodingUtils (= 5.7.0)
- AppMetricaHostState (= 5.7.0)
- AppMetricaLog (= 5.7.0)
- AppMetricaPlatform (= 5.7.0)
- AppMetricaProtobufUtils (= 5.7.0)
- AppMetricaStorageUtils (= 5.7.0)
- KSCrash/Recording (~> 1.17.0)
- KSCrash/Recording/Tools
- AppMetricaEncodingUtils (5.7.0):
- AppMetricaCoreUtils (= 5.7.0)
- AppMetricaLog (= 5.7.0)
- AppMetricaPlatform (= 5.7.0)
- AppMetricaFMDB (5.7.0)
- AppMetricaHostState (5.7.0):
- AppMetricaCoreUtils (= 5.7.0)
- AppMetricaLog (= 5.7.0)
- AppMetricaLog (5.7.0)
- AppMetricaNetwork (5.7.0):
- AppMetricaCoreUtils (= 5.7.0)
- AppMetricaLog (= 5.7.0)
- AppMetricaPlatform (= 5.7.0)
- AppMetricaPlatform (5.7.0):
- AppMetricaCoreUtils (= 5.7.0)
- AppMetricaLog (= 5.7.0)
- AppMetricaProtobuf (5.7.0)
- AppMetricaProtobufUtils (5.7.0):
- AppMetricaProtobuf (= 5.7.0)
- AppMetricaStorageUtils (5.7.0):
- AppMetricaCoreUtils (= 5.7.0)
- AppMetricaLog (= 5.7.0)
- AppMetricaWebKit (5.7.0):
- AppMetricaCore (= 5.7.0)
- AppMetricaCoreUtils (= 5.7.0)
- AppMetricaLog (= 5.7.0)
- Flutter (1.0.0)
- home_widget (0.0.1):
- Flutter
- KSCrash/Recording (1.17.4):
- KSCrash/Recording/Tools (= 1.17.4)
- KSCrash/Recording/Tools (1.17.4)
- path_provider_foundation (0.0.1):
- Flutter
- FlutterMacOS
Expand Down Expand Up @@ -30,6 +98,7 @@ PODS:
- sqlite3/rtree

DEPENDENCIES:
- appmetrica_plugin (from `.symlinks/plugins/appmetrica_plugin/ios`)
- Flutter (from `Flutter`)
- home_widget (from `.symlinks/plugins/home_widget/ios`)
- path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`)
Expand All @@ -39,9 +108,28 @@ DEPENDENCIES:

SPEC REPOS:
trunk:
- AppMetricaAdSupport
- AppMetricaAnalytics
- AppMetricaCore
- AppMetricaCoreExtension
- AppMetricaCoreUtils
- AppMetricaCrashes
- AppMetricaEncodingUtils
- AppMetricaFMDB
- AppMetricaHostState
- AppMetricaLog
- AppMetricaNetwork
- AppMetricaPlatform
- AppMetricaProtobuf
- AppMetricaProtobufUtils
- AppMetricaStorageUtils
- AppMetricaWebKit
- KSCrash
- sqlite3

EXTERNAL SOURCES:
appmetrica_plugin:
:path: ".symlinks/plugins/appmetrica_plugin/ios"
Flutter:
:path: Flutter
home_widget:
Expand All @@ -56,8 +144,26 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/sqlite3_flutter_libs/ios"

SPEC CHECKSUMS:
appmetrica_plugin: d5b67180992259ecfa040189027808915101b893
AppMetricaAdSupport: c1888375a6e85bb55d1e36cc8c2a86b1dcc3e88d
AppMetricaAnalytics: 3eba443a50eb56085dc69a6efb82aed7ff0fbb8b
AppMetricaCore: 2b88e6d5a5aafd9663bbc6056388ac0e95b6e2f1
AppMetricaCoreExtension: 1d7924bb168f7238fffeecaeea0a04a0b29037c5
AppMetricaCoreUtils: 9ed2396aa229554bd6b7532b5f52c638275bba67
AppMetricaCrashes: 1a9d2e68cfa92425d6ce3a997287db051219263d
AppMetricaEncodingUtils: b789fa4c1e65291751d2f2ddf71f63e097abfcc8
AppMetricaFMDB: adc2ca20358239db4c246113e2d4f0663779839c
AppMetricaHostState: 70a575d9226ae3fcf9adc3f948403e45a8199a60
AppMetricaLog: 8903049a47249067f01839f392552e91feb72171
AppMetricaNetwork: a738841c6987ca81da92ba721edcda3ebced351a
AppMetricaPlatform: 456d620ca7816ae57d9b94c19bddc10f36fab7b3
AppMetricaProtobuf: 4d9c8e28bc7ee7f2df7bc0b5882b5b8734add7a5
AppMetricaProtobufUtils: 69272f30e19e30d814b7f05cbd1130e888764c8d
AppMetricaStorageUtils: d72c866868dce22626441349bcd45eeec43b9e86
AppMetricaWebKit: bf5a05e7ed13857807522639c8984a1c192e88ef
Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
home_widget: 0434835a4c9a75704264feff6be17ea40e0f0d57
KSCrash: 158a0998f08ae7d4e54ef8a2da62d6e08b46d03a
path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46
share_plus: 8875f4f2500512ea181eef553c3e27dba5135aad
shared_preferences_foundation: fcdcbc04712aee1108ac7fda236f363274528f78
Expand Down
40 changes: 40 additions & 0 deletions lib/common/logging/logging_repository.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import 'package:appmetrica_plugin/appmetrica_plugin.dart';

abstract class ILoggingRepository {
Future<void> logError(
Object exception,
StackTrace stackTrace, {
String? hint,
bool fatal = false,
});
Future<void> logEvent(
String eventName, [
Map<String, Object>? attributes,
]);
}

class AppMetricaLoggingRepository implements ILoggingRepository {
@override
Future<void> logError(
Object exception,
StackTrace stackTrace, {
String? hint,
bool fatal = false,
}) async {
await AppMetrica.reportError(
message: exception.toString(),
errorDescription: AppMetricaErrorDescription.fromObjectAndStackTrace(
exception,
stackTrace,
),
);
}

@override
Future<void> logEvent(
String eventName, [
Map<String, Object>? attributes,
]) async {
await AppMetrica.reportEventWithMap(eventName, attributes);
}
}
21 changes: 21 additions & 0 deletions lib/common/logging/logging_repository_factory.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import 'package:appmetrica_plugin/appmetrica_plugin.dart';
import 'package:flutter/foundation.dart';
import 'package:uneconly/common/logging/logging_repository.dart';
import 'package:uneconly/constants.dart';

abstract class ILoggingRepositoryFactory {
Future<ILoggingRepository> create();
}

class LoggingRepositoryFactory implements ILoggingRepositoryFactory {
@override
Future<ILoggingRepository> create() async {
await AppMetrica.activate(
kDebugMode
? const AppMetricaConfig(appMetricaDevKey)
: const AppMetricaConfig(appMetricaProductionKey),
);

return AppMetricaLoggingRepository();
}
}
2 changes: 2 additions & 0 deletions lib/common/model/dependencies.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@ import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:uneconly/common/database/database.dart';
import 'package:uneconly/common/logging/logging_repository.dart';
import 'package:uneconly/feature/initialization/widget/inherited_dependencies.dart';
import 'package:uneconly/feature/settings/data/settings_repository.dart';

class Dependencies {
Dependencies();

late final ILoggingRepository loggingRepository;
late final SharedPreferences sharedPreferences;
late final ISettingsRepository settingsRepository;
late final MyDatabase database;
Expand Down
2 changes: 2 additions & 0 deletions lib/constants.dart
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
const serverAddress = 'https://roadmapik.com:5000';
const appMetricaDevKey = '97b0fe49-d3aa-4ef5-9825-e02dbf9b0efc';
const appMetricaProductionKey = '1498463f-e5a0-4da7-81fa-ccf7c3e1e131';
24 changes: 19 additions & 5 deletions lib/feature/initialization/data/initialization.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import 'dart:async';
import 'dart:ui';

import 'package:flutter/material.dart';
import 'package:uneconly/common/logging/logging_repository.dart';
import 'package:uneconly/common/logging/logging_repository_factory.dart';
import 'package:uneconly/common/model/dependencies.dart';
import 'package:uneconly/common/util/error_util.dart';
import 'package:uneconly/feature/initialization/data/initialize_dependencies.dart';
Expand All @@ -14,23 +16,30 @@ Future<Dependencies> $initializeApp({
void Function(int progress, String message)? onProgress,
FutureOr<void> Function(Dependencies dependencies)? onSuccess,
void Function(Object error, StackTrace stackTrace)? onError,
void Function(ILoggingRepository loggingRepository)?
onLoggingRepositoryInitialized,
}) =>
_$initializeApp ??= Future<Dependencies>(() async {
late final WidgetsBinding binding;
final stopwatch = Stopwatch()..start();
ILoggingRepository? loggingRepository;
try {
binding = WidgetsFlutterBinding.ensureInitialized()..deferFirstFrame();
await _catchExceptions();
final dependencies =
await $initializeDependencies(onProgress: onProgress)
.timeout(const Duration(minutes: 7));
loggingRepository = await LoggingRepositoryFactory().create();
onLoggingRepositoryInitialized?.call(loggingRepository);
await _catchExceptions(loggingRepository);
final dependencies = await $initializeDependencies(
onProgress: onProgress,
loggingRepository: loggingRepository,
).timeout(const Duration(minutes: 7));
await onSuccess?.call(dependencies);

return dependencies;
} on Object catch (error, stackTrace) {
onError?.call(error, stackTrace);
ErrorUtil.logError(error, stackTrace, hint: 'Failed to initialize app')
.ignore();
loggingRepository?.logError(error, stackTrace);
rethrow;
} finally {
stopwatch.stop();
Expand All @@ -51,9 +60,10 @@ Future<void> $resetApp(Dependencies dependencies) async {}
@visibleForTesting
Future<void> $disposeApp(Dependencies dependencies) async {}

Future<void> _catchExceptions() async {
Future<void> _catchExceptions(ILoggingRepository loggingRepository) async {
try {
PlatformDispatcher.instance.onError = (error, stackTrace) {
loggingRepository.logError(error, stackTrace);
ErrorUtil.logError(
error,
stackTrace,
Expand All @@ -65,6 +75,10 @@ Future<void> _catchExceptions() async {

final sourceFlutterError = FlutterError.onError;
FlutterError.onError = (final details) {
loggingRepository.logError(
details.exception,
details.stack ?? StackTrace.current,
);
ErrorUtil.logError(
details.exception,
details.stack ?? StackTrace.current,
Expand Down
60 changes: 38 additions & 22 deletions lib/feature/initialization/data/initialize_dependencies.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import 'package:dio/dio.dart';
import 'package:l/l.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:uneconly/common/database/database.dart';
import 'package:uneconly/common/logging/logging_repository.dart';
import 'package:uneconly/common/model/dependencies.dart';
import 'package:uneconly/constants.dart';
import 'package:uneconly/feature/initialization/data/platform/platform_initialization.dart';
Expand All @@ -13,11 +14,15 @@ import 'package:uneconly/feature/settings/data/settings_repository.dart';
/// Initializes the app and returns a [Dependencies] object
Future<Dependencies> $initializeDependencies({
void Function(int progress, String message)? onProgress,
required ILoggingRepository loggingRepository,
}) async {
final dependencies = Dependencies();
final totalSteps = _initializationSteps.length;
final initializationSteps = _getInitializationSteps(
loggingRepository: loggingRepository,
);
final totalSteps = initializationSteps.length;
var currentStep = 0;
for (final step in _initializationSteps.entries) {
for (final step in initializationSteps.entries) {
try {
currentStep++;
final percent = (currentStep * 100 ~/ totalSteps).clamp(0, 100);
Expand All @@ -41,25 +46,36 @@ Future<Dependencies> $initializeDependencies({
typedef _InitializationStep = FutureOr<void> Function(
Dependencies dependencies,
);
final Map<String, _InitializationStep> _initializationSteps =
<String, _InitializationStep>{
'Platform pre-initialization': (_) => $platformInitialization(),
'Log app open': (_) {},
'Initialize shared preferences': (dependencies) async =>
dependencies.sharedPreferences = await SharedPreferences.getInstance(),
'Initialize database': (dependencies) async =>
dependencies.database = MyDatabase(),
'Initialize settings repository': (dependencies) async =>
dependencies.settingsRepository = SettingsRepository(
localDataProvider: SettingsLocalDataProvider(
prefs: dependencies.sharedPreferences,
database: dependencies.database,

Map<String, _InitializationStep> _getInitializationSteps({
required ILoggingRepository loggingRepository,
}) {
final Map<String, _InitializationStep> initializationSteps =
<String, _InitializationStep>{
'Platform pre-initialization': (_) => $platformInitialization(),
'Provide logging pepository': (dependencies) =>
dependencies.loggingRepository = loggingRepository,
'Log app open': (dependencies) {
dependencies.loggingRepository.logEvent('appOpen');
},
'Initialize shared preferences': (dependencies) async =>
dependencies.sharedPreferences = await SharedPreferences.getInstance(),
'Initialize database': (dependencies) async =>
dependencies.database = MyDatabase(),
'Initialize settings repository': (dependencies) async =>
dependencies.settingsRepository = SettingsRepository(
localDataProvider: SettingsLocalDataProvider(
prefs: dependencies.sharedPreferences,
database: dependencies.database,
),
),
),
'Initialize dio': (dependencies) async => dependencies.dio = Dio(
BaseOptions(
baseUrl: serverAddress,
'Initialize dio': (dependencies) async => dependencies.dio = Dio(
BaseOptions(
baseUrl: serverAddress,
),
),
),
'Log app initialized': (_) {},
};
'Log app initialized': (_) {},
};

return initializationSteps;
}
Loading

0 comments on commit 49c2580

Please sign in to comment.