From 32bf834108c380dc5ce83d04ce1a87746d232b4b Mon Sep 17 00:00:00 2001 From: LittleSheep Date: Thu, 27 Feb 2025 22:58:31 +0800 Subject: [PATCH] :sparkles: Logging framework --- assets/translations/en-US.json | 5 ++- assets/translations/zh-CN.json | 5 ++- assets/translations/zh-HK.json | 5 ++- assets/translations/zh-TW.json | 5 ++- lib/logger.dart | 3 ++ lib/main.dart | 12 ++--- lib/router.dart | 6 +++ lib/screens/logging.dart | 82 ++++++++++++++++++++++++++++++++++ lib/screens/settings.dart | 10 +++++ pubspec.lock | 48 ++++++++++++++++++-- pubspec.yaml | 7 ++- 11 files changed, 173 insertions(+), 15 deletions(-) create mode 100644 lib/logger.dart create mode 100644 lib/screens/logging.dart diff --git a/assets/translations/en-US.json b/assets/translations/en-US.json index 894c24a..386f7b0 100644 --- a/assets/translations/en-US.json +++ b/assets/translations/en-US.json @@ -727,5 +727,8 @@ "trayMenuMuteNotification": "Do Not Disturb", "update": "Update", "forceUpdate": "Force Update", - "forceUpdateDescription": "Force to show the application update popup, even the new version is not available." + "forceUpdateDescription": "Force to show the application update popup, even the new version is not available.", + "debugLogging": "Runtime Logs", + "runtimeLogsOpen": "Open Logs", + "runtimeLogsDescription": "Show the runtime logs to help debugging." } diff --git a/assets/translations/zh-CN.json b/assets/translations/zh-CN.json index 34a54ed..e99331a 100644 --- a/assets/translations/zh-CN.json +++ b/assets/translations/zh-CN.json @@ -725,5 +725,8 @@ "trayMenuMuteNotification": "静音通知", "update": "更新", "forceUpdate": "强制更新", - "forceUpdateDescription": "强制更新应用程序,即使有更新的版本可能不可用。" + "forceUpdateDescription": "强制更新应用程序,即使有更新的版本可能不可用。", + "runtimeLogs": "运行时日志", + "runtimeLogsOpen": "打开日志文件", + "runtimeLogsDescription": "显示运行时的日志记录。" } diff --git a/assets/translations/zh-HK.json b/assets/translations/zh-HK.json index 698d583..905094f 100644 --- a/assets/translations/zh-HK.json +++ b/assets/translations/zh-HK.json @@ -725,5 +725,8 @@ "trayMenuMuteNotification": "靜音通知", "update": "更新", "forceUpdate": "強制更新", - "forceUpdateDescription": "強制更新應用程序,即使有更新的版本可能不可用。" + "forceUpdateDescription": "強制更新應用程序,即使有更新的版本可能不可用。", + "runtimeLogs": "運行時日誌", + "runtimeLogsOpen": "打開日誌文件", + "runtimeLogsDescription": "顯示運行時的日誌記錄。" } diff --git a/assets/translations/zh-TW.json b/assets/translations/zh-TW.json index 917b4eb..3804acb 100644 --- a/assets/translations/zh-TW.json +++ b/assets/translations/zh-TW.json @@ -725,5 +725,8 @@ "trayMenuMuteNotification": "靜音通知", "update": "更新", "forceUpdate": "強制更新", - "forceUpdateDescription": "強制更新應用程序,即使有更新的版本可能不可用。" + "forceUpdateDescription": "強制更新應用程序,即使有更新的版本可能不可用。", + "runtimeLogs": "運行時日誌", + "runtimeLogsOpen": "打開日誌文件", + "runtimeLogsDescription": "顯示運行時的日誌記錄。" } diff --git a/lib/logger.dart b/lib/logger.dart new file mode 100644 index 0000000..7c2cad4 --- /dev/null +++ b/lib/logger.dart @@ -0,0 +1,3 @@ +import 'package:talker/talker.dart'; + +final logging = Talker(); diff --git a/lib/main.dart b/lib/main.dart index 0a5467c..3fd49ed 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -20,6 +20,7 @@ import 'package:relative_time/relative_time.dart'; import 'package:responsive_framework/responsive_framework.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:surface/firebase_options.dart'; +import 'package:surface/logger.dart'; import 'package:surface/providers/channel.dart'; import 'package:surface/providers/chat_call.dart'; import 'package:surface/providers/config.dart'; @@ -235,7 +236,7 @@ class _AppSplashScreenState extends State<_AppSplashScreen> with TrayListener { await inAppReview.requestReview(); prefs.setBool('rating_requested', true); } else { - log('Unable request app review, unavailable'); + logging.error('Unable request app review, unavailable'); } } } else { @@ -263,17 +264,18 @@ class _AppSplashScreenState extends State<_AppSplashScreen> with TrayListener { int.tryParse(remoteVersionString.split('+').last) ?? 0; final localBuildNumber = int.tryParse(localVersionString.split('+').last) ?? 0; - log("[Update] Local: $localVersionString, Remote: $remoteVersionString"); + logging.info( + "[Update] Local: $localVersionString, Remote: $remoteVersionString"); if ((remoteVersion > localVersion || remoteBuildNumber > localBuildNumber) && mounted) { final config = context.read(); config.setUpdate( remoteVersionString, resp.data?['body'] ?? 'No changelog'); - log("[Update] Update available: $remoteVersionString"); + logging.info("[Update] Update available: $remoteVersionString"); } } catch (e) { - log('[Error] Unable to check update: $e'); + logging.error('[Error] Unable to check update...', e); if (mounted) context.showErrorDialog('Unable to check update: $e'); } } @@ -306,7 +308,7 @@ class _AppSplashScreenState extends State<_AppSplashScreen> with TrayListener { if (!mounted) return; final sticker = context.read(); await sticker.listSticker(); - log('[Bootstrap] Everything initialized!'); + logging.info('[Bootstrap] Everything initialized!'); } catch (err) { if (!mounted) return; await context.showErrorDialog(err); diff --git a/lib/router.dart b/lib/router.dart index c2732e3..87cf943 100644 --- a/lib/router.dart +++ b/lib/router.dart @@ -21,6 +21,7 @@ import 'package:surface/screens/chat/room.dart'; import 'package:surface/screens/explore.dart'; import 'package:surface/screens/friend.dart'; import 'package:surface/screens/home.dart'; +import 'package:surface/screens/logging.dart'; import 'package:surface/screens/news/news_detail.dart'; import 'package:surface/screens/news/news_list.dart'; import 'package:surface/screens/notification.dart'; @@ -249,6 +250,11 @@ final _appRoutes = [ ), ], ), + GoRoute( + path: '/debug/logging', + name: 'debugLogging', + builder: (context, state) => const DebugLoggingScreen(), + ), GoRoute( path: '/album', name: 'album', diff --git a/lib/screens/logging.dart b/lib/screens/logging.dart new file mode 100644 index 0000000..263951e --- /dev/null +++ b/lib/screens/logging.dart @@ -0,0 +1,82 @@ +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:google_fonts/google_fonts.dart'; +import 'package:material_symbols_icons/symbols.dart'; +import 'package:styled_widget/styled_widget.dart'; +import 'package:surface/logger.dart'; +import 'package:surface/widgets/navigation/app_scaffold.dart'; +import 'package:talker/talker.dart'; + +final Map kLogLevelIcons = { + LogLevel.error: Symbols.error, + LogLevel.critical: Symbols.error, + LogLevel.warning: Symbols.warning, + LogLevel.info: Symbols.info, + LogLevel.debug: Symbols.info_i, + LogLevel.verbose: Symbols.info_i, +}; + +final Map kLogLevelColors = { + LogLevel.error: Colors.red, + LogLevel.critical: Colors.red, + LogLevel.warning: Colors.orange, + LogLevel.info: Colors.blue, + LogLevel.debug: Colors.green, + LogLevel.verbose: Colors.green, +}; + +final Map kLogLevelFilled = { + LogLevel.error: false, + LogLevel.critical: true, + LogLevel.warning: true, + LogLevel.info: true, + LogLevel.debug: false, + LogLevel.verbose: false, +}; + +class DebugLoggingScreen extends StatelessWidget { + const DebugLoggingScreen({super.key}); + + @override + Widget build(BuildContext context) { + return AppScaffold( + appBar: AppBar( + leading: const PageBackButton(), + title: Text('debugLogging').tr(), + ), + body: SelectionArea( + child: ListView.builder( + padding: EdgeInsets.zero, + itemCount: logging.history.length, + itemBuilder: (context, index) { + final log = logging.history[index]; + return ListTile( + leading: Icon( + kLogLevelIcons[log.logLevel ?? LogLevel.debug] ?? Symbols.help, + color: kLogLevelColors[log.logLevel ?? LogLevel.debug], + fill: (kLogLevelFilled[log.logLevel ?? LogLevel.debug] ?? false) + ? 1 + : 0, + ), + title: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + log.message ?? 'unknown'.tr(), + style: GoogleFonts.robotoMono(fontSize: 13), + ), + if (log.error != null) + Text( + log.error!.toString(), + style: GoogleFonts.robotoMono(fontSize: 13), + ).bold(), + ], + ), + subtitle: Text(log.time.toString()).fontSize(11), + ); + }, + ), + ), + ); + } +} diff --git a/lib/screens/settings.dart b/lib/screens/settings.dart index 32dbcb6..22add6d 100644 --- a/lib/screens/settings.dart +++ b/lib/screens/settings.dart @@ -664,6 +664,16 @@ class _SettingsScreenState extends State { ); }, ), + ListTile( + title: Text('runtimeLogsOpen').tr(), + subtitle: Text('runtimeLogsDescription').tr(), + contentPadding: const EdgeInsets.symmetric(horizontal: 24), + leading: const Icon(Symbols.receipt_long), + trailing: const Icon(Symbols.chevron_right), + onTap: () async { + GoRouter.of(context).pushNamed('debugLogging'); + }, + ), ListTile( title: Text('settingsMiscAbout').tr(), subtitle: Text('settingsMiscAboutDescription').tr(), diff --git a/pubspec.lock b/pubspec.lock index 12a90ee..0eee680 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -897,18 +897,18 @@ packages: dependency: "direct dev" description: name: freezed - sha256: "59a584c24b3acdc5250bb856d0d3e9c0b798ed14a4af1ddb7dc1c7b41df91c9c" + sha256: "532008570b7fd20310db8cb9c8ebc5bafd5aa4e52c4358db4e5ddc29f74f4be3" url: "https://pub.dev" source: hosted - version: "2.5.8" + version: "3.0.1" freezed_annotation: dependency: "direct main" description: name: freezed_annotation - sha256: c2e2d632dd9b8a2b7751117abcfc2b4888ecfe181bd9fca7170d9ef02e595fe2 + sha256: c87ff004c8aa6af2d531668b46a4ea379f7191dc6dfa066acd53d506da6e044b url: "https://pub.dev" source: hosted - version: "2.4.4" + version: "3.0.0" frontend_server_client: dependency: transitive description: @@ -965,6 +965,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.3.2" + group_button: + dependency: transitive + description: + name: group_button + sha256: "0610fcf28ed122bfb4b410fce161a390f7f2531d55d1d65c5375982001415940" + url: "https://pub.dev" + source: hosted + version: "5.3.4" highlight: dependency: transitive description: @@ -2050,6 +2058,38 @@ packages: url: "https://pub.dev" source: hosted version: "3.3.1" + talker: + dependency: "direct main" + description: + name: talker + sha256: "5ab7d974ad92042b3e2382441c41ec4c6e5b3fa2b4b024d8ccbfc4bc2244b7bb" + url: "https://pub.dev" + source: hosted + version: "4.6.14" + talker_dio_logger: + dependency: "direct main" + description: + name: talker_dio_logger + sha256: "71780c52951d36e94964ca06158d827dfc67aa2fb75c8b880603cfefa4377b39" + url: "https://pub.dev" + source: hosted + version: "4.6.14" + talker_flutter: + dependency: "direct main" + description: + name: talker_flutter + sha256: "0cc816260b226c0ff930909c9f22984316b652b140f5eabb97ae9813ee0de135" + url: "https://pub.dev" + source: hosted + version: "4.6.14" + talker_logger: + dependency: transitive + description: + name: talker_logger + sha256: "16ff0cfdf011f65b37957c9ff7ef7043dd9f1c8af3ccb4a44ac4a448defb9eb5" + url: "https://pub.dev" + source: hosted + version: "4.6.14" term_glyph: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 1272135..c5dc197 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -47,7 +47,7 @@ dependencies: dio: ^5.7.0 dio_smart_retry: ^7.0.1 very_good_infinite_list: ^0.9.0 - freezed_annotation: ^2.4.4 + freezed_annotation: ^3.0.0 json_annotation: ^4.9.0 gap: ^3.0.1 markdown: ^7.2.2 @@ -130,6 +130,9 @@ dependencies: local_notifier: ^0.1.6 flutter_markdown_latex: ^0.3.4 flutter_highlight: ^0.7.0 + talker_flutter: ^4.6.14 + talker_dio_logger: ^4.6.14 + talker: ^4.6.14 dev_dependencies: flutter_test: @@ -142,7 +145,7 @@ dev_dependencies: # rules and activating additional ones. flutter_lints: ^5.0.0 build_runner: ^2.4.15 - freezed: ^2.5.7 + freezed: ^3.0.1 json_serializable: ^6.8.0 icons_launcher: ^3.0.0 flutter_native_splash: ^2.4.2