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 _loggers = {}; // 日志级别与Logging包级别的映射 static final Map _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 = []; 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'); } }