Files
xianyan/lib/core/utils/logger.dart
Developer 7564e8893d chore: 完成多平台适配与代码优化
此提交包含多项变更:
1. 新增鸿蒙平台支持,完善设备检测与数据库适配
2. 替换旧版分享插件API为SharePlus
3. 批量迁移StateNotifier到Notifier以适配新版Riverpod
4. 修复zip编码判断、图表API参数等bug
5. 更新应用图标、启动页资源与多尺寸适配图标
6. 调整Android最小SDK版本与应用名称
7. 优化日志打印与正则表达式使用
8. 修正编辑器画布样式初始化与配置逻辑
9. 更新依赖与CI插件配置
2026-05-17 07:17:07 +08:00

199 lines
5.7 KiB
Dart

/// ============================================================
/// 闲言APP — 日志工具
/// 创建时间: 2026-04-20
/// 更新时间: 2026-05-07
/// 作用: 统一日志封装,支持分级与格式化 + 日志查看器 + 日志导出
/// 上次更新: 新增日志导出文件功能 + 按级别过滤
/// ============================================================
import 'dart:convert';
import 'dart:io';
import 'package:logger/logger.dart';
import 'package:path_provider/path_provider.dart';
import 'package:share_plus/share_plus.dart';
/// 全局日志器
final appLogger = Logger(printer: PrettyPrinter(), level: Level.debug);
/// 日志级别
enum LogLevel {
all('全部', 0),
verbose('详细', 1),
debug('调试', 2),
info('信息', 3),
warning('警告', 4),
error('错误', 5);
const LogLevel(this.label, this.value);
final String label;
final int value;
}
/// 日志条目
class LogEntry {
const LogEntry({
required this.level,
required this.message,
required this.time,
this.error,
this.stackTrace,
});
final LogLevel level;
final String message;
final DateTime time;
final dynamic error;
final StackTrace? stackTrace;
}
/// 日志工具 — 静态便捷方法 + 内存缓冲
class Log {
Log._();
static const _maxEntries = 500;
static final List<LogEntry> _entries = [];
/// 获取所有日志条目
static List<LogEntry> get entries => List.unmodifiable(_entries);
/// 日志条目数量
static int get entryCount => _entries.length;
/// 清空日志
static void clearEntries() => _entries.clear();
static void _addEntry(
LogLevel level,
dynamic message, [
dynamic error,
StackTrace? stackTrace,
]) {
_entries.add(
LogEntry(
level: level,
message: message?.toString() ?? '',
time: DateTime.now(),
error: error,
stackTrace: stackTrace,
),
);
if (_entries.length > _maxEntries) {
_entries.removeRange(0, _entries.length - _maxEntries);
}
}
/// 调试日志
static void d(dynamic message, [dynamic error, StackTrace? stackTrace]) {
appLogger.d(message, error: error, stackTrace: stackTrace);
_addEntry(LogLevel.debug, message, error, stackTrace);
}
/// 信息日志
static void i(dynamic message, [dynamic error, StackTrace? stackTrace]) {
appLogger.i(message, error: error, stackTrace: stackTrace);
_addEntry(LogLevel.info, message, error, stackTrace);
}
/// 警告日志
static void w(dynamic message, [dynamic error, StackTrace? stackTrace]) {
appLogger.w(message, error: error, stackTrace: stackTrace);
_addEntry(LogLevel.warning, message, error, stackTrace);
}
/// 错误日志
static void e(dynamic message, [dynamic error, StackTrace? stackTrace]) {
appLogger.e(message, error: error, stackTrace: stackTrace);
_addEntry(LogLevel.error, message, error, stackTrace);
}
/// 致命错误日志
static void f(dynamic message, [dynamic error, StackTrace? stackTrace]) {
appLogger.f(message, error: error, stackTrace: stackTrace);
_addEntry(LogLevel.error, message, error, stackTrace);
}
/// 按级别过滤日志
static List<LogEntry> filterByLevel(LogLevel level) {
if (level == LogLevel.all) return entries;
return _entries.where((e) => e.level.value >= level.value).toList();
}
/// 导出日志为 JSON 字符串
static String exportToJson({LogLevel level = LogLevel.all}) {
final filtered = filterByLevel(level);
final data = filtered
.map(
(e) => {
'time': e.time.toIso8601String(),
'level': e.level.label,
'message': e.message,
if (e.error != null) 'error': e.error.toString(),
if (e.stackTrace != null) 'stackTrace': e.stackTrace.toString(),
},
)
.toList();
return const JsonEncoder.withIndent(' ').convert({
'export_time': DateTime.now().toIso8601String(),
'app_name': '闲言',
'log_count': data.length,
'filter_level': level.label,
'entries': data,
});
}
/// 导出日志为纯文本
static String exportToText({LogLevel level = LogLevel.all}) {
final filtered = filterByLevel(level);
final buffer = StringBuffer();
buffer.writeln('闲言APP 日志导出');
buffer.writeln('导出时间: ${DateTime.now().toIso8601String()}');
buffer.writeln('过滤级别: ${level.label}');
buffer.writeln('条目数量: ${filtered.length}');
buffer.writeln('=' * 60);
for (final e in filtered) {
buffer.writeln(
'[${e.time.toString().substring(0, 19)}] [${e.level.label}] ${e.message}',
);
if (e.error != null) buffer.writeln(' Error: ${e.error}');
if (e.stackTrace != null) buffer.writeln(' Stack: ${e.stackTrace}');
}
return buffer.toString();
}
/// 导出日志到文件
static Future<String> exportToFile({
LogLevel level = LogLevel.all,
bool asJson = true,
}) async {
try {
final content = asJson
? exportToJson(level: level)
: exportToText(level: level);
final ext = asJson ? 'json' : 'txt';
final dir = await getTemporaryDirectory();
final timestamp = DateTime.now().millisecondsSinceEpoch;
final file = File('${dir.path}/xianyan_logs_$timestamp.$ext');
await file.writeAsString(content);
return file.path;
} catch (e) {
appLogger.e('日志导出失败', error: e);
rethrow;
}
}
/// 分享日志文件
static Future<void> shareLogs({LogLevel level = LogLevel.all}) async {
try {
final path = await exportToFile(level: level);
await SharePlus.instance.share(ShareParams(files: [XFile(path)], text: '闲言APP 日志文件'));
} catch (e) {
appLogger.e('日志分享失败', error: e);
}
}
}