update
Build Android APK / 编译 libcore.aar (push) Has been cancelled
Build Android APK / 编译 Android APK (release) (push) Has been cancelled
Build Android APK / 创建 GitHub Release (push) Has been cancelled
Build Multi-Platform / 编译 libcore (iOS/tvOS) (push) Has been cancelled
Build Multi-Platform / 编译 libcore (Android) (push) Has been cancelled
Build Multi-Platform / 编译 libcore (Windows) (push) Has been cancelled
Build Multi-Platform / 编译 libcore (macOS) (push) Has been cancelled
Build Multi-Platform / 编译 libcore (Linux) (push) Has been cancelled
Build Multi-Platform / 构建 Android APK (push) Has been cancelled
Build Multi-Platform / 构建 Windows (push) Has been cancelled
Build Multi-Platform / 构建 macOS (push) Has been cancelled
Build Multi-Platform / 构建 Linux (push) Has been cancelled
Build Multi-Platform / 构建 iOS (push) Has been cancelled
Build Multi-Platform / 创建 Release (push) Has been cancelled
Build Windows / 编译 libcore (Windows) (push) Has been cancelled
Build Windows / build (push) Has been cancelled

(cherry picked from commit 52f368c3417efe0e88052bb811c8fc8d8fb7e8b9)
This commit is contained in:
2025-11-02 17:10:52 +08:00
committed by speakeloudest
parent f1e8e7f530
commit b8d0417d0f
+308
View File
@@ -0,0 +1,308 @@
import 'dart:io';
import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:path_provider/path_provider.dart';
import 'package:intl/intl.dart';
import '../common/app_config.dart';
/// 初始化日志收集器
/// 用于收集应用启动和初始化过程中的所有日志,方便问题诊断
///
/// 使用说明:
/// 1. 通过 AppConfig.enableInitLogCollection 全局开关控制是否收集日志
/// 2. 日志文件保存在:{应用文档目录}/init_logs/init_log_yyyyMMdd_HHmmss.txt
/// 3. 自动保留最近5个日志文件,旧文件会被自动清理
class KRInitLogCollector {
static final KRInitLogCollector _instance = KRInitLogCollector._internal();
factory KRInitLogCollector() => _instance;
KRInitLogCollector._internal();
/// 日志文件
File? _logFile;
/// 🔧 终极修复:使用 RandomAccessFile 进行真正的同步写入
RandomAccessFile? _logFileHandle;
/// 日志缓冲区(用于在文件创建前暂存日志)
final List<String> _logBuffer = [];
/// 是否已初始化
bool _isInitialized = false;
/// 初始化开始时间
DateTime? _initStartTime;
/// 日志文件路径
String? _logFilePath;
/// 🔧 关键:检查是否启用日志收集
bool get _isEnabled => AppConfig.enableInitLogCollection;
/// 初始化日志收集器
Future<void> initialize() async {
// 🔧 检查全局开关
if (!_isEnabled) {
if (kDebugMode) {
print('📝 初始化日志收集已关闭(AppConfig.enableInitLogCollection = false');
}
return;
}
if (_isInitialized) return;
try {
_initStartTime = DateTime.now();
// 获取应用文档目录
final directory = await getApplicationDocumentsDirectory();
final logDir = Directory('${directory.path}/init_logs');
// 创建日志目录
if (!await logDir.exists()) {
await logDir.create(recursive: true);
}
// 创建日志文件(使用时间戳命名)
final timestamp = DateFormat('yyyyMMdd_HHmmss').format(_initStartTime!);
_logFile = File('${logDir.path}/init_log_$timestamp.txt');
_logFilePath = _logFile!.path;
// 🔧 终极修复:使用 RandomAccessFile 进行真正的同步写入
_logFileHandle = await _logFile!.open(mode: FileMode.write);
// 写入日志头部
_writeHeader();
// 将缓冲区中的日志写入文件
if (_logBuffer.isNotEmpty) {
for (var log in _logBuffer) {
_writeToFile(log);
}
_logBuffer.clear();
}
_isInitialized = true;
// 打印日志文件路径到控制台,方便查看
if (kDebugMode) {
print('📝 初始化日志文件已创建: $_logFilePath');
}
// 清理旧日志文件(保留最近5个)
await _cleanOldLogs(logDir);
} catch (e) {
if (kDebugMode) {
print('❌ 初始化日志收集器失败: $e');
}
}
}
/// 写入日志头部信息
void _writeHeader() {
final header = '''
═══════════════════════════════════════════════════════════
应用初始化日志
═══════════════════════════════════════════════════════════
开始时间: ${DateFormat('yyyy-MM-dd HH:mm:ss').format(_initStartTime!)}
设备信息: ${Platform.operatingSystem} ${Platform.operatingSystemVersion}
Flutter版本: ${Platform.version}
═══════════════════════════════════════════════════════════
''';
_writeToFile(header);
}
/// 记录日志
void log(String message, {String tag = 'INIT'}) {
// 🔧 检查全局开关
if (!_isEnabled) return;
final timestamp = DateTime.now();
final elapsed = _initStartTime != null
? timestamp.difference(_initStartTime!).inMilliseconds
: 0;
final logLine = '[${DateFormat('HH:mm:ss.SSS').format(timestamp)}] '
'[+${elapsed}ms] '
'[$tag] '
'$message';
// 同时输出到控制台(DEBUG模式)
if (kDebugMode) {
print(logLine);
}
// 🔧 终极修复:检查 RandomAccessFile 是否可用
if (_isInitialized && _logFileHandle != null) {
_writeToFile('$logLine\n');
} else {
// 文件未创建前,暂存到缓冲区
_logBuffer.add('$logLine\n');
}
}
/// 记录分隔线
void logSeparator() {
log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━', tag: '');
}
/// 记录错误
void logError(String message, {String tag = 'ERROR', Object? error, StackTrace? stackTrace}) {
log('$message', tag: tag);
if (error != null) {
log(' 错误详情: $error', tag: tag);
}
if (stackTrace != null) {
log(' 堆栈跟踪: $stackTrace', tag: tag);
}
}
/// 记录警告
void logWarning(String message, {String tag = 'WARN'}) {
log('⚠️ $message', tag: tag);
}
/// 记录成功
void logSuccess(String message, {String tag = 'SUCCESS'}) {
log('$message', tag: tag);
}
/// 记录阶段开始
void logPhaseStart(String phase) {
logSeparator();
log('🎬 开始阶段: $phase', tag: 'PHASE');
logSeparator();
}
/// 记录阶段完成
void logPhaseEnd(String phase, {bool success = true}) {
final icon = success ? '' : '';
log('$icon 完成阶段: $phase', tag: 'PHASE');
logSeparator();
}
/// 🔧 终极修复:使用 RandomAccessFile 进行真正的同步写入
void _writeToFile(String content) {
try {
if (_logFileHandle != null) {
// 使用 writeStringSync 进行同步写入
_logFileHandle!.writeStringSync(content);
// 立即刷新到磁盘,确保数据持久化
_logFileHandle!.flushSync();
}
} catch (e, stackTrace) {
if (kDebugMode) {
print('❌ 写入日志文件失败: $e');
print('📚 堆栈跟踪: $stackTrace');
}
// 尝试输出到控制台作为备份
if (kDebugMode) {
print('[日志备份] $content');
}
}
}
/// 清理旧日志文件(保留最近N个)
Future<void> _cleanOldLogs(Directory logDir, {int keepCount = 5}) async {
try {
final files = logDir.listSync()
.whereType<File>()
.where((f) => f.path.contains('init_log_'))
.toList();
if (files.length <= keepCount) return;
// 按修改时间排序
files.sort((a, b) => b.lastModifiedSync().compareTo(a.lastModifiedSync()));
// 删除多余的旧文件
for (var i = keepCount; i < files.length; i++) {
await files[i].delete();
if (kDebugMode) {
print('🧹 已删除旧日志文件: ${files[i].path}');
}
}
} catch (e) {
if (kDebugMode) {
print('❌ 清理旧日志文件失败: $e');
}
}
}
/// 记录完成并写入汇总信息
Future<void> finalize() async {
if (!_isInitialized || _logFileHandle == null) return;
try {
final endTime = DateTime.now();
final totalDuration = endTime.difference(_initStartTime!);
final footer = '''
═══════════════════════════════════════════════════════════
初始化完成
═══════════════════════════════════════════════════════════
结束时间: ${DateFormat('yyyy-MM-dd HH:mm:ss').format(endTime)}
总耗时: ${totalDuration.inMilliseconds}ms (${totalDuration.inSeconds}秒)
═══════════════════════════════════════════════════════════
''';
_writeToFile(footer);
// 🔧 终极修复:使用同步方法关闭文件,确保所有数据都写入
_logFileHandle!.flushSync();
await _logFileHandle!.close();
_logFileHandle = null;
if (kDebugMode) {
print('📝 初始化日志已完成: $_logFilePath');
print('📊 总耗时: ${totalDuration.inMilliseconds}ms');
}
} catch (e, stackTrace) {
if (kDebugMode) {
print('❌ 完成日志记录失败: $e');
print('📚 堆栈跟踪: $stackTrace');
}
}
}
/// 获取日志文件路径(用于分享给用户)
String? getLogFilePath() => _logFilePath;
/// 获取所有日志文件列表
Future<List<File>> getAllLogFiles() async {
try {
final directory = await getApplicationDocumentsDirectory();
final logDir = Directory('${directory.path}/init_logs');
if (!await logDir.exists()) return [];
return logDir.listSync()
.whereType<File>()
.where((f) => f.path.contains('init_log_'))
.toList()
..sort((a, b) => b.lastModifiedSync().compareTo(a.lastModifiedSync()));
} catch (e) {
if (kDebugMode) {
print('❌ 获取日志文件列表失败: $e');
}
return [];
}
}
/// 获取最新的日志文件
Future<File?> getLatestLogFile() async {
final files = await getAllLogFiles();
return files.isNotEmpty ? files.first : null;
}
/// 读取日志文件内容
Future<String> readLogFile(File file) async {
try {
return await file.readAsString();
} catch (e) {
return '读取日志文件失败: $e';
}
}
}