From d438e333d5141fd9f3559a8cbb7fa13a72e9cb1d Mon Sep 17 00:00:00 2001 From: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com> Date: Sat, 21 Sep 2024 10:14:21 +0530 Subject: [PATCH] refactor: logging --- mobile-v2/lib/domain/models/log.model.dart | 24 ++++---- .../domain/repositories/asset.repository.dart | 8 +-- .../domain/repositories/store.repository.dart | 6 +- .../domain/repositories/user.repository.dart | 6 +- .../domain/services/asset_sync.service.dart | 12 ++-- .../lib/domain/services/login.service.dart | 14 ++--- .../domain/services/server_info.service.dart | 8 +-- .../lib/domain/services/user.service.dart | 8 +-- .../login/states/login_page.state.dart | 8 +-- .../router/pages/splash_screen.page.dart | 6 +- mobile-v2/lib/utils/immich_api_client.dart | 6 +- mobile-v2/lib/utils/log_manager.dart | 55 +++++++++++++++---- mobile-v2/lib/utils/mixins/log.mixin.dart | 8 +++ .../lib/utils/mixins/log_context.mixin.dart | 8 --- 14 files changed, 105 insertions(+), 72 deletions(-) create mode 100644 mobile-v2/lib/utils/mixins/log.mixin.dart delete mode 100644 mobile-v2/lib/utils/mixins/log_context.mixin.dart diff --git a/mobile-v2/lib/domain/models/log.model.dart b/mobile-v2/lib/domain/models/log.model.dart index d90e3b7329..c62dd2dad7 100644 --- a/mobile-v2/lib/domain/models/log.model.dart +++ b/mobile-v2/lib/domain/models/log.model.dart @@ -4,22 +4,24 @@ import 'package:logging/logging.dart'; /// Log levels according to dart logging [Level] enum LogLevel { // do not change this order! - all, - finest, - finer, - fine, - config, + verbose, + debug, info, warning, - severe, - shout, - off, + error, + wtf, } extension LevelExtension on Level { - LogLevel toLogLevel() => - LogLevel.values.elementAtOrNull(Level.LEVELS.indexOf(this)) ?? - LogLevel.info; + LogLevel toLogLevel() => switch (this) { + Level.FINEST => LogLevel.verbose, + Level.FINE => LogLevel.debug, + Level.INFO => LogLevel.info, + Level.WARNING => LogLevel.warning, + Level.SEVERE => LogLevel.error, + Level.SHOUT => LogLevel.wtf, + _ => LogLevel.info, + }; } @immutable diff --git a/mobile-v2/lib/domain/repositories/asset.repository.dart b/mobile-v2/lib/domain/repositories/asset.repository.dart index f1e220a126..0dc1b2c58d 100644 --- a/mobile-v2/lib/domain/repositories/asset.repository.dart +++ b/mobile-v2/lib/domain/repositories/asset.repository.dart @@ -8,9 +8,9 @@ import 'package:immich_mobile/domain/models/render_list.model.dart'; import 'package:immich_mobile/domain/models/render_list_element.model.dart'; import 'package:immich_mobile/domain/repositories/database.repository.dart'; import 'package:immich_mobile/utils/extensions/drift.extension.dart'; -import 'package:immich_mobile/utils/mixins/log_context.mixin.dart'; +import 'package:immich_mobile/utils/mixins/log.mixin.dart'; -class RemoteAssetDriftRepository with LogContext implements IAssetRepository { +class RemoteAssetDriftRepository with LogMixin implements IAssetRepository { final DriftDatabaseRepository _db; const RemoteAssetDriftRepository(this._db); @@ -25,7 +25,7 @@ class RemoteAssetDriftRepository with LogContext implements IAssetRepository { return true; } catch (e, s) { - log.severe("Cannot insert remote assets into table", e, s); + log.e("Cannot insert remote assets into table", e, s); return false; } } @@ -36,7 +36,7 @@ class RemoteAssetDriftRepository with LogContext implements IAssetRepository { await _db.asset.deleteAll(); return true; } catch (e, s) { - log.severe("Cannot clear remote assets", e, s); + log.e("Cannot clear remote assets", e, s); return false; } } diff --git a/mobile-v2/lib/domain/repositories/store.repository.dart b/mobile-v2/lib/domain/repositories/store.repository.dart index 4d71516b97..5d1b80af9a 100644 --- a/mobile-v2/lib/domain/repositories/store.repository.dart +++ b/mobile-v2/lib/domain/repositories/store.repository.dart @@ -5,9 +5,9 @@ import 'package:immich_mobile/domain/entities/store.entity.drift.dart'; import 'package:immich_mobile/domain/interfaces/store.interface.dart'; import 'package:immich_mobile/domain/models/store.model.dart'; import 'package:immich_mobile/domain/repositories/database.repository.dart'; -import 'package:immich_mobile/utils/mixins/log_context.mixin.dart'; +import 'package:immich_mobile/utils/mixins/log.mixin.dart'; -class StoreDriftRepository with LogContext implements IStoreRepository { +class StoreDriftRepository with LogMixin implements IStoreRepository { final DriftDatabaseRepository _db; const StoreDriftRepository(this._db); @@ -42,7 +42,7 @@ class StoreDriftRepository with LogContext implements IStoreRepository { )); return true; } catch (e, s) { - log.severe("Cannot set store value - ${key.name}; id - ${key.id}", e, s); + log.e("Cannot set store value - ${key.name}; id - ${key.id}", e, s); return false; } } diff --git a/mobile-v2/lib/domain/repositories/user.repository.dart b/mobile-v2/lib/domain/repositories/user.repository.dart index 7a8c3ea222..8fa3c17046 100644 --- a/mobile-v2/lib/domain/repositories/user.repository.dart +++ b/mobile-v2/lib/domain/repositories/user.repository.dart @@ -5,9 +5,9 @@ import 'package:immich_mobile/domain/entities/user.entity.drift.dart'; import 'package:immich_mobile/domain/interfaces/user.interface.dart'; import 'package:immich_mobile/domain/models/user.model.dart'; import 'package:immich_mobile/domain/repositories/database.repository.dart'; -import 'package:immich_mobile/utils/mixins/log_context.mixin.dart'; +import 'package:immich_mobile/utils/mixins/log.mixin.dart'; -class UserDriftRepository with LogContext implements IUserRepository { +class UserDriftRepository with LogMixin implements IUserRepository { final DriftDatabaseRepository _db; const UserDriftRepository(this._db); @@ -40,7 +40,7 @@ class UserDriftRepository with LogContext implements IUserRepository { ); return true; } catch (e, s) { - log.severe("Cannot insert User into table - $user", e, s); + log.e("Cannot insert User into table - $user", e, s); return false; } } diff --git a/mobile-v2/lib/domain/services/asset_sync.service.dart b/mobile-v2/lib/domain/services/asset_sync.service.dart index 917301855e..7a9001b0b1 100644 --- a/mobile-v2/lib/domain/services/asset_sync.service.dart +++ b/mobile-v2/lib/domain/services/asset_sync.service.dart @@ -9,11 +9,11 @@ import 'package:immich_mobile/utils/collection_util.dart'; import 'package:immich_mobile/utils/constants/globals.dart'; import 'package:immich_mobile/utils/immich_api_client.dart'; import 'package:immich_mobile/utils/isolate_helper.dart'; -import 'package:immich_mobile/utils/mixins/log_context.mixin.dart'; -import 'package:logging/logging.dart'; +import 'package:immich_mobile/utils/log_manager.dart'; +import 'package:immich_mobile/utils/mixins/log.mixin.dart'; import 'package:openapi/api.dart'; -class AssetSyncService with LogContext { +class AssetSyncService with LogMixin { const AssetSyncService(); Future doFullRemoteSyncForUserDrift( @@ -23,7 +23,7 @@ class AssetSyncService with LogContext { }) async { return await IsolateHelper.run(() async { try { - final logger = Logger("SyncService "); + final logger = LogManager.I.get("SyncService "); final syncClient = di().getSyncApi(); final chunkSize = limit ?? kFullSyncChunkSize; @@ -32,7 +32,7 @@ class AssetSyncService with LogContext { String? lastAssetId; while (true) { - logger.info( + logger.d( "Requesting more chunks from lastId - ${lastAssetId ?? ""}", ); @@ -67,7 +67,7 @@ class AssetSyncService with LogContext { return true; } catch (e, s) { - log.severe("Error performing full sync for user - ${user.name}", e, s); + log.e("Error performing full sync for user - ${user.name}", e, s); } return false; }); diff --git a/mobile-v2/lib/domain/services/login.service.dart b/mobile-v2/lib/domain/services/login.service.dart index acff9e1b49..2984967ca4 100644 --- a/mobile-v2/lib/domain/services/login.service.dart +++ b/mobile-v2/lib/domain/services/login.service.dart @@ -9,10 +9,10 @@ import 'package:immich_mobile/domain/models/store.model.dart'; import 'package:immich_mobile/domain/services/user.service.dart'; import 'package:immich_mobile/service_locator.dart'; import 'package:immich_mobile/utils/immich_api_client.dart'; -import 'package:immich_mobile/utils/mixins/log_context.mixin.dart'; +import 'package:immich_mobile/utils/mixins/log.mixin.dart'; import 'package:openapi/api.dart'; -class LoginService with LogContext { +class LoginService with LogMixin { const LoginService(); Future isEndpointAvailable(Uri uri, {ImmichApiClient? client}) async { @@ -27,7 +27,7 @@ class LoginService with LogContext { try { await serverAPI.pingServer(); } catch (e) { - log.severe("Exception occured while validating endpoint", e); + log.e("Exception occured while validating endpoint", e); return false; } return true; @@ -52,7 +52,7 @@ class LoginService with LogContext { return endpoint.startsWith('/') ? "$baseUrl$endpoint" : endpoint; } } catch (e) { - log.fine("Could not locate /.well-known/immich at $baseUrl", e); + log.e("Could not locate /.well-known/immich at $baseUrl", e); } // No well-known, return the baseUrl @@ -68,7 +68,7 @@ class LoginService with LogContext { return loginResponse?.accessToken; } catch (e, s) { - log.severe("Exception occured while performing password login", e, s); + log.e("Exception occured while performing password login", e, s); } return null; } @@ -85,7 +85,7 @@ class LoginService with LogContext { final oAuthUrlRes = oAuthUrl?.url; if (oAuthUrlRes == null) { - log.severe( + log.e( "oAuth Server URL not available. Kindly ensure oAuth login is enabled in the server", ); return null; @@ -102,7 +102,7 @@ class LoginService with LogContext { return loginResponse?.accessToken; } catch (e) { - log.severe("Exception occured while performing oauth login", e); + log.e("Exception occured while performing oauth login", e); } return null; } diff --git a/mobile-v2/lib/domain/services/server_info.service.dart b/mobile-v2/lib/domain/services/server_info.service.dart index 73700785e3..e76af4324d 100644 --- a/mobile-v2/lib/domain/services/server_info.service.dart +++ b/mobile-v2/lib/domain/services/server_info.service.dart @@ -1,9 +1,9 @@ import 'package:immich_mobile/domain/models/server-info/server_config.model.dart'; import 'package:immich_mobile/domain/models/server-info/server_features.model.dart'; -import 'package:immich_mobile/utils/mixins/log_context.mixin.dart'; +import 'package:immich_mobile/utils/mixins/log.mixin.dart'; import 'package:openapi/api.dart'; -class ServerInfoService with LogContext { +class ServerInfoService with LogMixin { final ServerApi _serverInfo; const ServerInfoService(this._serverInfo); @@ -15,7 +15,7 @@ class ServerInfoService with LogContext { return ServerFeatures.fromDto(dto); } } catch (e, s) { - log.severe("Error while fetching server features", e, s); + log.e("Error while fetching server features", e, s); } return null; } @@ -27,7 +27,7 @@ class ServerInfoService with LogContext { return ServerConfig.fromDto(dto); } } catch (e, s) { - log.severe("Error while fetching server config", e, s); + log.e("Error while fetching server config", e, s); } return null; } diff --git a/mobile-v2/lib/domain/services/user.service.dart b/mobile-v2/lib/domain/services/user.service.dart index 95376e4f5b..7c111fe89d 100644 --- a/mobile-v2/lib/domain/services/user.service.dart +++ b/mobile-v2/lib/domain/services/user.service.dart @@ -1,8 +1,8 @@ import 'package:immich_mobile/domain/models/user.model.dart'; -import 'package:immich_mobile/utils/mixins/log_context.mixin.dart'; +import 'package:immich_mobile/utils/mixins/log.mixin.dart'; import 'package:openapi/api.dart'; -class UserService with LogContext { +class UserService with LogMixin { final UsersApi _userApi; const UserService(this._userApi); @@ -18,13 +18,13 @@ class UserService with LogContext { ]); if (userDto == null) { - log.severe("Cannot fetch my user."); + log.e("Cannot fetch my user."); return null; } return User.fromAdminDto(userDto, preferencesDto); } catch (e, s) { - log.severe("Error while fetching my user", e, s); + log.e("Error while fetching my user", e, s); } return null; } diff --git a/mobile-v2/lib/presentation/modules/login/states/login_page.state.dart b/mobile-v2/lib/presentation/modules/login/states/login_page.state.dart index ecf5fdc4f8..a979e7a4f1 100644 --- a/mobile-v2/lib/presentation/modules/login/states/login_page.state.dart +++ b/mobile-v2/lib/presentation/modules/login/states/login_page.state.dart @@ -13,10 +13,10 @@ import 'package:immich_mobile/presentation/modules/common/states/server_info/ser import 'package:immich_mobile/presentation/modules/login/models/login_page.model.dart'; import 'package:immich_mobile/service_locator.dart'; import 'package:immich_mobile/utils/immich_api_client.dart'; -import 'package:immich_mobile/utils/mixins/log_context.mixin.dart'; +import 'package:immich_mobile/utils/mixins/log.mixin.dart'; import 'package:immich_mobile/utils/snackbar_manager.dart'; -class LoginPageCubit extends Cubit with LogContext { +class LoginPageCubit extends Cubit with LogMixin { LoginPageCubit() : super(LoginPageState.reset()); String _appendSchema(String url) { @@ -96,7 +96,7 @@ class LoginPageCubit extends Cubit with LogContext { await _postLogin(accessToken); } catch (e, s) { SnackbarManager.showError(t.login.error.error_login); - log.severe("Cannot perform password login", e, s); + log.e("Cannot perform password login", e, s); } finally { emit(state.copyWith(isValidationInProgress: false)); } @@ -116,7 +116,7 @@ class LoginPageCubit extends Cubit with LogContext { await _postLogin(accessToken); } catch (e, s) { SnackbarManager.showError(t.login.error.error_login_oauth); - log.severe("Cannot perform oauth login", e, s); + log.e("Cannot perform oauth login", e, s); } finally { emit(state.copyWith(isValidationInProgress: false)); } diff --git a/mobile-v2/lib/presentation/router/pages/splash_screen.page.dart b/mobile-v2/lib/presentation/router/pages/splash_screen.page.dart index b7b6ecddd4..337b1b6959 100644 --- a/mobile-v2/lib/presentation/router/pages/splash_screen.page.dart +++ b/mobile-v2/lib/presentation/router/pages/splash_screen.page.dart @@ -8,7 +8,7 @@ import 'package:immich_mobile/presentation/components/image/immich_logo.widget.d import 'package:immich_mobile/presentation/modules/login/states/login_page.state.dart'; import 'package:immich_mobile/presentation/router/router.dart'; import 'package:immich_mobile/service_locator.dart'; -import 'package:immich_mobile/utils/mixins/log_context.mixin.dart'; +import 'package:immich_mobile/utils/mixins/log.mixin.dart'; @RoutePage() class SplashScreenWrapperPage extends AutoRouter implements AutoRouteWrapper { @@ -30,7 +30,7 @@ class SplashScreenPage extends StatefulWidget { } class _SplashScreenState extends State - with SingleTickerProviderStateMixin, LogContext { + with SingleTickerProviderStateMixin, LogMixin { late final AnimationController _animationController; @override @@ -65,7 +65,7 @@ class _SplashScreenState extends State if (snap.hasData) { _tryLogin(); } else if (snap.hasError) { - log.severe( + log.wtf( "Error while initializing the app", snap.error, snap.stackTrace, diff --git a/mobile-v2/lib/utils/immich_api_client.dart b/mobile-v2/lib/utils/immich_api_client.dart index 05b42c0f56..196115c034 100644 --- a/mobile-v2/lib/utils/immich_api_client.dart +++ b/mobile-v2/lib/utils/immich_api_client.dart @@ -9,10 +9,10 @@ import 'package:immich_mobile/domain/models/store.model.dart'; import 'package:immich_mobile/presentation/router/router.dart'; import 'package:immich_mobile/service_locator.dart'; import 'package:immich_mobile/utils/constants/globals.dart'; -import 'package:immich_mobile/utils/mixins/log_context.mixin.dart'; +import 'package:immich_mobile/utils/mixins/log.mixin.dart'; import 'package:openapi/api.dart'; -class ImmichApiClient extends ApiClient with LogContext { +class ImmichApiClient extends ApiClient with LogMixin { ImmichApiClient({required String endpoint}) : super(basePath: endpoint); Map get headers => defaultHeaderMap; @@ -58,7 +58,7 @@ class ImmichApiClient extends ApiClient with LogContext { ); if (res.statusCode == HttpStatus.unauthorized) { - log.severe("Token invalid. Redirecting to login route"); + log.e("Token invalid. Redirecting to login route"); await di().replaceAll([const LoginRoute()]); throw ApiException(res.statusCode, "Unauthorized"); } diff --git a/mobile-v2/lib/utils/log_manager.dart b/mobile-v2/lib/utils/log_manager.dart index 70c1a77b0b..aab06e535d 100644 --- a/mobile-v2/lib/utils/log_manager.dart +++ b/mobile-v2/lib/utils/log_manager.dart @@ -4,7 +4,7 @@ import 'package:flutter/foundation.dart'; import 'package:immich_mobile/domain/interfaces/log.interface.dart'; import 'package:immich_mobile/domain/models/log.model.dart'; import 'package:immich_mobile/service_locator.dart'; -import 'package:logging/logging.dart'; +import 'package:logging/logging.dart' as logging; /// [LogManager] is a custom logger that is built on top of the [logging] package. /// The logs are written to the database and onto console, using `debugPrint` method. @@ -14,14 +14,16 @@ import 'package:logging/logging.dart'; class LogManager { LogManager._(); static final LogManager _instance = LogManager._(); + static final Map _loggers = {}; + // ignore: match-getter-setter-field-names static LogManager get I => _instance; List _msgBuffer = []; Timer? _timer; - late final StreamSubscription _subscription; + late final StreamSubscription _subscription; - void _onLogRecord(LogRecord record) { + void _onLogRecord(logging.LogRecord record) { // Only print in development assert(() { debugPrint('[${record.level.name}] [${record.time}] ${record.message}'); @@ -55,11 +57,17 @@ class LogManager { } void init() { - _subscription = Logger.root.onRecord.listen(_onLogRecord); + _subscription = logging.Logger.root.onRecord.listen(_onLogRecord); } + Logger get(String? loggerName) => _loggers.putIfAbsent( + loggerName ?? 'main', + () => Logger(loggerName ?? 'main'), + ); + void updateLevel(LogLevel level) { - Logger.root.level = Level.LEVELS.elementAtOrNull(level.index); + logging.Logger.root.level = + logging.Level.LEVELS.elementAtOrNull(level.index); } void dispose() { @@ -75,18 +83,41 @@ class LogManager { static void setGlobalErrorCallbacks() { FlutterError.onError = (details) { - Logger("FlutterError").severe( - 'Unknown framework error occured in library ${details.library ?? ""} at node ${details.context ?? ""}', - details.exception, - details.stack, - ); + LogManager.I.get("FlutterError").wtf( + 'Unknown framework error occured in library ${details.library ?? ""} at node ${details.context ?? ""}', + details.exception, + details.stack, + ); FlutterError.presentError(details); }; PlatformDispatcher.instance.onError = (error, stack) { - Logger("PlatformDispatcher") - .severe('Unknown error occured in root isolate', error, stack); + LogManager.I + .get("PlatformDispatcher") + .wtf('Unknown error occured in root isolate', error, stack); return true; }; } } + +class Logger { + final String _loggerName; + const Logger(this._loggerName); + + logging.Logger get _logger => logging.Logger(_loggerName); + + // Highly detailed + void v(String message) => _logger.finest(message); + // Troubleshooting + void d(String message) => _logger.fine(message); + // General purpose + void i(String message) => _logger.info(message); + // Potential issues + void w(String message) => _logger.warning(message); + // Error + void e(String message, [Object? error, StackTrace? stack]) => + _logger.severe(message, error, stack); + // Crash / Serious failure + void wtf(String message, [Object? error, StackTrace? stack]) => + _logger.shout(message, error, stack); +} diff --git a/mobile-v2/lib/utils/mixins/log.mixin.dart b/mobile-v2/lib/utils/mixins/log.mixin.dart new file mode 100644 index 0000000000..4c2b59d9d1 --- /dev/null +++ b/mobile-v2/lib/utils/mixins/log.mixin.dart @@ -0,0 +1,8 @@ +import 'package:flutter/foundation.dart'; +import 'package:immich_mobile/utils/log_manager.dart'; + +mixin LogMixin { + @protected + @nonVirtual + Logger get log => LogManager.I.get(runtimeType.toString()); +} diff --git a/mobile-v2/lib/utils/mixins/log_context.mixin.dart b/mobile-v2/lib/utils/mixins/log_context.mixin.dart deleted file mode 100644 index 7c3b7d8ea0..0000000000 --- a/mobile-v2/lib/utils/mixins/log_context.mixin.dart +++ /dev/null @@ -1,8 +0,0 @@ -import 'package:flutter/foundation.dart'; -import 'package:logging/logging.dart'; - -mixin LogContext { - @protected - @nonVirtual - Logger get log => Logger(runtimeType.toString()); -}