vlambda博客
学习文章列表

探索 Swift 中的日志系统


Session:https://developer.apple.com/videos/play/wwdc2020/10168/

概括

这个 session 介绍了 Swift 统一 logging API 的最新版本。在这个视频中,你将学会:

  1. 在记录应用中的事件和错误的同时,保持日志信息的隐私性。

  2. 在不牺牲性能的情况下,使用功能强大又提供较好可读性的格式化数据选项。

  3. 收集和处理日志信息,以理解和调试应用中的意外行为。

在实际的开发任务中,我们难免会遇到一些难以复现的 bug。使用 log API 能帮助我们理解及处理这类疑难杂症。我们可以在日志记录的众多线索中理解 bug 产生的原因。

来看新版 log API

我们可以利用 log API 在应用运行时记录下重要的事件。记录下的日志会被操作系统存档,以方便我们之后读取这些日志。由于新版 log API 十分高效,它们可以在应用中广泛地使用,且不会拖慢应用的运行速度。仅需三步,就能在应用中使用 log API:

// 第一步:导入 os 模块
import os

// 第二步:创建 Logger 的实例
// subsystem 一般为 bundle identifier,以便从众多日志中辨别出:哪些是来自该应用的信息
// category 一般表示应用的某个模块,以便进一步辨别出:是来自该应用哪一模块的信息
let logger = Logger(subsystem: "com.example.Fruta", category: "giftcards")

func beginTask(url: URL, handler: (Data) -> Void) {
    launchTask(with: url) {
       handler($0)
    }

// 第三步:使用 log 函数
    logger.log("Started a task \(taskId)")
}

调用 log 函数与调用 print 函数有点相似,但其实有着本质上的不同。因为转换成字符串会很慢,所以 log 信息不会完全转换为字符串。编译器和 logging library 合作对此进行了高度优化。在这种优化之下,只有当 log 信息真正显示的时候,才会付出将其转换为字符串的成本。

新版 log API 支持众多数据类型

log 函数支持很多数据类型,比如数字类型 IntDouble 、Objective-C 对象及所有遵循 CustomStringConvertible 协议的类型。当在 log 消息中加入非数字类型的数据时,非数字类型的数据默认会被系统编辑,这是为了确保应用在实机运行时,不会泄漏个人信息。

看到这里,你可能在心里发问:那当在模拟器上运行时是怎么样的呢?根据 Apple Developer Forums 上的 回答:在 Xcode console 上显示的 log 信息始终不会被系统编辑,也就是说:如果应用从 Xcode 中启动,即使这条 log 信息是非公开的,在 console 中也会完全显示出来,以便开发者调试。

举个例子,这里是在记录类型为字符串的 accountNumber 变量:

但在输出的 log 信息中,accountNumber 被编辑为 <private>

我们也可以手动控制 log 信息为公开的:

探索 Swift 中的日志系统

在终端中收集日志信息

我们可以使用命令行来收集 log 信息。先把设备连接至电脑,再在终端中输入以下命令:

探索 Swift 中的日志系统

使用 log help collect  可以获得该命令行的使用帮助。

在收集完之后,在当前路径下会产生一个以 .logarchive 结尾的文件。我们可以使用系统自带的 Console App 浏览该文件。

当在 Console App 中浏览这个文件时,常常会在搜索栏输入关键词来过滤无关的信息。这里分享一个小技巧:在搜索栏输入关键词,输入完毕后,点击右下角的保存按钮,可以将关键词保存起来。

探索 Swift 中的日志系统
探索 Swift 中的日志系统
探索 Swift 中的日志系统

<<< 左右滑动见更多 >>>

下次搜索时,直接点击 log1 按钮,就自动填充搜索关键词,这样就可以免于重复输入搜索关键字。

在 Console App 中实时检视日志信息

使用 log collect 命令,我们可以在应用运行结束后,打开日志文件,阅读日志信息。在应用运行时,我们可以在 Xcode console 阅读日志信息。类似地,在应用运行时(无论是在真机上运行还是在模拟器上运行),我们也可以实时地在 Console App 里阅读日志信息!

只要在 Console App 内选中正确的设备即可(注:这里的设备包括真机和模拟器):

探索 Swift 中的日志系统

但在我自己动手尝试之后发现:即使在 Console App 的 Action 菜单里勾选 Include Debug Messages 选项,在 Xcode 中以模拟器的方式启动应用,Console App 里实时显示的日志信息竟然不包含 debug 等级的信息。然而,在 Xcode 中以真机的方式启动应用,Console App 里实时显示的日志信息却包含了 debug 等级的信息!

这可能是个 bug。我已经在 Apple Developer Forums 提交了 反馈。

日志等级

系统提供五种不同的日志记录等级:

  1. Debug level;在 debug 时使用

  2. Info level;在排查问题时辅助使用

  3. Notice level;也是默认日志记录等级,在排查问题时使用

  4. Error;在程序执行出错时使用

  5. Fault;在程序出现 bug 时使用

日志消息持久化

当我们要在应用运行之后读取日志时,只有那些可以持久化的日志消息才会被读取到。日志消息是否持久保存取决于日志记录等级。

探索 Swift 中的日志系统

经过我自己的测试,如果在 Console App 的 Action 菜单里勾选 Include Debug Messages 选项,在 .logarchive 文件里也能阅读到 debug 等级的日志信息,也就是说:在这种条件下,debug 等级的日志信息也是可以持久化保存的。

日志记录等级各自的性能也有差异:

探索 Swift 中的日志系统

处于 Debug 的日志记录等级的运行速度是很快的,所以在此等级的 log 函数内调用比较耗时的函数是安全的。

探索 Swift 中的日志系统

格式化日志信息

在 log API 里,我们往往会添加一些应用运行时产生的原始数据。而直接阅读这些原始数据是比较困难的,所以我们可以利用 log API 里的数据格式化功能来提高数据的可读性,且格式化功能对应用的运行时没有影响。

探索 Swift 中的日志系统

视频中展示的范例使用 log API 记录了 taskIDgiftCardIDserverIDseconds 这些原始数据。下图是未对原始数据进行格式化的日志信息:

探索 Swift 中的日志系统

可以看出,日志信息的可读性不佳:giftCardID 数据显示的宽度应当统一,这个宽度应为最长的 giftCardID 的长度;seconds 的精度应四舍五入至两位小数,以便比较。

探索 Swift 中的日志系统

对数据使用格式化选项之后,让我们再来看看:

探索 Swift 中的日志系统

格式化后的日志信息可读性大大提高,甚至可以直接拷贝数据到 Numbers 里,对数据作可视化分析:

探索 Swift 中的日志系统

除了上述提到的几个数据格式化的选项以外,还有很多其他的选项:

探索 Swift 中的日志系统

保护日志信息安全

当应用下载安装到用户手机上时,日志记录仍会进行。只要把有安装该应用的设备使用线缆连接到电脑上,任何人都可以查看日志信息,所以有关个人信息的日志绝对不能被标记为 .public

但是当我们需要比较两条非公开的信息是否一样时,这时候该怎么办呢?log API 还提供一种 equality-preserving hash 方法。在经过处理之后,不会显示真正的数据,但也能让我们判断这两条信息是否一样。

探索 Swift 中的日志系统

API 可用性

最新版的 log 函数可以使用字符串插值,而旧版只能使用格式占位符:

let userName = "Jack"
let userID = "15030225"
let balance = 123

override func viewDidLoad() {
   super.viewDidLoad()

   // 旧版的写法
   os_log("balance: %{private}d"log: .demo, type: .info, balance)
   os_log("user: %{private}@"log: .demo, type: .info, userName)
}

extension OSLog {
    static let demo = OSLog(subsystem: "demo", category: "sunset")
}

结语

  • 使用新版 log API 有助于帮助开发者理解难以复现的 bug。

  • 新版 log API 既有不俗的性能,又有丰富的数据格式化选项。

  • 手中的 print() 函数突然不香了!

推荐阅读

关注我们

我们是「老司机技术周报」,每周会发布一份关于 iOS 的周报,也会定期分享一些和 iOS 相关的技术。欢迎关注。

支持作者

WWDC 内参 系列是由老司机周报、知识小集合以及 SwiftGG 几个技术组织发起的。已经做了几年了,口碑一直不错。 主要是针对每年的 WWDC 的内容,做一次精选,并号召一群一线互联网的 iOS 开发者,结合自己的实际开发经验、苹果文档和视频内容做二次创作。