基础库
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

363 lines
8.9 KiB

  1. library let_log;
  2. import 'dart:convert';
  3. import 'package:flutter/material.dart';
  4. import 'package:flutter/services.dart';
  5. import 'dart:developer';
  6. part 'log_widget.dart';
  7. part 'net_widget.dart';
  8. enum _Type { log, debug, warn, error }
  9. List<String> _printNames = ["😄", "🐛", "❗", "❌", "⬆️", "⬇️"];
  10. List<String> _tabNames = ["[Log]", "[Debug]", "[Warn]", "[Error]"];
  11. final RegExp _tabReg = RegExp(r"\[|\]");
  12. String _getTabName(int index) {
  13. return _tabNames[index].replaceAll(_tabReg, "");
  14. }
  15. class _Config {
  16. /// Whether to display the log in reverse order
  17. bool reverse = false;
  18. /// Whether or not to print logs in the ide
  19. bool printNet = true;
  20. /// Whether or not to print net logs in the ide
  21. bool printLog = true;
  22. /// Maximum number of logs, larger than this number, will be cleaned up, default value 500
  23. int maxLimit = 500;
  24. /// Set the names in ide print.
  25. void setPrintNames({
  26. String log,
  27. String debug,
  28. String warn,
  29. String error,
  30. String request,
  31. String response,
  32. }) {
  33. _printNames = [
  34. log ?? "[Log]",
  35. debug ?? "[Debug]",
  36. warn ?? "[Warn]",
  37. error ?? "[Error]",
  38. request ?? "[Req]",
  39. response ?? "[Res]",
  40. ];
  41. }
  42. /// Set the names in the app.
  43. void setTabNames({
  44. String log,
  45. String debug,
  46. String warn,
  47. String error,
  48. String request,
  49. String response,
  50. }) {
  51. _tabNames = [
  52. log ?? "[Log]",
  53. debug ?? "[Debug]",
  54. warn ?? "[Warn]",
  55. error ?? "[Error]",
  56. ];
  57. }
  58. }
  59. class Logger extends StatelessWidget {
  60. @override
  61. Widget build(BuildContext context) {
  62. return DefaultTabController(
  63. length: 2,
  64. child: Scaffold(
  65. appBar: AppBar(
  66. title: const TabBar(
  67. tabs: [
  68. Tab(child: Text("Log")),
  69. Tab(child: Text("Net")),
  70. ],
  71. ),
  72. elevation: 0,
  73. ),
  74. body: const TabBarView(
  75. children: [
  76. LogWidget(),
  77. NetWidget(),
  78. ],
  79. ),
  80. ),
  81. );
  82. }
  83. static bool enabled = true;
  84. static _Config config = _Config();
  85. /// Logging
  86. static void log(Object message, [Object detail]) {
  87. if (enabled) _Log.add(_Type.log, message, detail);
  88. }
  89. /// Record debug information
  90. static void debug(Object message, [Object detail]) {
  91. if (enabled) _Log.add(_Type.debug, message, detail);
  92. }
  93. /// Record warnning information
  94. static void warn(Object message, [Object detail]) {
  95. if (enabled) _Log.add(_Type.warn, message, detail);
  96. }
  97. /// Record error information
  98. static void error(Object message, [Object detail]) {
  99. if (enabled) _Log.add(_Type.error, message, detail);
  100. }
  101. /// Start recording time
  102. static void time(Object key) {
  103. assert(key != null);
  104. if (enabled) _Log.time(key);
  105. }
  106. /// End of record time
  107. static void endTime(Object key) {
  108. assert(key != null);
  109. if (enabled) _Log.endTime(key);
  110. }
  111. /// Clearance log
  112. static void clear() {
  113. _Log.clear();
  114. }
  115. /// Recording network information
  116. static void net(String api,
  117. {String type = "接口", int status = 100, Object data}) {
  118. assert(api != null);
  119. if (enabled) _Net.request(api, type, status, data);
  120. }
  121. /// End of record network information, with statistics on duration and size.
  122. static void endNet(String api,
  123. {int status = 200, Object data, Object headers, String type}) {
  124. assert(api != null);
  125. if (enabled) _Net.response(api, status, data, headers, type);
  126. }
  127. }
  128. class _Log {
  129. static final List<_Log> list = [];
  130. static final ValueNotifier<int> length = ValueNotifier(0);
  131. static final Map<Object, Object> _map = {};
  132. final _Type type;
  133. final String message;
  134. final String detail;
  135. final DateTime start;
  136. const _Log({this.type, this.message, this.detail, this.start});
  137. String get typeName {
  138. return _printNames[type.index];
  139. }
  140. String get tabName {
  141. return _tabNames[type.index];
  142. }
  143. bool contains(String keyword) {
  144. if (keyword.isEmpty) return true;
  145. return message != null && message.contains(keyword) ||
  146. detail != null && detail.contains(keyword);
  147. }
  148. @override
  149. String toString() {
  150. final StringBuffer sb = StringBuffer();
  151. sb.writeln("Message: $message");
  152. sb.writeln("Time: $start");
  153. if (detail != null && detail.length > 100) {
  154. sb.writeln("Detail: ");
  155. sb.writeln(detail);
  156. } else {
  157. sb.writeln("Detail: $detail");
  158. }
  159. return sb.toString();
  160. }
  161. static void add(_Type type, Object value, Object detail) {
  162. final logUtil = _Log(
  163. type: type,
  164. message: value?.toString(),
  165. detail: detail?.toString(),
  166. start: DateTime.now(),
  167. );
  168. list.add(logUtil);
  169. _clearWhenTooMuch();
  170. length.value++;
  171. if (Logger.config.printLog) {
  172. // debugPrint(
  173. // "${logUtil.typeName} ${logUtil.message}${logUtil.detail == null ? '' : '\n${logUtil.detail}'}\n--------------------------------");
  174. log("\n${logUtil.typeName} ${logUtil.message}${logUtil.detail == null ? '' : '\n${logUtil.detail}'}\n--------------------------------");
  175. }
  176. }
  177. static void _clearWhenTooMuch() {
  178. if (list.length > Logger.config.maxLimit) {
  179. list.removeRange(0, (Logger.config.maxLimit * 0.2).ceil());
  180. }
  181. }
  182. static void time(Object key) {
  183. _map[key] = DateTime.now();
  184. }
  185. static void endTime(Object key) {
  186. final data = _map[key];
  187. if (data != null) {
  188. _map.remove(key);
  189. final spend = DateTime.now().difference(data).inMilliseconds;
  190. _Log.add(_Type.log, '$key: $spend ms', null);
  191. }
  192. }
  193. static void clear() {
  194. list.clear();
  195. _map.clear();
  196. length.value = 0;
  197. }
  198. }
  199. class _Net extends ChangeNotifier {
  200. static const all = "All";
  201. static final List<_Net> list = [];
  202. static final ValueNotifier<int> length = ValueNotifier(0);
  203. static final Map<String, _Net> _map = {};
  204. static final List<String> types = [all];
  205. static final ValueNotifier<int> typeLength = ValueNotifier(1);
  206. final String api;
  207. final String req;
  208. final DateTime start;
  209. String type;
  210. int status = 100;
  211. int spend = 0;
  212. String res;
  213. String headers;
  214. bool showDetail = false;
  215. int _reqSize = -1;
  216. int _resSize = -1;
  217. _Net({
  218. this.api,
  219. this.type,
  220. this.req,
  221. this.headers,
  222. this.start,
  223. this.res,
  224. this.spend = 0,
  225. this.status = 100,
  226. });
  227. int getReqSize() {
  228. if (_reqSize > -1) return _reqSize;
  229. if (req != null && req.isNotEmpty) {
  230. try {
  231. return _reqSize = utf8.encode(req).length;
  232. } catch (e) {
  233. // print(e);
  234. }
  235. }
  236. return 0;
  237. }
  238. int getResSize() {
  239. if (_resSize > -1) return _resSize;
  240. if (res != null && res.isNotEmpty) {
  241. try {
  242. return _resSize = utf8.encode(res).length;
  243. } catch (e) {
  244. // print(e);
  245. }
  246. }
  247. return 0;
  248. }
  249. bool contains(String keyword) {
  250. if (keyword.isEmpty) return true;
  251. return api.contains(keyword) ||
  252. req != null && req.contains(keyword) ||
  253. res != null && res.contains(keyword);
  254. }
  255. @override
  256. String toString() {
  257. final StringBuffer sb = StringBuffer();
  258. sb.writeln("[$status] $api");
  259. sb.writeln("Start: $start");
  260. sb.writeln("Spend: $spend ms");
  261. sb.writeln("Headers: $headers");
  262. sb.writeln("Request: $req");
  263. sb.writeln("Response: $res");
  264. return sb.toString();
  265. }
  266. static void request(String api, String type, int status, Object data) {
  267. final net = _Net(
  268. api: api,
  269. type: type,
  270. status: status,
  271. req: data?.toString(),
  272. start: DateTime.now(),
  273. );
  274. list.add(net);
  275. _map[api] = net;
  276. if (type != null && type != "" && !types.contains(type)) {
  277. types.add(type);
  278. typeLength.value++;
  279. }
  280. _clearWhenTooMuch();
  281. length.value++;
  282. if (Logger.config.printNet) {
  283. log("\n${_printNames[4]} ${type == null ? '' : '$type: '}${net.api}${net.req == null ? '' : '\n${_printNames[0]} 请求数据: ${net.req}'}\n--------------------------------");
  284. }
  285. }
  286. static void _clearWhenTooMuch() {
  287. if (list.length > Logger.config.maxLimit) {
  288. list.removeRange(0, (Logger.config.maxLimit * 0.2).ceil());
  289. }
  290. }
  291. static void response(
  292. String api, int status, Object data, Object headers, String type) {
  293. _Net net = _map[api];
  294. if (net != null) {
  295. _map.remove(net);
  296. net.spend = DateTime.now().difference(net.start).inMilliseconds;
  297. net.status = status;
  298. net.headers = headers?.toString();
  299. net.res = data?.toString();
  300. length.notifyListeners();
  301. } else {
  302. net = _Net(api: api, start: DateTime.now(), type: type);
  303. net.status = status;
  304. net.headers = headers?.toString();
  305. net.res = data?.toString();
  306. list.add(net);
  307. _clearWhenTooMuch();
  308. length.value++;
  309. }
  310. if (Logger.config.printNet) {
  311. log("\n${_printNames[5]} ${net.type == null ? '' : '${net.type}: '}${net.api}${net.res == null ? '' : '\n${_printNames[0]} 响应数据: ${net.res}'}\nSpend: ${net.spend} ms\n--------------------------------");
  312. }
  313. }
  314. static void clear() {
  315. list.clear();
  316. _map.clear();
  317. length.value = 0;
  318. }
  319. }