mirror of
https://github.com/NohamR/knowledge-kit.git
synced 2026-05-25 04:17:17 +00:00
feature: APM
This commit is contained in:
@@ -7778,7 +7778,149 @@ typedef NS_ENUM(NSUInteger, WeexAPMType) {
|
||||
|
||||
// todo? 参考前端资源模块化,如何实现资源预加载(全局 LRU或者基于用户行为路径的预热功能,比如某个收银员角色固定的情况下,他日常的操作行为是固定的,商品扫码、开单)
|
||||
|
||||
#### 2. Flutter 异常监控
|
||||
|
||||
|
||||
### Flutter 异常监控
|
||||
|
||||
Flutter 异常指的是,Flutter 程序中 Dart 代码运行时意外发生的错误事件。我们可以通过与 Java 类似的 try-catch 机制来捕获它。但与 Java 不同的是,Dart 程序不强制要求我们必须处理异常。
|
||||
|
||||
这是由于 Flutter 本身是 Dart 事件循环机制来运行任务的。各个任务的运行状态是相互独立的。即使某个任务出现了异常,Dart 也不会退出,只会导致当前任务的后续代码不会执行,用户还可以继续使用其他功能。
|
||||
|
||||
Dart 异常可以分为 App 异常和 Framework 异常,分别处理。
|
||||
|
||||
#### App 异常监控
|
||||
|
||||
App 异常,就是应用代码的异常,通常由未处理应用层其他模块所抛出的异常引起。根据异常代码的执行时序,App 异常可以分为两类:
|
||||
|
||||
- 同步异常:可以通过 try-catch 机制捕获
|
||||
- 异步异常:需要采用 Future 提供的 catchError 语句捕获
|
||||
|
||||
```dart
|
||||
// 使用 try-catch 捕获同步异常
|
||||
try {
|
||||
throw StateError('This is a Dart exception.');
|
||||
} catch(e) {
|
||||
print(e);
|
||||
}
|
||||
|
||||
// 使用 catchError 捕获异步异常
|
||||
Future.delayed(Duration(seconds: 1))
|
||||
.then((e) => throw StateError('This is a Dart exception in Future.'))
|
||||
.catchError((e)=>print(e));
|
||||
```
|
||||
|
||||
需要注意的是,这两种方式是不能混用的。可以看到,在上面的代码中,我们是无法使用 try-catch 去捕获一个异步调用所抛出的异常的
|
||||
|
||||
```dart
|
||||
// try...catch...无法捕获异步异常
|
||||
try {
|
||||
Future.delayed(Duration(seconds: 1))
|
||||
.then((e) => throw StateError('This is a Dart exception in Future.'))
|
||||
} catch(e) {
|
||||
print("This line will never be executed. ");
|
||||
}
|
||||
```
|
||||
|
||||
但这适合写业务代码的时候分散处理,如果做 APM,需要一种统一收口的方式监控异常,该怎么办呢?
|
||||
|
||||
Flutter 提供了 `Zone.runZoned` 方法,给代码执行对象指定一个 Zone,在 Dart 中,Zone 表示一个代码执行的环境范围,其概念类似沙盒,不同沙盒之间是互相隔离的。如果我们想要观察沙盒中代码执行出现的异常,沙盒提供了 onError 回调函数,拦截那些在代码执行对象中的未捕获异常。
|
||||
|
||||
可以将代码都写到 Zone 里,即使没有使用 `try...catch...` 和 `catchError` ,任何同步、异步异常也都被 Zone 捕获到
|
||||
|
||||
```dart
|
||||
// 同步异常
|
||||
runZoned(() {
|
||||
throw StateError('This is a Dart exception.');
|
||||
}, onError: (dynamic e, StackTrace stack) {
|
||||
print('Sync error caught by zone');
|
||||
});
|
||||
|
||||
// 异步异常
|
||||
runZoned(() {
|
||||
Future.delayed(Duration(seconds: 1))
|
||||
.then((e) => throw StateError('This is a Dart exception in Future.'));
|
||||
}, onError: (dynamic e, StackTrace stack) {
|
||||
print('Async error aught by zone');
|
||||
});
|
||||
```
|
||||
|
||||
如何收口?
|
||||
|
||||
要集中捕获 Flutter 应用中的未处理异常,可以把 main 函数中的 runApp 语句也放置在 Zone 中。这样在检测到代码中运行异常时,我们就能根据获取到的异常上下文信息,进行统一处理。
|
||||
|
||||
```dart
|
||||
runZoned<Future<Null>>(() async {
|
||||
runApp(MyApp());
|
||||
}, onError: (error, stackTrace) async {
|
||||
// 异常数据收集,上报 APM
|
||||
});
|
||||
```
|
||||
|
||||
|
||||
|
||||
### Framework 异常监控
|
||||
|
||||
Framework 异常,就是 Flutter 框架引发的异常,通常是由应用代码触发了 Flutter 框架底层的异常判断引起的。比如,当布局不合规范时,Flutter 就会自动弹出一个红色错误界面。
|
||||
|
||||
这是由于 Flutter 框架在调用 build 方法构建页面时进行了 `try...catch...` 处理,并提供了一个 `ErrorWidget`,用于组建渲染错误的时候进行信息展示
|
||||
|
||||
```dart
|
||||
@override
|
||||
void performRebuild() {
|
||||
Widget built;
|
||||
try {
|
||||
// 创建页面
|
||||
built = build();
|
||||
} catch (e, stack) {
|
||||
// 使用 ErrorWidget 创建页面,展示错误信息
|
||||
built = ErrorWidget.builder(_debugReportException(ErrorDescription("building $this"), e, stack));
|
||||
...
|
||||
}
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
该方案适合在开发阶段定位问题。但如果让用户看到这样一个页面,就很糟糕。因此,通常会重写 `ErrorWidget.builder` 方法,将这样的错误提示页面替换成一个更加友好的页面。
|
||||
|
||||
如何重写?
|
||||
|
||||
```dart
|
||||
ErrorWidget.builder = (FlutterErrorDetails flutterErrorDetails){
|
||||
return Scaffold(
|
||||
body: Center(
|
||||
// ... UI 描述
|
||||
)
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
`ErrorWidget.builder`方法提供了一个参数 `FlutterErrorDetails` 用于表示当前的错误上下文,可以将异常信息上报 APM,用于后续分析异常原因。
|
||||
|
||||
为了集中处理框架异常,Flutter 提供了 `FlutterError` 类,这个类的 `onError` 属性会在接收到框架异常时执行相应的回调。因此,要实现自定义捕获逻辑,我们只要为它提供一个自定义的错误处理回调即可。
|
||||
|
||||
|
||||
|
||||
### 如何收口
|
||||
|
||||
App 异常和 Framework 异常都存在,写2个口子收集也可以。但 Flutter 提供了更加友好的口子。使用 Zone 提供的 `handleUncaughtError` 语句,将 Flutter 框架的异常统一转发到当前的 Zone 中,这样我们就可以统一使用 Zone 去处理应用内的所有异常了
|
||||
|
||||
```dart
|
||||
FlutterError.onError = (FlutterErrorDetails details) async {
|
||||
// Framework 异常转发至 Zone 中
|
||||
Zone.current.handleUncaughtError(details.exception, details.stack);
|
||||
};
|
||||
|
||||
runZoned<Future<Null>>(() async {
|
||||
// App 异常本身就在 Zone 的 onError 中收集
|
||||
runApp(MyApp());
|
||||
}, onError: (error, stackTrace) async {
|
||||
//Do sth for error
|
||||
});
|
||||
```
|
||||
|
||||
异常信息收集了,走 Native 的数据聚合上报策略。用于后续的异常问题自动定位和告警机制。
|
||||
|
||||
|
||||
|
||||
## 十、子线程 UI 监控
|
||||
|
||||
|
||||
Reference in New Issue
Block a user