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

时间:2017-02-01 08:16:40

标签: swift

以下是我想要做的一个例子:

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

当前情况我完成了硬编码值line numberfile name。但是可以以编程方式选择line numberfile name

5 个答案:

答案 0 :(得分:87)

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.

示例

print("Function: \(#function), line: \(#line)") 

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

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

可以像这样使用

track("enters app")

在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.

有关详细信息,请参阅the documentation

答案 1 :(得分:8)

您可以使用#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
}

答案 2 :(得分:2)

static func DLog(message: String, file: String = #file, function: String = #function, line: Int = #line, column: Int = #column) {
    print("\(file) : \(function) : \(line) : \(column) - \(message)")
}

答案 3 :(得分:1)

示例日志代码 (TL;DR)

import os.log

@available(OSX 11.0, iOS 14.0, *)
extension Logger {
  private static var subsystem = Bundle.main.bundleIdentifier!

  /// Logs the payment flows like Apple Pay.
  @available(OSX 11.0, iOS 14.0, *)
  static let payments = Logger(subsystem: subsystem, category: "payments")
}

static func DLog(message: StaticString, file: StaticString = #file, function: StaticString = #function, line: UInt = #line, column: UInt = #column, category: String, type: OSLogType = .info, bundle: Bundle = .main) {

  // This method is only for iOS 14+
  if #available(OSX 11.0, iOS 14.0, *) {

    Logger.payments.debug("\(file) : \(function) : \(line) : \(column) - \(message, privacy: .private)")
    // This makes the message unreadable without a debugger attached.

  } else {
    // Fallback on earlier versions

    let customLog = OSLog(subsystem: bundle.bundleIdentifier!,
                          category: category)

    // 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`!
    os_log(message, log: customLog, type: type)

    // Unfortunately this legacy API doesn't support non-StaticString logs. :(
  }
}

请注意,您可能需要重新编写此代码,以提高私有/公共访问级别的灵活性 - os_logLogger 处理隐私级别的方式不同。这只是为了说明如果您不在 os_log 中添加 message 隐私级别,如何使用 API。

Apple 推荐使用 OS Logging 这就是我使用这种方法而不是 print 语句的原因。我添加此答案是因为 iOS 14 中的新 Logger API 也支持字符串插值。

动机

我认为这里的答案解决了 的问题,但可以进一步改进内存效率,因为此调试日志功能很可能会在整个应用程序中使用iOS 14+ 中的代码库(新的 Logger API)。 Apple 推荐使用 OS Logging 这就是为什么我使用这种方法而不是 print 语句(尽管这些仍然有效)。

这些是次要改进,但我认为它们可以提供帮助,因为即使是微小的改进也会加起来。事实上,Swift 标准库会在任何地方使用这些优化)!这些优化是 Swift 令人难以置信的内存效率的促成因素尽管是一种高级语言(并且是伟大的 API 设计的一部分!)。 (-:

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

改进

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

我建议在可以使用的地方使用 StaticString,例如函数名和文件名,以及 UInt 用于行号和列号。现在只能通过 Logger iOS 14+ API 实现。

(奖励)记录注意事项

我应该做很多日志记录吗?

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

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

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

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

操作系统日志

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

最后,确保设置子系统和类别。统一日志系统处理大量日志条目 - 带有类别的子系统使您可以更轻松地关注特定问题

如何选择类别和子系统?

这是一个非常固执己见的话题,所以我将专注于我在决定时会考虑的问题。 YMMV:可能有更好的选择,如果有,请告诉我。

1.按功能(特定于产品):例如,如果您是购物应用,则可以按 subsystempayment 或其他应用流程组织 login。如果您还没有使用模块化代码库来组织功能(或共享代码的框架),那么我可以推荐 this tutorial series

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

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

let myBundle = Bundle(for: MyClass.self)

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

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

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

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

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

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

答案 4 :(得分:0)

swift 3 swift 4

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"