Android
API
Mriver.setProxy(PrepareNotifyProxy.class, new PrepareNotifyProxy() {
@Override
public void notify(String s, PrepareStatus prepareStatus) {
}
@Override
public void apmEvent(final String event, final String param1, final String param2, final String param3, final String param4) {
runOnUiThread(new Runnable() {
@Override
public void run() {
if (logs == null) {
logs = new StringBuilder();
}
logs.append(s).append(" ").append(s1).append(" ").append(s2).append(" ").append(s3).append(" ").append(s4).append("\n");
}
});
}
});
事件如下:
MINI_APP_PREPARE 參數errc!=1表示應用打開異常
MINI_PAGE_ABNORMAL 白屏
MINI_APP_REQUEST 參數step=fail表示應用拉包異常
MINI_AL_NETWORK_PERMISSON_ERROR 頁面訪問受限
MINI_AL_JSAPI_RESULT_ERROR jsapiName=httpRequest或者request表示request請求異常,其他表示JSAPI異常
MINI_CUSTOM_JS_ERROR JS異常
MINI_AL_NETWORK_PERFORMANCE_ERROR 資源請求異常
MiniAppStart 啟動耗時,startCost 表示時間,單位毫秒
iOS
API
途徑為開發一個插件,攔截對應的系統事件,Plist 配置如下:
// Plist 初始化代碼:
- (void)application:(UIApplication *)application beforeDidFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// [MPNebulaAdapterInterface initNebula];
NSString* h5Json = [[NSBundle mainBundle] pathForResource:[NSString stringWithFormat:@"MPCustomPresetApps.bundle/h5_json.json"]ofType:nil];
NSString* amrBundle = [[NSBundle mainBundle] pathForResource:[NSString stringWithFormat:@"MPCustomPresetApps.bundle"] ofType:nil];
NSString* jsPath = [NSBundle.mainBundle.bundlePath stringByAppendingPathComponent:@"MPCustomPluginsJsapis.bundle/Poseidon-UserDefine-Extra-Config.plist"];
[MPNebulaAdapterInterface initNebulaWithCustomPresetApplistPath:h5Json
customPresetAppPackagePath:amrBundle
customPluginsJsapisPath:jsPath];
}
代碼如下:
// .h 文件
#import <AriverApp/RVAPluginBase.h>
NS_ASSUME_NONNULL_BEGIN
@interface DemoPlugin4APM : RVAPluginBase
@end
NS_ASSUME_NONNULL_END
// .m 文件
#import "DemoPlugin4APM.h"
#import <NebulaPoseidon/PSDMonitorEvent.h>
@implementation DemoPlugin4APM
- (void)pluginDidLoad
{
self.scope = kPSDScope_Service;
[self.target addEventListener:kEvent_Monitor_Log_Before withListener:self useCapture:NO];
[super pluginDidLoad];
}
- (void)handleEvent:(RVKEvent *)event
{
[super handleEvent:event];
if ([kEvent_Monitor_Log_Before isEqualToString:event.eventType]){
PSDMonitorEvent *mEvent = (PSDMonitorEvent *)event;
NSArray *params = [mEvent.params isKindOfClass:[NSArray class]]? mEvent.params : @[];
NSString *bizType = [NSString stringWithFormat:@"%@", mEvent.bizType];
NSString *seedId = [NSString stringWithFormat:@"%@", mEvent.seedId];
if ([params count] != 4 || ![bizType length]) {
return;
}
NSString *aplogstr = [NSString stringWithFormat:@"%@\nbizType=%@,param1=%@,param2=%@,param4=%@",params[2],bizType,params[0],params[1],params[3]];
NSLog(@"seed seedId:[%@]\nlog:[%@]", seedId, aplogstr);
// 打開異常:復現路徑,打開demo后點 “打開異常 H5_APP_PREPARE”
if ([seedId isEqualToString:@"H5_APP_PREPARE"]) {
NSString *currentStep = [self mp_valueFromLogStr:params[2] key:@"step"];
// step 參數為 noexistForce 表示打開異常
if ([currentStep isEqualToString:@"noexistForce"]) {
NSLog(@"seedId:[%@]\nlog:[%@]", seedId, aplogstr);
}
}
// js異常:復現路徑,打開demo后點 “APM 小程序 -> js error”
else if ([seedId isEqualToString:@"H5_CUSTOM_ERROR"]) {
NSLog(@"seedId:[%@]\nlog:[%@]", seedId, aplogstr);
}
// 白屏:復現路徑,斷網之后(飛行模式,WiFi斷開),打開demo后點 “打開異常 H5_APP_PREPARE”
else if ([seedId isEqualToString:@"H5_PAGE_ABNORMAL"]) {
NSLog(@"捕獲白屏:[%@]\nlog:[%@]", seedId, aplogstr);
}
// 拉包異常,暫無復現路徑,需要遇到小程序拉包接口異常才可復現:
else if ([seedId isEqualToString:@"H5_APP_REQUEST"]) {
NSString *currentStep = [self mp_valueFromLogStr:params[2] key:@"step"];
NSLog(@"拉包:[%@]\nlog[%@]:[%@]", seedId, currentStep, aplogstr);
if ([currentStep containsString:@"fail"]) {
NSLog(@"拉包異常:[%@]\nlog[%@]:[%@]", seedId, currentStep, aplogstr);
}
}
// 頁面訪問受限 H5_AL_NETWORK_PERMISSON_ERROR
// 復現路徑,打開demo后點 “APM 小程序 -> 頁面訪問受限”
else if ([seedId isEqualToString:@"H5_AL_PAGE_UNAUTHORIZED"] || [seedId isEqualToString:@"H5_AL_NETWORK_PERMISSON_ERROR"]) {
NSLog(@"seedId:[%@]\nlog:[%@]", seedId, aplogstr);
}
// request請求異常 / JSAPI異常 H5_AL_JSAPI_RESULT_ERROR
// 復現路徑,打開demo后點 “APM 小程序 -> js api error”
else if ([seedId isEqualToString:@"H5_AL_JSAPI_RESULT_ERROR"]) {
NSLog(@"seedId:[%@]\nlog:[%@]", seedId, aplogstr);
}
// 資源請求異常 H5_AL_NETWORK_PERFORMANCE_ERROR
// 復現路徑,斷網 -> 打開demo后點 “跳轉小程序 -> 點刷新”
else if ([seedId isEqualToString:@"H5_AL_NETWORK_PERFORMANCE_ERROR"]) {
NSLog(@"seedId:[%@]\nlog:[%@]", seedId, aplogstr);
}
}
// 啟動耗時
// 計算規則:從 AppStart 起,到 PageLoad 止,中間消耗的時間,單位是秒。
// 業務取值應該只計算第一次回調,即首頁加載完畢(從下邊 h5WebVC.url 的 log 可以看出區別)
if ([event.eventType isEqualToString:kEvent_Session_Create]) {
// 記住時間戳:
CFTimeInterval tm = CACurrentMediaTime(); // CACurrentMediaTime() 是基于內建時鐘的,能夠更精確更原子化地測量,并且不會因為外部時間變化而變化(例如時區變化、夏時制、秒突變等),但它和系統的uptime有關,系統重啟后CACurrentMediaTime()會被重置。 CACurrentMediaTime() 常用于測試代碼的效率。
self.appStartTimestamp = tm;
NSLog(@"kEvent_Session_Create: AppStart [%f]", tm);
}
if ([event.eventType isEqualToString:kEvent_Page_Load_Complete]) {
PSDEvent *oldEvent = (PSDEvent *)event;
H5WebViewController *h5WebVC = (H5WebViewController *)[oldEvent.context currentViewController];
NSString* currentUrl = [h5WebVC.url absoluteString];
NSLog(@"kEvent_Session_Create: page[%@]", currentUrl);
if ([currentUrl containsString:@"#"]) { //
// 時間戳:
CFTimeInterval tm = CACurrentMediaTime();
NSLog(@"kEvent_Session_Create: PageLoad [%f]", tm);
CFTimeInterval appStartTime = tm - self.appStartTimestamp;
NSLog(@"kEvent_Session_Create: AppStart - PageLoad = 啟動時間[%f]", appStartTime);
}
}
}
@end
Android 、iOS 事件對照表
事件 | Android | iOS |
打開異常 | MINI_APP_PREPARE | H5_APP_PREPARE |
白屏 | MINI_PAGE_ABNORMAL | H5_PAGE_ABNORMAL |
拉包異常 | MINI_APP_REQUEST | H5_APP_REQUEST |
頁面訪問受限 | MINI_AL_NETWORK_PERMISSON_ERROR | H5_AL_NETWORK_PERMISSON_ERROR / H5_AL_PAGE_UNAUTHORIZED |
request 請求異常 /JSAPI 異常 | MINI_AL_JSAPI_RESULT_ERROR | H5_AL_JSAPI_RESULT_ERROR |
js 異常 | MINI_CUSTOM_JS_ERROR | H5_CUSTOM_ERROR |
資源請求異常 | MINI_AL_NETWORK_PERFORMANCE_ERROR | H5_AL_NETWORK_PERFORMANCE_ERROR |
啟動耗時 | MiniAppStart |
小程序
my.onError(Function listener)
簡介
my.onError 監聽小程序錯誤事件。
入參
Function listener
參數
屬性 | 類型 | 兼容性 | 描述 |
error | String | - | 異常描述,一般為 Error 對象的 message 字段。 |
stack | String | 基礎庫:2.7.4 | 異常堆棧,一般為 Error 對象的 stack 字段。 |
代碼示例
my.onError(Function listener)
Page({
onLoad() {
my.onError(this.errorHandler);
},
errorHandler(error, stack) {
console.log('onError error', error);
console.log('onError stack', stack);
}
})
使用 my.onError 監聽到的報錯,app.js 中的 onError 方法也會監聽到。
使用 my.onError 監聽頁面報錯,如果在多個頁面開啟監聽沒有關閉,則頁面報錯時會觸發多個監聽事件,建議在頁面關閉時調用 my.offError 關閉監聽。
my.onUnhandledRejection(Function listener)
簡介
my.onUnhandledRejection 監聽未處理的 Promise
拒絕(unhandled rejection)事件。
入參
Function listener
未處理的 Promise 拒絕事件的回調函數。
參數
Object res
屬性 | 類型 | 描述 |
reason | any | 拒絕原因。reject() 的接收值,一般是 Error 對象。 |
promise | Promise | 被拒絕的 Promise 對象。 注:僅 iOS 上支持 |
代碼示例
my.onUnhandledRejection(Function listener)
Page({
onLoad() {
my.onUnhandledRejection(this.unhandledRejectionHandler);
},
unhandledRejectionHandler(res) {
console.log('onUnhandledRejection reason', res.reason);
console.log('onUnhandledRejection promise', res.promise);
}
})
my.onUnhandledRejection 的回調函數內繼續觸發
Promise
的unhandledrejection
事件,則可能會導致循環觸發unhandledrejection
事件,請注意規避。所有的
unhandledRejection
都可以被這一監聽捕獲,但只有Error
類型的才會在小程序后臺觸發報警。