9

我正在尝试os_log像这样记录函数参数:

func foo(x: String, y: [String:String]) {
    //...
    os_log("foo: \(x) \(y.description)", log: OSLog.default, type: .debug)
}

但出现错误:

无法将“String”类型的值转换为预期的参数类型“StaticString”

那么如何记录函数参数或任何其他动态数据?

4

5 回答 5

27

请参阅日志记录

格式化日志消息

要格式化日志消息,请使用标准 NSString 或 printf 格式字符串,...

和标准格式字符串说明符的字符串格式说明符,例如%@%d

在你的情况下:

os_log("foo: %@ %@", log: .default, type: .debug, x, y.description)

格式字符串仅限于静态字符串,以防止(无意)扩展格式字符串说明符。这是一个演示问题的示例,NSLog()因为它不会将格式限制为常量字符串:

let s = "50%"
NSLog("\(s)percent")
// Output: 500x0ercent

期望变量参数列表上的指针,该%p指针未提供。这是未定义的行为,它可能导致崩溃或意外输出。

于 2018-10-27T20:02:31.187 回答
10

在 Xcode 12 / Swift 5.3 / iOS 14 中,您根本不必os_log直接调用。相反,将您对 OSLog 类的使用替换为新的 Logger 类(在您使用时可用import os)。这是一个例子:

let myLog = Logger(subsystem: "testing", category: "exploring")

然后,您可以直接在 Logger 对象上调用方法以使用该子系统和类别进行记录:

myLog.log("logging at \(#function)")

要在默认级别以外的级别登录,请使用该级别作为方法的名称:

myLog.debug("logging at \(#function)")

如您所见,在消息字符串中,Swift 字符串插值是合法的。它允许用于description符合 CustomStringConvertible 的 Int、Double、Objective-C 对象和 Swift 对象。

此处 Swift 字符串插值的合法性令人惊讶,因为os_log格式说明符的目的是推迟对参数的评估,将其推出您的应用程序(以便您的应用程序不会因日志记录而减慢)并进入日志记录机制本身。嗯,惊喜!由于 Swift 5 中引入的自定义 Swift 字符串插值钩子,插值推迟了。

并且在这里使用自定义字符串插值还有两个好处。首先,自定义字符串插值机制允许插值附带指定其行为的附加参数。这就是防止值被编辑的方式:

myLog.log("logging at \(#function, privacy: .public)")

您还可以使用其他参数来执行各种类型的字符串格式化,否则您必须使用NSLog格式说明符执行这些格式化,例如指定小数点后的位数以及其他类型的填充和对齐:

myLog.log("the number is \(i, format: .decimal(minDigits: 5))") // e.g. 00001

因此,您再也不需要os_log直接调用,也不再需要使用NSLog-type 格式说明符。


iOS 13 及之前的旧答案:

Martin R 的回答扩展了两点:

os_log("foo: %@ %@", log: .default, type: .debug, x, y.description)

可以省略type:参数,但不能省略log:参数;你必须拥有它,包括log:标签,否则os_log会误解你的意图。

此外,该log:值不必是.default。通常会预先创建一个或多个 OSLog 对象,用作参数的log:参数。这里的优点是您可以为 OSLog 对象指定子系统和类别,而这些又允许您在 Xcode 控制台或控制台应用程序中过滤结果。


另外,关于 pkamb 的回答,如果我们知道我们的消息总是一个字符串,我们可以这样编写 OSLog 扩展(利用新的 Swift 5.2callAsFunction方法):

extension OSLog {
    func callAsFunction(_ s: String) {
        os_log("%{public}s", log: self, s)
    }
}

结果是我们现在可以将我们的 OSLog 对象myLog本身视为一个函数:

myLog("The main view's bounds are \(self.view.bounds)")

这很好,因为它就像一个基本print语句一样简单。我很欣赏 WWDC 2016 警告不要进行这种预格式化,但如果这是您在print声明中已经在做的事情,我无法想象它会那么有害。

于 2020-06-20T16:02:58.257 回答
2

这是我的方法:

import Foundation
import os.log

struct Log {
    enum LogLevel: String {
        case error = "⛔️"
        case warning = "⚠️"
        case debug = ""
    }

    static func debug(_ info: String, level: LogLevel = .debug, file: String = #file, function: String = #function, line: Int = #line) {
        os_log("%@ %@:%d %@: %@", type: .default, level.rawValue, (file as NSString).lastPathComponent, line, function, info)
    }

    static func warning(_ info: String, level: LogLevel = .warning, file: String = #file, function: String = #function, line: Int = #line) {
        os_log("%@ %@:%d %@: %@", type: .default, level.rawValue, (file as NSString).lastPathComponent, line, function, info)
    }

    static func error(_ error: NSError, level: LogLevel = .error, file: String = #file, function: String = #function, line: Int = #line) {
        os_log("%@ %@:%d %@: %@", type: .default, level.rawValue, (file as NSString).lastPathComponent, line, function, "\(error)")
    }
}

用法:

Log.debug("MyLog")

输出示例:

AppDelegate.swift:26 应用程序(_:didFinishLaunchingWithOptions:): MyLog

于 2020-11-11T20:51:19.717 回答
1

我对无法"\(variable)"os_log.

我写了一个小扩展来解决这个问题:

import os.log

extension OSLog {
    
    static func log(_ message: String, log: OSLog = .default, type: OSLogType = .default) {
        os_log("%@", log: log, type: type, message)
    }
    
}

这确实会导致“私人”日志记录,这是预期的。

App Name <private>

在 Console.app 中,我如何才能揭示<private>实际所指的标签?


在 Apple 的 WWDC 2016 演示文稿“统一日志记录和活动跟踪”中,他们说:

避免将 os 日志 API 包装在其他函数中。

如果您将其包装在另一个函数中,那么您将失去我们为您收集文件和行号的能力。

如果您绝对必须包装我们的 API,那么将它们包装在宏中而不是函数中。

因此,如果您担心额外收集的信息,这可能不是最佳解决方案。尽管即使使用 stock 也可能无法获得该信息os_logHow to find source file and line number from os_log()

"\(variable)"如果有人想编写允许替换的“宏”替代方案,将受到欢迎。

于 2020-03-12T05:56:44.633 回答
1

os_log现在可以通过 Swift 字符串插值的 macOS 11 Big Sur 发行说明状态:

https://developer.apple.com/documentation/macos-release-notes/macos-big-sur-11-beta-release-notes

日志记录

新功能

  • 新的 API 可用于os_logSwift 作为 os 框架的一部分:

    • 可以使用子系统和类别来实例化新类型的 Logger,并提供不同级别的日志记录方法( debug(_:) , error(_:) , fault(_:) )

    • Logger API 支持指定旧os_logAPI 支持的大多数格式和隐私选项。

    • 与传统 API 相比,新 API 提供了显着的性能改进。

    • 您现在可以将 Swift 字符串插值传递给os_log函数。

注意:新的 API 不能回部署;但是,现有os_logAPI 仍可用于后部署。(22539144)

于 2020-06-23T17:10:56.583 回答