1
0
Fork 0
mirror of https://github.com/immich-app/immich.git synced 2024-12-29 15:11:58 +00:00

refactor(mobile): move error details to separate DB column (#6898)

* Add "details" column to LoggerMessage

* Include error details in log details page

* Move error details out of log message

* Add error message to mixin

* Create extension for HTTP Response logging

* Fix analyze errors

* format

* fix analyze errors, format again
This commit is contained in:
Robert Vollmer 2024-02-24 04:38:57 +01:00 committed by GitHub
parent 878932f87e
commit bc3979029d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
28 changed files with 106 additions and 154 deletions

View file

@ -30,7 +30,7 @@ extension LogOnError<T> on AsyncValue<T> {
} }
if (hasError && !hasValue) { if (hasError && !hasValue) {
_asyncErrorLogger.severe("$error", error, stackTrace); _asyncErrorLogger.severe('Could not load value', error, stackTrace);
return onError?.call(error, stackTrace) ?? return onError?.call(error, stackTrace) ??
ScaffoldErrorBody(errorMsg: error?.toString()); ScaffoldErrorBody(errorMsg: error?.toString());
} }

View file

@ -0,0 +1,5 @@
import 'package:http/http.dart';
extension LoggerExtension on Response {
String toLoggerString() => "Status: $statusCode $reasonPhrase\n\n$body";
}

View file

@ -73,15 +73,14 @@ Future<void> initApp() async {
FlutterError.onError = (details) { FlutterError.onError = (details) {
FlutterError.presentError(details); FlutterError.presentError(details);
log.severe( log.severe(
'FlutterError - Catch all error: ${details.toString()} - ${details.exception} - ${details.library} - ${details.context} - ${details.stack}', 'FlutterError - Catch all',
details, "${details.toString()}\nException: ${details.exception}\nLibrary: ${details.library}\nContext: ${details.context}",
details.stack, details.stack,
); );
}; };
PlatformDispatcher.instance.onError = (error, stack) { PlatformDispatcher.instance.onError = (error, stack) {
log.severe('PlatformDispatcher - Catch all error: $error', error, stack); log.severe('PlatformDispatcher - Catch all', error, stack);
debugPrint("PlatformDispatcher - Catch all error: $error $stack");
return true; return true;
}; };

View file

@ -10,13 +10,14 @@ mixin ErrorLoggerMixin {
/// Else, logs the error to the overrided logger and returns an AsyncError<> /// Else, logs the error to the overrided logger and returns an AsyncError<>
AsyncFuture<T> guardError<T>( AsyncFuture<T> guardError<T>(
Future<T> Function() fn, { Future<T> Function() fn, {
required String errorMessage,
Level logLevel = Level.SEVERE, Level logLevel = Level.SEVERE,
}) async { }) async {
try { try {
final result = await fn(); final result = await fn();
return AsyncData(result); return AsyncData(result);
} catch (error, stackTrace) { } catch (error, stackTrace) {
logger.log(logLevel, "$error", error, stackTrace); logger.log(logLevel, errorMessage, error, stackTrace);
return AsyncError(error, stackTrace); return AsyncError(error, stackTrace);
} }
} }
@ -26,12 +27,13 @@ mixin ErrorLoggerMixin {
Future<T> logError<T>( Future<T> logError<T>(
Future<T> Function() fn, { Future<T> Function() fn, {
required T defaultValue, required T defaultValue,
required String errorMessage,
Level logLevel = Level.SEVERE, Level logLevel = Level.SEVERE,
}) async { }) async {
try { try {
return await fn(); return await fn();
} catch (error, stackTrace) { } catch (error, stackTrace) {
logger.log(logLevel, "$error", error, stackTrace); logger.log(logLevel, errorMessage, error, stackTrace);
} }
return defaultValue; return defaultValue;
} }

View file

@ -24,6 +24,7 @@ class ActivityService with ErrorLoggerMixin {
return list != null ? list.map(Activity.fromDto).toList() : []; return list != null ? list.map(Activity.fromDto).toList() : [];
}, },
defaultValue: [], defaultValue: [],
errorMessage: "Failed to get all activities for album $albumId",
); );
} }
@ -35,6 +36,7 @@ class ActivityService with ErrorLoggerMixin {
return dto?.comments ?? 0; return dto?.comments ?? 0;
}, },
defaultValue: 0, defaultValue: 0,
errorMessage: "Failed to statistics for album $albumId",
); );
} }
@ -45,6 +47,7 @@ class ActivityService with ErrorLoggerMixin {
return true; return true;
}, },
defaultValue: false, defaultValue: false,
errorMessage: "Failed to delete activity",
); );
} }
@ -54,21 +57,24 @@ class ActivityService with ErrorLoggerMixin {
String? assetId, String? assetId,
String? comment, String? comment,
}) async { }) async {
return guardError(() async { return guardError(
final dto = await _apiService.activityApi.createActivity( () async {
ActivityCreateDto( final dto = await _apiService.activityApi.createActivity(
albumId: albumId, ActivityCreateDto(
type: type == ActivityType.comment albumId: albumId,
? ReactionType.comment type: type == ActivityType.comment
: ReactionType.like, ? ReactionType.comment
assetId: assetId, : ReactionType.like,
comment: comment, assetId: assetId,
), comment: comment,
); ),
if (dto != null) { );
return Activity.fromDto(dto); if (dto != null) {
} return Activity.fromDto(dto);
throw NoResponseDtoError(); }
}); throw NoResponseDtoError();
},
errorMessage: "Failed to create $type for album $albumId",
);
} }
} }

View file

@ -1,6 +1,7 @@
import 'dart:io'; import 'dart:io';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/extensions/response_extensions.dart';
import 'package:immich_mobile/shared/models/asset.dart'; import 'package:immich_mobile/shared/models/asset.dart';
import 'package:immich_mobile/shared/providers/api.provider.dart'; import 'package:immich_mobile/shared/providers/api.provider.dart';
import 'package:immich_mobile/shared/services/api.service.dart'; import 'package:immich_mobile/shared/services/api.service.dart';
@ -39,7 +40,8 @@ class ImageViewerService {
final failedResponse = final failedResponse =
imageResponse.statusCode != 200 ? imageResponse : motionReponse; imageResponse.statusCode != 200 ? imageResponse : motionReponse;
_log.severe( _log.severe(
"Motion asset download failed with status - ${failedResponse.statusCode} and response - ${failedResponse.body}", "Motion asset download failed",
failedResponse.toLoggerString(),
); );
return false; return false;
} }
@ -75,9 +77,7 @@ class ImageViewerService {
.downloadFileWithHttpInfo(asset.remoteId!); .downloadFileWithHttpInfo(asset.remoteId!);
if (res.statusCode != 200) { if (res.statusCode != 200) {
_log.severe( _log.severe("Asset download failed", res.toLoggerString());
"Asset download failed with status - ${res.statusCode} and response - ${res.body}",
);
return false; return false;
} }
@ -98,7 +98,7 @@ class ImageViewerService {
return entity != null; return entity != null;
} }
} catch (error, stack) { } catch (error, stack) {
_log.severe("Error saving file ${error.toString()}", error, stack); _log.severe("Error saving downloaded asset", error, stack);
return false; return false;
} finally { } finally {
// Clear temp files // Clear temp files

View file

@ -48,7 +48,7 @@ class DescriptionInput extends HookConsumerWidget {
); );
} catch (error, stack) { } catch (error, stack) {
hasError.value = true; hasError.value = true;
_log.severe("Error updating description $error", error, stack); _log.severe("Error updating description", error, stack);
ImmichToast.show( ImmichToast.show(
context: context, context: context,
msg: "description_input_submit_error".tr(), msg: "description_input_submit_error".tr(),

View file

@ -245,7 +245,7 @@ class BackupNotifier extends StateNotifier<BackUpState> {
} catch (e, stack) { } catch (e, stack) {
log.severe( log.severe(
"Failed to get thumbnail for album ${album.name}", "Failed to get thumbnail for album ${album.name}",
e.toString(), e,
stack, stack,
); );
} }

View file

@ -108,7 +108,7 @@ class AuthenticationNotifier extends StateNotifier<AuthenticationState> {
.then((_) => log.info("Logout was successful for $userEmail")) .then((_) => log.info("Logout was successful for $userEmail"))
.onError( .onError(
(error, stackTrace) => (error, stackTrace) =>
log.severe("Error logging out $userEmail", error, stackTrace), log.severe("Logout failed for $userEmail", error, stackTrace),
); );
await Future.wait([ await Future.wait([
@ -129,8 +129,8 @@ class AuthenticationNotifier extends StateNotifier<AuthenticationState> {
shouldChangePassword: false, shouldChangePassword: false,
isAuthenticated: false, isAuthenticated: false,
); );
} catch (e) { } catch (e, stack) {
log.severe("Error logging out $e"); log.severe('Logout failed', e, stack);
} }
} }

View file

@ -36,7 +36,7 @@ class OAuthService {
), ),
); );
} catch (e, stack) { } catch (e, stack) {
log.severe("Error performing oAuthLogin: ${e.toString()}", e, stack); log.severe("OAuth login failed", e, stack);
return null; return null;
} }
} }

View file

@ -1,6 +1,7 @@
import 'dart:io'; import 'dart:io';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:immich_mobile/extensions/response_extensions.dart';
import 'package:immich_mobile/modules/map/models/map_state.model.dart'; import 'package:immich_mobile/modules/map/models/map_state.model.dart';
import 'package:immich_mobile/modules/settings/providers/app_settings.provider.dart'; import 'package:immich_mobile/modules/settings/providers/app_settings.provider.dart';
import 'package:immich_mobile/modules/settings/services/app_settings.service.dart'; import 'package:immich_mobile/modules/settings/services/app_settings.service.dart';
@ -51,7 +52,8 @@ class MapStateNotifier extends _$MapStateNotifier {
lightStyleFetched: AsyncError(lightResponse.body, StackTrace.current), lightStyleFetched: AsyncError(lightResponse.body, StackTrace.current),
); );
_log.severe( _log.severe(
"Cannot fetch map light style with status - ${lightResponse.statusCode} and response - ${lightResponse.body}", "Cannot fetch map light style",
lightResponse.toLoggerString(),
); );
return; return;
} }
@ -77,9 +79,7 @@ class MapStateNotifier extends _$MapStateNotifier {
state = state.copyWith( state = state.copyWith(
darkStyleFetched: AsyncError(darkResponse.body, StackTrace.current), darkStyleFetched: AsyncError(darkResponse.body, StackTrace.current),
); );
_log.severe( _log.severe("Cannot fetch map dark style", darkResponse.toLoggerString());
"Cannot fetch map dark style with status - ${darkResponse.statusCode} and response - ${darkResponse.body}",
);
return; return;
} }

View file

@ -28,6 +28,7 @@ class MapSerivce with ErrorLoggerMixin {
return markers?.map(MapMarker.fromDto) ?? []; return markers?.map(MapMarker.fromDto) ?? [];
}, },
defaultValue: [], defaultValue: [],
errorMessage: "Failed to get map markers",
); );
} }
} }

View file

@ -105,10 +105,8 @@ class MapUtils {
timeLimit: const Duration(seconds: 5), timeLimit: const Duration(seconds: 5),
); );
return (currentUserLocation, null); return (currentUserLocation, null);
} catch (error) { } catch (error, stack) {
_log.severe( _log.severe("Cannot get user's current location", error, stack);
"Cannot get user's current location due to ${error.toString()}",
);
return (null, LocationPermission.unableToDetermine); return (null, LocationPermission.unableToDetermine);
} }
} }

View file

@ -147,7 +147,7 @@ class MapAssetGrid extends HookConsumerWidget {
}, },
error: (error, stackTrace) { error: (error, stackTrace) {
log.warning( log.warning(
"Cannot get assets in the current map bounds $error", "Cannot get assets in the current map bounds",
error, error,
stackTrace, stackTrace,
); );

View file

@ -47,7 +47,7 @@ class MemoryService {
return memories.isNotEmpty ? memories : null; return memories.isNotEmpty ? memories : null;
} catch (error, stack) { } catch (error, stack) {
log.severe("Cannot get memories ${error.toString()}", error, stack); log.severe("Cannot get memories", error, stack);
return null; return null;
} }
} }

View file

@ -40,7 +40,7 @@ class PartnerService {
return userDtos.map((u) => User.fromPartnerDto(u)).toList(); return userDtos.map((u) => User.fromPartnerDto(u)).toList();
} }
} catch (e) { } catch (e) {
_log.warning("failed to get partners for direction $direction:\n$e"); _log.warning("Failed to get partners for direction $direction", e);
} }
return null; return null;
} }
@ -51,7 +51,7 @@ class PartnerService {
partner.isPartnerSharedBy = false; partner.isPartnerSharedBy = false;
await _db.writeTxn(() => _db.users.put(partner)); await _db.writeTxn(() => _db.users.put(partner));
} catch (e) { } catch (e) {
_log.warning("failed to remove partner ${partner.id}:\n$e"); _log.warning("Failed to remove partner ${partner.id}", e);
return false; return false;
} }
return true; return true;
@ -66,7 +66,7 @@ class PartnerService {
return true; return true;
} }
} catch (e) { } catch (e) {
_log.warning("failed to add partner ${partner.id}:\n$e"); _log.warning("Failed to add partner ${partner.id}", e);
} }
return false; return false;
} }
@ -81,7 +81,7 @@ class PartnerService {
return true; return true;
} }
} catch (e) { } catch (e) {
_log.warning("failed to update partner ${partner.id}:\n$e"); _log.warning("Failed to update partner ${partner.id}", e);
} }
return false; return false;
} }

View file

@ -22,7 +22,7 @@ class SharedLinkService {
? AsyncData(list.map(SharedLink.fromDto).toList()) ? AsyncData(list.map(SharedLink.fromDto).toList())
: const AsyncData([]); : const AsyncData([]);
} catch (e, stack) { } catch (e, stack) {
_log.severe("failed to fetch shared links - $e"); _log.severe("Failed to fetch shared links", e, stack);
return AsyncError(e, stack); return AsyncError(e, stack);
} }
} }
@ -31,7 +31,7 @@ class SharedLinkService {
try { try {
return await _apiService.sharedLinkApi.removeSharedLink(id); return await _apiService.sharedLinkApi.removeSharedLink(id);
} catch (e) { } catch (e) {
_log.severe("failed to delete shared link id - $id with error - $e"); _log.severe("Failed to delete shared link id - $id", e);
} }
} }
@ -81,7 +81,7 @@ class SharedLinkService {
} }
} }
} catch (e) { } catch (e) {
_log.severe("failed to create shared link with error - $e"); _log.severe("Failed to create shared link", e);
} }
return null; return null;
} }
@ -113,7 +113,7 @@ class SharedLinkService {
return SharedLink.fromDto(responseDto); return SharedLink.fromDto(responseDto);
} }
} catch (e) { } catch (e) {
_log.severe("failed to update shared link id - $id with error - $e"); _log.severe("Failed to update shared link id - $id", e);
} }
return null; return null;
} }

View file

@ -44,7 +44,7 @@ class TrashNotifier extends StateNotifier<bool> {
.read(syncServiceProvider) .read(syncServiceProvider)
.handleRemoteAssetRemoval(idsToRemove.cast<String>().toList()); .handleRemoteAssetRemoval(idsToRemove.cast<String>().toList());
} catch (error, stack) { } catch (error, stack) {
_log.severe("Cannot empty trash ${error.toString()}", error, stack); _log.severe("Cannot empty trash", error, stack);
} }
} }
@ -70,7 +70,7 @@ class TrashNotifier extends StateNotifier<bool> {
return isRemoved; return isRemoved;
} catch (error, stack) { } catch (error, stack) {
_log.severe("Cannot empty trash ${error.toString()}", error, stack); _log.severe("Cannot remove assets", error, stack);
} }
return false; return false;
} }
@ -93,7 +93,7 @@ class TrashNotifier extends StateNotifier<bool> {
return true; return true;
} }
} catch (error, stack) { } catch (error, stack) {
_log.severe("Cannot restore trash ${error.toString()}", error, stack); _log.severe("Cannot restore assets", error, stack);
} }
return false; return false;
} }
@ -123,7 +123,7 @@ class TrashNotifier extends StateNotifier<bool> {
await _db.assets.putAll(updatedAssets); await _db.assets.putAll(updatedAssets);
}); });
} catch (error, stack) { } catch (error, stack) {
_log.severe("Cannot restore trash ${error.toString()}", error, stack); _log.severe("Cannot restore trash", error, stack);
} }
} }
} }

View file

@ -25,7 +25,7 @@ class TrashService {
await _apiService.trashApi.restoreAssets(BulkIdsDto(ids: remoteIds)); await _apiService.trashApi.restoreAssets(BulkIdsDto(ids: remoteIds));
return true; return true;
} catch (error, stack) { } catch (error, stack) {
_log.severe("Cannot restore assets ${error.toString()}", error, stack); _log.severe("Cannot restore assets", error, stack);
return false; return false;
} }
} }
@ -34,7 +34,7 @@ class TrashService {
try { try {
await _apiService.trashApi.emptyTrash(); await _apiService.trashApi.emptyTrash();
} catch (error, stack) { } catch (error, stack) {
_log.severe("Cannot empty trash ${error.toString()}", error, stack); _log.severe("Cannot empty trash", error, stack);
} }
} }
@ -42,7 +42,7 @@ class TrashService {
try { try {
await _apiService.trashApi.restoreTrash(); await _apiService.trashApi.restoreTrash();
} catch (error, stack) { } catch (error, stack) {
_log.severe("Cannot restore trash ${error.toString()}", error, stack); _log.severe("Cannot restore trash", error, stack);
} }
} }
} }

View file

@ -9,6 +9,7 @@ part 'logger_message.model.g.dart';
class LoggerMessage { class LoggerMessage {
Id id = Isar.autoIncrement; Id id = Isar.autoIncrement;
String message; String message;
String? details;
@Enumerated(EnumType.ordinal) @Enumerated(EnumType.ordinal)
LogLevel level = LogLevel.INFO; LogLevel level = LogLevel.INFO;
DateTime createdAt; DateTime createdAt;
@ -17,6 +18,7 @@ class LoggerMessage {
LoggerMessage({ LoggerMessage({
required this.message, required this.message,
required this.details,
required this.level, required this.level,
required this.createdAt, required this.createdAt,
required this.context1, required this.context1,

View file

@ -90,7 +90,7 @@ class AssetService {
return allAssets; return allAssets;
} catch (error, stack) { } catch (error, stack) {
log.severe( log.severe(
'Error while getting remote assets: ${error.toString()}', 'Error while getting remote assets',
error, error,
stack, stack,
); );
@ -117,7 +117,7 @@ class AssetService {
); );
return true; return true;
} catch (error, stack) { } catch (error, stack) {
log.severe("Error deleteAssets ${error.toString()}", error, stack); log.severe("Error while deleting assets", error, stack);
} }
return false; return false;
} }

View file

@ -12,7 +12,7 @@ import 'package:share_plus/share_plus.dart';
/// [ImmichLogger] is a custom logger that is built on top of the [logging] package. /// [ImmichLogger] 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. /// The logs are written to the database and onto console, using `debugPrint` method.
/// ///
/// The logs are deleted when exceeding the `maxLogEntries` (default 200) property /// The logs are deleted when exceeding the `maxLogEntries` (default 500) property
/// in the class. /// in the class.
/// ///
/// Logs can be shared by calling the `shareLogs` method, which will open a share dialog /// Logs can be shared by calling the `shareLogs` method, which will open a share dialog
@ -58,6 +58,7 @@ class ImmichLogger {
debugPrint('[${record.level.name}] [${record.time}] ${record.message}'); debugPrint('[${record.level.name}] [${record.time}] ${record.message}');
final lm = LoggerMessage( final lm = LoggerMessage(
message: record.message, message: record.message,
details: record.error?.toString(),
level: record.level.toLogLevel(), level: record.level.toLogLevel(),
createdAt: record.time, createdAt: record.time,
context1: record.loggerName, context1: record.loggerName,

View file

@ -2,6 +2,7 @@ import 'dart:io';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/extensions/response_extensions.dart';
import 'package:immich_mobile/shared/models/asset.dart'; import 'package:immich_mobile/shared/models/asset.dart';
import 'package:immich_mobile/shared/providers/api.provider.dart'; import 'package:immich_mobile/shared/providers/api.provider.dart';
import 'package:logging/logging.dart'; import 'package:logging/logging.dart';
@ -41,7 +42,8 @@ class ShareService {
if (res.statusCode != 200) { if (res.statusCode != 200) {
_log.severe( _log.severe(
"Asset download failed with status - ${res.statusCode} and response - ${res.body}", "Asset download for ${asset.fileName} failed",
res.toLoggerString(),
); );
continue; continue;
} }
@ -68,7 +70,7 @@ class ShareService {
); );
return true; return true;
} catch (error) { } catch (error) {
_log.severe("Share failed with error $error"); _log.severe("Share failed", error);
} }
return false; return false;
} }

View file

@ -140,7 +140,7 @@ class SyncService {
try { try {
await _db.writeTxn(() => a.put(_db)); await _db.writeTxn(() => a.put(_db));
} on IsarError catch (e) { } on IsarError catch (e) {
_log.severe("Failed to put new asset into db: $e"); _log.severe("Failed to put new asset into db", e);
return false; return false;
} }
return true; return true;
@ -173,7 +173,7 @@ class SyncService {
} }
return false; return false;
} on IsarError catch (e) { } on IsarError catch (e) {
_log.severe("Failed to sync remote assets to db: $e"); _log.severe("Failed to sync remote assets to db", e);
} }
return null; return null;
} }
@ -232,7 +232,7 @@ class SyncService {
await _db.writeTxn(() => _db.assets.deleteAll(idsToDelete)); await _db.writeTxn(() => _db.assets.deleteAll(idsToDelete));
await upsertAssetsWithExif(toAdd + toUpdate); await upsertAssetsWithExif(toAdd + toUpdate);
} on IsarError catch (e) { } on IsarError catch (e) {
_log.severe("Failed to sync remote assets to db: $e"); _log.severe("Failed to sync remote assets to db", e);
} }
await _updateUserAssetsETag(user, now); await _updateUserAssetsETag(user, now);
return true; return true;
@ -364,7 +364,7 @@ class SyncService {
}); });
_log.info("Synced changes of remote album ${album.name} to DB"); _log.info("Synced changes of remote album ${album.name} to DB");
} on IsarError catch (e) { } on IsarError catch (e) {
_log.severe("Failed to sync remote album to database $e"); _log.severe("Failed to sync remote album to database", e);
} }
if (album.shared || dto.shared) { if (album.shared || dto.shared) {
@ -441,7 +441,7 @@ class SyncService {
assert(ok); assert(ok);
_log.info("Removed local album $album from DB"); _log.info("Removed local album $album from DB");
} catch (e) { } catch (e) {
_log.severe("Failed to remove local album $album from DB"); _log.severe("Failed to remove local album $album from DB", e);
} }
} }
@ -577,7 +577,7 @@ class SyncService {
}); });
_log.info("Synced changes of local album ${ape.name} to DB"); _log.info("Synced changes of local album ${ape.name} to DB");
} on IsarError catch (e) { } on IsarError catch (e) {
_log.severe("Failed to update synced album ${ape.name} in DB: $e"); _log.severe("Failed to update synced album ${ape.name} in DB", e);
} }
return true; return true;
@ -623,7 +623,7 @@ class SyncService {
}); });
_log.info("Fast synced local album ${ape.name} to DB"); _log.info("Fast synced local album ${ape.name} to DB");
} on IsarError catch (e) { } on IsarError catch (e) {
_log.severe("Failed to fast sync local album ${ape.name} to DB: $e"); _log.severe("Failed to fast sync local album ${ape.name} to DB", e);
return false; return false;
} }
@ -656,7 +656,7 @@ class SyncService {
await _db.writeTxn(() => _db.albums.store(a)); await _db.writeTxn(() => _db.albums.store(a));
_log.info("Added a new local album to DB: ${ape.name}"); _log.info("Added a new local album to DB: ${ape.name}");
} on IsarError catch (e) { } on IsarError catch (e) {
_log.severe("Failed to add new local album ${ape.name} to DB: $e"); _log.severe("Failed to add new local album ${ape.name} to DB", e);
} }
} }
@ -706,9 +706,7 @@ class SyncService {
}); });
_log.info("Upserted ${assets.length} assets into the DB"); _log.info("Upserted ${assets.length} assets into the DB");
} on IsarError catch (e) { } on IsarError catch (e) {
_log.severe( _log.severe("Failed to upsert ${assets.length} assets into the DB", e);
"Failed to upsert ${assets.length} assets into the DB: ${e.toString()}",
);
// give details on the errors // give details on the errors
assets.sort(Asset.compareByOwnerChecksum); assets.sort(Asset.compareByOwnerChecksum);
final inDb = await _db.assets.getAllByOwnerIdChecksum( final inDb = await _db.assets.getAllByOwnerIdChecksum(
@ -776,7 +774,7 @@ class SyncService {
}); });
return true; return true;
} catch (e) { } catch (e) {
_log.severe("Failed to remove all local albums and assets: $e"); _log.severe("Failed to remove all local albums and assets", e);
return false; return false;
} }
} }

View file

@ -42,7 +42,7 @@ class UserService {
final dto = await _apiService.userApi.getAllUsers(isAll); final dto = await _apiService.userApi.getAllUsers(isAll);
return dto?.map(User.fromUserDto).toList(); return dto?.map(User.fromUserDto).toList();
} catch (e) { } catch (e) {
_log.warning("Failed get all users:\n$e"); _log.warning("Failed get all users", e);
return null; return null;
} }
} }
@ -65,7 +65,7 @@ class UserService {
), ),
); );
} catch (e) { } catch (e) {
_log.warning("Failed to upload profile image:\n$e"); _log.warning("Failed to upload profile image", e);
return null; return null;
} }
} }

View file

@ -15,7 +15,7 @@ class AppLogDetailPage extends HookConsumerWidget {
Widget build(BuildContext context, WidgetRef ref) { Widget build(BuildContext context, WidgetRef ref) {
var isDarkTheme = context.isDarkTheme; var isDarkTheme = context.isDarkTheme;
buildStackMessage(String stackTrace) { buildTextWithCopyButton(String header, String text) {
return Padding( return Padding(
padding: const EdgeInsets.all(8.0), padding: const EdgeInsets.all(8.0),
child: Column( child: Column(
@ -28,7 +28,7 @@ class AppLogDetailPage extends HookConsumerWidget {
Padding( Padding(
padding: const EdgeInsets.only(bottom: 8.0), padding: const EdgeInsets.only(bottom: 8.0),
child: Text( child: Text(
"STACK TRACES", header,
style: TextStyle( style: TextStyle(
fontSize: 12.0, fontSize: 12.0,
color: context.primaryColor, color: context.primaryColor,
@ -38,8 +38,7 @@ class AppLogDetailPage extends HookConsumerWidget {
), ),
IconButton( IconButton(
onPressed: () { onPressed: () {
Clipboard.setData(ClipboardData(text: stackTrace)) Clipboard.setData(ClipboardData(text: text)).then((_) {
.then((_) {
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(context).showSnackBar(
SnackBar( SnackBar(
content: Text( content: Text(
@ -68,73 +67,7 @@ class AppLogDetailPage extends HookConsumerWidget {
child: Padding( child: Padding(
padding: const EdgeInsets.all(8.0), padding: const EdgeInsets.all(8.0),
child: SelectableText( child: SelectableText(
stackTrace, text,
style: const TextStyle(
fontSize: 12.0,
fontWeight: FontWeight.bold,
fontFamily: "Inconsolata",
),
),
),
),
],
),
);
}
buildLogMessage(String message) {
return Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Padding(
padding: const EdgeInsets.only(bottom: 8.0),
child: Text(
"MESSAGE",
style: TextStyle(
fontSize: 12.0,
color: context.primaryColor,
fontWeight: FontWeight.bold,
),
),
),
IconButton(
onPressed: () {
Clipboard.setData(ClipboardData(text: message)).then((_) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
"Copied to clipboard",
style: context.textTheme.bodyLarge?.copyWith(
color: context.primaryColor,
),
),
),
);
});
},
icon: Icon(
Icons.copy,
size: 16.0,
color: context.primaryColor,
),
),
],
),
Container(
decoration: BoxDecoration(
color: isDarkTheme ? Colors.grey[900] : Colors.grey[200],
borderRadius: BorderRadius.circular(15.0),
),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: SelectableText(
message,
style: const TextStyle( style: const TextStyle(
fontSize: 12.0, fontSize: 12.0,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
@ -194,11 +127,16 @@ class AppLogDetailPage extends HookConsumerWidget {
body: SafeArea( body: SafeArea(
child: ListView( child: ListView(
children: [ children: [
buildLogMessage(logMessage.message), buildTextWithCopyButton("MESSAGE", logMessage.message),
if (logMessage.details != null)
buildTextWithCopyButton("DETAILS", logMessage.details.toString()),
if (logMessage.context1 != null) if (logMessage.context1 != null)
buildLogContext1(logMessage.context1.toString()), buildLogContext1(logMessage.context1.toString()),
if (logMessage.context2 != null) if (logMessage.context2 != null)
buildStackMessage(logMessage.context2.toString()), buildTextWithCopyButton(
"STACK TRACE",
logMessage.context2.toString(),
),
], ],
), ),
), ),

View file

@ -35,10 +35,10 @@ class SplashScreenPage extends HookConsumerWidget {
deviceIsOffline = true; deviceIsOffline = true;
log.fine("Device seems to be offline upon launch"); log.fine("Device seems to be offline upon launch");
} else { } else {
log.severe(e); log.severe("Failed to resolve endpoint", e);
} }
} catch (e) { } catch (e) {
log.severe(e); log.severe("Failed to resolve endpoint", e);
} }
try { try {
@ -53,7 +53,7 @@ class SplashScreenPage extends HookConsumerWidget {
ref.read(authenticationProvider.notifier).logout(); ref.read(authenticationProvider.notifier).logout();
log.severe( log.severe(
'Cannot set success login info: $error', 'Cannot set success login info',
error, error,
stackTrace, stackTrace,
); );