Swift 学习笔记——iOS 14 中的 Logger

Swift学习笔记——iOS 14中的Logger

今天看 WWDC 2020 的内容,突然发现一个有意思的东西,Apple 在 iOS 14、macOS 11等新系统中引入了一个全新将 log 输出到统一系统日志中的方法。

以往要输出日志,我一般使用 print 方法配合直接输出,或者还有一个 iOS 10 以上可用的 API os_log。但是使用 print 方法输出的日志难以定位问题,也很难对 log 进行筛选和处理。而 os_log 灵活性则有些不足,甚至无法使用字符串插值进行 log 输出,必须使用 StaticString。不过现在有个全新的 Logger 类可以解决这些问题了。

创建 Logger

1
2
3
import os

let logger = Logger(subsystem: "com.example.logtest", category: "Main")

创建一个 Logger 对象需要提供两个参数,subsystem 和 category 都是用于标示日志记录的,在筛选日志记录时可以帮助我们缩小范围。subsystem 一般使用我们 App 的 Bundle Identifier,通过 Bundle Identifier 可以快速筛选出所有我们 App 记录的 log。category 一般标明我们这条 log 的业务类型,方便我们继续缩小范围。

记录 log

有了 Logger 实例,我们就可以通过 Logger 实例记录 log 了。

1
2
3
4
5
6
7
8
9
10
11
12
13
logger.fault("fault")
logger.critical("critical")

logger.error("error")
logger.warning("warning")

logger.log("log")
logger.notice("notice")

logger.info("info")

logger.debug("debug")
logger.trace("trace")

根据日志级别不同,一共有9个记录 log 的方法。

日志级别

因为不是所有的日志都对我们寻找 bug 有作用,有的 log 目的仅仅只是看看某一个变量的值。所以我们需要日志级别,日志级别定义了特定消息的严重性和重要性,我们在每次记录 log 的时候指定一个日志级别,不同的日志级别对消息的处理方式不同。根据我们选择的日志级别,日志的持久化策略也不同。系统首先将所有日志存储在内存中,再将日志级别更严格的消息存储到磁盘中进行持久化,并在磁盘空间不足时优先删除重要性更低的日志。

OSLogType 中,提供了五个日志级别:

日志级别 持久化 备注
debug 不持久化 用于输出调试期间有用的信息
info 默认存储到内存中,当 faults 或 errors 发生时,存储到磁盘 用于输出有用但不是必须的信息
notice 持久化到磁盘 用于输出对定位错误有用的信息
error 持久化到磁盘 用于输出执行期间发生的错误
fault 持久化到磁盘 用于输出代码中的错误

回到之前9个记录 log 的方法,这些方法两两成对,fault(_:)critical(_:) 均输出 fault 级别的日志,error(_:)warning(_:) 输出 error 级别的日志,log(_:)notice(_:) 输出 notice 级别的日志,注意,notice 也是默认的日志级别。debug(_:)trace(_:) 输出 debug 级别的日志,info(_:) 输出 info 级别的日志。

日志格式化

我们可以通过指定输出变量的宽度、指定对齐方式、指定整型变量的进制、指定浮点变量的格式、指定小数的精度等使我们输出的日志更容易阅读。

1
2
3
4
5
6
7
8
let shapeType: String = getShapeType()
let selectedColor: String = getSelectedColor()
let bigNumber = 1.0234e30
let theAnswer = true

customLog.debug("Shape type: \(shapeType, align: .right(columns: 15)) Color: \(selectedColor, align: .left(columns: 10))")
customLog.info("The big number is \(bigNumber, format: .exponential(precision: 10, explicitPositiveSign: true, uppercase: false) )")
customLog.debug("The answer is \(theAnswer, format: .answer)")

还有更多的格式化选项,不一一介绍了。

隐藏日志敏感信息

因为日志有时会输出一些敏感的用户信息,并且任何拥有设备密码的人都可以获得日志,所以对于一些敏感信息,我们要使用 privacy 修饰符配置变量的输出。要使用修饰符也很简单,对于任何通过字符串插值插入的变量都可以设置。

1
logger.log("User name is \(username, privacy: .private)")

隐藏日志的敏感信息后,再采集日志时就只能看见 User name is <private>

但是有时候我们需要采集某一个特定敏感信息关联的日志,例如采集与该用户名相关的日志,如果这时候将用户名直接隐藏起来,我们就无从下手了。所以 privacy 修饰符还可以使用一个哈希对变量进行隐藏,最后输出变量的时候输出该变量的哈希值,这样的话我们就可以通过相同的哈希值筛选所有相关联的日志了。

需要注意的是,我在调试的时候发现 privacy 修饰符设置为 private 也不管用,还是直接明文输出。要注意的是如果调试设备与 Mac 是连接的状态,也就是直接在 Xcode 里的控制台查看日志的话,是会忽略隐私修饰符的。必须当设备不在调试状态的时候运行 App 记录下来的日志才是隐私的。

采集日志

将设备连接到控制台后,可以使用这条命令采集设备的日志,在采集到的日志中可以通过我们设定的 subsystemcategory 筛选日志。

1
log collect --device-name <divece-name> --start '2020-10-22 11:23:00' --output iPhoneLog.logarchive

Swift 学习笔记——iOS 14 中的 Logger
https://wenchanyuan.com/swift_logger_in_ios14/
作者
蟾圆
发布于
2020年10月21日
许可协议