Skip to content

Commit 06ffd3b

Browse files
committed
feat(bootstrap): add robust logging to bootstrap process
1 parent 6bfec18 commit 06ffd3b

File tree

1 file changed

+56
-16
lines changed

1 file changed

+56
-16
lines changed

lib/bootstrap.dart

Lines changed: 56 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -39,27 +39,42 @@ Future<Widget> bootstrap(
3939
app_config.AppConfig appConfig,
4040
app_config.AppEnvironment environment,
4141
) async {
42-
Logger.root.level = Level.ALL;
43-
Logger.root.onRecord.listen(
44-
(record) => print(
42+
// Setup logging
43+
Logger.root.level = environment == app_config.AppEnvironment.production
44+
? Level.INFO
45+
: Level.ALL;
46+
Logger.root.onRecord.listen((record) {
47+
print(
4548
'${record.level.name}: ${record.time}: ${record.loggerName}: ${record.message}',
46-
),
47-
);
49+
);
50+
if (record.error != null) {
51+
print('Error: ${record.error}');
52+
}
53+
if (record.stackTrace != null) {
54+
print('Stack Trace: ${record.stackTrace}');
55+
}
56+
});
57+
final logger = Logger('bootstrap');
58+
logger.config('--- Starting Bootstrap Process ---');
59+
logger.config('App Environment: $environment');
60+
4861
WidgetsFlutterBinding.ensureInitialized();
4962
Bloc.observer = const AppBlocObserver();
50-
final logger = Logger('bootstrap');
5163
timeago.setLocaleMessages('en', EnTimeagoMessages());
5264
timeago.setLocaleMessages('ar', ArTimeagoMessages());
5365

66+
logger.info('1. Initializing KV Storage Service...');
5467
// 1. Initialize KV Storage Service first, as it's a foundational dependency.
5568
final kvStorage = await KVStorageSharedPreferences.getInstance();
69+
logger.fine('KV Storage Service initialized (SharedPreferences).');
5670

5771
// Initialize InlineAdCacheService early as it's a singleton and needs AdService.
5872
// It will be fully configured once AdService is available.
5973
InlineAdCacheService? inlineAdCacheService;
6074

6175
// 2. Initialize HttpClient. Its tokenProvider now directly reads from
6276
// kvStorage, breaking the circular dependency with AuthRepository.
77+
logger.info('2. Initializing HttpClient...');
6378
// This HttpClient instance is used for all subsequent API calls, including
6479
// the initial unauthenticated fetch of RemoteConfig.
6580
final httpClient = HttpClient(
@@ -68,18 +83,22 @@ Future<Widget> bootstrap(
6883
kvStorage.readString(key: StorageKey.authToken.stringValue),
6984
logger: logger,
7085
);
86+
logger.fine('HttpClient initialized for base URL: ${appConfig.baseUrl}');
7187

7288
// 3. Initialize RemoteConfigClient and Repository, and fetch RemoteConfig.
89+
logger.info('3. Initializing RemoteConfig client and repository...');
7390
// This is done early because RemoteConfig is now publicly accessible (unauthenticated).
7491
late DataClient<RemoteConfig> remoteConfigClient;
7592
if (appConfig.environment == app_config.AppEnvironment.demo) {
93+
logger.fine('Using in-memory client for RemoteConfig.');
7694
remoteConfigClient = DataInMemory<RemoteConfig>(
7795
toJson: (i) => i.toJson(),
7896
getId: (i) => i.id,
7997
initialData: remoteConfigsFixturesData,
8098
logger: logger,
8199
);
82100
} else {
101+
logger.fine('Using API client for RemoteConfig.');
83102
// For development and production environments, use DataApi.
84103
remoteConfigClient = DataApi<RemoteConfig>(
85104
httpClient: httpClient,
@@ -92,60 +111,62 @@ Future<Widget> bootstrap(
92111
final remoteConfigRepository = DataRepository<RemoteConfig>(
93112
dataClient: remoteConfigClient,
94113
);
114+
logger.fine('RemoteConfig repository initialized.');
95115

96116
// Fetch the initial RemoteConfig. This is a critical step to determine
97117
// the app's global status (e.g., maintenance mode, update required)
98118
// before proceeding with other initializations.
99119
RemoteConfig? initialRemoteConfig;
100120
HttpException? initialRemoteConfigError;
101121

122+
logger.info('4. Fetching initial RemoteConfig...');
102123
try {
103124
initialRemoteConfig = await remoteConfigRepository.read(
104125
id: kRemoteConfigId,
105126
);
106-
logger.info('[bootstrap] Initial RemoteConfig fetched successfully.');
127+
logger.fine('Initial RemoteConfig fetched successfully.');
107128
} on HttpException catch (e) {
108-
logger.severe(
109-
'[bootstrap] Failed to fetch initial RemoteConfig (HttpException): $e',
110-
);
129+
logger.severe('Failed to fetch initial RemoteConfig (HttpException): $e');
111130
initialRemoteConfigError = e;
112131
} catch (e, s) {
113-
logger.severe(
114-
'[bootstrap] Unexpected error fetching initial RemoteConfig.',
115-
e,
116-
s,
117-
);
132+
logger.severe('Unexpected error fetching initial RemoteConfig.', e, s);
118133
initialRemoteConfigError = UnknownException(e.toString());
119134
}
120135

121136
// 4. Conditionally initialize Auth services based on environment.
122137
// This is done after RemoteConfig is fetched, as Auth services might depend
123-
// on configurations defined in RemoteConfig (though not directly in this case).
138+
// on configurations defined in RemoteConfig.
139+
logger.info('5. Initializing Authentication services...');
124140
late final AuthClient authClient;
125141
late final AuthRepository authenticationRepository;
126142
if (appConfig.environment == app_config.AppEnvironment.demo) {
143+
logger.fine('Using in-memory client for Authentication.');
127144
// In-memory authentication for demo environment.
128145
authClient = AuthInmemory();
129146
authenticationRepository = AuthRepository(
130147
authClient: authClient,
131148
storageService: kvStorage,
132149
);
133150
} else {
151+
logger.fine('Using API client for Authentication.');
134152
// Now that httpClient is available, initialize AuthApi and AuthRepository.
135153
authClient = AuthApi(httpClient: httpClient);
136154
authenticationRepository = AuthRepository(
137155
authClient: authClient,
138156
storageService: kvStorage,
139157
);
140158
}
159+
logger.fine('Authentication repository initialized.');
141160

142161
// 5. Initialize AdProvider and AdService.
162+
logger.info('6. Initializing Ad providers and AdService...');
143163
late final Map<AdPlatformType, AdProvider> adProviders;
144164

145165
// Conditionally instantiate ad providers based on the application environment.
146166
// This ensures that only the relevant ad providers are available for the
147167
// current environment, preventing unintended usage.
148168
if (appConfig.environment == app_config.AppEnvironment.demo || kIsWeb) {
169+
logger.fine('Using DemoAdProvider for all ad platforms.');
149170
final demoAdProvider = DemoAdProvider(logger: logger);
150171
adProviders = {
151172
// In the demo environment or on the web, all ad platform types map to
@@ -157,6 +178,7 @@ Future<Widget> bootstrap(
157178
AdPlatformType.demo: demoAdProvider,
158179
};
159180
} else {
181+
logger.fine('Using AdMobAdProvider and LocalAdProvider.');
160182
// For development and production environments (non-web), use real ad providers.
161183
adProviders = {
162184
// AdMob provider for Google Mobile Ads.
@@ -186,22 +208,28 @@ Future<Widget> bootstrap(
186208
logger: logger,
187209
);
188210
await adService.initialize();
211+
logger.fine('AdService initialized.');
189212

190213
// Initialize InlineAdCacheService with the created AdService.
191214
inlineAdCacheService = InlineAdCacheService(adService: adService);
215+
logger.fine('InlineAdCacheService initialized.');
192216

193217
// Fetch the initial user from the authentication repository.
194218
// This ensures the AppBloc starts with an accurate authentication status.
219+
logger.info('7. Fetching initial user...');
195220
final initialUser = await authenticationRepository.getCurrentUser();
221+
logger.fine('Initial user fetched: ${initialUser?.id ?? 'none'}.');
196222

197223
// Create a GlobalKey for the NavigatorState to be used by AppBloc
198224
// and InterstitialAdManager for BuildContext access.
199225
final navigatorKey = GlobalKey<NavigatorState>();
200226

201227
// Initialize PackageInfoService
202228
final packageInfoService = PackageInfoServiceImpl(logger: logger);
229+
logger.fine('PackageInfoService initialized.');
203230

204231
// 6. Initialize all other DataClients and Repositories.
232+
logger.info('8. Initializing Data clients and repositories...');
205233
// These now also have a guaranteed valid httpClient.
206234
late final DataClient<Headline> headlinesClient;
207235
late final DataClient<Topic> topicsClient;
@@ -212,6 +240,7 @@ Future<Widget> bootstrap(
212240
late final DataClient<User> userClient;
213241
late final DataClient<LocalAd> localAdClient;
214242
if (appConfig.environment == app_config.AppEnvironment.demo) {
243+
logger.fine('Using in-memory clients for all data repositories.');
215244
headlinesClient = DataInMemory<Headline>(
216245
toJson: (i) => i.toJson(),
217246
getId: (i) => i.id,
@@ -286,6 +315,7 @@ Future<Widget> bootstrap(
286315
logger: logger,
287316
);
288317
} else if (appConfig.environment == app_config.AppEnvironment.development) {
318+
logger.fine('Using API clients for all data repositories (Development).');
289319
headlinesClient = DataApi<Headline>(
290320
httpClient: httpClient,
291321
modelName: 'headline',
@@ -343,6 +373,7 @@ Future<Widget> bootstrap(
343373
logger: logger,
344374
);
345375
} else {
376+
logger.fine('Using API clients for all data repositories (Production).');
346377
// Default to API clients for production
347378
headlinesClient = DataApi<Headline>(
348379
httpClient: httpClient,
@@ -401,6 +432,7 @@ Future<Widget> bootstrap(
401432
logger: logger,
402433
);
403434
}
435+
logger.fine('All data clients instantiated.');
404436

405437
final headlinesRepository = DataRepository<Headline>(
406438
dataClient: headlinesClient,
@@ -419,6 +451,7 @@ Future<Widget> bootstrap(
419451
dataClient: userAppSettingsClient,
420452
);
421453
final userRepository = DataRepository<User>(dataClient: userClient);
454+
logger.fine('All data repositories initialized.');
422455

423456
// Conditionally instantiate DemoDataMigrationService
424457
final demoDataMigrationService =
@@ -428,6 +461,9 @@ Future<Widget> bootstrap(
428461
userContentPreferencesRepository: userContentPreferencesRepository,
429462
)
430463
: null;
464+
logger.fine(
465+
'DemoDataMigrationService initialized: ${demoDataMigrationService != null}',
466+
);
431467

432468
// Conditionally instantiate DemoDataInitializerService
433469
// This service is responsible for ensuring that essential user-specific data
@@ -441,7 +477,11 @@ Future<Widget> bootstrap(
441477
userContentPreferencesRepository: userContentPreferencesRepository,
442478
)
443479
: null;
480+
logger.fine(
481+
'DemoDataInitializerService initialized: ${demoDataInitializerService != null}',
482+
);
444483

484+
logger.info('--- Bootstrap Process Complete. Returning App widget. ---');
445485
return App(
446486
authenticationRepository: authenticationRepository,
447487
headlinesRepository: headlinesRepository,

0 commit comments

Comments
 (0)