hi-client/lib/app/utils/kr_init_log_collector.dart
Rust b8d0417d0f
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
update
(cherry picked from commit 52f368c3417efe0e88052bb811c8fc8d8fb7e8b9)
2025-11-02 01:12:40 -08:00

309 lines
9.6 KiB
Dart
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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';
}
}
}