Swift 宏中的 OSLog 不保留原始文件/行号

OSLog in Swift macro doesn't persist original file/line number

提问人:Ortwin Gentz 提问时间:9/25/2023 更新时间:10/14/2023 访问量:100

问:

我需要并行登录到一个文件(由于无法提供旧日志)。OSLogOSLogStore

在 Objective-C 中,我可以使用宏来实现这一点,并且在宏实现中仍然引用原始代码中的位置。 我试图创建一个 Swift 宏来从 Swift 完成这项工作。但是,采用宏定义文件的文件和行号,而不是调用代码中的位置。因此,当我单击元数据链接时,我被带到宏而不是调用位置。__FILE____LINE__log()

有什么解决办法吗? 并在宏中正确设置,但无法为函数指定文件和行号。#file#linelog()

swift xcode 日志记录 oslog swift-macro

评论


答:

-1赞 killobatt 9/30/2023 #1

我遇到了类似的问题,通过偶尔将内容转储到文件中来解决它。OSLogStore

评论

1赞 Ortwin Gentz 9/30/2023
我很想使用 OSLogStore 而不是普通文件。遗憾的是,OSLogStore(在 iOS 下)仅提供当前正在运行的进程的日志,而不提供以前运行或扩展的日志。
1赞 Claus Jørgensen 10/14/2023 #2

很遗憾,您没有办法这样做*。苹果在他们的无限智慧中不认为有人会做这样的事情。

苹果论坛上常驻的内部爱斯基摩人的建议是,你写一个 Swift 宏来包装它,基本上为每个日志语句写两行日志记录。

* 严格来说不是真的。您可以自己包装 C API,但通常不建议这样做

1赞 iUrii 11/26/2023 #3

Xcode 的编译器会查看代码中 OSLog 记录器调用的位置,并将此信息指向元数据,因此您需要对不同的代码行进行不同的调用。

您可以使用带有闭包的方法,该闭包包含对传递给日志函数的记录器的调用:

func writeToFile(_ message: String, file: String, line: Int) {
  let fileName =  NSString(string: file).lastPathComponent
  print("<\(fileName):\(line)> \(message)")
}

func log(_ message: String, file: String = #file, line: Int = #line, writeToLog: (String) -> Void) {
  writeToLog(message)
  writeToFile(message, file: file, line: line)
}

现在,您可以按以下方式调用日志函数:

let logger = Logger(subsystem: "com.logger.Test", category: "Test")

log("a") { logger.log("\($0)") }
log("b") { logger.log("\($0)") }

Xcode 的调试控制台输出:

<AppDelegate.swift:35> a
<AppDelegate.swift:36> b
a
Timestamp: 2023-11-26 15:55:08.177017+02:00 | .../AppDelegate.swift 35:23
b
Timestamp: 2023-11-26 15:55:08.177078+02:00 | .../AppDelegate.swift 36:23

为了消除不必要的重复代码,你可以制作 swift 宏:#log

@freestanding(expression)
public macro log(_ message: String) = #externalMacro(module: "MyMacroMacros", type: "LogMacro")

public struct LogMacro: ExpressionMacro {
  public static func expansion(of node: some FreestandingMacroExpansionSyntax, in context: some MacroExpansionContext) throws -> ExprSyntax {
    guard let message = node.argumentList.first?.expression.description else {
      fatalError("No message")
    }
    return "log(\(raw: message)) { logger.log(\"\\($0)\") }"
  }
}

您的代码将转换为:

import MyMacro

...

#log("a")
#log("b")

评论

0赞 Ortwin Gentz 11/27/2023
我已经尝试过了,它工作正常,但前提是我不使用宏。使用宏时,单击元数据链接将打开宏定义,而不是宏调用位置。这对你有用吗?