我正尝试将函数参数记录到os_log
中,
func foo(x: String, y: [String:String]) {
//...
os_log("foo: \(x) \(y.description)", log: OSLog.default, type: .debug)
}
但是出现错误:
无法将“字符串”类型的值转换为预期的参数类型“ StaticString”
那么如何记录函数参数或任何其他动态数据?
答案 0 :(得分:8)
请参见Logging:
格式化日志消息
要格式化日志消息,请使用标准的NSString或printf格式字符串,...
和String Format Specifiers用于标准格式的字符串说明符,例如%@
和%d
。
在您的情况下:
os_log("foo: %@ %@", log: .default, type: .debug, x, y.description)
格式字符串仅限于静态字符串,以防止
(无意)扩展格式字符串说明符。这是一个演示
问题,请使用NSLog()
,因为这不会限制格式
常量字符串:
let s = "50%"
NSLog("\(s)percent")
// Output: 500x0ercent
%p
期望在变量参数列表上有一个指针,即
不提供。这是未定义的行为,可能导致崩溃
或意外的输出。
答案 1 :(得分:4)
在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)")
在消息字符串中,如您所见,快速字符串内插是合法的。允许使用符合description
的Int,Double,Objective-C对象和符合CustomStringConvertible的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
类型的格式说明符。
iOS 13及更高版本的旧答案:
Martin R的回答扩展了两点:
os_log("foo: %@ %@", log: .default, type: .debug, x, y.description)
您可以省略type:
参数,但不能省略log:
参数;您必须拥有它,包括log:
标签,否则os_log
会误解您的意图。
此外,log:
的值不必为.default
。通常,可以在前面创建一个或多个OSLog对象,以用作log:
参数的参数。这样做的好处是,您可以为OSLog对象指定Subsystem和Category,然后依次使用它们在Xcode控制台或Console应用程序中对结果进行过滤。
另外,关于pkamb的答案,如果我们知道消息始终是字符串,则可以这样编写OSLog扩展(利用新的Swift 5.2 callAsFunction
方法):>
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
语句中进行了这种格式化,那么我就无法想象这会带来很大的危害。
答案 2 :(得分:1)
我因为无法在"\(variable)"
中使用os_log
Swift字符串插值而感到恼火。
我写了一个小扩展名来解决这个问题:
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>
In Console.app, how can I reveal to what <private>
tags are actually referring?
在Apple的WWDC 2016演示文稿"Unified Logging and Activity Tracing"中,他们说:
避免在其他功能中包装操作系统日志API。
如果将其包装在另一个函数中,则会失去我们为您收集文件和行号的能力。
如果绝对必须包装我们的API,则将它们包装在宏而不是函数中。
因此,如果您担心其他收集的信息,那么这可能不是最佳解决方案。尽管即使使用库存os_log
仍可能无法获得该信息:How to find source file and line number from os_log()
如果有人想写一个允许使用"\(variable)"
替代的“宏”替代方案,将是受欢迎的。
答案 3 :(得分:0)
macOS 11 Big Sur发行说明指出os_log
现在可以传递Swift字符串插值:
https://developer.apple.com/documentation/macos-release-notes/macos-big-sur-11-beta-release-notes
记录
新功能
新的API可用于将Swift中的
os_log
用作os框架的一部分:
可以使用子系统和类别来实例化新型Logger,并提供用于在不同级别
( debug(_:) , error(_:) , fault(_:) )
进行日志记录的方法。Logger API支持指定旧版
os_log
API支持的大多数格式和隐私选项。新的API与旧版API相比,性能得到了显着改善。
您现在可以将Swift字符串插值传递给
os_log
函数。注意:不能重新部署新的API。但是,现有的
os_log
API仍可用于向后部署。 (22539144)
答案 4 :(得分:0)
这是我的方法:
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