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
类型的才会在小程序后台触发报警。