如何在swift中打印方法名称和行号

Naz*_*san 41 swift

这是我想要做的一个例子:

func application(application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: NSError)
{
    let  nm =  NetworkModel()
    nm.sendlog("file name :AppDelegate , line number : 288", info: " Failed to register: \(error)")
}
Run Code Online (Sandbox Code Playgroud)

目前的情况下我做的是硬编码值line numberfile name.但是有可能以编程方式选择line numberfile name.

小智 104

Literal        Type     Value

#file          String   The name of the file in which it appears.
#line          Int      The line number on which it appears.
#column        Int      The column number in which it begins.
#function      String   The name of the declaration in which it appears.
#dsohandle     String   The dso handle.
Run Code Online (Sandbox Code Playgroud)

print("Function: \(#function), line: \(#line)") 
Run Code Online (Sandbox Code Playgroud)

使用参数中的默认值,您还可以创建一个函数

public func track(_ message: String, file: String = #file, function: String = #function, line: Int = #line ) { 
    print("\(message) called from \(function) \(file):\(line)") 
}
Run Code Online (Sandbox Code Playgroud)

可以像这样使用

track("enters app")
Run Code Online (Sandbox Code Playgroud)

在Swift 2.1中

 Literal        Type     Value

__FILE__       String   The name of the file in which it appears.
__LINE__       Int      The line number on which it appears.
__COLUMN__     Int      The column number in which it begins.
__FUNCTION__   String   The name of the declaration in which it appears.
Run Code Online (Sandbox Code Playgroud)

有关详细信息,请参阅文档 检查文档


Ind*_*ore 9

您可以使用#function,#file,#line

这是swift中log方法的实现:https://github.com/InderKumarRathore/SwiftLog

以下是片段

public func debugLog(object: Any, functionName: String = #function, fileName: String = #file, lineNumber: Int = #line) {
  #if DEBUG
    let className = (fileName as NSString).lastPathComponent
    print("<\(className)> \(functionName) [#\(lineNumber)]| \(object)\n")
  #endif
}
Run Code Online (Sandbox Code Playgroud)


gan*_*ena 7

对于swift 4swift 5

func printLog(_ message: String, file: String = #file, function: String = #function, line: Int = #line) {
    #if DEVELOPMENT
        let className = file.components(separatedBy: "/").last
        print(" ? Error ----> File: \(className ?? ""), Function: \(function), Line: \(line), Message: \(message)")
    #endif
}

// "? Error ----> File: classNameViewController.swift, function: functionName(), Line: 123, Message: messageError"
Run Code Online (Sandbox Code Playgroud)


Pra*_*tti 7

示例记录代码 (TL;DR)

\n
import os.log\n\n@available(OSX 11.0, iOS 14.0, *)\nextension Logger {\n  private static var subsystem = Bundle.main.bundleIdentifier!\n\n  /// Logs the payment flows like Apple Pay.\n  @available(OSX 11.0, iOS 14.0, *)\n  static let payments = Logger(subsystem: subsystem, category: "payments")\n}\n\nstatic func DLog(message: StaticString, file: StaticString = #file, function: StaticString = #function, line: UInt = #line, column: UInt = #column, category: String, type: OSLogType = .info, bundle: Bundle = .main) {\n\n  // This method is only for iOS 14+\n  if #available(OSX 11.0, iOS 14.0, *) {\n\n    Logger.payments.debug("\\(file) : \\(function) : \\(line) : \\(column) - \\(message, privacy: .private)")\n    // This makes the message unreadable without a debugger attached.\n\n  } else {\n    // Fallback on earlier versions\n\n    let customLog = OSLog(subsystem: bundle.bundleIdentifier!,\n                          category: category)\n\n    // IMPORTANT: I have assumed here that you only print out non-sensitive data! Using %{private}@ or %{public}@ in an interpolated string is not portable to `Logger`!\n    os_log(message, log: customLog, type: type)\n\n    // Unfortunately this legacy API doesn\'t support non-StaticString logs. :(\n  }\n}\n
Run Code Online (Sandbox Code Playgroud)\n

请注意,您可能需要重新编写此代码,以便在私有/公共访问级别上获得更大的灵活性 -os_log并且Loggerdon\xe2\x80\x99t 以相同的方式处理隐私级别。这只是为了展示如果您不\xe2\x80\x99tos_logmessage.

\n

Apple建议使用操作系统日志记录,这就是我使用这种方法而不是print语句的原因。我添加这个答案是因为LoggeriOS 14 中的新 API 也支持字符串插值。

\n

动机

\n

我认为这里的答案解决了< iOS 14 的问题,但可以进一步提高内存效率,因为此调试日志记录功能可能会在 iOS 14+(新 API)的整个代码库中使用Logger。苹果建议使用操作系统日志记录,这就是为什么我使用这种方法而不是print语句(尽管这些仍然有效)。

\n

这些都是微小的改进,但我认为它们会有所帮助,因为即使是微小的改进也会累积起来。事实上,Swift 标准库在任何可以使用的地方都使用了这些优化)!尽管Swift是一个高级别的语言,但这些优化是实现令人难以置信的内存效率的因素之一。语言(也是出色的 API 设计的一部分!),(-:

\n

由于此功能可能非常适合作为通用(操作系统)日志记录服务的一部分,因此我还包含了在应用程序内进行日志记录时的注意事项。我认为这些可以在出于调试目的进行日志记录时对您有所帮助(我回答了这个问题,因为它可能是用于调试日志记录!)。

\n

改进

\n
    \n
  1. UInt如果我知道情况Int会是积极的,并且任何边缘情况崩溃都不太可能,我更喜欢在某些情况下使用。请注意,由于这个原因,我在与使用类型的类交互时使用。这会在运行时分配更少的内存,而且我发现更具体也可以帮助我更好地理解代码。UIntFoundationInt
  2. \n
  3. StaticString我更喜欢在我知道字符串在编译时已知的地方使用。从文档来看,StaticString仅提供对内容的低级访问String并且是不可变的。因此仅在适当的情况下使用它。即使串联的字符串文字也不能用于初始化StaticString. 因为它的功能比这更受限制,String这意味着它是轻量级的——只需要一个地址指针和底层的长度。使用StaticString还通过操作系统级内存管理提供了小幅性能提升,因为它既不分配也不释放(不需要引用计数)。
  4. \n
  5. 使用 Apple 推荐的LoggerAPI 进行日志记录比打印报表更好,原因有多种:性能、隐私和统一管理。它可以更好地调试已发布产品中的问题(操作系统日志提供更多上下文)。
  6. \n
\n

我建议StaticString尽可能使用,例如函数和文件名,以及UInt行号和列号。现在只有LoggeriOS 14+ API 才可以实现这一点。

\n

(奖励)记录注意事项

\n

我应该做大量的日志记录吗?

\n

我认为记录是一种平衡行为。如果您有权访问这些日志记录,那么在查看崩溃报告时,大量日志记录对于调试问题非常有帮助。但是,您需要平衡隐私、编译的二进制文件的大小和压倒日志系统。

\n

1. 隐私:使用"\\(message, privacy: .private)""\\(message, privacy: .public)"来编辑敏感信息Logger,并且在使用或语句时不要打印敏感信息。我建议在出于调试目的时将语句(用于生产)替换为操作系统日志。或者在日志服务中使用预处理器指令 ( )仅在一个方案上。NSLogprintprint#if DEBUGprintDebug

\n

2. 编译后的二进制文件的大小:日志语句本质上是更多的代码行。添加的代码越多,二进制文件就越大。只要您不记录阳光下的所有内容,这通常不是问题,因为只有该指标的大幅增加才会影响二进制大小

\n

3. 日志系统不堪重负:如果日志记录过多,设备可能必须丢弃一些日志才能继续运行(由于操作系统多任务处理,运行时RAM 或交换空间有限- 对于旧设备来说情况更糟)。苹果通常可以可靠地处理大量用户日志记录,但在实践中很难实现这一点。不过,如果您确实遇到这种情况,一些更有用的日志事件可能会被推出窗口。

\n

操作系统日志

\n

最重要的是要正确设置日志级别。默认情况下,iOS 上的日志条目.info将在日志生成时被抑制,因此唯一真正的负面影响是二进制大小。如果您以( os_log) 或更高级别登录.log.default,则需要小心不要记录太多。Apple 的一般建议是您查看每个高级日志条目,以确保它们包含在调试时可能有用信息。

\n

最后,确保设置了subsystem和category。统一的日志系统处理大量的日志条目——带有类别的子系统让您更容易专注于具体问题

\n

如何选择类别和子系统?

\n

这是一个非常固执己见的话题,所以我将重点关注我在做出决定时会想到的内容。YMMV:可能有更好的选择,如果有,请告诉我。

\n

1. 按功能(特定于产品):例如,如果您是购物应用程序,则可以subsystempaymentlogin其他应用程序流程进行组织。如果您尚未使用模块化代码库来组织功能(或共享代码的框架),那么我可以推荐本教程系列

\n

2. 通过技术:我的意思是代码的领域,是推送通知、深层链接、用户默认(或持久性)逻辑。这可能对category参数有帮助。

\n

3. 按目标:如果可以的话,我也喜欢使用bundleIdentifier当前的。如果您有扩展目标(推送通知)、多个目标或不同的平台(如 WatchOS、SiriKit 扩展等),这更有意义。您可以使用以下代码获取当前包:Bundlesubsystem

\n
let myBundle = Bundle(for: MyClass.self)\n
Run Code Online (Sandbox Code Playgroud)\n

使用操作系统设备日志调试用户问题

\n

请参阅此处了解详细演练 ( Access Device Console Logs)。主要想法是将设备连接到您的 Mac(或使用 Mac 本身来运行 Mac 应用程序;)),打开Console.app,运行应用程序并尝试重现崩溃。如果您一开始就做到了这一点,那么您可以将崩溃时间与日志相关联以获取更多上下文。

\n

还有其他减少日志记录的原因吗?

\n

如果您是 Apple 的早期采用者,那么您就会知道某些 iOS Beta 版本的 bug 有多多;)。当向 Apple 发布错误报告时,操作系统日志会包含在sysdiagnose文件中,因此您可能会获得更快的周转时间,因为您的错误报告噪音较少

\n

我应该使用第三方日志服务吗?

\n

取决于你是否负担得起以及你是否真的需要它。我更喜欢最大限度地减少在代码中导入的第三方依赖项的数量(如果我可以控制这一点),并且本机 (Apple) 日志记录 API 对于我的大多数用例都工作得很好。这些服务每月额外收费,但优点是用户无法通过将设备连接到 Mac来查看日志(即使他愿意) 。Console.app请注意,如果您应用日志级别,这不是问题,因此我通常不使用第三方服务(YMMV)。使用网络日志的一个潜在原因是,只要您相信第三方供应商可以防止数据泄露,就可以更安全地防止逆向工程(但它也不那么环保;))。.private

\n