Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Passing variadic args in Swift 4 for os_log

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)

like image 883
smithco Avatar asked Sep 29 '17 23:09

smithco


3 Answers

Your idea contains a couple of issues:

  1. 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.

  2. 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?

like image 150
Kappe Avatar answered Nov 03 '22 00:11

Kappe


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/

like image 3
Julio Bailon Avatar answered Nov 03 '22 01:11

Julio Bailon


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)
}
like image 9
Leszek Zarna Avatar answered Nov 02 '22 23:11

Leszek Zarna