Day9_开源鸿蒙_Flutter_for_OpenHarmony_logger实战_本地日志与异常捕获
本文介绍了如何在开源鸿蒙Flutter应用中实现本地日志记录和全局异常捕获功能。主要内容包括: 使用logger库替代print,实现带时间戳和级别的统一日志格式 创建AppLogger单例,同时输出到控制台和内存缓存(最多300行) 实现全局异常捕获,覆盖Flutter框架错误、平台层错误和未捕获异常 开发日志查看页面,支持日志复制、清空和模拟异常测试 在应用右上角添加日志入口,方便用户反馈问题
开源鸿蒙 Flutter for OpenHarmony:logger 实战(本地日志 + 全局异常捕获)
欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net
离线笔记做到 Day8,功能看起来已经很“像样”了,但只要开始真机跑,就会遇到一个很现实的问题:用户说“闪退/点不了/没反应”,你没有任何线索。
尤其是 OpenHarmony 场景下:
- 有些问题只在真机/模拟器出现
- 没接入远程日志平台(我们这个项目是纯离线)
- 靠
print很难统一格式,也不方便在手机上复制出来发给你排查
Day9 的目标:把日志做成“可用工具”。
- 统一日志格式(带时间、级别)
- 在应用内保存一份“最近 300 行日志”
- 加一个“应用日志”页面:复制/清空/手动写日志/模拟异常
- 全局捕获未处理异常:FlutterError / PlatformDispatcher / runZonedGuarded
1. 今天用到的第三方库:logger(为什么不用 print)
print 的问题不在于它不能用,而在于:
- 没级别(info/warn/error)
- 没统一格式(时间、tag、堆栈)
- 打出来之后,手机上很难“拿到一份完整日志”
logger 就是做这件事的:把日志当成一个正式能力来用。
添加依赖:
flutter pub add logger
2. 实现一个 AppLogger:既写控制台,也写“内存日志”
📌 文件:lib/shared/app_logger.dart
我们做一个单例 AppLogger,有两个输出方向:
- 用
logger输出到控制台(调试时看) - 自己维护一个
lines(最多 300 行),用于 App 内展示/复制
class AppLogger {
AppLogger._();
static final AppLogger instance = AppLogger._();
final _logger = Logger(
printer: PrettyPrinter(
methodCount: 0,
errorMethodCount: 20,
colors: false,
printEmojis: false,
dateTimeFormat: DateTimeFormat.onlyTimeAndSinceStart,
),
);
final lines = <String>[].obs;
}
然后封装 3 个最常用的方法:i/w/e:
void i(String message) {
_logger.i(message);
_add('I', message);
}
void w(String message, {Object? error, StackTrace? stackTrace}) {
_logger.w(message, error: error, stackTrace: stackTrace);
_add('W', _format(message, error, stackTrace));
}
void e(String message, {Object? error, StackTrace? stackTrace}) {
_logger.e(message, error: error, stackTrace: stackTrace);
_add('E', _format(message, error, stackTrace));
}
📌 _add 里做两件事:
- 拼一个本地时间戳(更适合读)
- 超过 300 行就裁掉最旧的
void _add(String level, String text) {
final now = DateTime.now();
final ts =
'${now.year}-${_two(now.month)}-${_two(now.day)} ${_two(now.hour)}:${_two(now.minute)}:${_two(now.second)}';
lines.add('[$ts][$level] $text');
if (lines.length > 300) {
lines.removeRange(0, lines.length - 300);
}
}
3. 全局异常捕获:把“没抓住的异常”也写进日志
很多异常不是你主动 try/catch 的,它会直接冒到框架层。
这一步就是把三条常用的“兜底入口”接上。
📌 文件:lib/main.dart
3.1 FlutterError.onError(Flutter 框架错误)
FlutterError.onError = (details) {
FlutterError.presentError(details);
AppLogger.instance.e(
'FlutterError',
error: details.exception,
stackTrace: details.stack,
);
};
3.2 PlatformDispatcher.instance.onError(更底层的 uncaught)
PlatformDispatcher.instance.onError = (error, stack) {
AppLogger.instance.e('PlatformDispatcher', error: error, stackTrace: stack);
return true;
};
3.3 runZonedGuarded(兜底一层)
runZonedGuarded(
() => runApp(const NotesApp()),
(error, stack) =>
AppLogger.instance.e('Zone', error: error, stackTrace: stack),
);
到这里,哪怕你忘了 try/catch,至少也能在“应用日志”页看到异常堆栈。
4. 做一个“应用日志”页:复制/清空/模拟异常
📌 文件:lib/features/debug/ui/app_logs_page.dart
4.1 日志列表:用 Obx 实时刷新
lines 是 RxList<String>,所以用 Obx 渲染:
Expanded(
child: Obx(() {
final lines = logger.lines;
if (lines.isEmpty) return const Center(child: Text('暂无日志'));
return ListView.builder(
itemCount: lines.length,
itemBuilder: (_, i) => SelectableText(lines[i]),
);
}),
)
4.2 顶部按钮:复制/清空
复制时直接把整份 dump() 放到剪贴板:
await Clipboard.setData(ClipboardData(text: logger.dump()));
清空就一句:
logger.clear();
4.3 “模拟异常”按钮(方便自测)
这一步不是给用户用的,是开发时确认“全局兜底有没有生效”:
Timer.run(() => throw StateError('Day9 模拟异常'));
点一下,日志页应该能看到一条 error 级别的堆栈信息。
📷 截图位(建议 2~3 张)



5. 给日志页加入口:放在列表页右上角
📌 文件:lib/features/note/ui/notes_list_page.dart
IconButton(
onPressed: () {
Navigator.of(context).push(
MaterialPageRoute(builder: (_) => const AppLogsPage()),
);
},
icon: const Icon(Icons.bug_report),
tooltip: '日志',
),
这样真机遇到问题时,让对方打开日志页点“复制”,直接把文本发过来,排查成本会小很多。
📷


6. 自测清单(Day9)
- 打开日志页,点“写日志” → 列表出现一条 info 记录
- 点“模拟异常” → 列表出现 error + 堆栈
- 点“复制” → 能把日志完整复制到剪贴板(随便粘贴到备忘录验证)
- 点“清空” → 列表清空
更多推荐




所有评论(0)