I am trying to write a convenience wrapper for os_log in Swift 4 / iOS 11, but I've run into an uphill battle with passing the variadic arguments.
Basically, I want to write a function that looks like the following.
static let logger = OSLog(subsystem: "com.example.foo", category: "foobar")
func logError(_ message: StaticString, _ args: Any...) {
os_log(message, log: logger, type: .error, args)
}
Unfortunately, I can't seem to figure out the magic syntax to get the arguments passed along and have gotten a bit lost in the quagmire of CVarArg discussions.
(... this makes me miss Python's splatting syntax)
Your idea contains a couple of issues:
Apple discourages the wrapping of os_log
in another function, doing so results in losing some nice feature of the Unified Logging System, like having the code line, library, file etc in the logs automagically.
As soon as args
is passed to your own function that type pass from cvargs to [String] and in theory it's impossible re-build the list of args, you can find an amazing explanation in the first answer here: Why does wrapping os_log() cause doubles to not be logged correctly?
This is what I am using to wrap 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)
}
}
And I created it like this:
self.logService = LogService(subsystem: "com.softbolt.app", category: "network")
And use it like this:
self.logService.info("HttpResponse %{public}@", url)
If you want to know more about os_log and the benefits of private and public logging check this link:
https://www.testdevlab.com/blog/2018/04/how-to-create-categorize-and-filter-ios-logs/
I also haven't found solution yet so made this silly hack:
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)
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With