马良AI写作初始化仓库
This commit is contained in:
218
AINoval/lib/utils/logger.dart
Normal file
218
AINoval/lib/utils/logger.dart
Normal file
@@ -0,0 +1,218 @@
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
|
||||
/// 日志级别
|
||||
enum LogLevel {
|
||||
verbose, // 详细信息
|
||||
debug, // 调试信息
|
||||
info, // 普通信息
|
||||
warning, // 警告信息
|
||||
error, // 错误信息
|
||||
wtf // 严重错误
|
||||
}
|
||||
|
||||
/// 应用程序日志管理类
|
||||
class AppLogger {
|
||||
static bool _initialized = false;
|
||||
static final Map<String, Logger> _loggers = {};
|
||||
|
||||
// 日志级别与Logging包级别的映射
|
||||
static final Map<LogLevel, Level> _levelMap = {
|
||||
LogLevel.verbose: Level.FINEST,
|
||||
LogLevel.debug: Level.FINE,
|
||||
LogLevel.info: Level.INFO,
|
||||
LogLevel.warning: Level.WARNING,
|
||||
LogLevel.error: Level.SEVERE,
|
||||
LogLevel.wtf: Level.SHOUT,
|
||||
};
|
||||
|
||||
/// 初始化日志系统
|
||||
static void init() {
|
||||
if (_initialized) return;
|
||||
|
||||
hierarchicalLoggingEnabled = true;
|
||||
|
||||
// 在调试模式下显示所有日志,在生产模式下只显示INFO级别以上
|
||||
Logger.root.level = kDebugMode ? Level.ALL : Level.INFO;
|
||||
|
||||
// 配置日志监听器
|
||||
Logger.root.onRecord.listen((record) {
|
||||
// 不在生产环境打印Verbose和Debug日志,即使 Root Level 允许
|
||||
if (!kDebugMode &&
|
||||
(record.level == Level.FINEST ||
|
||||
record.level == Level.FINER ||
|
||||
record.level == Level.FINE)) {
|
||||
return;
|
||||
}
|
||||
|
||||
final lvlColor = _getLogLevelColor(record.level);
|
||||
const resetColor = '\x1B[0m'; // ANSI 重置颜色代码
|
||||
final emoji = _getLogEmoji(record.level);
|
||||
final timestamp = DateTime.now().toString().substring(0, 19);
|
||||
// 格式: 时间戳 [级别] [模块名] Emoji 日志内容
|
||||
final messageHeader =
|
||||
'$lvlColor$timestamp [${record.level.name}] [${record.loggerName}] $emoji $resetColor';
|
||||
final messageBody = '$lvlColor${record.message}$resetColor';
|
||||
|
||||
final String logMessage;
|
||||
|
||||
if (record.error != null) {
|
||||
// 添加错误详情和格式化的堆栈信息
|
||||
final errorString = '$lvlColor错误: ${record.error}$resetColor';
|
||||
// StackTrace 过滤:只显示应用相关的堆栈,限制行数
|
||||
final stackTraceString = _formatStackTrace(record.stackTrace,
|
||||
filterAppCode: true, maxLines: 15);
|
||||
logMessage =
|
||||
'$messageHeader $messageBody\n$errorString${stackTraceString.isNotEmpty ? '\n$lvlColor堆栈:$resetColor\n$stackTraceString' : ''}';
|
||||
} else {
|
||||
logMessage = '$messageHeader $messageBody';
|
||||
}
|
||||
|
||||
// 使用 print 输出,以便颜色代码生效
|
||||
// 在 release 版本中,由于 Logger.root.level 的限制,低于 INFO 的日志不会走到这里
|
||||
print(logMessage);
|
||||
});
|
||||
|
||||
_initialized = true;
|
||||
}
|
||||
|
||||
/// 获取指定模块的日志记录器
|
||||
static Logger getLogger(String name) {
|
||||
if (!_initialized) init();
|
||||
|
||||
return _loggers.putIfAbsent(name, () {
|
||||
final logger = Logger(name);
|
||||
logger.level = Logger.root.level;
|
||||
return logger;
|
||||
});
|
||||
}
|
||||
|
||||
/// 记录详细日志
|
||||
static void v(String tag, String message,
|
||||
[Object? error, StackTrace? stackTrace]) {
|
||||
_log(tag, LogLevel.verbose, message, error, stackTrace);
|
||||
}
|
||||
|
||||
/// 记录调试日志
|
||||
static void d(String tag, String message,
|
||||
[Object? error, StackTrace? stackTrace]) {
|
||||
_log(tag, LogLevel.debug, message, error, stackTrace);
|
||||
}
|
||||
|
||||
/// 记录信息日志
|
||||
static void i(String tag, String message,
|
||||
[Object? error, StackTrace? stackTrace]) {
|
||||
_log(tag, LogLevel.info, message, error, stackTrace);
|
||||
}
|
||||
|
||||
/// 记录警告日志
|
||||
static void w(String tag, String message,
|
||||
[Object? error, StackTrace? stackTrace]) {
|
||||
_log(tag, LogLevel.warning, message, error, stackTrace);
|
||||
}
|
||||
|
||||
/// 记录错误日志
|
||||
static void e(String tag, String message,
|
||||
[Object? error, StackTrace? stackTrace]) {
|
||||
_log(tag, LogLevel.error, message, error, stackTrace);
|
||||
}
|
||||
|
||||
/// 记录严重错误日志
|
||||
static void wtf(String tag, String message,
|
||||
[Object? error, StackTrace? stackTrace]) {
|
||||
_log(tag, LogLevel.wtf, message, error, stackTrace);
|
||||
}
|
||||
|
||||
// 为了向后兼容,添加简化的方法名
|
||||
/// 记录信息日志(简化版)
|
||||
static void info(String tag, String message,
|
||||
[Object? error, StackTrace? stackTrace]) {
|
||||
_log(tag, LogLevel.info, message, error, stackTrace);
|
||||
}
|
||||
|
||||
/// 记录错误日志(简化版)
|
||||
static void error(String tag, String message,
|
||||
[Object? error, StackTrace? stackTrace]) {
|
||||
_log(tag, LogLevel.error, message, error, stackTrace);
|
||||
}
|
||||
|
||||
/// 内部日志记录方法
|
||||
static void _log(String tag, LogLevel level, String message,
|
||||
[Object? error, StackTrace? stackTrace]) {
|
||||
final logger = getLogger(tag);
|
||||
final logLevel = _levelMap[level]!;
|
||||
|
||||
logger.log(logLevel, message, error, stackTrace);
|
||||
}
|
||||
|
||||
/// 获取日志级别对应的emoji
|
||||
static String _getLogEmoji(Level level) {
|
||||
if (level == Level.FINEST || level == Level.FINER || level == Level.FINE) {
|
||||
return '🔍'; // 调试
|
||||
}
|
||||
if (level == Level.CONFIG || level == Level.INFO) return '📘'; // 信息
|
||||
if (level == Level.WARNING) return '⚠️'; // 警告
|
||||
if (level == Level.SEVERE) return '❌'; // 错误
|
||||
if (level == Level.SHOUT) return '💥'; // 严重错误
|
||||
return '📝'; // 默认
|
||||
}
|
||||
|
||||
/// 获取日志级别对应的ANSI颜色代码
|
||||
static String _getLogLevelColor(Level level) {
|
||||
if (level == Level.FINEST || level == Level.FINER || level == Level.FINE) {
|
||||
return '\x1B[90m'; // 灰色 (Verbose/Debug)
|
||||
}
|
||||
if (level == Level.CONFIG || level == Level.INFO) {
|
||||
return '\x1B[34m'; // 蓝色 (Info/Config)
|
||||
}
|
||||
if (level == Level.WARNING) return '\x1B[33m'; // 黄色 (Warning)
|
||||
if (level == Level.SEVERE) return '\x1B[31m'; // 红色 (Error)
|
||||
if (level == Level.SHOUT) return '\x1B[35;41m'; // 紫色 + 红色背景 (WTF/Shout)
|
||||
return '\x1B[0m'; // 默认 (重置)
|
||||
}
|
||||
|
||||
/// 格式化并过滤堆栈信息
|
||||
static String _formatStackTrace(StackTrace? stackTrace,
|
||||
{int maxLines = 10, bool filterAppCode = true}) {
|
||||
if (stackTrace == null) return '';
|
||||
|
||||
final lines = stackTrace.toString().split('\n');
|
||||
final formattedLines = <String>[];
|
||||
const appPackagePrefix = 'package:ainoval/'; // 修改为你的应用包名
|
||||
const flutterPackagePrefix = 'package:flutter/';
|
||||
const dartPrefix = 'dart:';
|
||||
|
||||
int linesAdded = 0;
|
||||
for (final line in lines) {
|
||||
final trimmedLine = line.trim();
|
||||
if (trimmedLine.isEmpty) continue;
|
||||
|
||||
bool isAppCode = trimmedLine.contains(appPackagePrefix);
|
||||
bool isFrameworkCode = trimmedLine.contains(flutterPackagePrefix) ||
|
||||
trimmedLine.startsWith(dartPrefix);
|
||||
|
||||
// 如果开启过滤,只保留应用代码;否则不过滤
|
||||
// 同时,排除纯dart:前缀和flutter框架内部调用(除非没有应用代码帧时酌情显示)
|
||||
if (!filterAppCode ||
|
||||
isAppCode ||
|
||||
(!isFrameworkCode && !trimmedLine.startsWith('#'))) {
|
||||
// 也包含一些非 package 的项目内部调用格式
|
||||
// 尝试保持可点击的格式
|
||||
// IDE 通常能识别类似 'package:my_app/my_file.dart:123:45' 的格式
|
||||
formattedLines.add(' $trimmedLine'); // 添加缩进
|
||||
linesAdded++;
|
||||
if (linesAdded >= maxLines) break; // 限制最大行数
|
||||
}
|
||||
}
|
||||
|
||||
// 如果过滤后为空(可能错误发生在框架深处),则显示原始堆栈的前几行
|
||||
if (formattedLines.isEmpty && lines.isNotEmpty) {
|
||||
formattedLines.addAll(lines
|
||||
.take(maxLines)
|
||||
.map((l) => ' ${l.trim()}')
|
||||
.where((l) => l.length > 2));
|
||||
}
|
||||
|
||||
return formattedLines.join('\n');
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user