Beacon

CI SPM

Swift 和 Objective-C 中的结构化日志记录

Beacon 与传统的日志记录系统不同之处在于,它处理任意值,而不仅仅是字符串,并且摒弃了严重性级别,而采用基于类型的过滤。

此框架提供了将任何类型的值记录到控制台、文件或远程服务所需的一切。它还提供了对缓冲和基于流的日志记录的更广泛支持,从而可以轻松实现自定义日志记录工具。

更多信息

使用

使用 Beacon 进行日志记录是启动记录器并发出值的问题。

let consoleLogger = ConsoleLogger.starting(name: "Console")
emit("A message")

以上等同于在控制台中打印带时间戳的消息,类似于传统的日志记录系统。但是,您不仅限于发出字符串 - 可以发出任何类型的值

do { 
	let result: MyThing = try something()
	emit(result)
} catch { 
	emit(error: error) 
}

无需指定调试级别 - 只需发出感兴趣的值即可。 Beacon 允许您通过过滤器和约束来控制记录的内容和位置。

简而言之...

let memoryLogger = MemoryLogger(name: "Memory")let consoleLogger = ConsoleLogger.starting(name: "Console") { 
②	$0 is StringSignal 
   }

③ Constraint.activate {
	-Signal.self
	+ErrorSignal.self ~> memoryLogger
   }

④ emit()

  do {
⑤	memoryLogger.run {
		let result = try something()
⑥		emit(result, on: [.shared], userInfo: ["detail": "Detail info"])
	 }
  }
  catch {
⑦	emit(error: error)
  }
  
⑧ consoleLogger.stop()
⑨ Constraint.enableAllSignals()

⓪ 创建一个 MemoryLogger 实例。此记录器只是将信号捕获到数组中,可通过 recordings 属性获得。请注意,此记录器尚未运行。

① 创建并启动 ConsoleLogger。此记录器只是将发出值的带时间戳的 debugDescription 打印到控制台,类似于传统系统记录消息的方式。 与 ⓪ 中创建的内存记录器相比 - 此记录器正在运行并将处理发出的信号。 有关可用记录器的列表,请参见 组件

② 控制台记录器设置为过滤掉不是 StringSignal 的任何内容 - 这就是当我们调用 emit("with a string") 时实际记录的内容。 不同的值类型由不同类型的信号表示,回退到 WrapperSignal 以捕获任意值。 信号可以轻松扩展以适应特定类型的值,并且是 可移植的。 有关可用信号的列表,请参见 组件

③ 通过定义约束来控制信号流。 第一个约束禁用所有类型信号的日志记录。 第二个约束启用 ErrorSignal 的日志记录,但仅由 memoryLogger 进行。 效果是仅记录 ErrorSignal,并且仅由 memoryLogger 记录。 默认情况下,所有类型的信号都是记录器,这等同于 +Signal.self 作为唯一的约束。 有关约束的更多信息,请参见 过滤

④ 调用没有值的 emit() 会记录执行上下文,捕获导致调用的堆栈跟踪。 这是通过创建和发出 ContextSignal 来实现的。 此外,Beacon 提供了专用信号来捕获 符号化那些堆栈跟踪 所需的信息。

⑤ 可以执行一次性日志记录 - 即,仅在块闭包的持续时间内启动记录器。

⑥ 每种形式的 emit() 都有两个额外的可选参数:要在其上发出信号的 Beacon 列表(默认为 Beacon.shared)和任意 userInfo 值。

⑦ 发出一个 ErrorSignal,包装错误。 请注意,此 emit() 变体具有命名参数。 emit(anError)(将发出 WrapperSignal)和 emit(error: anError)(将发出 ErrorSignal)之间存在差异。

⑧ 由于我们在 ① 中启动了控制台记录器,因此我们应该在使用完毕后停止它。

⑨ 将约束恢复为其默认值。

组件

该框架提供以下构建块

信号 描述
ContextSignal 在初始化站点捕获执行上下文 - 例如,包含方法的名称和堆栈跟踪
ErrorSignal 捕获错误以及导致错误的堆栈跟踪
StringSignal 捕获字符串 - 类似于传统的日志记录系统
WrapperSignal 捕获任意值 - 请注意变异值,因为某些日志记录工具可能会在稍后的时间记录
IdentitySignal 捕获有关 Beacon 本身的信息 - 例如版本、当前平台、体系结构等
MachImageImportsSignal 捕获 MachO 映像的添加和删除 - 这主要是为了帮助进行堆栈符号化

自定义信号通常实现为 WrapperSignal 的子类,因为它在基本 Signal 类的基础上提供了一些有用的机制。

记录器 描述
MemoryLogger 将信号记录到固定大小的数组中
ConsoleLogger 将信号打印到控制台
JRPCLogger 将信号发送到 JSON-RPC 服务器(有关 Pharo 中的服务器实现,请参见 Beacon-Server
FileLogger 将信号记录到文件,具有轮换支持

此外,还有一些抽象记录器

堆栈符号化

一些信号,即 ErrorSignalContextSignal,捕获导致信号发出的堆栈跟踪。 在某些情况下,二进制文件可能会被剥离其符号,并且需要对这些堆栈跟踪进行符号化才能理解它们。 为了帮助实现这一点,Beacon 使用两个特殊信号:IdentitySignalMachImageImportsSignal。 前者捕获当前运行环境 - 操作系统和处理器体系结构,而后者捕获 MachO 映像的插入和删除。 凭借二进制文件及其依赖项的体系结构和加载地址,可以使用 atos 之类的工具对这些堆栈跟踪进行符号化。

可以配置记录器以发出这些信号

let consoleLogger = ConsoleLogger(name: "Console")

// Will emit IdentitySignal when started
consoleLogger.identifiesOnStart = true

// Will emit MachImageImportsSignal for all loaded MachO images when started 
// and then track subsequently loaded and unloaded images 
consoleLogger.tracksMachImageImports = true

consoleLogger.start() 

Objective-C

该框架同时支持 Swift 和 Objective-C。 但是,两者之间存在一些特定于语言的差异。 在 Objective-C 中发出信号时,为了方便起见,定义了以下宏

// emit context signal
BeaconEmit(beacons, userInfo);

// emit value
BeaconEmit(someObject, beacons, userInfo);

// emit error
BeaconEmitError(someError, beacons, userInfo);

beacons 参数需要 NSArray<Beacon*>* 或 nil,这意味着共享的 beacon 对象。

当需要发出自定义信号时,您必须提供自己的宏或使用此流程

MySignal *signal = [MySignal new];
BeaconEmitSignal(signal, on: arrayOfBeacons, userInfo: aUserInfoDictionary)

Xcode Goodies

您可以在此处找到一个代码片段,以简化自定义信号的创建。 如果您喜欢这种事情,请将其添加到您现有的 Xcode 代码段集合中...