feat(mobile) Add in app logging to show app's log information (#1014)

This commit is contained in:
Alex 2022-11-27 14:34:19 -06:00 committed by GitHub
parent fb3b36a569
commit 024177515d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
20 changed files with 539 additions and 86 deletions

View file

@ -349,7 +349,6 @@ class BackgroundService {
Hive.openBox<HiveDuplicatedAssets>(duplicatedAssetsBox),
Hive.openBox<HiveBackupAlbums>(hiveBackupInfoBox),
]);
ApiService apiService = ApiService();
apiService.setEndpoint(Hive.box(userInfoBox).get(serverEndpointKey));
apiService.setAccessToken(Hive.box(userInfoBox).get(accessTokenKey));

View file

@ -1,7 +1,6 @@
import 'dart:io';
import 'package:cancellation_token_http/http.dart';
import 'package:flutter/foundation.dart';
import 'package:hive_flutter/hive_flutter.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/constants/hive_box.dart';
@ -18,6 +17,7 @@ import 'package:immich_mobile/modules/login/models/authentication_state.model.da
import 'package:immich_mobile/modules/login/providers/authentication.provider.dart';
import 'package:immich_mobile/shared/providers/app_state.provider.dart';
import 'package:immich_mobile/shared/services/server_info.service.dart';
import 'package:logging/logging.dart';
import 'package:openapi/api.dart';
import 'package:photo_manager/photo_manager.dart';
@ -62,6 +62,7 @@ class BackupNotifier extends StateNotifier<BackUpState> {
getBackupInfo();
}
final log = Logger('BackupNotifier');
final BackupService _backupService;
final ServerInfoService _serverInfoService;
final AuthenticationState _authState;
@ -218,13 +219,16 @@ class BackupNotifier extends StateNotifier<BackUpState> {
);
if (backupAlbumInfo == null) {
debugPrint("[ERROR] getting Hive backup album infomation");
log.severe(
"backupAlbumInfo == null",
"Failed to get Hive backup album information",
);
return;
}
// First time backup - set isAll album is the default one for backup.
if (backupAlbumInfo.selectedAlbumIds.isEmpty) {
debugPrint("First time backup setup recent album as default");
log.info("First time backup; setup 'Recent(s)' album as default");
// Get album that contains all assets
var list = await PhotoManager.getAssetPathList(
@ -286,8 +290,8 @@ class BackupNotifier extends StateNotifier<BackUpState> {
selectedBackupAlbums: selectedAlbums,
excludedBackupAlbums: excludedAlbums,
);
} catch (e) {
debugPrint("[ERROR] Failed to generate album from id $e");
} catch (e, stackTrace) {
log.severe("Failed to generate album from id", e, stackTrace);
}
}
@ -338,7 +342,7 @@ class BackupNotifier extends StateNotifier<BackUpState> {
);
if (allUniqueAssets.isEmpty) {
debugPrint("No Asset On Device");
log.info("Not found albums or assets on the device to backup");
state = state.copyWith(
backupProgress: BackUpProgressEnum.idle,
allAssetsInDatabase: allAssetsInDatabase,
@ -412,7 +416,7 @@ class BackupNotifier extends StateNotifier<BackUpState> {
await PhotoManager.clearFileCache();
if (state.allUniqueAssets.isEmpty) {
debugPrint("No Asset On Device - Abort Backup Process");
log.info("No Asset On Device - Abort Backup Process");
state = state.copyWith(backupProgress: BackUpProgressEnum.idle);
return;
}
@ -530,7 +534,7 @@ class BackupNotifier extends StateNotifier<BackUpState> {
// User has been logged out return
if (accessKey == null || !_authState.isAuthenticated) {
debugPrint("[resumeBackup] not authenticated - abort");
log.info("[_resumeBackup] not authenticated - abort");
return;
}
@ -539,17 +543,17 @@ class BackupNotifier extends StateNotifier<BackUpState> {
_authState.deviceInfo.isAutoBackup) {
// check if backup is alreayd in process - then return
if (state.backupProgress == BackUpProgressEnum.inProgress) {
debugPrint("[resumeBackup] Backup is already in progress - abort");
log.info("[_resumeBackup] Backup is already in progress - abort");
return;
}
if (state.backupProgress == BackUpProgressEnum.inBackground) {
debugPrint("[resumeBackup] Background backup is running - abort");
log.info("[_resumeBackup] Background backup is running - abort");
return;
}
// Run backup
debugPrint("[resumeBackup] Start back up");
log.info("[_resumeBackup] Start back up");
await startBackupProcess();
}
@ -565,7 +569,7 @@ class BackupNotifier extends StateNotifier<BackUpState> {
state = state.copyWith(backupProgress: BackUpProgressEnum.inBackground);
final bool hasLock = await _backgroundService.acquireLock();
if (!hasLock) {
debugPrint("WARNING [resumeBackup] failed to acquireLock");
log.warning("WARNING [resumeBackup] failed to acquireLock");
return;
}
await Future.wait([
@ -612,7 +616,11 @@ class BackupNotifier extends StateNotifier<BackUpState> {
AvailableAlbum a = albums.firstWhere((e) => e.id == ids[i]);
result.add(a.copyWith(lastBackup: times[i]));
} on StateError {
debugPrint("[_updateAlbumBackupTime] failed to find album in state");
log.severe(
"[_updateAlbumBackupTime] failed to find album in state",
"State Error",
StackTrace.current,
);
}
}
return result;
@ -631,21 +639,29 @@ class BackupNotifier extends StateNotifier<BackUpState> {
await Hive.box<HiveBackupAlbums>(hiveBackupInfoBox).close();
}
} catch (error) {
debugPrint("[_notifyBackgroundServiceCanRun] failed to close box");
log.info("[_notifyBackgroundServiceCanRun] failed to close box");
}
try {
if (Hive.isBoxOpen(duplicatedAssetsBox)) {
await Hive.box<HiveDuplicatedAssets>(duplicatedAssetsBox).close();
}
} catch (error) {
debugPrint("[_notifyBackgroundServiceCanRun] failed to close box");
} catch (error, stackTrace) {
log.severe(
"[_notifyBackgroundServiceCanRun] failed to close box",
error,
stackTrace,
);
}
try {
if (Hive.isBoxOpen(backgroundBackupInfoBox)) {
await Hive.box(backgroundBackupInfoBox).close();
}
} catch (error) {
debugPrint("[_notifyBackgroundServiceCanRun] failed to close box");
} catch (error, stackTrace) {
log.severe(
"[_notifyBackgroundServiceCanRun] failed to close box",
error,
stackTrace,
);
}
_backgroundService.releaseLock();
}

View file

@ -2,12 +2,12 @@ import 'package:auto_route/auto_route.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/modules/backup/providers/backup.provider.dart';
import 'package:immich_mobile/modules/home/ui/profile_drawer/profile_drawer_header.dart';
import 'package:immich_mobile/modules/home/ui/profile_drawer/server_info_box.dart';
import 'package:immich_mobile/modules/login/providers/authentication.provider.dart';
import 'package:immich_mobile/routing/router.dart';
import 'package:immich_mobile/shared/providers/asset.provider.dart';
import 'package:immich_mobile/modules/login/providers/authentication.provider.dart';
import 'package:immich_mobile/modules/backup/providers/backup.provider.dart';
import 'package:immich_mobile/shared/providers/websocket.provider.dart';
class ProfileDrawer extends HookConsumerWidget {
@ -70,6 +70,30 @@ class ProfileDrawer extends HookConsumerWidget {
);
}
buildAppLogButton() {
return ListTile(
horizontalTitleGap: 0,
leading: SizedBox(
height: double.infinity,
child: Icon(
Icons.assignment_outlined,
color: Theme.of(context).textTheme.labelMedium?.color,
size: 20,
),
),
title: Text(
"profile_drawer_app_logs",
style: Theme.of(context)
.textTheme
.labelLarge
?.copyWith(fontWeight: FontWeight.bold),
).tr(),
onTap: () {
AutoRouter.of(context).push(const AppLogRoute());
},
);
}
return Drawer(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
@ -80,6 +104,7 @@ class ProfileDrawer extends HookConsumerWidget {
children: [
const ProfileDrawerHeader(),
buildSettingButton(),
buildAppLogButton(),
buildSignoutButton(),
],
),

View file

@ -1,14 +1,65 @@
import 'package:auto_route/auto_route.dart';
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/modules/login/ui/login_form.dart';
import 'package:immich_mobile/routing/router.dart';
import 'package:package_info_plus/package_info_plus.dart';
class LoginPage extends HookConsumerWidget {
const LoginPage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context, WidgetRef ref) {
return const Scaffold(
body: LoginForm(),
final appVersion = useState('0.0.0');
getAppInfo() async {
PackageInfo packageInfo = await PackageInfo.fromPlatform();
appVersion.value = packageInfo.version;
}
useEffect(
() {
getAppInfo();
return null;
},
);
return Scaffold(
body: const LoginForm(),
bottomNavigationBar: Padding(
padding: const EdgeInsets.only(bottom: 16.0),
child: SizedBox(
height: 50,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'v${appVersion.value}',
style: const TextStyle(
color: Colors.grey,
fontWeight: FontWeight.bold,
fontFamily: "Inconsolata",
),
),
const Text(' '),
GestureDetector(
child: Text(
'Logs',
style: TextStyle(
color: Theme.of(context).primaryColor,
fontWeight: FontWeight.bold,
fontFamily: "Inconsolata",
),
),
onTap: () {
AutoRouter.of(context).push(const AppLogRoute());
},
),
],
),
),
),
);
}
}