实用工具类,轮询 OSLogStore 并将任何有效的日志发送到订阅的日志驱动程序。
OSLog
是 Apple 和 Swift 核心团队推荐的日志记录方法。一个全面且可靠的日志记录系统在软件开发中至关重要。然而,项目通常需要将日志发送给第三方供应商或其他服务。这就提出了一个挑战:如何在使用 OSLog
的同时,确保与外部日志解决方案的无缝和灵活集成。
OSLogClient
旨在弥合这一差距,充当中间媒介,将 OSLog
的优势和便利性引导到自定义日志机制中。它通过轮询底层的 OSLogStore
,评估日志,然后将后处理的日志消息转发到注册的 LogDriver
实例来实现这一点。因此,注册的 LogDriver
可以接收到日志消息,同时保留所有基于 OSLog
的隐私、安全性和格式。驱动程序还将接收元数据,例如日期时间、日志级别、记录器子系统和记录器类别。
随着对用户数据隐私和安全性的日益重视,Apple 的 OSLog
已成为开发者不可或缺的工具。OSLog
提供了许多优势:
OSLog 允许您格式化和编辑敏感数据,确保用户数据不会被意外泄露。
OSLog 在设计时就考虑了效率,它最大限度地减少了对应用程序性能的影响。
OSLog 与系统的诊断框架无缝集成,使故障排除更加容易。您可以使用原生的“控制台”应用程序来更轻松地过滤和监控日志。
Apple 和 Swift 核心团队推荐使用 OSLog
而不是其他日志记录机制,因为它具有内置的功能。
通过将 OSLog
与我们的库集成,您可以在利用 OSLog 优势的同时,确保灵活的日志记录基础设施,并可以根据您的项目需求进行扩展。
使用 OSLogClient
非常简单。以下是帮助您入门的简单指南:
// Import the library (OSLog is also included in the import)
import OSLogClient
// Initialize the OSLogClient
try OSLogClient.initialize(pollingInterval: .short)
// Register your custom log driver
let myDriver = MyLogDriver(id: "myLogDriver")
await OSLogClient.registerDriver(myDriver)
// Start polling
await OSLogClient.startPolling()
注意: 如果您尚未使用结构化并发(或仍在采用等),您将需要在任务中运行您的注册和调用。以下是一个人为设计的示例,您应该考虑任务生命周期和状态等。
try OSLogClient.initialize(pollingInterval: .short)
let setupTask = Task(priority: .userInitiated) {
// Register your custom log driver
let myDriver = MyLogDriver(id: "myLogDriver")
await OSLogClient.registerDriver(myDriver)
// Start polling
await OSLogClient.startPolling()
}
只需这三个步骤,OSLogClient
就会开始监控来自 OSLog
的日志,并将它们转发到您注册的日志驱动程序,让您可以像往常一样使用 OSLog.Logger
实例。
let logger = Logger(subsystem: "com.company.AppName", category: "ui")
logger.info("Password '\(password, privacy: .private)' did not pass validation")
当您的驱动程序收到日志消息时,它将是经过处理的消息,确保已应用任何隐私和格式设置。例如,当未连接到调试器时,上述操作将使用以下内容调用:
"密码 '<private>' 未通过验证"
虽然该库的预期用法是使用 OSLogClient
入口点,但您可以初始化您自己的 LogClient
类型的实例来自行维护。
let logStore = OSLogStore(scope: .currentProcessIdentifier)
let client = try LogClient(pollingInterval: .medium, logStore: logStore)
注意: 这是为那些需要使用自己的实例的边缘情况提供的。请记住,OSLogClient
入口点在这些设置中仍然可用且功能齐全。
虽然基本的 LogDriver 类为处理 OS 日志提供了必要的基础,但您可以轻松地对其进行子类化以进行自定义处理,例如将日志写入文本文件。
import OSLogClient
class TextLogDriver: LogDriver {
// MARK: - Properties
var logFileUrl: URL
// MARK: - Lifecycle
required init(id: String, logFileUrl: URL, logFilters: [LogFilter] = []) {
self.logFileUrl = logFileUrl
super.init(id: id, logFilters: logFilters)
}
required init(id: String, logFilters: [LogFilter] = []) {
fatalError("init(id:logFilters:) has not been implemented")
}
// MARK: - Overrides
#if os(macOS)
override func processLog(level: LogDriver.LogLevel, subsystem: String, category: String, date: Date, message: String, components: [OSLogMessageComponent]) {
formatAndWriteMessage(level: level, category: category, date: date, message: message)
}
#else
override func processLog(level: LogDriver.LogLevel, subsystem: String, category: String, date: Date, message: String) {
formatAndWriteMessage(level: level, category: category, date: date, message: message)
}
#endif
// MARK: - Helpers
func formatAndWriteMessage(level: LogLevel, category: String, date: Date, message: String) {
let message = "[\(category)-\(level.rawValue.uppercased())]: \(date): \(message)"
var contents = (try? String(contentsOf: logFileUrl).trimmingCharacters(in: .whitespacesAndNewlines)) ?? ""
contents += "\(contents.isEmpty ? "" : "\n")\(message)"
try? contents.write(to: logFileUrl, atomically: true, encoding: .utf8)
}
}
除了仅在 processLog
方法中评估日志级别、日期和类别之外,您还可以通过指定有效的 LogFilter
条件来微调哪些日志应由 LogDriver
实例处理。
如果指定了日志过滤器(即,列表不为空),则它们用于评估传入的日志条目,确保存在匹配的过滤器。
有关支持的选项,请参阅 LogFilter
和 LogCategoryFilter
文档。
例如,要配置日志驱动程序以仅接收 ui
和 api
日志条目:
let apiLogger = Logger(subsystem: "com.company.AppName", category: "api")
let uiLogger = Logger(subsystem: "com.company.AppName", category: "ui")
let storageLogger = Logger(subsystem: "com.vendor.AppName", category: "storage")
myLogDriver.addLogFilters([
.subsystem("com.company.AppName", categories: "ui", "api")
])
通过此设置,记录器实例像往常一样工作,但驱动程序将仅捕获至少一个日志源验证的日志。
// Driver will capture these logs:
apiLogger.info("api info message")
uiLogger.info("button was tapped")
// Driver **won't** capture this log:
storageLogger.error("database error message")
这种方法有助于根据需要跨不同的驱动程序实例管理具有不同类别的记录器。
PollingInterval
支持四种枚举:
.short // 10 second intervals
.medium // 30 second intervals
.long // 60 second intervals
.custom(TimeInterval) // Poll at the given duration (in seconds)
注意: 对于 custom
间隔选项,强制执行的最小值为 1 秒。
您还可以请求从给定时间点轮询日志。要轮询的日期是可选的,默认为最近轮询的日志的时间。
OSLogClient.pollImmediately() // Use last processed
OSLogClient.pollImmediately(from: customDate) // Custom point in time
目前,OSLogClient 支持 Swift Package Manager (SPM)。
要将 OSLogClient 添加到您的项目,请将以下行添加到您的 Package.swift 文件中的 dependencies 中:
.package(url: "https://github.com/CheekyGhost-Labs/OSLogClient", from: "2.0.0")
然后,将 OSLogClient 添加为您的目标的依赖项:
.target(
name: "YourTarget",
dependencies: [
// other dependencies
.product(name: "OSLogClient", package: "OSLogClient")
]
),
OSLogClient 在 MIT 许可证下发布。有关更多信息,请参阅 LICENSE 文件。
欢迎为 OSLogClient 做出贡献!如果您有错误要报告,请随时通过打开新 issue 或提交 pull request 来提供帮助。
OSLogClient 非常严格地遵循标准的 git flow 流程。在大多数情况下,pull request 应该针对 develop
分支进行,以协调任何发布。这也提供了一种从 develop
分支在实际环境中进行测试的方法,以进一步测试待发布的版本。一旦发布准备就绪,它将被合并到 main
,标记,并切出一个发布分支。
Fork 仓库:首先,fork 项目到您自己的 GitHub 帐户。
Clone fork 的仓库:fork 后,将 fork 的仓库 clone 到您的本地计算机,以便您可以进行更改。
git clone https://github.com/CheekyGhost-Labs/OSLogClient.git
git checkout -b your-feature-branch
遵循 Swift 语言指南:确保您的代码符合 Swift 语言指南 的样式和语法约定。
进行更改:实施您的功能或错误修复,遵循项目的代码风格和最佳实践。不要忘记添加测试并根据需要更新文档。
提交您的更改:使用描述性和简洁的提交消息提交您的更改。使用祈使语气,并解释您的提交做了什么,而不是您做了什么。
# Feature
git commit -m "Feature: Adding convenience method of awesomeness"
# Bug
git commit -m "Bug: Fixing issue where awesome thing was not including awesome"
git pull origin develop
git push origin your-feature-branch
develop
分支。填写 pull request 模板的必要详细信息,并等待项目维护人员审查您的贡献。请确保为您所做的任何更改添加单元测试。目标不是 100%
覆盖率,而是有意义的测试覆盖率,以确保您的更改按预期行为,而不会对现有行为产生负面影响。
请注意,项目维护人员可能会要求您对您的贡献进行更改或提供其他信息。对反馈持开放态度,并愿意根据需要进行调整。一旦您的 pull request 获得批准并合并,您的更改将成为项目的一部分!
要深入了解 Apple 的 OSLog,请参阅以下文档: