update
Some checks failed
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
Some checks failed
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:
parent
f1e8e7f530
commit
b8d0417d0f
308
lib/app/utils/kr_init_log_collector.dart
Normal file
308
lib/app/utils/kr_init_log_collector.dart
Normal 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';
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user