| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313 |
- 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<LogEntry> _logQueue = Queue<LogEntry>();
- Timer? _batchWriteTimer;
- bool _isWriting = false;
- /// 初始化日志管理器
- Future<void> 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<void> _flushLogs() async {
- if (_isWriting || _logQueue.isEmpty) return;
- _isWriting = true;
- try {
- final logsToWrite = <LogEntry>[];
- // 取出要写入的日志条目
- 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<void> _writeLogsToFile(List<LogEntry> logs) async {
- try {
- // 按日期分组
- final logsByDate = <String, List<LogEntry>>{};
- 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<void> _writeToDateFile(String dateKey, List<LogEntry> 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<void> _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<void> _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<String> 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<String> readRecentLogs(int days) async {
- if (!_isInitialized) {
- await init();
- }
- try {
- final allLogs = <String>[];
- 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<String> readAllLogs() async {
- return await readRecentLogs(_retentionDays);
- }
- /// 清空所有日志
- Future<void> 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<void> forceFlush() async {
- await _flushLogs();
- }
- /// 销毁日志管理器
- Future<void> 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);
- }
- }
|