boost_logger.dart 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  1. import 'dart:convert';
  2. import 'dart:io';
  3. import 'package:path_provider/path_provider.dart';
  4. import 'package:flutter_app_group_directory/flutter_app_group_directory.dart';
  5. import 'log/logger.dart';
  6. class BoostLogger {
  7. static const String LOG_FOLDER = 'boost_logs';
  8. static const String APP_LOG_FILE = 'app.json';
  9. static const String CORE_LOG_FILE = 'core.json';
  10. static const String TAG = 'BoostLogger';
  11. Map<String, dynamic> _currentSession = {};
  12. String? _logPath;
  13. /// 单例模式实现
  14. static final BoostLogger _instance = BoostLogger._internal();
  15. factory BoostLogger() => _instance;
  16. BoostLogger._internal();
  17. /// 获取日志目录
  18. Future<Directory> _getLogDirectory() async {
  19. if (Platform.isAndroid) {
  20. final appDir = await getApplicationSupportDirectory();
  21. final logDir = Directory('${appDir.path}/$LOG_FOLDER');
  22. if (!await logDir.exists()) {
  23. await logDir.create(recursive: true);
  24. }
  25. return logDir;
  26. } else {
  27. try {
  28. // iOS 使用 App Group 目录
  29. final Directory? appGroupDirectory =
  30. await FlutterAppGroupDirectory.getAppGroupDirectory(
  31. 'group.app.xixi.nomo',
  32. );
  33. // 验证 App Group 目录是否有效
  34. if (appGroupDirectory != null) {
  35. final logDir = Directory('${appGroupDirectory.path}/$LOG_FOLDER');
  36. if (!await logDir.exists()) {
  37. await logDir.create(recursive: true);
  38. }
  39. return logDir;
  40. } else {
  41. // App Group 目录不可用,回退到应用文档目录
  42. log(
  43. TAG,
  44. 'App Group directory not available, falling back to documents directory',
  45. );
  46. final appDir = await getApplicationDocumentsDirectory();
  47. final logDir = Directory('${appDir.path}/$LOG_FOLDER');
  48. if (!await logDir.exists()) {
  49. await logDir.create(recursive: true);
  50. }
  51. return logDir;
  52. }
  53. } catch (e) {
  54. // 如果 App Group 出现错误,回退到应用文档目录
  55. log(
  56. TAG,
  57. 'Error with App Group directory: $e, falling back to documents directory',
  58. );
  59. final appDir = await getApplicationDocumentsDirectory();
  60. final logDir = Directory('${appDir.path}/$LOG_FOLDER');
  61. if (!await logDir.exists()) {
  62. await logDir.create(recursive: true);
  63. }
  64. return logDir;
  65. }
  66. }
  67. }
  68. /// 初始化日志系统
  69. Future<void> init() async {
  70. try {
  71. final logDir = await _getLogDirectory();
  72. _logPath = logDir.path;
  73. // 清除旧的app日志文件
  74. final appFile = File('${logDir.path}/$APP_LOG_FILE');
  75. if (await appFile.exists()) {
  76. await appFile.delete();
  77. }
  78. // 清除旧的内核日志文件
  79. final coreFile = File('${logDir.path}/$CORE_LOG_FILE');
  80. if (await coreFile.exists()) {
  81. await coreFile.delete();
  82. }
  83. // 初始化空的会话数据
  84. _currentSession = {};
  85. await _saveSession();
  86. } catch (e) {
  87. log(TAG, 'BoostLogger init error: $e');
  88. }
  89. }
  90. //读取历史日志
  91. Future<void> readHistoryLog() async {
  92. try {
  93. final logDir = await _getLogDirectory();
  94. _logPath = logDir.path;
  95. getCurrentSession();
  96. } catch (e) {
  97. log(TAG, 'BoostLogger readHistoryLog error: $e');
  98. }
  99. }
  100. /// 设置整个部分的数据
  101. Future<void> setSection(String section, Map<String, dynamic> data) async {
  102. try {
  103. _currentSession[section] = data;
  104. await _saveSession();
  105. } catch (e) {
  106. log(TAG, 'BoostLogger setSection error: $e');
  107. }
  108. }
  109. /// 设置整个部分的数据
  110. Future<void> setSectionObj(String section, dynamic data) async {
  111. try {
  112. _currentSession[section] = data;
  113. await _saveSession();
  114. } catch (e) {
  115. log(TAG, 'BoostLogger setSectionObj error: $e');
  116. }
  117. }
  118. /// 更新部分中的特定字段
  119. Future<void> updateField(String section, String field, dynamic value) async {
  120. try {
  121. final sectionData = _currentSession[section] as Map<String, dynamic>;
  122. sectionData[field] = value;
  123. _currentSession[section] = sectionData;
  124. await _saveSession();
  125. } catch (e) {
  126. log(TAG, 'BoostLogger updateField error: $e');
  127. }
  128. }
  129. /// 向数组添加数据
  130. Future<void> appendToArray(
  131. String section,
  132. String arrayField,
  133. Map<String, dynamic> data,
  134. ) async {
  135. try {
  136. final sectionData =
  137. _currentSession[section] as Map<String, dynamic>? ?? {};
  138. final array = sectionData[arrayField] as List<dynamic>? ?? [];
  139. array.add(data);
  140. sectionData[arrayField] = array;
  141. _currentSession[section] = sectionData;
  142. await _saveSession();
  143. } catch (e) {
  144. log(TAG, 'BoostLogger appendToArray error: $e');
  145. }
  146. }
  147. /// 保存会话数据到文件
  148. Future<void> _saveSession() async {
  149. if (_logPath == null) return;
  150. try {
  151. final file = File('$_logPath/$APP_LOG_FILE');
  152. // 使用 writeAsStringSync 确保写入完成
  153. final jsonString = const JsonEncoder.withIndent(
  154. ' ',
  155. ).convert(_currentSession);
  156. await file.writeAsString(jsonString, flush: true);
  157. // 验证文件写入
  158. final content = await file.readAsString();
  159. final savedData = jsonDecode(content) as Map<String, dynamic>;
  160. log(TAG, 'BoostLogger verify saved data: $savedData');
  161. } catch (e) {
  162. log(TAG, 'BoostLogger save session error: $e');
  163. }
  164. }
  165. /// 获取当前会话数据
  166. Map<String, dynamic> getCurrentSession() {
  167. try {
  168. // 从文件读取最新数据
  169. final file = File('$_logPath/$APP_LOG_FILE');
  170. if (file.existsSync()) {
  171. final content = file.readAsStringSync();
  172. _currentSession = jsonDecode(content) as Map<String, dynamic>;
  173. }
  174. return Map<String, dynamic>.from(_currentSession);
  175. } catch (e) {
  176. log(TAG, 'BoostLogger getCurrentSession error: $e');
  177. return {};
  178. }
  179. }
  180. /// 获取日志文件路径
  181. Future<String?> getAppLogFilePath() async {
  182. return _logPath != null ? '$_logPath/$APP_LOG_FILE' : null;
  183. }
  184. /// 获取核心日志文件路径
  185. Future<String?> getCoreLogFilePath() async {
  186. return _logPath != null ? '$_logPath/$CORE_LOG_FILE' : null;
  187. }
  188. /// 清理资源
  189. Future<void> release() async {
  190. try {
  191. // 检查 _currentSession 是否为空
  192. if (_currentSession.isEmpty) {
  193. return;
  194. }
  195. await _saveSession();
  196. _currentSession.clear();
  197. } catch (e) {
  198. log(TAG, 'BoostLogger release error: $e');
  199. }
  200. }
  201. }