我正在尝试在 Swift 4 / iOS 11 中为 os_log 编写一个方便的包装器,但是我在传递可变参数方面遇到了一场艰苦的战斗。
基本上,我想编写一个如下所示的函数。
static let logger = OSLog(subsystem: "com.example.foo", category: "foobar")
func logError(_ message: StaticString, _ args: Any...) {
os_log(message, log: logger, type: .error, args)
}
不幸的是,我似乎无法弄清楚传递参数的神奇语法,并且在 CVarArg 讨论的泥潭中有点迷失了。
(...这让我怀念 Python 的 splatting 语法)
我还没有找到解决方案,所以做了这个愚蠢的黑客:
switch args.count {
case 0:
os_log(message, log: log!, type: type)
case 1:
os_log(message, log: log!, type: type, args[0])
case 2:
os_log(message, log: log!, type: type, args[0], args[1])
case 3:
os_log(message, log: log!, type: type, args[0], args[1], args[2])
default:
os_log(message, log: log!, type: type, args)
}
您的想法包含几个问题:
Apple 不鼓励将
os_log
包装在另一个函数中,这样做会导致失去统一日志系统的一些不错的功能,例如自动在日志中包含代码行、库、文件等。
一旦将
args
传递给您自己的函数,该函数类型从 cvargs 传递到 [String] 并且理论上不可能重新构建参数列表,您可以在此处的答案中找到令人惊奇的解释:https: //stackoverflow.com/a/50942917/465916
这就是我用来包装 os_log 的:
import Foundation
import os.log
protocol LogServicing: class {
func debug(_ message: StaticString, _ args: CVarArg...)
func info(_ message: StaticString, _ args: CVarArg...)
func error(_ message: StaticString, _ args: CVarArg...)
}
enum LogType {
case debug
case info
case error
case fault
}
class LogService: LogServicing {
private var osLog: OSLog?
let subsystem: String
let category: String
init(subsystem: String = Bundle.main.bundleIdentifier ?? "", category: String = "") {
if #available(iOS 10.0, *) {
let osLog = OSLog(subsystem: subsystem, category: category)
self.osLog = osLog
}
self.subsystem = subsystem
self.category = category
}
func log(type: LogType, message: StaticString) {
log(type: type, message: message, "")
}
func log(type: LogType, message: StaticString, _ args: CVarArg...) {
if #available(iOS 10.0, *) {
guard let osLog = osLog else { return }
let logType: OSLogType
switch type {
case .debug:
logType = .debug
case .error:
logType = .error
case .fault:
logType = .fault
case .info:
logType = .info
}
os_log(message, log: osLog, type: logType, args)
print(message, args)
} else {
NSLog(message.description, args)
}
}
func debug(_ message: StaticString, _ args: CVarArg...) {
log(type: .debug, message: message, args)
}
func info(_ message: StaticString, _ args: CVarArg...) {
log(type: .info, message: message, args)
}
func error(_ message: StaticString, _ args: CVarArg...) {
log(type: .error, message: message, args)
}
}
我是这样创建的:
self.logService = LogService(subsystem: "com.softbolt.app", category: "network")
并像这样使用它:
self.logService.info("HttpResponse %{public}@", url)
如果您想了解有关 os_log 以及私有和公共日志记录的好处的更多信息,请查看此链接:
https://www.testdevlab.com/blog/2018/04/how-to-create-categorize-and-filter-ios-logs/
对此做了更多研究。结果发现
os_log
实际上是一个 C 宏。这在如何映射到 Swifts 可变参数方面产生了各种各样的问题。
但是,该宏还捕获其他调试信息,并且无论如何都可能不安全。