MetricKit 详解

MetricKit 详解

一、概览

MetricKit 从 iOS 13 系统开始引入。借助 MetricKit,您可以接收系统捕获的诊断报告、电源和性能指标。

1.1 基本特性

  • 报告频率:已注册的应用每天最多只能接收一次包含前 24 小时数据的报告
  • 即时分发:iOS 15 以上和 macOS 12 以上系统会立即分发诊断报告
  • 系统级监控:MetricKit 由系统自动收集数据,无需手动埋点

1.2 使用场景

MetricKit 主要用于:
– 监控应用的性能指标
– 分析应用崩溃和异常退出
– 追踪电池消耗情况
– 检测应用响应性问题
– 监控磁盘 I/O 性能

二、指标分类

MetricKit 提供了四大类指标:

2.1 Battery Metric(电池指标)

Battery Metric 用于监控应用的电池消耗情况,包括:

  • 信号:信号强度对电池的影响
  • CPU:CPU 使用情况
  • GPU:GPU 使用情况
  • 位置:定位服务的使用情况
  • 网络:网络请求对电池的影响
  • CPU 异常诊断:CPU 异常使用导致的电池消耗

2.2 Performance Metric(性能指标)

Performance Metric 用于监控应用的性能表现,包括应用退出统计、运行时间、内存使用、启动诊断和崩溃诊断。

2.2.1 应用退出统计

分类 名称 描述 前台 后台
正常退出 cumulativeNormalAppExitCount 正常退出 ✅ ✅
abort cumulativeAbnormalExitCount abort 退出 ✅ ✅
crash cumulativeBadAccessExitCount 内存访问违规退出 ✅ ✅
crash cumulativeIllegalInstructionExitCount 无效指令退出 ✅ ✅
系统终止 cumulativeAppWatchdogExitCount watchdog 强杀 ✅ ✅
系统终止 cumulativeCPUResourceLimitExitCount 超过 CPU 资源使用限制 ❌ ✅
系统终止 cumulativeMemoryResourceLimitExitCount 超过内存资源限制 ✅ ✅
系统终止 cumulativeMemoryPressureExitCount 内存压力退出 ❌ ✅
系统终止 cumulativeSuspendedWithLockedFileExitCount 持有文件锁并且处于挂起状态 ❌ ✅
超时 cumulativeBackgroundTaskAssertionTimeoutExitCount 后台任务超时 ❌ ✅

说明:
– ✅ 表示该指标在该状态下可用
– ❌ 表示该指标在该状态下不可用

2.2.2 其他性能指标

  • 运行时间:应用运行时长统计
  • 内存:内存使用情况统计
  • 启动诊断:应用启动性能分析
  • Crash 诊断:崩溃堆栈和诊断信息

2.3 Responsive Metric(响应性指标)

Responsive Metric 用于监控应用的响应性能,包括:

  • 启动:应用启动响应时间
  • 响应:用户交互响应时间
  • 动画:动画流畅度指标
  • 卡顿诊断:卡顿堆栈和诊断信息

2.4 Disk Access Metric(磁盘访问指标)

Disk Access Metric 用于监控应用的磁盘 I/O 性能,包括:

  • 磁盘 I/O:磁盘读写操作统计
  • 磁盘写诊断:磁盘写入性能分析

三、MetricKit 上报逻辑

3.1 堆栈上报方式

MetricKit 支持两种堆栈上报方式:

  1. 树形堆栈展开为列表堆栈进行上报
  2. 将树形结构的堆栈信息展开为线性列表
  3. 便于后端解析和分析

  4. 原始堆栈直接写文件

  5. 保留完整的树形结构信息
  6. 用于详细分析和调试

3.2 优化策略

堆栈上报优化:
– Crash 堆栈:使用列表堆栈进行上报,便于快速定位问题
– 其他堆栈:使用树形结构进行上报,保留完整的调用关系

补充上报:
– 动画指标:补充动画相关的性能数据
– 启动诊断:补充应用启动过程的详细诊断信息

四、使用示例

4.1 基本使用

#import <MetricKit/MetricKit.h>

@interface AppDelegate () <MXMetricManagerSubscriber>
@end

@implementation AppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    // 订阅 MetricKit 报告
    [[MXMetricManager sharedManager] addSubscriber:self];
    return YES;
}

#pragma mark - MXMetricManagerSubscriber

- (void)didReceiveMetricPayloads:(NSArray<MXMetricPayload *> *)payloads {
    for (MXMetricPayload *payload in payloads) {
        // 处理性能指标
        [self handlePerformanceMetrics:payload.performanceMetrics];
        // 处理电池指标
        [self handleBatteryMetrics:payload.batteryMetrics];
        // 处理响应性指标
        [self handleResponsivenessMetrics:payload.responsivenessMetrics];
        // 处理磁盘访问指标
        [self handleDiskIOMetrics:payload.diskIOMetrics];
    }
}

- (void)handlePerformanceMetrics:(MXPerformanceMetrics *)metrics {
    // 处理性能指标
    MXAppExitMetrics *exitMetrics = metrics.applicationExitMetrics;
    NSLog(@"Normal exits: %lu", exitMetrics.cumulativeNormalAppExitCount);
    NSLog(@"Abnormal exits: %lu", exitMetrics.cumulativeAbnormalExitCount);
}

- (void)handleBatteryMetrics:(MXBatteryMetrics *)metrics {
    // 处理电池指标
    NSLog(@"CPU usage: %@", metrics.cpuMetrics);
}

- (void)handleResponsivenessMetrics:(MXResponsivenessMetrics *)metrics {
    // 处理响应性指标
    NSLog(@"Hang count: %lu", metrics.hangCount);
}

- (void)handleDiskIOMetrics:(MXDiskIOMetrics *)metrics {
    // 处理磁盘 I/O 指标
    NSLog(@"Cumulative logical writes: %llu", metrics.cumulativeLogicalWrites);
}

@end

4.2 诊断报告处理

- (void)didReceiveDiagnosticPayloads:(NSArray<MXDiagnosticPayload *> *)payloads {
    for (MXDiagnosticPayload *payload in payloads) {
        // 处理崩溃报告
        for (MXCrashDiagnostic *crashDiagnostic in payload.crashDiagnostics) {
            NSLog(@"Crash reason: %@", crashDiagnostic.crashReason);
            NSLog(@"Exception type: %@", crashDiagnostic.exceptionType);
            NSLog(@"Exception code: %@", crashDiagnostic.exceptionCode);
        }

        // 处理 CPU 异常报告
        for (MXCPUExceptionDiagnostic *cpuException in payload.cpuExceptionDiagnostics) {
            NSLog(@"CPU exception: %@", cpuException);
        }

        // 处理磁盘写入异常报告
        for (MXDiskWriteExceptionDiagnostic *diskException in payload.diskWriteExceptionDiagnostics) {
            NSLog(@"Disk write exception: %@", diskException);
        }

        // 处理挂起报告
        for (MXHangDiagnostic *hangDiagnostic in payload.hangDiagnostics) {
            NSLog(@"Hang duration: %f", hangDiagnostic.hangDuration);
            NSLog(@"Hang call stack: %@", hangDiagnostic.callStackTree);
        }

        // 处理启动报告
        for (MXLaunchDiagnostic *launchDiagnostic in payload.launchDiagnostics) {
            NSLog(@"Launch duration: %f", launchDiagnostic.launchDuration);
        }
    }
}

五、注意事项

5.1 系统要求

  • 最低版本:iOS 13.0+ / macOS 12.0+
  • 即时分发:iOS 15.0+ / macOS 12.0+ 支持即时分发报告
  • 报告频率:每天最多接收一次报告

5.2 数据隐私

  • MetricKit 收集的数据仅包含性能指标,不包含用户隐私信息
  • 所有数据都在设备本地处理,不会上传到 Apple 服务器
  • 开发者需要自行实现数据上报逻辑

5.3 性能影响

  • MetricKit 由系统自动管理,对应用性能影响极小
  • 建议在后台线程处理报告数据,避免阻塞主线程
  • 合理设置数据上报频率,避免过度上报

六、最佳实践

  1. 及时处理报告:在收到报告后及时处理,避免数据堆积
  2. 数据聚合:对相同类型的指标进行聚合分析
  3. 异常告警:设置阈值,对异常指标进行告警
  4. 数据可视化:将指标数据可视化展示,便于分析
  5. 持续监控:建立持续监控机制,及时发现性能问题

留下评论

您的电子邮箱地址不会被公开。 必填项已用 * 标注

→ Index