TF-3719 Add LogTracking to trace error notFound email in Email/get
Signed-off-by: dab246 <tdvu@linagora.com>
This commit is contained in:
@@ -54,6 +54,7 @@ export 'utils/mail/domain.dart';
|
||||
export 'utils/mail/mail_address.dart';
|
||||
export 'utils/application_manager.dart';
|
||||
export 'utils/preview_eml_file_utils.dart';
|
||||
export 'utils/logger/log_tracking.dart';
|
||||
|
||||
// Views
|
||||
export 'presentation/views/text/slogan_builder.dart';
|
||||
|
||||
@@ -0,0 +1,150 @@
|
||||
import 'dart:async';
|
||||
import 'dart:collection';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:core/domain/exceptions/download_file_exception.dart';
|
||||
import 'package:core/utils/platform_info.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
|
||||
class LogTracking {
|
||||
static const String logFolder = 'TraceLogs';
|
||||
static const String logFileNameDatePattern = 'yyyy-MM-dd';
|
||||
static const String logMessageDatePattern = 'yyyy-MM-dd, HH:mm:ss';
|
||||
|
||||
LogTracking._();
|
||||
|
||||
factory LogTracking() => _instance ??= LogTracking._();
|
||||
|
||||
static LogTracking? _instance;
|
||||
|
||||
final Queue<String> _messagesQueue = Queue();
|
||||
bool _isScheduled = false;
|
||||
|
||||
Future<void> addLog({required String message}) async {
|
||||
try {
|
||||
_messagesQueue.add(message);
|
||||
|
||||
if (!_isScheduled) {
|
||||
_isScheduled = true;
|
||||
await _executeTraceLog();
|
||||
}
|
||||
} catch (_) {
|
||||
_messagesQueue.remove(message);
|
||||
_isScheduled = false;
|
||||
}
|
||||
}
|
||||
|
||||
Future _executeTraceLog() async {
|
||||
while (true) {
|
||||
try {
|
||||
if (_messagesQueue.isEmpty) {
|
||||
_isScheduled = false;
|
||||
return;
|
||||
}
|
||||
|
||||
final message = _messagesQueue.removeFirst();
|
||||
await saveLog(message: message);
|
||||
} catch (_) {}
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> saveLog({required String message}) async {
|
||||
try {
|
||||
final currentDate = DateTime.timestamp();
|
||||
|
||||
final logFileName = generateLogFileName(currentDate: currentDate);
|
||||
|
||||
final messageSanitized = sanitizeMessage(
|
||||
message: message,
|
||||
currentDate: currentDate,
|
||||
);
|
||||
|
||||
await saveToFile(
|
||||
nameFile: logFileName,
|
||||
folderPath: logFolder,
|
||||
content: messageSanitized,
|
||||
);
|
||||
} catch (_) {}
|
||||
}
|
||||
|
||||
String sanitizeMessage({
|
||||
required String message,
|
||||
required DateTime currentDate,
|
||||
}) {
|
||||
final dateFormat = getDateFormatAsString(
|
||||
pattern: logMessageDatePattern,
|
||||
currentDate: currentDate,
|
||||
);
|
||||
|
||||
return '($dateFormat): $message \n';
|
||||
}
|
||||
|
||||
String getDateFormatAsString({
|
||||
required String pattern,
|
||||
required DateTime currentDate,
|
||||
}) {
|
||||
final dateFormat = DateFormat(pattern);
|
||||
return dateFormat.format(currentDate);
|
||||
}
|
||||
|
||||
String generateLogFileName({required DateTime currentDate}) {
|
||||
final dateFormat = getDateFormatAsString(
|
||||
pattern: logFileNameDatePattern,
|
||||
currentDate: currentDate,
|
||||
);
|
||||
|
||||
return '${dateFormat}_log';
|
||||
}
|
||||
|
||||
Future<String> _getInternalStorageDirPath({
|
||||
String? nameFile,
|
||||
String? folderPath,
|
||||
String? extensionFile,
|
||||
}) async {
|
||||
if (PlatformInfo.isMobile) {
|
||||
String fileDirectory =
|
||||
(await getApplicationDocumentsDirectory()).absolute.path;
|
||||
|
||||
if (folderPath != null) {
|
||||
fileDirectory = '$fileDirectory/$folderPath';
|
||||
}
|
||||
|
||||
Directory directory = Directory(fileDirectory);
|
||||
|
||||
if (!await directory.exists()) {
|
||||
await directory.create(recursive: true);
|
||||
}
|
||||
|
||||
if (nameFile != null) {
|
||||
fileDirectory = '$fileDirectory/$nameFile';
|
||||
}
|
||||
|
||||
if (extensionFile != null) {
|
||||
fileDirectory = '$fileDirectory.$extensionFile';
|
||||
}
|
||||
|
||||
return fileDirectory;
|
||||
} else {
|
||||
throw DeviceNotSupportedException();
|
||||
}
|
||||
}
|
||||
|
||||
Future<File> saveToFile({
|
||||
required String nameFile,
|
||||
required String content,
|
||||
String? folderPath,
|
||||
String? extensionFile,
|
||||
FileMode fileMode = FileMode.append,
|
||||
}) async {
|
||||
final internalStorageDirPath = await _getInternalStorageDirPath(
|
||||
nameFile: nameFile,
|
||||
folderPath: folderPath,
|
||||
extensionFile: extensionFile,
|
||||
);
|
||||
|
||||
final file = File(internalStorageDirPath);
|
||||
|
||||
return await file.writeAsString(content, mode: fileMode);
|
||||
}
|
||||
}
|
||||
@@ -9,4 +9,6 @@ import 'package:tmail_ui_user/features/caching/utils/cache_utils.dart';
|
||||
extension ListEmailIdExtension on List<EmailId> {
|
||||
List<String> toCacheKeyList(AccountId accountId, UserName userName) =>
|
||||
map((id) => TupleKey(id.asString, accountId.asString, userName.value).encodeKey).toList();
|
||||
|
||||
List<String> get asListString => map((id) => id.asString).toList();
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:core/utils/app_logger.dart';
|
||||
import 'package:core/utils/logger/log_tracking.dart';
|
||||
import 'package:jmap_dart_client/http/http_client.dart';
|
||||
import 'package:jmap_dart_client/jmap/account_id.dart';
|
||||
import 'package:jmap_dart_client/jmap/core/filter/filter.dart';
|
||||
@@ -25,10 +26,12 @@ import 'package:tmail_ui_user/features/thread/data/extensions/list_email_extensi
|
||||
import 'package:jmap_dart_client/jmap/mail/email/search_snippet/search_snippet.dart';
|
||||
import 'package:jmap_dart_client/jmap/mail/email/search_snippet/search_snippet_get_method.dart';
|
||||
import 'package:jmap_dart_client/jmap/mail/email/search_snippet/search_snippet_get_response.dart';
|
||||
import 'package:tmail_ui_user/features/thread/data/extensions/list_email_id_extension.dart';
|
||||
import 'package:tmail_ui_user/features/thread/data/model/email_change_response.dart';
|
||||
import 'package:tmail_ui_user/features/thread/domain/model/email_response.dart';
|
||||
import 'package:tmail_ui_user/features/thread/domain/model/search_emails_response.dart';
|
||||
import 'package:tmail_ui_user/main/error/capability_validator.dart';
|
||||
import 'package:tmail_ui_user/main/utils/app_config.dart';
|
||||
|
||||
class ThreadAPI {
|
||||
|
||||
@@ -101,6 +104,11 @@ class ThreadAPI {
|
||||
?.toEmailIds()
|
||||
.toList();
|
||||
log('ThreadAPI::getAllEmail:notFoundEmailIds = $notFoundEmailIds');
|
||||
if (notFoundEmailIds?.isNotEmpty == true && AppConfig.isApiLoggingEnabled) {
|
||||
await LogTracking().addLog(
|
||||
message: 'ThreadAPI::getAllEmail:notFoundEmailIds = ${notFoundEmailIds!.asListString.toString()}',
|
||||
);
|
||||
}
|
||||
return EmailsResponse(
|
||||
emailList: emailList,
|
||||
notFoundEmailIds: notFoundEmailIds,
|
||||
@@ -299,7 +307,15 @@ class ThreadAPI {
|
||||
newStateEmail = emailResponseUpdated?.state;
|
||||
final notFoundIdsUpdated = emailResponseUpdated?.notFound?.toEmailIds().toList() ?? [];
|
||||
log('ThreadAPI::getChanges:notFoundIdsUpdated = $notFoundIdsUpdated');
|
||||
destroyedEmailIds.addAll(notFoundIdsUpdated);
|
||||
if (notFoundIdsUpdated.isNotEmpty) {
|
||||
destroyedEmailIds.addAll(notFoundIdsUpdated);
|
||||
|
||||
if (AppConfig.isApiLoggingEnabled) {
|
||||
await LogTracking().addLog(
|
||||
message: 'ThreadAPI::getChanges:notFoundIdsUpdated = ${notFoundIdsUpdated.asListString.toString()}',
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (getEmailCreatedInvocation != null) {
|
||||
@@ -311,7 +327,15 @@ class ThreadAPI {
|
||||
newStateEmail = emailResponseCreated?.state;
|
||||
final notFoundIdsCreated = emailResponseCreated?.notFound?.toEmailIds().toList() ?? [];
|
||||
log('ThreadAPI::getChanges:notFoundIdsCreated = $notFoundIdsCreated');
|
||||
destroyedEmailIds.addAll(notFoundIdsCreated);
|
||||
if (notFoundIdsCreated.isNotEmpty) {
|
||||
destroyedEmailIds.addAll(notFoundIdsCreated);
|
||||
|
||||
if (AppConfig.isApiLoggingEnabled) {
|
||||
await LogTracking().addLog(
|
||||
message: 'ThreadAPI::getChanges:notFoundIdsCreated = ${notFoundIdsCreated.asListString.toString()}',
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
log('ThreadAPI::getChanges:newStateChanges = $newStateChanges | newStateEmail = $newStateEmail | hasMoreChanges = $hasMoreChanges');
|
||||
log('ThreadAPI::getChanges:updatedEmailSize = ${updatedEmail?.length} | createdEmailSize = ${createdEmail?.length}');
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:core/utils/platform_info.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_dotenv/flutter_dotenv.dart';
|
||||
@@ -13,6 +14,9 @@ class AppConfig {
|
||||
static const int warningAttachmentFileSizeInMegabytes = 10;
|
||||
static const int defaultLimitAutocomplete = 8;
|
||||
|
||||
// For testing
|
||||
static bool isApiLoggingEnabled = PlatformInfo.isAndroid;
|
||||
|
||||
static const String envFileName = 'env.file';
|
||||
static const String appDashboardConfigurationPath = "configurations/app_dashboard.json";
|
||||
static const String appFCMConfigurationPath = "configurations/env.fcm";
|
||||
|
||||
Reference in New Issue
Block a user