Swift 的一个简单、快速和灵活的日志框架。
当我开始严肃地进行 Swift 开发时,我自然地四处寻找一个日志框架。我找到了 Dave Wood @DaveWoodCom 的 XCGLogger。虽然它非常快,结构良好,但我还需要一些额外功能,因此决定自己构建。但我确实从他那里学到了一些东西,所以他应该得到一些赞誉。
Slogger 使用与 XCGLogger 类似的方法和相同的函数签名,因此可以在不修改现有日志站点的情况下互换使用。请务必查看下面的 高级功能 部分 - 尤其是关于 放射性日志 和 分类,以了解我为何选择此方案。
另外请注意:Slogger 完全独立于 API,除了 Swift 标准库和 codeFoundation导入。因此,它也可以用于 Mac OS X 项目。
Slogger 需要 Xcode 7.1 和 Swift 2.1。
支持典型的日志级别
public enum Level : Int, Comparable {
case None, Severe, Error, Warning, Info, Debug, Verbose
static let allValues = [None, Severe, Error, Warning, Info, Debug, Verbose]
}
级别顺序是高优先级的先。因此,阈值评估使用<=运算符。以下是在内部确定是否应该记录消息的函数。关于override和category参数的信息,请见下文。
public func canLog (override override: Level?, category: T?, siteLevel: Level) -> Bool {
let effectiveLevel : Level
if override != nil {
effectiveLevel = override!
} else if category != nil, let categoryLevel = categories[category!] {
effectiveLevel = categoryLevel
} else {
effectiveLevel = level
}
return effectiveLevel == .None ? false : siteLevel <= effectiveLevel
}
设置日志器可以像一行代码一样简单
let log = Slogger<NoCategories>(defaultLevel: .Info)
并且您可能会想要为调试/发布量身定制您的构建
#if DEBUG
let log = Slogger<NoCategories>(defaultLevel: .Info)
#else
let log = Slogger<NoCategories>(defaultLevel: .Warning)
#endif
公共接口已在头来完全记录,以供在 Xcode 中参考(使用 Alt-Click)。有关基于 HTML 的版本,请参阅 Docs 目录。在某个时刻将为它提供 docset,但目前我还没有时间解决与 Jazzy 和 AppleDoc 的问题。
Slogger 类是通用的,以支持分类,如下所述。
每个日志级别都有 autoclosure 和 noescape 后续关闭的实施方案,因此以下两个形式都是有效的:
log.debug("Enter")
log.debug() { "Enter" }
重要提示:如果日志站点未通过级别阈值,则不会评估生成的闭包。因此,不要担心其中昂贵的计算。也不应依赖于它们产生副作用。
为了完整性,提供了对 .None 级别的函数,它们具有无操作实现。因此,唯一的开销是在堆栈上分配闭包和函数调用。您可以使用它们有效地禁用具有最高效率的某些日志站点。(请参阅下面的性能章节以获取更多详细信息。)
log.none("Enter")
log.none() { "Enter" }
以下每个日志实例的属性都公开并提供读写访问(除默认实现外)。它们可以在运行时进行修改,无论是编程方式还是通过在断点处使用调试器。
属性 | 类型 | 注释 |
---|---|---|
level | 级别 | 记录器实例的活动、全局级别。 |
categories | [T : 级别] | 类别与级别之间的映射。 |
destinations | [目的地] | 此记录器将写入这些目的地。默认为带有 XcodeColors 装饰器的 ConsoleDestination 。 |
hits | UInt64 | 记录的事件数。 |
misses | UInt64 | 由于日志阈值评估而没有记录的事件数。 |
_Slogger 使用一个私有、串行调度队列来执行大部分工作,包括对生成器、装饰器和所有日志目的地的调用。仅有的同步执行记录函数的代码是阈值评估(并且只有当 destinations.count > 0
时)以及如果通过该评估,则评估闭包以从日志站点生成消息。
因此,所有 Slogger 类型本身就是线程安全的。如果您决定实现自己的,可以这样做而无需担心并发问题。然而,如果您创建了任何类型需要执行在主线程上的自定义实现,您 MUST 在主队列的 dispatch_async
调用中包装该代码。
Destination 类允许您编写自己的日志目的地子类并将它们添加到记录器中。以下是在实现中提供的目的地
类 | 状态 | 注释 |
---|---|---|
ConsoleDestination | 默认情况下具有智能输出且支持 XcodeColors。 | |
MemoryDestination | 将条目追加到内存数组中。 | |
TextFileDestination | 默认行为类似于 ConsoleDestination,但无装饰。 | |
JSONFileDestination | 以每行一个条目的 JSON 格式输出。 | |
XMLFileDestination | 以每行一个条目的 XML 格式输出。 | |
TabFileDestination | 输出制表符分隔的文件。 | |
CSVFileDestination | 输出逗号分隔值文件。 | |
NetworkDestination | 🤔 | 计划中,但无截止时间 |
这些是基于日志站点信息输出日志条目的类。它们可以针对每个日志目的地进行配置。您可以使用提供的生成器或实现自己的。
默认使用典型模式
- [2015-11-07 23:53:18.187 -0500] File.swift (82) function() [] Error : Message...
支持的生成器列表(请参阅源代码以获取详细信息)
生成器 | 状态 |
---|---|
生成器 | |
JSONGenerator | |
XMLGenerator | |
CSVGenerator | |
TSVGenerator |
您可以通过为每个支持的项目提供一个枚举值数组来配置您希望看到日志中的哪些细节 - 以及它们的顺序。这使得自定义输出格式变得简单。
默认值包括所有可用的 细节 值,按照典型的顺序。
[.Override, .Date, .File, .Line, .Function, .Category, .Level, .Message]
您可以为生成器输出进一步调整格式的装饰器提供。这些是为每个目标配置的。
装饰器 | 状态 | 信息 |
---|---|---|
XcodeColors | 支持 | 获取 XcodeColors。 |
ANSI | 搁置 | 目前正在设法支持这个。 |
创建自定义颜色映射,以平台和装饰器无关的方式通过级别自定义日志条目的颜色。有关更多信息,请参阅颜色映射类型。(注意:您可以使用XcodeColorsDecorator类在控制台的非日志用途中而不必自己编写。)
放射性日志使日志可以根据日志位置的可选覆盖值进行评估来执行。如果覆盖值非nil,则首先对其进行评估。如果日志函数的级别小于或等于覆盖值,则将记录该站点。否则,将按照正常过程继续进行日志阈值评估。
举个例子,假设你在服务实现中有一个新的请求对象基类。你可以在服务代码中定义一个类型为级别的logOverride属性,默认值为nil。然后,提供请求的logOverride属性的值,作为所有日志位置的覆盖参数。这将导致在处理请求时使用的任何非nil值来覆盖服务的日志。
作为一个用例,如果你有一个正在处理大量请求但一个特定请求以微妙方式失败的服务,你可以使用以下程序来只为该请求获取更多信息,如下所示
然后你会看到仅那个特定请求的日志,无论你指定了什么级别的覆盖。这允许你关注系统中流动的特定对象,而不必简单地设置日志器的级别属性到一个更高的值,以便得到不关心的请求的大量日志信息。
这个程序可以通过简单地修改请求创建的地方的代码来做,也可以通过在运行时设置断点并使用调试器修改属性来做。
除了上述每个级别的两个日志位置函数外,Slogger还添加了两个具有额外类别参数的函数。虽然类别可以是任何符合协议的类型,但最好将它们定义为枚举,以确保类型安全和方便。
定义类别后,您可以配置记录器,在运行时自定义该类别的日志级别(例如,.Debug 或 .Verbose)。这允许在需要时进行更精细的日志调整,例如,你想要查看特定问题(数据库调用、网络事务等)的事件的更多日志。
日志记录器的设计允许使用它的第三方框架暴露其日志实例并记录其分类值供开发者使用。如果您需要诊断框架内部的问题,您可以简单地调整特定分类的日志级别——或者如果框架支持,将对象变得“放射性”——以获取更多信息。这对框架开发者不发布源代码的情况尤其有用。
以下是如何实现您自定义的分类和您的日志记录器子类的示例。
首先,定义您的分类枚举
public enum MyCategories : String, SloggerCategory {
case Foo, Bar, Baz
}
}
其次,从通用日志记录器类派生以绑定到您的分类类型
class MyLogger : Slogger<MyCategories> {}
然后创建您的日志记录器
public let log = MyLogger(defaultLevel: .Info)
当然,如果您想自定义日志记录器的值(生成器、装饰器等),您可以覆盖init方法并提供相关信息。
将日志记录器作为顶级全局变量创建可以在您代码的任何地方方便地访问。由于您的Swift代码定义了一个模块,您)不用担心名称冲突,
性能
以下是使用发布构建进行日志调用的一些初始性能数据(截至1.0版本)。有关详细信息,请参阅性能测试类和日志记录器性能IOS项目。(日志记录器在Mac OS X应用程序中的性能应与模拟器相同。) | 级别 | 目标 | 日志 | iPhone 6 |
---|---|---|---|---|
[] | 详细 | 是 | 53ns | 108ns |
内存目标 | 严重 | 否 | 374ns | 1µs |
控制台目标 | 严重 | 否 | 370ns | 1µs |
JSON文件目标 | 严重 | 否 | 463ns | 1µs |
XML文件目标 | 严重 | 否 | 425ns | 1µs |
内存目标 | 详细 | 是 | 3µs | 8µs |
控制台目标 | 详细 | 是 | 4µs | 15µs |
JSON文件目标 | 详细 | 是 | 4µs | 10µs |
XML文件目标 | 详细 | 是 | 12µs | 16µs |
从时间可以看出,如果您想以最高效的方式完全关闭日志,请将destinations属性设置为空数组。这避免了进行级别阈值测试。
应注意,当Can Log设置为true时的时间不包括生成器、装饰器或目标开销。如果可以用日志记录,内部执行的操作只有在线计算消息闭包(因为它是不逃逸的)。其余工作是通过对私有串行队列的调用通过dispatch_async
完成的。
请在Github的“问题”部分报告错误、提出问题、提出改进建议或提出有关实现的疑问。如果您想贡献,请随时在问题部分讨论或在问题处发出pull请求。
祝您日志记录愉快!
版本 | 状态 | 注释 |
---|---|---|
1.0 | 发布 | 首次发布。 |
0.1.x | 可用 | 预发布(CocoaPods/Carthage支持和TextFileDestination变种)。 |