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