Flutter学习LogUtil封装与实现实例详解

这篇文章主要为大家介绍了Flutter学习LogUtil封装与实现实例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

一. 为什么要封装打印类

虽然 flutter/原生给我们提供了日志打印的功能,但是超出一定长度以后会被截断

Json打印挤在一起看不清楚

堆栈打印深度过深多打印一些不需要的东西

实现 log 的多种展示方式

二. 需要哪些类

为了可以实现对日志的多种内容格式化和各种显示输出所以抽出来以下几个类

  • 一些常量的字符串表示
  • 对日志内容的打印输出抽象类
  • 对日志内容格式化的抽象类
  • 日志工具的config类
  • 日志工具的管理类
  • 日志工具的Util类

三. 打印输出的抽象类

打印类核心的功能就是打印日志 所以它有一个方法就是打印的方法
而我们要打印输出的内容有 当前 log等级 log的tag 需要打印的数据 当前堆栈信息 亦或是获取的Json数据

/// 日志打印输出的接口类 abstract class IHCLogPrint { void logPrint({ required LogType type, required String tag, required String message, StackTrace? stackTrace, Map? json, }); } 

四. 格式化日志内容

这里定义一个IHCLogFormatter抽象类

///格式化的接口类 abstract class IHCLogFormatter { String format(T data); } 

格式化堆栈

堆栈的格式例如这样

#0 LogUtil._logPrint (package:com.halfcity.full_flutter_app/utils/log/log_util.dart:104:42)
#1 LogUtil._logPrint (package:com.halfcity.full_flutter_app/utils/log/log_util.dart:104:42)
#2 LogUtil._logPrint (package:com.halfcity.full_flutter_app/utils/log/log_util.dart:104:42)
....

会返回来很多无用的数据 而我们实际用到的也不过前五层就可以了
所以需要一个工具来剔除无用的数据和当前自己的包名

堆栈裁切工具类

class StackTraceUtil { ///正则表达式 表示#+数字+空格的格式 static final RegExp _startStr = RegExp(r'#\d+[\s]+'); ///正则表达式表示 多个非换行符+ (非空) 正则表达式中()代表子项 如果需要正则()需要转义\( \) ///了解更多 https://www.runoob.com/regexp/regexp-syntax.html static final RegExp _stackReg = RegExp(r'.+ \(([^\s]+)\)'); /// 把StackTrace 转成list 并去除无用信息 /// [stackTrace] 堆栈信息 ///#0      LogUtil._logPrint (package:com.halfcity.full_flutter_app/utils/log/log_util.dart:104:42) static List _fixStack(StackTrace stackTrace) { List tempList = stackTrace.toString().split("\n"); List stackList = []; for (String str in tempList) { if (str.startsWith(_startStr)) { //又是#号又是空格比较占地方 这里省去了 如果你不想省去直接传入str即可 stackList.add(str.replaceFirst(_startStr, ' ')); } } return stackList; } ///获取剔除忽略包名及其其他无效信息的堆栈 /// [stackTrace] 堆栈 /// [ignorePackage] 需要忽略的包名 static List _getRealStackTrack( StackTrace stackTrace, String ignorePackage) { ///由于Flutter 上的StackTrack上的不太一样,Android返回的是list flutter返回的是StackTrack 所以需要手动切割 再处理 List stackList = _fixStack(stackTrace); int ignoreDepth = 0; int allDepth = stackList.length; //倒着查询 查到倒数第一包名和需要屏蔽的包名一致时,数据往上的数据全部舍弃掉 for (int i = allDepth - 1; i > -1; i--) { Match? match = _stackReg.matchAsPrefix(stackList[i]); //如果匹配且第一个子项也符合  group 0 表示全部 剩下的数字看子项的多少返回 if (match != null && (match.group(1)!.startsWith("package:$ignorePackage"))) { ignoreDepth = i + 1; break; } } stackList = stackList.sublist(ignoreDepth); return stackList; } /// 裁切堆栈 /// [stackTrace] 堆栈 /// [maxDepth] 深度 static List _cropStackTrace(List stackTrace, int? maxDepth) { int realDeep = stackTrace.length; realDeep = maxDepth != null && maxDepth > 0 ? min(maxDepth, realDeep) : realDeep; return stackTrace.sublist(0, realDeep); } ///裁切获取到最终的stack 并获取最大深度的栈信息 static getCroppedRealStackTrace( {required StackTrace stackTrace, ignorePackage, maxDepth}) { return _cropStackTrace( _getRealStackTrack(stackTrace, ignorePackage), maxDepth); } } 

格式化堆栈信息

 class StackFormatter implements ILogFormatter> { @override String format(List stackList) { ///每一行都设置成单独的 字符串 StringBuffer sb = StringBuffer(); ///堆栈是空的直接返回 if (stackList.isEmpty) { return ""; ///堆栈只有一行那么就返回 - 堆栈 } else if (stackList.length == 1) { return "\n\t-${stackList[0].toString()}\n"; ///多行堆栈格式化 } else { for (int i = 0; i 

格式化JSON

class JsonFormatter extends ILogFormatter> { @override String format(Map data) { ///递归调用循环遍历data 在StringBuffer中添加StringBuffer String finalString = _forEachJson(data, 0); finalString = "\ndata:$finalString"; return finalString; } /// [data]  传入需要格式化的数据 /// [spaceCount]  需要添加空格的长度 一个数字是两个空格 /// [needSpace] 需不需要添加空格 /// [needEnter] 需不需要回车 String _forEachJson(dynamic data, int spaceCount, {bool needSpace = true, needEnter = true}) { StringBuffer sb = StringBuffer(); int newSpace = spaceCount + 1; if (data is Map) { ///如果它是Map走这里 ///是否需要空格 sb.write(buildSpace(needSpace ? spaceCount : 0)); sb.write(needEnter ? "{\n" : "{"); data.forEach((key, value) { ///打印输出 key sb.write("${buildSpace(needEnter ? newSpace : 0)}$key: "); ///递归调用看value是什么类型 如果字符长度少于30就不回车显示 sb.write(_forEachJson(value, newSpace, needSpace: false, needEnter: !(value is Map ? false : value.toString().length <50))); ///不是最后一个就加, if (data.keys.last != key) { sb.write(needEnter ? ",\n" : ","); } }); if (needEnter) { sb.writeln(); } sb.write("${buildSpace(needEnter ? spaceCount : 0)}}"); } else if (data is List) { ///如果他是列表 走这里 sb.write(buildSpace(needSpace ? spaceCount : 0)); sb.write("[${needEnter ? "\n" : ""}"); for (var item in data) { sb.write(_forEachJson(item, newSpace, needEnter: !(item.toString().length <30))); ///不是最后一个就加的, if (data.last != item) { sb.write(needEnter ? ",\n" : ","); } } sb.write(needEnter ? "\n" : ""); sb.write("${buildSpace(needSpace?spaceCount:0)}]"); } else if (data is num || data is bool) { ///bool 或者数组不加双引号 sb.write(data); } else if (data is String) { ///string 或者其他的打印加双引号 如果他是回车就改变他 按回车分行会错乱 sb.write("\"${data.replaceAll("\n", r"\n")}\""); } else { sb.write("$data"); } return sb.toString(); } ///构造空格 String buildSpace(int deep) { String temp = ""; for (int i = 0; i 

五. 需要用到的常量

///常量 //log的type enum LogType { V, //VERBOSE E, //ERROR A, //ASSERT W, //WARN I, //INFO D, //DEBUG } int logMaxLength=1024; ///log的type 字符串说明 List logTypeStr = ["VERBOSE", "ERROR", "ASSERT", "WARN", "INFO", "DEBUG"]; ///log的type 数字说明(匹配的Android原生,ios暂不清楚) List logTypeNum = [2, 6, 7, 5, 4, 3]; 

六. 为了控制多个打印器的设置做了一个配置类

class LogConfig { ///是否开启日志 bool _enable = false; ///默认的Tag String _globalTag = "LogTag"; ///堆栈显示的深度 int _stackTraceDepth = 0; ///打印的方式 List? _printers; LogConfig({enable, globalTag, stackTraceDepth, printers}) { _enable = enable; _globalTag = globalTag; _stackTraceDepth = stackTraceDepth; _printers?.addAll(printers); } @override String toString() { return 'LogConfig{_enable: $_enable, _globalTag: $_globalTag, _stackTraceDepth: $_stackTraceDepth, _printers: $_printers}'; } get enable => _enable; get globalTag => _globalTag; get stackTraceDepth => _stackTraceDepth; get printers => _printers; } 

七. Log的管理类

class LogManager { ///config late LogConfig _config; ///打印器列表 List _printers = []; ///单例模式 static LogManager? _instance; factory LogManager() => _instance ??= LogManager._(); LogManager._(); ///初始化 Manager方法 LogManager.init({config, printers}) { _config = config; _printers.addAll(printers); _instance = this; } get printers => _printers; get config => _config; void addPrinter(ILogPrint print) { bool isHave = _printers.any((element) => element == print); if (!isHave) { _printers.add(print); } } void removePrinter(ILogPrint print) { _printers.remove(print); } } 

九. 调用LogUtil

class LogUtil { static const String _ignorePackageName = "log_demo/utils/log"; static void V( {String? tag, dynamic? message, LogConfig? logConfig, StackTrace? stackTrace, Map? json}) { _logPrint( type: LogType.V, tag: tag ??= "", logConfig: logConfig, message: message, json: json, stackTrace: stackTrace); } static void E( {String? tag, dynamic? message, LogConfig? logConfig, StackTrace? stackTrace, Map? json}) { _logPrint( type: LogType.E, tag: tag ??= "", message: message, logConfig: logConfig, json: json, stackTrace: stackTrace); } static void I( {String? tag, dynamic? message, LogConfig? logConfig, StackTrace? stackTrace, Map? json}) { _logPrint( type: LogType.I, tag: tag ??= "", message: message, json: json, stackTrace: stackTrace); } static void D( {String? tag, dynamic? message, LogConfig? logConfig, StackTrace? stackTrace, Map? json}) { _logPrint( type: LogType.D, tag: tag ??= "", logConfig: logConfig, message: message, json: json, stackTrace: stackTrace); } static void A( {String? tag, LogConfig? logConfig, dynamic? message, StackTrace? stackTrace, Map? json}) { _logPrint( type: LogType.A, tag: tag ??= "", message: message, logConfig: logConfig, json: json, stackTrace: stackTrace); } static void W( {String? tag, dynamic? message, LogConfig? logConfig, StackTrace? stackTrace, Map? json}) { _logPrint( type: LogType.W, tag: tag ??= "", message: message, logConfig: logConfig, json: json, stackTrace: stackTrace); } static Future _logPrint({ required LogType type, required String tag, LogConfig? logConfig, dynamic message, StackTrace? stackTrace, Map? json, }) async { ///如果logConfig为空那么就用默认的 logConfig ??= LogManager().config; if (!logConfig?.enable) { return; } StringBuffer sb = StringBuffer(); ///打印当前页面 if (message.toString().isNotEmpty) { sb.write(message); } ///如果传入了栈且 要展示的深度大于0 if (stackTrace != null && logConfig?.stackTraceDepth > 0) { sb.writeln(); String stackTraceStr = StackFormatter().format( StackTraceUtil.getCroppedRealStackTrace( stackTrace: stackTrace, ignorePackage: _ignorePackageName, maxDepth: logConfig?.stackTraceDepth)); sb.write(stackTraceStr); } if (json != null) { sb.writeln(); String body = JsonFormatter().format(json); sb.write(body); } ///获取有几个打印器 List prints = logConfig?.printers ?? LogManager().printers; if (prints.isEmpty) { return; } ///遍历打印器 分别打印数据 for (ILogPrint print in prints) { print.logPrint(type: type, tag: tag, message: sb.toString()); } } } 

十. 定义一个Flutter 控制台打印输出的方法

class ConsolePrint extends ILogPrint { @override void logPrint( {required LogType type, required String tag, required String message, StackTrace? stackTrace, Map? json}) { ///如果要开启颜色显示 那么就是1000 ///如果不开启颜色显示 那么就是1023 int _maxCharLength = 1000; //匹配中文字符以及这些中文标点符号 。 ? ! , 、 ; : “ ” ‘ ' ( ) 《 》 〈 〉 【 】 『 』 「 」 ﹃ ﹄ 〔 〕 … — ~ ﹏ ¥ RegExp _chineseRegex = RegExp(r"[\u4e00-\u9fa5|\u3002|\uff1f|\uff01|\uff0c|\u3001|\uff1b|\uff1a|\u201c|\u201d|\u2018|\u2019|\uff08|\uff09|\u300a|\u300b|\u3008|\u3009|\u3010|\u3011|\u300e|\u300f|\u300c|\u300d|\ufe43|\ufe44|\u3014|\u3015|\u2026|\u2014|\uff5e|\ufe4f|\uffe5]"); ///用回车做分割 List strList = message.split("\n"); ///判断每句的长度 如果长度过长做切割 for (String str in strList) { ///获取总长度 int len = 0; ///获取当前长度 int current = 0; ///获取截断点数据 List entry = [0]; ///遍历文字 查看真实长度 for (int i = 0; i = _maxCharLength) { entry.add(i); current = 0; } } ///如果最后一个阶段点不是最后一个字符就添加上 if (entry.last != str.length - 1) { entry.add(str.length); } ///如果所有的长度小于1023 那么打印没有问题 if (len <_maxCharLength) { _logPrint(type, tag, str); } else { ///按照获取的截断点来打印 for (int i = 0; i 

十一. 现在使用前初始化log打印器一次

 Widget build(BuildContext context) { LogManager.init( config: LogConfig(enable: true, globalTag: "TAG", stackTraceDepth: 5), printers: [ConsolePrint()]); 

使用

///打印堆栈 LogUtil.I(tag: "test", stackTrace: StackTrace.current); ///打印json LogUtil.E(tag: "JSON", json: json); ///打印信息 LogUtil.V(tag: "LogText", message: message);

以上就是Flutter学习LogUtil封装与实现实例详解的详细内容,更多关于Flutter LogUtil 封装实现的资料请关注0133技术站其它相关文章!

以上就是Flutter学习LogUtil封装与实现实例详解的详细内容,更多请关注0133技术站其它相关文章!

赞(0) 打赏
未经允许不得转载:0133技术站首页 » 移动