更新时间:2022.10.13 19:46:40
应用性能监控全链路版的iOS SDK基本为无侵入式,只需要根据接入章节的步骤完成SDK的初始化,引入需要的功能子库即可。
注意
SDK会在初始化的时候就采集用户信息,请确保采集用户信息之前已经获得用户授权。
请确保您的用户在同意隐私协议后,再初始化APMSDK。
在 Podfile 中添加如下代码,然后执行 pod install
或者 pod update
安装SDK。
source 'https://github.com/volcengine/volcengine-specs.git' pod 'RangersAPM', '3.0.0', :subspecs => [ 'Crash', 'WatchDog', 'UserException', 'EventMonitor', 'SessionTracker', 'CN' #必须引入 ]
引入字库,请参见4.subspecs说明。
在 SDK 的初始化代码中添加如下代码。
#import <RangersAPMForSDK.h> - (void)init { RangersAPMForSDKConfig *sdkConfig = [RangersAPMForSDKConfig configWithSDKID:@"{{sdk_id}}" appToken:@"{{app_token}}"]; sdkConfig.channel = @"Cocoapods"; //SDK发布渠道,非必填 sdkConfig.hostAppID = @"host_app_id"; //宿主APP标识,非必填 sdkConfig.sdkVersion = @"1.0.0"; //SDK版本,必填 RangersAPMForSDK *sdkMonitor = [[RangersAPMForSDK alloc] initWithConfig:sdkConfig]; return YES; }
[RangersAPMForSDKConfig configWithSDKID:@"{{app_id}}" appToken:@"{{app_token}}"] 中的 {{app_id}} 替换为创建的应用对应的id,{{app_token}}替换为创建的应用对应的AppToken。
例如:[RangersAPMForSDKConfig configWithSDKID:@"2338" appToken:@"4f16"]
说明
代码初始化时机应尽量靠前,否则可能出现启动阶段(上述代码调用之前)发生的崩溃无法捕获,或者启动分析数据产生误差。
您可以根据需要,按照以下各模块说明,检查对应模块是否接入成功。
开启Debug日志输出功能后,SDK在关键事件发生(初始化成功,上报成功等)时会向Xcode控制台输出日志,帮助您对SDK的接入和上报进行验证,参考如下代码开启:
#import <RangersAPM+DebugLog.h> [RangersAPM allowDebugLogUsingLogger:^(NSString * _Nonnull log) { NSLog(@"APMInsight : %@", log); }];
支持通过修改block自定义日志输出格式。
上述代码示例是SDK内部默认的输出格式,如果传入nil,SDK会使用默认的格式输出日志。
[RangersAPM allowDebugLogUsingLogger:nil];
请将上述代码在RangersAPM start方法之前初始化,否则对于一些同步事件可能无法输出日志。
#if DEBUG [RangersAPM allowDebugLogUsingLogger:^(NSString * _Nonnull log) { NSLog(@"APMInsight : %@", log); }]; #endif [RangersAPM startWithConfig:config]; //请先于此代码开启debug日志
通过测试用例可以验证SDK功能是否已正确开启,您可以自己在代码中添加测试用例。如果您不了解如何添加测试用例,可以参考下面各模块给出的样例代码,或者到GitHub下载我们的Example工程作为参考。
日志内容 | 说明 |
---|---|
Setup APMInsight - version : | APMInsight初始化开始,准备启动各功能模块,同时输出当前版本 |
完整的崩溃分析功能需要引入如下子库:Crash、WatchDog,支持单独引入各个子库。监控组件内部发生的崩溃,首先需要获取组件的地址区间,我们提供如下两种方案:
在组件中添加如下两个源文件,并定义两个函数,把一个函数的地址作为组件的起始地址,另一个函数的地址作为组件的结束地址。
注意
不要直接用下面的文件名和函数名,需要添加前缀,避免冲突。
//SDKBegin.c #include "SDKBegin.h" extern void * SDKBeginAddress(void) { return &SDKBeginAddress; } //SDKEnd.c #include "SDKEnd.h" extern void * SDKEndAddress(void) { return &SDKEndAddress; }
在Xcode Build Phases - Compile Sources 里面调整编译顺序。
如下所示,SDKBegin.c为第一个编译,SDKEnd.c为最后一个编译。
这种方案接入方式简单,不需要宿主APP做额外配置,但是对组件有一定的侵入性,且二进制重排后地址区间的准确性无法保证。#import <RangersAPMForSDK.h> extern void * SDKBeginAddress; extern void * SDKEndAddress; - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { NSArray * addressRanges = @[[RangersAddressRange addressRangeWithStartAddress:(int64_t)&SDKBeginAddress endAddress:(int64_t)&SDKEndAddress]]; RangersAPMAddressConfig * addressConfig = [RangersAPMAddressConfig configWithAddressRanges:addressRanges]; RangersAPMForSDKConfig *sdkConfig = [RangersAPMForSDKConfig configWithSDKID:@"{{app_id}}" appToken:@"{{app_token}}"]; sdkConfig.addressConfig = addressConfig; sdkConfig.channel = @"Cocoapods"; sdkConfig.hostAppID = @"host_app_id"; //宿主APP标识 sdkConfig.sdkVersion = @"1.0.0"; RangersAPMForSDK *sdkMonitor = [[RangersAPMForSDK alloc] initWithConfig:sdkConfig]; return YES; }
注意
添加的脚本是在宿主的Project中添加,而不是在组件的Project中。
如果这些SDK需要汇总到一个看板,即数据上报到APMPlus同一个APPId下面,则需要修改SDKName,并使用'|'把这些SDK name分隔开,即SDKName="SDK1|SDK2|SDK3",如下所示:
SDKAid="123456"
SDKName="RangersAPM|RangersAppLog"
writeAddressRangeFile $SDKName $SDKAid
如果这些SDK是相对独立的,即数据上报到各自的APPId下面,此时会有多个APPId,对每一个SDK进行上述调用,如下所示:
SDKAid1="123456"
SDKName1="RangersAPM"
writeAddressRangeFile $SDKName1 $SDKAid1
SDKAid2="4567890"
SDKName2="RangersAppLog"
writeAddressRangeFile $SDKName2 $SDKAid2
允许生成Link Map文件:在Xcode - Build Settings 中把 Write Link Map File 置为YES。
由于脚本执行有一定耗时,为了避免影响开发体验,建议仅在Release环境下修改Write Link Map File为YES。
把修改后的脚本粘贴到Run Script里面,完成脚本接入工作。
添加的Run Script需要位于Copy Pods Resources之前,Compile Sources之后。您可以通过拖动来移动Run Script的位置。
这种方案不需要在RangersAPM SDK初始化时做其他的配置,准确度比方案1更高,但是需要宿主配合在Xcode中添加脚本。
在初始化应用性能监控全链路版 SDK 时,配置 RangersAPMForSDKConfig 的 libNames 属性,传入您的 SDK 包名。
测试用例
下面的代码会触发NSException类型的Crash,更多类型的case,可以下载Example工程。
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ NSArray *array = [NSArray array]; [array objectAtIndex:10]; });
注意
不要直接通过Xcode Run启动APP,这样触发的崩溃无法捕获。
debug日志
日志内容 | 说明 |
---|---|
Crash-Monitor start successfully! | 崩溃监控模块启动成功 |
WatchDog-Monitor start successfully! | 卡死监控模块启动成功 |
Crash log is uploading... | 开始上传崩溃日志 |
Crash log is uploaded successfully! | 崩溃日志上报成功 |
Watchdog log is uploading... | 开始上传卡死日志 |
自定义错误模块需要引入子库UserException,自定义错误为自埋点功能,需要手动调用接口来记录APP发生的错误,并上报到火山引擎应用性能监控全链路版平台,统一查看。
下面的代码会记录一条自定义错误日志,SDK每记录5条日志会触发一次上报。网络错误日志可以通过发送一次会发生错误的网络请求来自动记录,可以参考Example工程。
#import <RangersAPMForSDK+UserException.h> //获取之前初始化的实例,需要先参考 初始化 章节初始化一个实例 RangersAPMForSDK *sdkMonitor = [RangersAPMForSDK monitorWithSDKID:@"{{sdk_id}}"]; [sdkMonitor trackAllThreadsLogExceptionType:@"testUserException" skippedDepth:0 customParams:@{@"testCustomKey":@"testCustomValue"} filters:@{@"testFilterKey":@"testFilterValue"} callback:^(NSError * _Nullable error){ NSLog(@"%@",error); } ];
日志内容 | 说明 |
---|---|
UserException-Monitor start successfully! | 自定义错误监控模块启动成功 |
事件分析模块是一个自埋点功能,需要您手动调用接口来进行事件的记录,使用该功能需要引入EventMonitor模块。
可以通过如下代码记录一个事件,相关参数可以查看头文件介绍。
#import "RangersAPMForSDK+EventMonitor.h" //获取之前初始化的实例,需要先参考 初始化 章节初始化一个实例 RangersAPMForSDK *sdkMonitor = [RangersAPMForSDK monitorWithSDKID:@"{{app_id}}"]; [sdkMonitor trackEvent:@"event_name1" metrics:@{@"metric1":@(0)} dimension:@{@"dimension1":@"test"} extraValue:@{@"extra1":@"extravalue"}];
日志内容 | 说明 |
---|---|
Record an event-log successfully, name: | 成功记录一条事件日志,并输出事件名称 |
子库名称 | 功能简介 | 对应平台模块 | 开始支持版本 |
---|---|---|---|
Crash | 崩溃监控:捕获CPP Exception、Mach Exception、NSException Exception 和 Signal Exception | 崩溃分析 | 2.8.1 |
WatchDog | 卡死监控:监控主线程长时间卡住被系统 watchdog 给强杀的情况 | 崩溃分析 | 2.8.1 |
UserException | 自定义错误,需要业务方手动打点 | 错误分析-自定义错误 | 2.8.1 |
EventMonitor | 事件分析,记录自定义事件,需要手动埋点 | 事件分析 | 2.8.1 |
SessionTracker | PV/UV统计,接入后会自动上报数据 | 各模块异常率、异常用户比例等 | 2.8.1 |
BootingProtectLite | 连续崩溃保护 | \ | 2.10.0 |
注意
组件监控的崩溃日志需要上传了符号表才能解析。如果您的 SDK 是静态库,符号表是接入宿主APP后,宿主APP打包产生的符号表。
请确保您的符号表满足如下格式。
把符号表压缩为zip文件。
Mac 下 zip 需要去除默认生成 DS_Store __MACOSX 文件。可以用如下命令(将test.app.dSYM.zip 和 test.app.dSYM 替换为您的符号表名称)。
zip -r test.app.dSYM.zip test.app.dSYM -x "*.DS_Store" -x "__MACOSX"
上传符号表。
curl https://console.volcengine.com/apmplus_api/eue/guest/app/mapping/upload -F "file=@dSYMZipName" -F "type=Dwarf" -F "os=iOS" -F "aid=APMInsightID" -H "Content-Type: multipart/form-data" -w %{http_code}
在Xcode中对应Target下配置Build Phases、添加Run Script,可以实现APP打包时自动上传符号表。
说明
默认Debug模式和模拟器编译不会上传符号表,如果需要在这两种情况下上传符号表,请参考下述 3.ii 部分内容
检查工程配置是否生成符号表,位置Build Settings - Debug Infomation Format。
添加Run Script。
说明
请将添加的这个Run Script置于最后,避免脚本执行时符号表还没有生成,如下图所示(可以通过拖动调整顺序)
修改脚本内容。
如果您接入的SDK版本大于1.5.0(包含),仅需要在脚本中添加/bin/sh ${PODS_ROOT}/RangersAPM/RangersAPM/APMInsight_DSYMUploader.sh "APMInsightID"
,并将命令中的 APMInsightID 替换为您的应用ID即可,这种方式在Debug模式和模拟器编译时不会上传符号表。
如果您接入的SDK为较低版本且不想升级为更高版本,或者需要Debug模式和模拟器编译也能自动上传符号表。
下载脚本文件。
将下载的脚本文件内容复制到Run Script中,并修改APMInsight_APP_ID为您的应用ID。
根据需要,修改脚本中的 UPLOAD_DEBUG_SYMBOLS 和 UPLOAD_SIMULATOR_SYMBOLS字段。
使用如下接口,您可以添加一些自定义的环境或业务信息,如用户email、定位信息、业务场景等,这些信息会随着SDK的日志一同上报,帮助您排查一些问题,您可以在应用性能监控全链路版上的日志详情页看到这些信息。
#import <RangersAPMForSDK.h> //获取之前初始化的实例,需要先参考 初始化 章节初始化一个实例 RangersAPMForSDK *sdkMonitor = [RangersAPMForSDK monitorWithSDKID:@"{{app_id}}"]; //自定义key-value [sdkMonitor setCustomContextValue:@"sdk1context1value" forKey:@"sdk1context1key"];
除了上报自定义数据外,您也可以在需要的时候添加一些自定义维度,作为自定义的的筛选项,用来筛选某些特定情况下的日志。
#import <RangersAPMForSDK.h> //获取之前初始化的实例,需要先参考 初始化 章节初始化一个实例 RangersAPMForSDK *sdkMonitor = [RangersAPMForSDK monitorWithSDKID:@"{{app_id}}"]; //自定义筛选项 [sdkMonitor setCustomFilterValue:@"sdk1filter1value" forKey:@"sdk1filter1key"];
连续崩溃保护需要接入SDK的BootingProtectLite模块,参考Subspecs说明。使用此功能,您可以在应用发生连续的崩溃时进行一些本地的处理,例如清理缓存、删除文件等。
#import <RangersAPMForSDK+BootingProtect.h> //获取之前初始化的实例,需要先参考 初始化 章节初始化一个实例 RangersAPMForSDK *sdkMonitor = [RangersAPMForSDK monitorWithSDKID:@"{{app_id}}"]; [sdkMonitor startProtectWithBootingThreshold:10 bootingCrashHandler:^(RangersAPMBootingInfo * _Nonnull info) { /** 对连续异常的场景进行防护策略,这里的demo只输出了一些log,您可以在自己的应用中做一些本地缓存清理或其他策略。可以针对不同的异常发生次数制定不同的策略。 */ if (info.consecutiveExceptionTimes >= 1) { NSLog(@"⚠️Consecutive exception 1 time"); } else if (info.consecutiveExceptionTimes >= 3) { NSAssert(NO, @"⚠️Consecutive exception 3 times !!!"); } /** 除了对连续异常进行防护,您也可以针对不同的异常类型执行不同的防护策略。 */ if (info.crashTimes >= 1) { NSLog(@"⚠️Consecutive crash 1 time"); } if (info.watchdogTimes >= 1) { NSLog(@"⚠️Consecutive watchdog 1 time"); } }];
应用性能监控全链路版 SDK 内部默认使用自身服务生成的 deviceID 用以标识一台设备和用户,如果您不想使用应用性能监控全链路版 SDK 自身服务生成的 deviceID,我们也提供了其他方案供您参考,使用其他方案则 SDK 内部不会再次请求自身服务生成 deviceID。
如果您想要使用自己的deviceID,请参考如下方式:
配置应用性能监控全链路版 SDK初始化config的deviceIDSource。
在应用性能监控全链路版 SDK初始化方法之后,设置deviceID。
#import <RangersAPMForSDK.h> - (void)init { RangersAPMForSDKConfig *sdkConfig = [RangersAPMForSDKConfig configWithSDKID:@"{{app_id}}" appToken:@"{{app_token}}"]; sdkConfig.channel = @"Cocoapods"; sdkConfig.hostAppID = @"host_app_id"; //宿主APP标识 sdkConfig.sdkVersion = @"1.0.0"; sdkConfig.deviceIDSource = RAPMDeviceIDSourceFromUser; RangersAPMForSDK *sdkMonitor = [[RangersAPMForSDK alloc] initWithConfig:sdkConfig]; [sdkMonitor setDeviceID:@"MYDEVICEID"]; return YES; }