迁移到Swift统一日志版

创建者: Jared Sinclair

考虑将您的iOS或macOS应用程序从一堆NSLogprint语句迁移到新的(ish)统一日志记录系统? 继续阅读一些可能会让您感到惊讶的事实和花絮,以及一些有关如何充分利用过渡的建议。

如果不确定为什么要使用统一日志记录,则可以快速了解一些主要优点:

  1. 这是新标准。 统一日志记录系统是多年来在Apple平台上登录的最大变化。 它具有苹果公司打算作为未来日志记录方式的所有迹象。
  2. 在不牺牲日志覆盖率的情况下提高应用程序的性能。 新的日志记录系统从头开始设计,以限制日志记录传统上对生产代码产生的观察者影响。 您现在可以同时使用两种方式:全面的日志覆盖出色的性能。
  3. 升级调试工作流程。 可以使用自定义的子系统和类别来“标记”使用新API记录的消息,这些子系统和类别使Console.app能够使用富有表现力的搜索过滤器来显示或隐藏消息。 在调试跨越多个模块或过程的复杂问题时,这可以节省大量时间。

我可以继续讲其他好处,但是随着我们探索统一日志记录及其替代之间的区别,这些好处将变得显而易见。

统一日志记录与传统日志记录技术不同的四种方法

以下不是统一日志的新增功能或不同功能的详尽列表,而是一些关键差异,这些差异可能会对您从旧式日志记录功能迁移的方式产生巨大影响。

1:OSLog不是功能。 这是一种数据类型。

有时您会听到人们通俗地将统一日志称为“ oh-ess-log”,这可能会使您OSLogOSLog函数的Foundation文档的OSLog 。 没有一个。 NSLog的真正类似物是中定义的各种函数,例如os_log ,其中大多数都将os_log_t (也称为通过Swift覆盖的OSLog )作为参数。 OSLog类型用于关联相关消息,以便它们可以参与Console.app的搜索和过滤器功能。

2:Console.app是强制性的。[¹]

统一日志记录不会以纯文本或人类可读文件格式序列化日志消息。 相反,所有消息均以不透明的数据格式写入磁盘,只能通过在Console.app中打开日志归档文件(请参阅下面的获取方法)来读取。 Console.app可以解压缩存档的消息,以易于搜索和过滤的方式显示它们。 数据格式的不透明性与您可能熟悉的日志记录系统有很大的不同。 它是由Apple采取的,旨在限制传统上记录对性能和磁盘空间的有害影响。

3:在Swift中,您只能将StaticString作为日志消息传递。

以下Swift代码将无法编译:

这是因为Swift编译器将原本不受限制的字符串文字的隐式类型解析为String ,但是os_log函数需要一个StaticString 。 您可以通过为变量指定显式类型来解决此问题:

或通过消除变量:

静态字符串字符串也可以是C样式的格式字符串:

请注意,在记录消息时不能使用Swift的愉快的字符串插值:

将您的Swift代码从NSLog迁移到统一日志记录时,这可能是您遇到的最重大的偏离。 在用os_log调用替换每个NSLogprint调用的方式时,请做好git工作目录中的准备工作, os_log

可以记录String ,但只能作为静态格式字符串的参数来记录:

这种解决方法会带来很大的麻烦,我们将在接下来看到。

4:除非有例外,否则格式参数将为。

默认情况下,当您将String记录为格式参数时:

该消息将在Console.app中呈现为:

处理消息MyApp什么是?

要在生产日志中显示完整的日志消息,您必须将该格式参数明确标记为{public}

然后,日志消息将在您的生产日志中显示为未编辑:

处理消息MyApp什么是threeve?

另外,您可以在运行应用程序之前执行以下任一操作,而无需使用{public}范围修饰符来临时获得相同的效果:

某些参数类型不需要这些解决方法。 标量值(布尔值,整数等)在用作格式参数时将默认为隐式公共范围。 如果需要确保在生产中删除该值,并覆盖默认的公共范围,也可以将标量参数标记为{private}

注意事项

以下是我认为的最佳做法(不分先后顺序):

要做:注意日志级别(类型)。

有五种标准日志类型,大多数与统一日志系统定义的标准级别相对应。 这里是直接从官方文档中抄录的简短摘要:

  • default :使用此级别来捕获有关可能导致失败的事情的信息。
  • info :使用此级别来捕获对故障排除可能有用但并非必不可少的信息。
  • debug :使用此级别来捕获在开发过程中或对特定问题进行故障排除时可能有用的信息。
  • error :使用此日志级别来捕获流程级别的信息以报告流程中的错误。
  • fault :使用此级别来捕获系统级别或多进程信息以报告系统错误。

视情况选择最合适的类型,因为日志记录系统不会平等地对待任何两个给定类型。 此WWDC视频深入探讨了这些差异。

不要:在运输代码中使用OSLog.default。

您没有义务初始化自己的OSLog实例。 OSLog.default值可以作为一种基本方法使用,并且是os_log需要OSLog参数的函数的默认值。 但是,当您使用OSLog.default ,由于没有为子系统或类别提供任何值,因此筛选日志消息的能力受到限制:

初始化自己的OSLog ,会为其提供一个子系统和一个类别。 这样可以轻松过滤Console.app中的可见输出:

请:始终为子系统和类别命名。

在整个应用程序中遵循一致的命名约定。 值得花一些时间来研究Apple如何为自己的流程发出的日志选择它们的值,因为这应该可以告知您自己的约定。 这是我的建议,这些建议是根据Apple的既定模式得出的:

  1. 始终使用反向域名样式命名子系统。 苹果公司自己的所有日志都有以com.apple子系统,例如com.apple.Siricom.apple.coredata
  2. 如果将代码组织到框架中,请使用调用模块的捆绑包ID作为该模块中所有日志的子系统,例如com.company.MyApp用于应用程序级日志, com.company.MyApp.SomeFramework用于框架级日志。
  3. 请勿对类别名称使用反向域名样式。 而是使用易于理解的简短名称,例如“ Web Service”。
  4. 选择类别名称,以帮助缩小其模块内调用代码的范围,或可以关联跨越多个文件或子系统的相关日志。 例如,如果在自定义框架中具有仅特定于一个Authenticator类的日志,则可以为其日志指定类别名称Authenticator ,以供该类专用。 另外,如果您有很多与身份验证相关的工作跨越一个以上的类或多个框架,则可以使它们全部使用一个类似Authentication的类别名称,这将帮助您查看整个应用程序中的身份验证活动。

请勿:将日志隐藏在条件句后。

由于日志的呈现是在Console.app完成之后完成的,因此无需通过#if指令或其他条件以编程方式过滤日志消息。 使用适当的类型,简单,直接地记录所有内容,然后让系统处理其余部分。

可以:练习收集系统诊断信息。

真正擅长从野外获得硬件的系统诊断。 此处提供了完整的说明,但过程的要点是:

  • 按住一堆按钮。
  • 等待十分钟。
  • 导航到Settings.app内部的屏幕
  • 点击压缩的sysdiagnose,然后将其空投到Mac(大小约为300MB)。

在Mac上进行sysdiagnose后,可以打开Console.app中包含的.logarchive文件,并查看该设备上所有日志的转储。 如果您听取了有关类别和子系统的建议,则应该能够在短时间内筛选出所需的信息。

[记录针刮擦]坚持。 你说我必须等十分钟吗?

是的,最多可能需要十分钟才能显示sysdiagnose。 由于该延迟,您不希望使sysdiagnoses成为日常调试例程的一部分。 相反,系统诊断在以下情况下很有用:

  • 精通技术的客户会在事件发生后的几分钟内报告生产中的错误。 向他们介绍sysdiagnose步骤,并找到一种将其发送给您的方法。
  • 您或团队中的某人在离开键盘时遇到错误。 立即触发系统诊断,然后将其从办公室中拿回设备。

请勿:将所有格式参数强制设置为公共范围。

如果您习惯于以纯文本格式读取所有日志输出,那么必须处理统一日志记录系统的默认私有策略可能会非常麻烦。 抵制诱惑,将所有格式参数强制设为{public}范围。 这不仅会泄露客户的私人信息(例如,如果他们将系统诊断发送给另一家公司),还可能会暴露您公司的秘密。 不难想像一个错误日志意外地在生产日志中以纯文本形式显示OAuth凭据的情况。

要:观看这些WWDC视频。

对于对统一日志感兴趣的任何人,这两项都是必需的材料:

  • 统一日志记录和活动跟踪
  • 使用日志衡量性能

出发通知

[¹]:好的,从技术上讲,您也可以使用Xcode的控制台窗格查看输出,但前提是您的应用当时已连接到调试器。 Xcode的控制台窗格比Console.app具有更少的功能,因此对于深入检查日志不是特别有用。