我正在尝试os_log
像这样记录函数参数:
func foo(x: String, y: [String:String]) {
//...
os_log("foo: \(x) \(y.description)", log: OSLog.default, type: .debug)
}
但出现错误:
无法将“String”类型的值转换为预期的参数类型“StaticString”
那么如何记录函数参数或任何其他动态数据?
请参阅日志记录:
格式化日志消息
要格式化日志消息,请使用标准 NSString 或 printf 格式字符串,...
和标准格式字符串说明符的字符串格式说明符,例如%@
和%d
。
在你的情况下:
os_log("foo: %@ %@", log: .default, type: .debug, x, y.description)
格式字符串仅限于静态字符串,以防止(无意)扩展格式字符串说明符。这是一个演示问题的示例,NSLog()
因为它不会将格式限制为常量字符串:
let s = "50%"
NSLog("\(s)percent")
// Output: 500x0ercent
期望变量参数列表上的指针,该%p
指针未提供。这是未定义的行为,它可能导致崩溃或意外输出。
在 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
声明中已经在做的事情,我无法想象它会那么有害。
这是我的方法:
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
我对无法"\(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_log
:How to find source file and line number from os_log()
"\(variable)"
如果有人想编写允许替换的“宏”替代方案,将受到欢迎。
os_log
现在可以通过 Swift 字符串插值的 macOS 11 Big Sur 发行说明状态:
https://developer.apple.com/documentation/macos-release-notes/macos-big-sur-11-beta-release-notes
日志记录
新功能
新的 API 可用于
os_log
Swift 作为 os 框架的一部分:
可以使用子系统和类别来实例化新类型的 Logger,并提供不同级别的日志记录方法
( debug(_:) , error(_:) , fault(_:) )
。Logger API 支持指定旧
os_log
API 支持的大多数格式和隐私选项。与传统 API 相比,新 API 提供了显着的性能改进。
您现在可以将 Swift 字符串插值传递给
os_log
函数。注意:新的 API 不能回部署;但是,现有
os_log
API 仍可用于后部署。(22539144)