import 'dart:io'; import 'dart:async'; import 'dart:developer' as developer; import 'dart:collection'; import 'package:path_provider/path_provider.dart'; import '../../app/constants/configs.dart'; import '../developer/ix_developer_tools.dart'; /// 日志条目 class LogEntry { final DateTime timestamp; final String message; final String name; LogEntry(this.timestamp, this.message, this.name); @override String toString() { return '[${timestamp.toIso8601String()}] $name $message'; } } /// 优化的日志管理器 /// 特性: /// 1. 使用队列批量写入,避免频繁文件操作 /// 2. 按日期分文件存储 /// 3. 自动清理7天前的日志 /// 4. 异步处理,不阻塞主线程 class LogManager { static final LogManager _instance = LogManager._internal(); // 单例模式 factory LogManager() => _instance; LogManager._internal(); // 配置常量 static const int _maxLogFileSize = 5 * 1024 * 1024; // 5MB per file static const int _batchWriteSize = 50; // 批量写入条数 static const Duration _batchWriteInterval = Duration(seconds: 5); // 批量写入间隔 static const int _retentionDays = 7; // 保留天数 static const int _maxQueueSize = 1000; // 队列最大大小 // 状态变量 bool _isInitialized = false; late Directory _logDirectory; final Queue _logQueue = Queue(); Timer? _batchWriteTimer; bool _isWriting = false; /// 初始化日志管理器 Future init() async { if (_isInitialized) return; try { // 获取日志存储目录 final appDir = await getApplicationDocumentsDirectory(); _logDirectory = Directory('${appDir.path}/logs'); // 创建日志目录 if (!await _logDirectory.exists()) { await _logDirectory.create(recursive: true); } // 清理过期日志文件 await _cleanupOldLogs(); // 启动批量写入定时器 _startBatchWriteTimer(); _isInitialized = true; } catch (e) { print('Failed to initialize LogManager: $e'); rethrow; } } /// 启动批量写入定时器 void _startBatchWriteTimer() { _batchWriteTimer?.cancel(); _batchWriteTimer = Timer.periodic(_batchWriteInterval, (_) { _flushLogs(); }); } /// 添加日志到队列 void _addToQueue(String message, String name) { final entry = LogEntry(DateTime.now(), message, name); _logQueue.add(entry); // 如果队列满了,立即写入 if (_logQueue.length >= _maxQueueSize) { _flushLogs(); } } /// 批量写入日志 Future _flushLogs() async { if (_isWriting || _logQueue.isEmpty) return; _isWriting = true; try { final logsToWrite = []; // 取出要写入的日志条目 final count = _logQueue.length > _batchWriteSize ? _batchWriteSize : _logQueue.length; for (int i = 0; i < count; i++) { logsToWrite.add(_logQueue.removeFirst()); } if (logsToWrite.isNotEmpty) { await _writeLogsToFile(logsToWrite); } } catch (e) { print('Failed to flush logs: $e'); } finally { _isWriting = false; } } /// 写入日志到文件 Future _writeLogsToFile(List logs) async { try { // 按日期分组 final logsByDate = >{}; for (final log in logs) { final dateKey = _getDateKey(log.timestamp); logsByDate.putIfAbsent(dateKey, () => []).add(log); } // 写入到对应的日期文件 for (final entry in logsByDate.entries) { final dateKey = entry.key; final dateLogs = entry.value; await _writeToDateFile(dateKey, dateLogs); } } catch (e) { print('Failed to write logs to file: $e'); } } /// 写入到指定日期的文件 Future _writeToDateFile(String dateKey, List logs) async { try { final logFile = File('${_logDirectory.path}/log_$dateKey.txt'); // 检查文件大小 if (await logFile.exists()) { final fileSize = await logFile.length(); if (fileSize > _maxLogFileSize) { // 文件过大,截断保留最新内容 await _truncateLogFile(logFile); } } // 写入日志 final logContent = '${logs.map((log) => log.toString()).join('\n')}\n'; await logFile.writeAsString(logContent, mode: FileMode.append); } catch (e) { print('Failed to write to date file: $e'); } } /// 截断日志文件,保留最新内容 Future _truncateLogFile(File logFile) async { try { final content = await logFile.readAsString(); final lines = content.split('\n'); // 保留最后一半的内容 final keepLines = (lines.length / 2).floor(); final newContent = '${lines.sublist(lines.length - keepLines).join('\n')}\n'; await logFile.writeAsString(newContent); } catch (e) { print('Failed to truncate log file: $e'); // 如果截断失败,重新创建文件 await logFile.writeAsString(''); } } /// 获取日期键值 String _getDateKey(DateTime date) { return '${date.year.toString().padLeft(4, '0')}' '${date.month.toString().padLeft(2, '0')}' '${date.day.toString().padLeft(2, '0')}'; } /// 清理过期日志文件 Future _cleanupOldLogs() async { try { final cutoffDate = DateTime.now().subtract( const Duration(days: _retentionDays), ); final cutoffDateKey = _getDateKey(cutoffDate); final files = await _logDirectory.list().toList(); for (final file in files) { if (file is File && file.path.contains('log_')) { final fileName = file.path.split('/').last; final dateStr = fileName .replaceAll('log_', '') .replaceAll('.txt', ''); if (dateStr.compareTo(cutoffDateKey) < 0) { await file.delete(); } } } } catch (e) { print('Failed to cleanup old logs: $e'); } } /// 读取指定日期的日志 Future readLogsForDate(DateTime date) async { if (!_isInitialized) { await init(); } try { final dateKey = _getDateKey(date); final logFile = File('${_logDirectory.path}/log_$dateKey.txt'); if (await logFile.exists()) { return await logFile.readAsString(); } return ''; } catch (e) { print('Failed to read logs for date: $e'); return ''; } } /// 读取最近几天的日志 Future readRecentLogs(int days) async { if (!_isInitialized) { await init(); } try { final allLogs = []; final now = DateTime.now(); for (int i = 0; i < days; i++) { final date = now.subtract(Duration(days: i)); final dateLogs = await readLogsForDate(date); if (dateLogs.isNotEmpty) { allLogs.add( '=== ${date.year}-${date.month.toString().padLeft(2, '0')}-${date.day.toString().padLeft(2, '0')} ===\n$dateLogs', ); } } return allLogs.join('\n'); } catch (e) { print('Failed to read recent logs: $e'); return ''; } } /// 读取所有日志 Future readAllLogs() async { return await readRecentLogs(_retentionDays); } /// 清空所有日志 Future clearAllLogs() async { if (!_isInitialized) { await init(); } try { // 清空队列 _logQueue.clear(); // 删除所有日志文件 final files = await _logDirectory.list().toList(); for (final file in files) { if (file is File && file.path.contains('log_')) { await file.delete(); } } } catch (e) { print('Failed to clear all logs: $e'); } } /// 强制刷新所有待写入的日志 Future forceFlush() async { await _flushLogs(); } /// 销毁日志管理器 Future dispose() async { _batchWriteTimer?.cancel(); await _flushLogs(); _isInitialized = false; } /// 静态日志方法 static void log(String message, String name) { if (Configs.debug) { developer.log(message, name: name); } _instance._addToQueue(message, name); // 添加到简化版开发者工具 IXDeveloperTools.addLog(message, name); } }