iOS使用 Metrickit 收集崩溃日志
Metrickit 是什么
MetricKit 是 苹果在 iOS13系统开始引入的用来汇总和分析有关异常和崩溃诊断以及电源和性能指标的每个设备的报告。
为什么要用 MetricKit 收集崩溃日志
当前市面上的开源框架,如 KSCrash,PLCrashReport 等框架都有一些崩溃不能抓取;比如PLCrashReport 不能抓取栈溢出的崩溃,不能抓取SIGKILL, SIGQUIT等信号量的崩溃。KSCrash 对SIGKILL的崩溃抓取也只能抓取一部分
Metrickit 缺陷
1.目前只支持 iOS14 以后的崩溃日志收集
2.崩溃日志没有返回具体的崩溃时间和启动时间,崩溃场景信息除了堆栈外没有其余信息
3.如果使用了段迁移编译技术,主程序 macho 的地址和 uuid 无法匹配。
iOS14的崩溃日志是24小时会回调通知一次,时效性低;iOS15 之后,崩溃日志会在下次启动之后就返回,但经验证,有的会立即回调,有的则不然,规律不可琢磨
开始接入
1.添加 MetricKit
2.添加 MetricKit 监听者
if (@available(iOS 14.0, *)) {
MXMetricManager *manager = [MXMetricManager sharedManager];
if (self && manager && [manager respondsToSelector:@selector(addSubscriber:)]) {
[manager addSubscriber:self];
}
}
3. 监听者实现 MXMetricManagerSubscriber 协议方法, payloadDic 里面包含着上次本应用发生的崩溃日志堆栈和信息
// 苹果如果有数据数据,注册监听之后就会回调
- (void)didReceiveDiagnosticPayloads:(NSArray<MXDiagnosticPayload *> * _Nonnull)payloads API_AVAILABLE(ios(14.0)){
if (@available(iOS 14.0, *)) {
for (MXDiagnosticPayload *payload in payloads) {
NSDictionary *payloadDic = [payload dictionaryRepresentation];
});
}
}
}
4.日志组装关键代码示例
NSArray *callStackRootFrames = [dicFrame ArrayValueForKey:kMetrkitCallStackRootFramesKey];
if (callStackRootFrames.count <= 0) {
continue;
}
NSDictionary *dicZero = [callStackRootFrames ObjectAtIndex:0];
int rootIndex = 0;
while (dicZero && dicZero.count > 0) {
NSString *binaryUUID = [dicZero stringValueForKey:kMetrkitBinaryUUIDKey];
NSString *binaryName = [dicZero stringValueForKey:kMetrkitBinaryNameKey];
long long baseAdd = [[dicZero NumberValueForKey:kMetrkitOffsetIntoBinaryTextSegmentKey] longLongValue];
long long address = [[dicZero numberValueForKey:kMetrkitAddressKey] longLongValue];
NSArray *subFrames = [dicZero arrayValueForKey:kMetrkitSubFramesKey];
[strStack appendFormat:@"%d %@ 0x%llx 0x%llx + %lld\n", rootIndex, binaryName, baseAdd, address, address - model.baseAddress];
rootIndex++;
if (subFrames && subFrames.count >= 0) {
dicZero = [subFrames ObjectAtIndex:0];
} else {
dicZero = nil;
}
MetricKit 返回字段含义及详情
JSON总格式
key | 值类型 | 解释 | demo值 |
---|
crashDiagnostics | Array | 记录的崩溃 | 见下面详情 | hangDiagnostics | Array | 记录的卡顿信息 | 见下面详情 | cpuExceptionDiagnostics | Array | 记录的cpu异常信息 | 见下面详情 | diskWriteExceptionDiagnostics | Array | 记录的磁盘写入异常信息 | 见下面详情 | timeStampBegin | Date | 记录事件开始间隔 | 2021-12-01 09:06:57 +0000 | timeStampEnd | Date | 记录事件结束间隔 | 2021-12-01 09:06:57 +0000 |
crashDiagnostics 详情
每一个崩溃为一个字典,具体内容为下
key | 值类型 | 解释 | demo值 |
---|
version | String | Metrkit 框架版本 | 1.0.0 | diagnosticMetaData | 字典 | 崩溃的一些核心信息(含App信息) | 见二级详情 | callStackTree | 字典 | 堆栈相关信息 | 见二级详情 |
diagnosticMetaData 字典详情
key | 值类型 | 解释 | demo值 |
---|
appBuildVersion | String | build | 12.29.0.1 | appVersion | String | app 版本 | 12.29.0 | regionFormat | String | 区域代码 | CN | exceptionType | String | 异常类型 | 1 | osVersion | String | iOS 操作系统 | iPhone OS 15.1 (19B74) | deviceType | String | Model | iPhone13,1 | bundleIdentifier | String | bundle | com.xxx.xxxxx | exceptionCode | String | exception Code | 1 | signal | String | 信号量符号 | 11 | platformArchitecture | String | cpu 架构 | arm64e | virtualMemoryRegionInfo | String | 虚拟内存信息 | 0x10 is not in any region. Bytes before following region: 433969560 REGION TYPE START- END [ VSIZE] PRT/MAX SHRMOD REGION DETAIL UNUSED SPACE AT START—> __**TEXT 102aa8000-11012c000 [214.5M] r-x/r-x SM=COW …p/xxxxx |
|terminationReason|String|崩溃原因| RBSTerminateContext domain:10 code:0x8BADF00D explanation:scene-update watchdog transgression: application<com.xxx.xxxx> :6308 exhausted real (wall clock) time allowance of 10.00 seconds|
callStackTree 字典详情
key | 值类型 | 解释 | demo值 |
---|
callStacks | Array | 每个线程详细堆栈 | 见三级详情 | callStackPerThread | BOOL | 是否只收集单线程 | 见二级详情 |
callStacks 数组详情
数组中元素为字典,详情如下
key | 值类型 | 解释 | demo值 |
---|
threadAttributed | BOOL | 是否是崩溃线程 | true | callStackRootFrames | Array | 单线程堆栈详细信息 | 见下一级级详情 |
callStackRootFrames 数组详情
数组中元素为字典,详情如下
key | 值类型 | 解释 | demo值 |
---|
binaryUUID | String | uuid | DC6B1885-FB91-34C4-A9FC-A539C17A08A7 | offsetIntoBinaryTextSegment | int | 基于二进制macho的偏移,十进制 | 4339695616 | sampleCount | int | 未知 | 1 | binaryName | String | 二进制macho的名称 | xxx | address | int | 二进制macho的基址 | 4446737180 | subFrames | Array | 诱发此次崩溃的上一级线程 | 有可能循环递归callStackRootFrames内容 |
diskWriteExceptionDiagnostics 详情
每一个崩溃为一个字典,具体内容为下
key | 值类型 | 解释 | demo值 |
---|
version | String | Metrkit 框架版本 | 1.0.0 | diagnosticMetaData | 字典 | 崩溃的一些核心信息(含App信息) | 见二级详情 | callStackTree | 字典 | 堆栈相关信息 | 见二级详情 |
diagnosticMetaData 字典详情
key | 值类型 | 解释 | demo值 |
---|
appBuildVersion | String | build | 12.29.0.1 | appVersion | String | app 版本 | 12.29.0 | regionFormat | String | 区域代码 | CN | osVersion | String | iOS 操作系统 | iPhone OS 15.1 (19B74) | deviceType | String | Model | iPhone13,1 | bundleIdentifier | String | bundle | com.xxx | writesCaused | String | 写原因 | 2,000字节 | platformArchitecture | String | cpu 架构 | arm64e |
callStackTree 字典详情
key | 值类型 | 解释 | demo值 |
---|
callStacks | Array | 每个线程详细堆栈 | 见三级详情 | callStackPerThread | BOOL | 是否只收集单线程 | 见二级详情 |
callStacks 数组详情
数组中元素为字典,详情如下
key | 值类型 | 解释 | demo值 |
---|
threadAttributed | BOOL | 是否是触发线程 | true | callStackRootFrames | Array | 单线程堆栈详细信息 | 见下一级级详情 |
callStackRootFrames 数组详情
数组中元素为字典,详情如下
key | 值类型 | 解释 | demo值 |
---|
binaryUUID | String | uuid | DC6B1885-FB91-34C4-A9FC-A539C17A08A7 | offsetIntoBinaryTextSegment | int | 基于二进制macho的偏移,十进制 | 4339695616 | sampleCount | int | 未知 | 1 | binaryName | String | 二进制macho的名称 | xxxxx | address | int | 二进制macho的基址 | 4446737180 | subFrames | Array | 诱发此次崩溃的上一级线程 | 有可能循环递归callStackRootFrames内容 |
cpuExceptionDiagnostics 详情
每一个崩溃为一个字典,具体内容为下
key | 值类型 | 解释 | demo值 |
---|
version | String | Metrkit 框架版本 | 1.0.0 | diagnosticMetaData | 字典 | 崩溃的一些核心信息(含App信息) | 见二级详情 | callStackTree | 字典 | 堆栈相关信息 | 见二级详情 |
diagnosticMetaData 字典详情
key | 值类型 | 解释 | demo值 |
---|
appBuildVersion | String | build | 12.29.0.1 | appVersion | String | app 版本 | 12.29.0 | regionFormat | String | 区域代码 | CN | osVersion | String | iOS 操作系统 | iPhone OS 15.1 (19B74) | deviceType | String | Model | iPhone13,1 | bundleIdentifier | String | bundle | com.xxx | totalCPUTime | String | cpu时间 | 20s | totalSampledTime | String | CPU总采样时间 | 20s | platformArchitecture | String | cpu 架构 | arm64e |
callStackTree 字典详情
key | 值类型 | 解释 | demo值 |
---|
callStacks | Array | 每个线程详细堆栈 | 见三级详情 | callStackPerThread | BOOL | 是否只收集单线程 | 见二级详情 |
callStacks 数组详情
数组中元素为字典,详情如下
key | 值类型 | 解释 | demo值 |
---|
threadAttributed | BOOL | 是否是触发线程 | true | callStackRootFrames | Array | 单线程堆栈详细信息 | 见下一级级详情 |
callStackRootFrames 数组详情
数组中元素为字典,详情如下
key | 值类型 | 解释 | demo值 |
---|
binaryUUID | String | uuid | DC6B1885-FB91-34C4-A9FC-A539C17A08A7 | offsetIntoBinaryTextSegment | int | 基于二进制macho的偏移,十进制 | 4339695616 | sampleCount | int | 未知 | 1 | binaryName | String | 二进制macho的名称 | xxxxx | address | int | 二进制macho的基址 | 4446737180 | subFrames | Array | 诱发此次崩溃的上一级线程 | 有可能循环递归callStackRootFrames内容 |
hangDiagnostics 详情
每一个崩溃为一个字典,具体内容为下
key | 值类型 | 解释 | demo值 |
---|
version | String | Metrkit 框架版本 | 1.0.0 | diagnosticMetaData | 字典 | 崩溃的一些核心信息(含App信息) | 见二级详情 | callStackTree | 字典 | 堆栈相关信息 | 见二级详情 |
diagnosticMetaData 字典详情
key | 值类型 | 解释 | demo值 |
---|
appBuildVersion | String | build | 12.29.0.1 | appVersion | String | app 版本 | 12.29.0 | regionFormat | String | 区域代码 | CN | osVersion | String | iOS 操作系统 | iPhone OS 15.1 (19B74) | deviceType | String | Model | iPhone13,1 | bundleIdentifier | String | bundle | com.xxx | hangDuration | String | 卡顿时间 | 20s | platformArchitecture | String | cpu 架构 | arm64e |
callStackTree 字典详情
key | 值类型 | 解释 | demo值 |
---|
callStacks | Array | 每个线程详细堆栈 | 见三级详情 | callStackPerThread | BOOL | 是否只收集单线程 | 见二级详情 |
callStacks 数组详情
数组中元素为字典,详情如下
key | 值类型 | 解释 | demo值 |
---|
threadAttributed | BOOL | 是否是触发线程 | true | callStackRootFrames | Array | 单线程堆栈详细信息 | 见下一级级详情 |
callStackRootFrames 数组详情
数组中元素为字典,详情如下
key | 值类型 | 解释 | demo值 |
---|
binaryUUID | String | uuid | DC6B1885-FB91-34C4-A9FC-A539C17A08A7 | offsetIntoBinaryTextSegment | int | 基于二进制macho的偏移,十进制 | 4339695616 | sampleCount | int | 未知 | 1 | binaryName | String | 二进制macho的名称 | xxxxx | address | int | 二进制macho的基址 | 4446737180 | subFrames | Array | 诱发此次崩溃的上一级线程 | 有可能循环递归callStackRootFrames内容 |
|