無特別說明,文中Rollbar統指Rollbar-flutter
Rollbar官方檔案說是純Dart實現,該特徵意味著自帶」程式碼複用」光環。
如圖當接入端(Third-APP)呼叫Rollbar SDK時表示包含的網路(異常資料上傳等)和儲存(異常儲存管理)可達到複用效果。
若Flutter異常監控框架非純Dart實現(第三篇中Bugsnag),就存在程式碼無法複用問題,如圖,Dart-Crash-SDK是這層殼依賴對端SDK,最終導致各平臺(android,ios,…)都須對端SDK(android-crash-sdk, ios-crash-sdk,…)適配,導致網路和儲存邏輯對端SDK都須各自實現一遍,嚴重邏輯重複。
由此在做軟體多端架構設計時,Dart側可理解成是多平臺公共程式碼集合,如果存在多端邏輯功能程式碼完全可以抽離到Dart側以複用,減少測試和人力成本。
前面兩篇文章我們知道,捕獲到原始異常後對其中的Error和StackTrace有相當部分的工作是對原始異常資料的包裝再將包裝類資料傳送給對端或者後臺,不同框架包裝過程是不一樣的,如下圖中Catcher框架包裝後類物件是Report,Bugsnag對異常進行兩次包裝,第一次是BugsnagError,第二次是BugsnagEvent。
這很好理解,因為對於同一事物不同框架的需求是不一樣的,不同需求註定了不同的包裝行為。
原始異常資料就像一條魚,口味清淡的Catcher選擇清蒸,重口味的Bugsnag選擇紅燒,不同框架就是不同口味的吃魚人。而Rollbar 將包裝行為抽象化,將原始的魚以某種方式提供給你,讓你享受自由烹飪樂趣。
異常產生後有很多耗時操作,如原始異常封包裝中存在讀取額外欄位,異常的儲存,查詢,加密,上報等。耗時操作都在main isolate 中做, 勢必會影響到main isolate的UI 構建等行為,異常資料量比大時UI會有卡頓情況,就像圖中情況,
Rollbar支援將異常耗時處理操作交給子isolate(crash isolate),讓main isolate保持專注做UI構建等以提高應用的流暢度。
該需求與第三篇Flutter異常監控 - 叄 |從bugsnag原始碼學習如何追溯異常產生路徑 相同
該需求目的是能完整記錄使用者操作的整個行為路徑,這樣達到清晰指導使用者操作過程,對問題的定位很有幫助。可以理解成一個小型的埋點系統,只是該埋點系統只是針對異常來做的。
區別在程式碼層面實現,bugsnag中有自動新增和手動新增路徑兩種情況,Rollbar中只有手動新增,但是手動新增分類更加細化,比如圖中將Breadcrumb構造過程被分成Breadcrumb.error、Breadcrumb.navigation、Breadcrumb.widget、Breadcrumb.log 對應不同圖示事件。
話說,追溯異常生成路徑需求是標配麼? 目前看Bugsnag和Rollbar都有實現。
pubspec.yaml
dependencies:
rollbar_flutter: ^0.3.0-beta
flutter pub get
程式碼中設定:
import 'package:rollbar_flutter/rollbar.dart';
Future<void> main() async {
const config = Config(
//accessToken到https://rollbar.com/註冊獲取
accessToken: 'YOUR-ROLLBAR-ACCESSTOKEN',
package: 'rollbar_flutter_example');
await RollbarFlutter.run(config, () {
runApp(const MyApp());
});
}
Rollbar是Flutter異常框架,當然少不了讀這類原始碼套路,直接拿出第三篇文章中的通用閱讀路徑, 按照如下流程一步步走:
這裡主要涉及到isolate雙向通訊知識,不清楚可以看下這個貼文Flutte 指北 -> Isolate
至此流程圖如下:
異常封包裝流程:
上面步驟中經過對Event二次封裝,生成最終包裝類為Payload, 最後該類轉換成字串傳送到Rollbar後臺。
上面分析可知執行緒切換通過Notifier實現,執行緒切換思路:通過Config設定自定義Notifier值來指定例外處理執行執行緒,AsyncNotifier是main UI isolate, IsolateNotifier會新建子執行緒執行異常相關操作。
abstract class Notifier {
// notifier version to be updated with each new release: [todo] automate
static const version = '0.4.0-beta';
static const name = 'rollbar-dart';
Sender get sender;
Wrangler get wrangler;
Telemetry get telemetry;
FutureOr<void> notify(Event event);
FutureOr<void> dispose();
}
預設初始化IsolatedNotifier.spwan 將產生一個新執行緒。
總結了幾點好處:
綜上將可能耗時都放到非同步執行緒,可以提高主執行緒流暢性。
上面分析可知,包裝過程通過Transformer來實現,自定義包裝類思路:通過Config設定自定義Transformer值來實現自定義處理異常資料邏輯,可以進行加密等。
abstract class Transformer {
FutureOr<Data> transform(Data data, {required Event event});
}
Config預設實現是這個,如果想自定義封包裝過程,可以複寫其中transform,對其中date和event操作。
class NoopTransformer implements Transformer {
const NoopTransformer(Config _);
@override
Data transform(Data data, {required Event event}) => data;
}
類功能抽象精準,清晰的職能分工:
Notifier
子類實現。Transformer
物件給了自定義和預設的轉換方式。Wrangler
將提供最終真實資料並傳輸給sender。Sender
子類實現,可以擴充套件出httpSender等。Telemetry
對資料庫的包裝,可插入,查詢 異常和異常路徑物件。可插拔意味更自由的功能和更開閉的設計。Rollbar像堆積木一樣,將包裝,傳輸,傳送,儲存通過組合方式統一設定起來更具靈活性。
通過非空命名建構函式提供預設實現,模組直接是以組合設定,外部可設定和替換,滿足開閉原則。
const Config({
this.notifier = IsolatedNotifier.spawn,
this.wrangler = DataWrangler.new,
this.transformer = NoopTransformer.new,
this.sender = PersistentHttpSender.new,
});
PS: 一直沒想明白Dart中建構函式的多非空可選引數與構建者模式有啥不同,感覺前者完全可以替換構建者模式的場景,哪位大佬能告訴我應用場景區別?
考慮到篇幅原因,文章分析了主要流程,其實還有很多點值得學習和借鑑。如
Flutter異常監控 - 叄 | 從bugsnag原始碼學習如何追溯異常產生路徑 - 掘金
Releases · rollbar/rollbar-flutter
如果覺得文章對你有幫助,點贊、收藏、關注、評論,一鍵四連支援,你的支援就是我創作最大的動力。
❤️ 本文原創聽蟬 公眾號:碼裡特別有禪 歡迎關注原創技術文章第一時間推播 ❤️