本文翻译自外文文章。原文链接,原文发表于2021-05-10

前言

很长一段时间内,相比web应用,iOS应用被认为非常轻量,不用考虑性能问题。然而事到如今,iOS应用原来越大、越来越复杂、表面下的工作越来越多。远程监控你的应用如何在不同的设备上运行,显得尤为重要。

一些iOS监控工具已经存在好久了。例如,你可以获知当前设备运行正在使用WiFi还是蜂窝网。还有很多的第三方工具,如Firebase、New Relic,可以帮助你知晓用户真正的体验是什么样的。

有很多信息可以用来帮助iOS开发者。苹果在iOS 13推出了MetricKit,使得从设备获取诊断信息以及远程监控更加容易。在这份指导中,你可以使用Xcode来接收诊断信息,从而探索MetricKit APIs.

了解MetircKit

有了MetricKit,你可以从操作系统获取物理硬件的诊断信息。MetricKit每24小时发送一份JSON格式的运维数据。你可以将数据传送给你的服务器。之后你可以对数据进行可视化和分享,从而改进你的iOS应用。

iOS 14新增API

MetricKit在iOS 14有了很多重大改进。

首先,退出了一个全新payload类型MXDiagnosticPayload,而在此之前只有MXMetricPayload一种类型。MXDiagnosticPayload提供更多信息,如crashes和exceptions。

另外,苹果退出了一种新的metric,叫MXAppExitMetric,代表了iOS应用进程退出类型,帮你知晓用户为啥退出应用以及应用在退出时的状态。

开始

可以在原文链接中下载本文所用材料。

材料是一个名为Shopping Trolley的应用,展示一个水果购物列表。

注意:MetricKit只能运行在真机上,不支持模拟器。所以需要一个真实设备才能运行材料里的工程。

Shopping Trolley

打开ShoppingListTableViewController.swift,加上如下一行:

import MetricKit

现在,你就可以访问MetricKit framework了。

将下面的代码添加到viewDidLoad()中:

let metricManager = MXMetricManager.shared
metricManager.add(self)

这会访问framework提供的MetricManager。之后添加self,也就是将ShoppingListTableViewController作为metricManager的订阅者,从而收听到系统的metrics payload。

接下来,你需要在ShoppingListTableViewController.swift末尾加上如下代码:

extension ShoppingListTableViewController: MXMetricManagerSubscriber {
  func didReceive(_ payloads: [MXMetricPayload]) {
    guard let firstPayload = payloads.first else { return }
    print(firstPayload.dictionaryRepresentation())
  }

  func didReceive(_ payloads: [MXDiagnosticPayload]) {
    guard let firstPayload = payloads.first else { return }
    print(firstPayload.dictionaryRepresentation())
  }
}

为ShoppingListTableViewController添加一个extention以遵循MXMetricManagerSubscriber,这个协议有两个方法从MetricKit接收payload。在本例中,收到payload之后仅仅做了打印。在实际使用中,可以将它们上传到到你的服务器。

理解 MetricKit API

Framework接口都非常简单直接,包括: - 一个manager类和订阅协议 - 每个metric和诊断都有一个类 - payload类 - 度量单位的类,如cellular bar - 展示数据的类,如直方图

理解MXMetricManager

MXMetricManager是MetricKit的核心。这是一个共享对象,用来管理订阅以接收来自硬件的metric日报。

访问MXMetricManager单例后,MetricKit开始为你的应用收集报告。你需要调用add:方法,并传入实现了MXMetricManagerSubscriber协议的类来启动接收metric报告。

系统每天至多发送一份报告。每份报告包含了过去24小时内的metric。如果之前报告周期内有发送失败的数据,也会一并包含在本次报告里。

Manager还提供remove:方法,通过此方法可以随时移除订阅。

实现MXSignpostMetric

使用MetricKit的一大好处是,你可以将自己的metric放进那些苹果提供了“out of box”的应用。这点非常强大,你可以在报告中添加自定义的metric,能让你对性能有更深的探索。

当用户加载ShoppingListTableViewController时我们添加一个自定义metric:

let fruitsLogHandle = MXMetricManager.makeLogHandle(category: "Fruits")

上面的代码会生成一个handle,类似一个桶,用于装载你的metric。

在viewDidLoad()方法中添加下面的代码:

mxSignpost(
  .event,
  log: fruitsLogHandle,
  name: "Loading Fruits TableViewController")

当ViewController加载完成时,mxSignpost向fruitsLogHandle内记录了一个自定义metric。这是一个很简单的实例,你自己去扩展。例如,你的应用有一个视频播放器,在流开始和结束时做记录。

实现MXMetricPayload

你可以从Metric中获取两个不同的payload。MXMetricPayload封装了每日的metric报告。注:必须在真机上才能收到报告。如果你没有真机,我们为你提供了一个示例JSON。

在真机上将工程运行起来之后,在Xcode中点击 DebugSimulate MetricKit Payloads

Simulate MetricKit Payloads

上图中的点击会触发一个示例日报。你可以在控制台中看到打印出的两个payload。

 AnyHashable("cellularConditionMetrics"): {
    cellConditionTime =     {
        histogramNumBuckets = 3;
        histogramValue =         {
            0 =             {
                bucketCount = 20;
                bucketEnd = "1 bars";
                bucketStart = "1 bars";
            };
            1 =             {
                bucketCount = 30;
                bucketEnd = "2 bars";
                bucketStart = "2 bars";
            };
            2 =             {
                bucketCount = 50;
                bucketEnd = "3 bars";
                bucketStart = "3 bars";
            };
        };
    };

上面的输出来自cellularConditionMetrics payload。payload的这个部分提供了用户过去24小时内使用你App时的蜂窝网环境数据。代码还告诉你处于一个bar、两个bar、三个bar服务的次数。你可以使用bucketCount来绘制直方图。这对于知晓用户在特定信号bar上持续的平均时间将非常有用。

理解MXDiagnosticPayload

MXDiagnosticPayload封装了如下设备诊断信息:

  • 性能:crash和exception报告
  • 响应:应用挂起率
  • 磁盘访问:磁盘读写
[AnyHashable("crashDiagnostics"): <__NSArrayM 0x283764390>(
{
    callStackTree =     {
        callStackPerThread = 1;
        callStacks =         (
                        {
                callStackRootFrames =                 (
                                        {
                        address = 74565;
                        binaryName = testBinaryName;
                        binaryUUID = "BE6FD323-B011-4E67-925B-A60362A1ADFA";
                        offsetIntoBinaryTextSegment = 123;
                        sampleCount = 20;
                    }
                );
                threadAttributed = 1;
            }
        );
    };
    diagnosticMetaData =     {
        appBuildVersion = 1;
        appVersion = "1.0";
        deviceType = "iPhone13,3";
        exceptionCode = 0;
        exceptionType = 1;
        osVersion = "iPhone OS 14.4 (18D52)";
        platformArchitecture = arm64e;
        regionFormat = GB;
        signal = 11;
        terminationReason = "Namespace SIGNAL, Code 0xb";
        virtualMemoryRegionInfo = "0 is not in any region.  Bytes before following region: 4000000000 REGION TYPE                      START - END             [ VSIZE] PRT/MAX SHRMOD  REGION DETAIL UNUSED SPACE AT START ---> __TEXT                 0000000000000000-0000000000000000 [   32K] r-x/r-x SM=COW  ...pp/Test";
    };
    version = "1.0.0";
}

上面的输出来自crashDiagnostics,捕捉到一个用户遇到的crash。其中diagnosticMetaData包含了系统版本号等有用信息。因为捕捉到的是个crash,所以数据还包含了callStackTree,下一节将对此做更进一步的说明。

理解MXCallStackTree

对MetricKit探索得越多,你越会发现它对于苹果自带工具有很大的增强,如Xcode Organizer。即使用户离开你的App,在Organizer页面仍旧可以看到MetricKit报告,即使你的App没有引入MetricKit。

MetricKit Xcode Organizer.png

如你所见(上图),这个App处于活跃状态并且在Xcode右侧能看到真实数据。因为苹果免费提供了很多数据。不过,要是再能引入MetricKit的话,你就能更充分的利用这些数据并且将它们与你自己的metric关联、更灵活的展示数据。

MXCallStackTree是使用MetricKit的一大好处,不仅可以知晓crash和exception,还能获得JSON格式的StackTree。对于解决bug非常有帮助。

理解MXAppExitMetric

MXAppExitMetric是iOS14引入的,App退出类型包括:

  • 前台退出
  • 后台退出

用户退出App的原因有很多。有时是用户有意关闭App,有时是操作系统关闭了App,或是低内存和exception导致App退出。

在Organizer里查阅

苹果在Organizer里提供免费的可视化数据,数据来自于App Store Connect。

尽管你在Organizer里无法获取到使用MetricKit时那么全的数据,但还是能获取到这些: Crash

  • 写磁盘
  • 电量使用
  • 电池使用
  • 挂起率
  • 加载时间
  • 内存
  • 滚动

创建表格和报告

上面章节中的内容都可以表现为表格。通常,数据以直方图表示。

Organizer Scrolling

上图是一个Scrolling metric的直方图,展示了用户开始滚动的时间。上图中,可以看到在持续下降后,数据突然飙升。也许你应当研究下这个现象的原因,以及为何版本间有如此差异。

上图是一个在App Store上线的App的真实数据。数据有很多细节。你可以看到不同因素对于用电量的影响:Networking、Display等。上图示例中,该App耗电量占每天总量的8.29% 虽然数据不能给出准确的基准值,但是可以给你很多性能改进的依据。


Comments

comments powered by Disqus