mirror of
https://github.com/twonlyapp/twonly-app.git
synced 2026-07-17 23:54:08 +00:00
improve error logging
This commit is contained in:
parent
4c42d55afd
commit
e8829ed2c9
20 changed files with 142 additions and 49 deletions
|
|
@ -36,7 +36,7 @@ abstract class VideoCompressionChannel {
|
|||
});
|
||||
return outputPath;
|
||||
} on PlatformException catch (e) {
|
||||
Log.error('Failed to compress video: $e');
|
||||
Log.warn('Failed to compress video: $e');
|
||||
return null;
|
||||
} finally {
|
||||
_currentProgressCallback = null;
|
||||
|
|
|
|||
|
|
@ -30,11 +30,15 @@ class ReactionsDao extends DatabaseAccessor<TwonlyDB> with _$ReactionsDaoMixin {
|
|||
.getMessageById(messageId)
|
||||
.getSingleOrNull();
|
||||
if (msg == null) {
|
||||
Log.error('updateReaction: Message $messageId not found!');
|
||||
Log.warn('updateReaction: Message $messageId not found!');
|
||||
return;
|
||||
}
|
||||
if (msg.groupId != groupId) {
|
||||
Log.error('updateReaction: Message groupId ${msg.groupId} != $groupId');
|
||||
Log.warn('updateReaction: Message groupId ${msg.groupId} != $groupId');
|
||||
Log.error(
|
||||
'updateReaction: Message groupId mismatch',
|
||||
onlyIfSentryEnabled: true,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ class ReceiptsDao extends DatabaseAccessor<TwonlyDB> with _$ReceiptsDaoMixin {
|
|||
))
|
||||
.go();
|
||||
}
|
||||
|
||||
|
||||
Future<void> deleteReceiptsByMessageId(String messageId) async {
|
||||
await (delete(receipts)..where(
|
||||
(t) => t.messageId.equals(messageId),
|
||||
|
|
@ -201,8 +201,8 @@ class ReceiptsDao extends DatabaseAccessor<TwonlyDB> with _$ReceiptsDaoMixin {
|
|||
);
|
||||
final updatedReceipt = await getReceiptById(newReceiptId);
|
||||
if (updatedReceipt == null) {
|
||||
Log.error(
|
||||
'Tried to change the receipt ID, but could not get the updated receipt...',
|
||||
Log.warn(
|
||||
'[$oldReceiptId] Tried to change the receipt ID to $newReceiptId, but could not get the updated receipt...',
|
||||
);
|
||||
}
|
||||
return updatedReceipt;
|
||||
|
|
@ -253,6 +253,9 @@ class ReceiptsDao extends DatabaseAccessor<TwonlyDB> with _$ReceiptsDaoMixin {
|
|||
Future<void> gotReceipt(String receiptId) async {
|
||||
await into(
|
||||
receivedReceipts,
|
||||
).insert(ReceivedReceiptsCompanion(receiptId: Value(receiptId)));
|
||||
).insert(
|
||||
ReceivedReceiptsCompanion(receiptId: Value(receiptId)),
|
||||
mode: InsertMode.insertOrIgnore,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -684,11 +684,11 @@ class ApiService {
|
|||
final req = createClientToServerFromHandshake(handshake);
|
||||
final result = await sendRequestSync(req, authenticated: false);
|
||||
if (result.isError) {
|
||||
Log.error('could not request proof of work params', result);
|
||||
Log.error('could not request proof of work params', error: result);
|
||||
if (result.error == ErrorCode.RegistrationDisabled) {
|
||||
return (null, true);
|
||||
}
|
||||
Log.error('could not request proof of work params', result);
|
||||
Log.error('could not request proof of work params', error: result);
|
||||
return (null, false);
|
||||
}
|
||||
return (result.value.proofOfWork as Response_ProofOfWork, false);
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ Future<void> handleErrorMessage(
|
|||
String receiptId, {
|
||||
String? groupId,
|
||||
}) async {
|
||||
Log.error('[$receiptId] Got error from $fromUserId: $error');
|
||||
Log.warn('[$receiptId] Got error from $fromUserId: $error');
|
||||
|
||||
switch (error.type) {
|
||||
case EncryptedContent_ErrorMessages_Type
|
||||
|
|
@ -38,14 +38,14 @@ Future<void> handleErrorMessage(
|
|||
break; // The other user initiated a new signal session, so ignore the error in this case, as the new session works...
|
||||
case EncryptedContent_ErrorMessages_Type.GROUP_NOT_FOUND_OR_NOT_A_MEMBER:
|
||||
if (groupId == null) {
|
||||
Log.error(
|
||||
Log.warn(
|
||||
'[$receiptId] GROUP_NOT_FOUND_OR_NOT_A_MEMBER error received, but groupId is null.',
|
||||
);
|
||||
return;
|
||||
}
|
||||
final group = await twonlyDB.groupsDao.getGroup(groupId);
|
||||
if (group == null) {
|
||||
Log.error(
|
||||
Log.warn(
|
||||
'[$receiptId] GROUP_NOT_FOUND_OR_NOT_A_MEMBER error received, but group $groupId is not found in database.',
|
||||
);
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -143,8 +143,12 @@ Future<void> handleDownloadStatusUpdate(TaskStatusUpdate update) async {
|
|||
failed = false;
|
||||
} else {
|
||||
failed = true;
|
||||
Log.warn(
|
||||
'[$mediaId] Got invalid response status code: ${update.responseStatusCode}',
|
||||
);
|
||||
Log.error(
|
||||
'Got invalid response status code: ${update.responseStatusCode}',
|
||||
onlyIfSentryEnabled: true,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
|
|
@ -227,7 +231,7 @@ Future<void> startDownloadMedia(MediaFile media, bool force) async {
|
|||
try {
|
||||
await downloadFileFast(media, apiUrl, mediaService.encryptedPath);
|
||||
} catch (e) {
|
||||
Log.error('Fast download failed: $e');
|
||||
Log.warn('Fast download failed: $e');
|
||||
await FileDownloader().enqueue(task);
|
||||
}
|
||||
} catch (e) {
|
||||
|
|
@ -251,9 +255,13 @@ Future<void> downloadFileFast(
|
|||
return;
|
||||
} else {
|
||||
if (response.statusCode == 404 || response.statusCode == 403) {
|
||||
Log.error(
|
||||
Log.warn(
|
||||
'Got ${response.statusCode} from server for media ID ${media.mediaId}. Requesting upload again',
|
||||
);
|
||||
Log.error(
|
||||
'Got ${response.statusCode} from server for media ID.',
|
||||
onlyIfSentryEnabled: true,
|
||||
);
|
||||
// Message was deleted from the server. Requesting it again from the sender to upload it again...
|
||||
await requestMediaReupload(media.mediaId);
|
||||
return;
|
||||
|
|
@ -293,7 +301,7 @@ Future<void> handleEncryptedFile(String mediaId) async {
|
|||
action: () async {
|
||||
final mediaService = await MediaFileService.fromMediaId(mediaId);
|
||||
if (mediaService == null) {
|
||||
Log.error('Media file not found in database.');
|
||||
Log.warn('[$mediaId] Media file not found in database.');
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -80,9 +80,13 @@ Future<void> handleUploadStatusUpdate(TaskStatusUpdate update) async {
|
|||
await markUploadAsSuccessful(media);
|
||||
return;
|
||||
}
|
||||
Log.error(
|
||||
Log.warn(
|
||||
'Got HTTP error ${update.responseStatusCode} for $mediaId',
|
||||
);
|
||||
Log.error(
|
||||
'Got HTTP error ${update.responseStatusCode} for media.',
|
||||
onlyIfSentryEnabled: true,
|
||||
);
|
||||
}
|
||||
|
||||
if (update.status == TaskStatus.notFound) {
|
||||
|
|
@ -118,9 +122,13 @@ Future<void> handleUploadStatusUpdate(TaskStatusUpdate update) async {
|
|||
|
||||
if (update.status == TaskStatus.failed ||
|
||||
update.status == TaskStatus.canceled) {
|
||||
Log.error(
|
||||
Log.warn(
|
||||
'Background upload failed for $mediaId with status ${update.status} and ${update.responseStatusCode}. ',
|
||||
);
|
||||
Log.error(
|
||||
'Background upload failed with status ${update.status} and ${update.responseStatusCode}.',
|
||||
onlyIfSentryEnabled: true,
|
||||
);
|
||||
final mediaService = MediaFileService(media);
|
||||
|
||||
// in case the media file is already uploaded to not reqtry
|
||||
|
|
|
|||
|
|
@ -252,7 +252,11 @@ Future<void> _handleClient2ClientMessage(
|
|||
await twonlyDB.receiptsDao.gotReceipt(receiptId);
|
||||
Log.info('[$receiptId] Finished processing');
|
||||
} catch (e) {
|
||||
Log.error('[$receiptId] Error marking message as received: $e');
|
||||
Log.warn('[$receiptId] Error marking message as received: $e');
|
||||
Log.error(
|
||||
'Error marking message as received: $e',
|
||||
onlyIfSentryEnabled: true,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -426,7 +430,14 @@ Future<(EncryptedContent?, PlaintextContent?)> handleEncryptedMessage(
|
|||
}
|
||||
|
||||
if (!content.hasGroupId()) {
|
||||
Log.error('[$receiptId] Messages should have a groupId $fromUserId.');
|
||||
final type = _getEncryptedContentType(content);
|
||||
Log.warn(
|
||||
'[$receiptId] Messages should have a groupId $fromUserId. Type: $type',
|
||||
);
|
||||
Log.error(
|
||||
'Messages should have a groupId. Type: $type',
|
||||
onlyIfSentryEnabled: true,
|
||||
);
|
||||
return (null, null);
|
||||
}
|
||||
|
||||
|
|
@ -453,7 +464,7 @@ Future<(EncryptedContent?, PlaintextContent?)> handleEncryptedMessage(
|
|||
);
|
||||
if (contact == null || !contact.accepted || contact.deletedByUser) {
|
||||
await handleNewContactRequest(fromUserId);
|
||||
Log.error(
|
||||
Log.warn(
|
||||
'[$receiptId] User tries to send message to direct chat while the user does not exist!',
|
||||
);
|
||||
return (
|
||||
|
|
@ -478,7 +489,7 @@ Future<(EncryptedContent?, PlaintextContent?)> handleEncryptedMessage(
|
|||
);
|
||||
} else {
|
||||
if (content.hasGroupJoin()) {
|
||||
Log.error(
|
||||
Log.warn(
|
||||
'[$receiptId] Got group join message, but group does not exist yet, retry later. As probably the GroupCreate was not yet received.',
|
||||
);
|
||||
// In case the group join was received before the GroupCreate the sender should send it later again.
|
||||
|
|
@ -489,7 +500,7 @@ Future<(EncryptedContent?, PlaintextContent?)> handleEncryptedMessage(
|
|||
);
|
||||
}
|
||||
|
||||
Log.error(
|
||||
Log.warn(
|
||||
'[$receiptId] User $fromUserId tried to access group ${content.groupId}. Sending GROUP_NOT_FOUND_OR_NOT_A_MEMBER error.',
|
||||
);
|
||||
return (
|
||||
|
|
@ -598,3 +609,26 @@ Future<(EncryptedContent?, PlaintextContent?)> handleEncryptedMessage(
|
|||
|
||||
return (null, null);
|
||||
}
|
||||
|
||||
String _getEncryptedContentType(EncryptedContent content) {
|
||||
if (content.hasMessageUpdate()) return 'messageUpdate';
|
||||
if (content.hasMedia()) return 'media';
|
||||
if (content.hasMediaUpdate()) return 'mediaUpdate';
|
||||
if (content.hasContactUpdate()) return 'contactUpdate';
|
||||
if (content.hasContactRequest()) return 'contactRequest';
|
||||
if (content.hasFlameSync()) return 'flameSync';
|
||||
if (content.hasPushKeys()) return 'pushKeys';
|
||||
if (content.hasReaction()) return 'reaction';
|
||||
if (content.hasTextMessage()) return 'textMessage';
|
||||
if (content.hasGroupCreate()) return 'groupCreate';
|
||||
if (content.hasGroupJoin()) return 'groupJoin';
|
||||
if (content.hasGroupUpdate()) return 'groupUpdate';
|
||||
if (content.hasResendGroupPublicKey()) return 'resendGroupPublicKey';
|
||||
if (content.hasErrorMessages()) return 'errorMessages';
|
||||
if (content.hasAdditionalDataMessage()) return 'additionalDataMessage';
|
||||
if (content.hasTypingIndicator()) return 'typingIndicator';
|
||||
if (content.hasUserDiscoveryRequest()) return 'userDiscoveryRequest';
|
||||
if (content.hasUserDiscoveryUpdate()) return 'userDiscoveryUpdate';
|
||||
if (content.hasKeyVerificationProof()) return 'keyVerificationProof';
|
||||
return 'unknown';
|
||||
}
|
||||
|
|
|
|||
|
|
@ -105,8 +105,9 @@ class BackupService {
|
|||
))) {
|
||||
final backupId = await RustBackupIdentity.getBackupId();
|
||||
if (backupId == null) {
|
||||
Log.error('No backup password was set by the user.');
|
||||
Log.warn('No backup password was set by the user.');
|
||||
backup.identityState = LastBackupUploadState.failed;
|
||||
await UserService.update((u) => u.isBackupEnabled = false);
|
||||
} else {
|
||||
Log.info('Performing a identity backup.');
|
||||
final encryptedBackup =
|
||||
|
|
@ -162,7 +163,7 @@ class BackupService {
|
|||
(backupDownloadToken, backupArchive) =
|
||||
await RustBackupArchive.createBackupArchive();
|
||||
} catch (e) {
|
||||
Log.error(e);
|
||||
Log.warn('Creating archive backup failed: $e');
|
||||
return;
|
||||
}
|
||||
Log.info(
|
||||
|
|
@ -337,7 +338,7 @@ class BackupService {
|
|||
},
|
||||
);
|
||||
} catch (e) {
|
||||
Log.error('Error fetching backup: $e');
|
||||
Log.warn('Error fetching backup: $e');
|
||||
return (null, RecoveryError.noInternet);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:twonly/locator.dart';
|
||||
|
|
@ -128,6 +130,12 @@ Future<void> handleUserStudyUpload() async {
|
|||
});
|
||||
}
|
||||
} catch (e) {
|
||||
Log.error(e);
|
||||
if (e is http.ClientException ||
|
||||
e is SocketException ||
|
||||
e is TimeoutException) {
|
||||
Log.warn('Error uploading user study data: $e');
|
||||
} else {
|
||||
Log.error(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -41,10 +41,14 @@ class Log {
|
|||
}
|
||||
|
||||
static void error(
|
||||
Object? messageInput, [
|
||||
Object? messageInput, {
|
||||
Object? error,
|
||||
StackTrace? stackTrace,
|
||||
]) {
|
||||
bool onlyIfSentryEnabled = false,
|
||||
}) {
|
||||
if (!AppState.allowErrorTrackingViaSentry && onlyIfSentryEnabled) {
|
||||
return;
|
||||
}
|
||||
final message = filterLogMessage('$messageInput');
|
||||
if (AppState.allowErrorTrackingViaSentry) {
|
||||
try {
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ class ScreenshotImageHelper {
|
|||
try {
|
||||
return imageBytes = await imageBytesFuture;
|
||||
} catch (e) {
|
||||
Log.error('Could not resolve imageBytesFuture: $e');
|
||||
Log.warn('Could not resolve imageBytesFuture: $e');
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
@ -36,14 +36,14 @@ class ScreenshotImageHelper {
|
|||
try {
|
||||
return imageBytes = await file!.readAsBytes();
|
||||
} catch (e) {
|
||||
Log.error('Could not read bytes from file: $e');
|
||||
Log.warn('Could not read bytes from file: $e');
|
||||
return null;
|
||||
}
|
||||
}
|
||||
if (image == null) return null;
|
||||
final img = await image!.toByteData(format: io.ImageByteFormat.png);
|
||||
if (img == null) {
|
||||
Log.error('Got no image');
|
||||
Log.warn('Got no image');
|
||||
return null;
|
||||
}
|
||||
return imageBytes = img.buffer.asUint8List();
|
||||
|
|
@ -94,7 +94,7 @@ class ScreenshotController {
|
|||
});
|
||||
return completer.future;
|
||||
}
|
||||
Log.error(e);
|
||||
Log.warn('Could not capture screenshot: $e');
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -289,12 +289,18 @@ class _CameraPreviewViewState extends State<CameraPreviewView> {
|
|||
mc.cameraController == null) {
|
||||
return;
|
||||
}
|
||||
await mc.cameraController?.setZoomLevel(
|
||||
newScale.clamp(
|
||||
mc.selectedCameraDetails.minAvailableZoom,
|
||||
mc.selectedCameraDetails.maxAvailableZoom,
|
||||
),
|
||||
);
|
||||
try {
|
||||
if (mc.cameraController!.value.isInitialized) {
|
||||
await mc.cameraController!.setZoomLevel(
|
||||
newScale.clamp(
|
||||
mc.selectedCameraDetails.minAvailableZoom,
|
||||
mc.selectedCameraDetails.maxAvailableZoom,
|
||||
),
|
||||
);
|
||||
}
|
||||
} on CameraException catch (e) {
|
||||
Log.warn('Failed to set zoom level: $e');
|
||||
}
|
||||
setState(() {
|
||||
mc.selectedCameraDetails.scaleFactor = newScale;
|
||||
});
|
||||
|
|
@ -491,9 +497,16 @@ class _CameraPreviewViewState extends State<CameraPreviewView> {
|
|||
(_basePanY - (details.localPosition.dy as double)) / 30)
|
||||
.clamp(1, mc.selectedCameraDetails.maxAvailableZoom);
|
||||
});
|
||||
await mc.cameraController!.setZoomLevel(
|
||||
mc.selectedCameraDetails.scaleFactor,
|
||||
);
|
||||
try {
|
||||
if (mc.cameraController != null &&
|
||||
mc.cameraController!.value.isInitialized) {
|
||||
await mc.cameraController!.setZoomLevel(
|
||||
mc.selectedCameraDetails.scaleFactor,
|
||||
);
|
||||
}
|
||||
} on CameraException catch (e) {
|
||||
Log.warn('Failed to set zoom level: $e');
|
||||
}
|
||||
if (!userService.currentUser.hasZoomed) {
|
||||
await UserService.update((u) => u.hasZoomed = true);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -81,6 +81,7 @@ class PermissionHandlerViewState extends State<PermissionHandlerView>
|
|||
}
|
||||
|
||||
Future<void> _checkAndTriggerSuccess() async {
|
||||
if (!mounted) return;
|
||||
if (_isSuccessTriggered) return;
|
||||
final route = ModalRoute.of(context);
|
||||
if (route != null && !route.isCurrent) {
|
||||
|
|
|
|||
|
|
@ -499,7 +499,7 @@ class _ShareImageEditorView extends State<ShareImageEditorView> {
|
|||
pixelRatio: pixelRatio,
|
||||
);
|
||||
if (image == null) {
|
||||
Log.error('screenshotController did not return image bytes');
|
||||
Log.warn('screenshotController did not return image bytes');
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
@ -544,7 +544,7 @@ class _ShareImageEditorView extends State<ShareImageEditorView> {
|
|||
if (image == null) return null;
|
||||
final bytes = await image.getBytes();
|
||||
if (bytes == null) {
|
||||
Log.error('imageBytes are empty');
|
||||
Log.warn('imageBytes are empty');
|
||||
return null;
|
||||
}
|
||||
if (media.type == MediaType.image || media.type == MediaType.gif) {
|
||||
|
|
|
|||
|
|
@ -50,7 +50,13 @@ Future<List<Sticker>> getStickerIndex() async {
|
|||
return res;
|
||||
}
|
||||
} catch (e) {
|
||||
Log.error('$e');
|
||||
if (e is http.ClientException ||
|
||||
e is SocketException ||
|
||||
e is TimeoutException) {
|
||||
Log.warn('Could not load stickers index: $e');
|
||||
} else {
|
||||
Log.error('Could not load stickers index: $e');
|
||||
}
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,9 +20,9 @@ class _TypingIndicatorSubtitleCompState
|
|||
extends State<TypingIndicatorSubtitleComp> {
|
||||
List<GroupMember> _groupMembers = [];
|
||||
|
||||
late StreamSubscription<List<(Contact, GroupMember)>> membersSub;
|
||||
StreamSubscription<List<(Contact, GroupMember)>>? membersSub;
|
||||
|
||||
late Timer _periodicUpdate;
|
||||
Timer? _periodicUpdate;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
|
|
@ -50,8 +50,8 @@ class _TypingIndicatorSubtitleCompState
|
|||
|
||||
@override
|
||||
void dispose() {
|
||||
membersSub.cancel();
|
||||
_periodicUpdate.cancel();
|
||||
membersSub?.cancel();
|
||||
_periodicUpdate?.cancel();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -99,6 +99,7 @@ class _ChatMessagesViewState extends State<ChatMessagesView>
|
|||
// }
|
||||
|
||||
bool _isViewActive() {
|
||||
if (!mounted) return false;
|
||||
return !AppState.isAppInBackground &&
|
||||
(ModalRoute.of(context)?.isCurrent ?? false);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -169,7 +169,7 @@ class _RegisterViewState extends State<RegisterView> {
|
|||
await apiService.authenticate();
|
||||
widget.callbackOnSuccess();
|
||||
} catch (e, stack) {
|
||||
Log.error('Error creating new user', e, stack);
|
||||
Log.error('Error creating new user', error: e, stackTrace: stack);
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_isTryingToRegister = false;
|
||||
|
|
|
|||
|
|
@ -46,6 +46,7 @@ class _SubscriptionViewState extends State<SubscriptionView> {
|
|||
additionalOwnerName = ownerId.toString();
|
||||
}
|
||||
}
|
||||
if (!mounted) return;
|
||||
setState(() {});
|
||||
await apiService.forceIpaCheck();
|
||||
}
|
||||
|
|
@ -224,6 +225,7 @@ class _PlanCardState extends State<PlanCard> {
|
|||
});
|
||||
await context.read<PurchasesProvider>().buy(product);
|
||||
await widget.onPurchase!();
|
||||
if (!mounted) return;
|
||||
setState(() {
|
||||
_isLoading = null;
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in a new issue