【问题标题】:Logging Method signature using swift使用 swift 记录方法签名
【发布时间】:2014-06-04 22:07:54
【问题描述】:

我正在尝试重写我的日志记录类,我想知道如何在 swift 文件中替换 PRETTY_FUNCTION 或 NSStringFromSelector(_cmd) 以跟踪方法调用?

【问题讨论】:

  • 这是一个日志库,您可以查看github.com/goktugyil/QorumLogs
  • 对于希望在其应用程序或框架中实现日志功能的任何人:Evergreen 以 Python 出色的 logging 模块为蓝本,具有可调整的日志级别以及您对日志框架的期望:github.com/viwid/Evergreen它基于记录器层次结构,还允许您调整软件部分的详细程度,例如降低您当前正在调试的部分的日志级别。这也让您的框架的用户有机会配置您的日志记录的详细程度。

标签: ios cmd swift ios8


【解决方案1】:

swift 中的特殊字面量如下(来自[the swift guide]

#file String 文件名。

#line Int 出现的行号。

#column Int 开始的列号。

#function String 出现的声明的名称。


在 Swift 2.2b4 之前,这些是

(https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/Expressions.html)):

__FILE__ String 文件名。

__LINE__ Int 出现的行号。

__COLUMN__ Int 开始的列号。

__FUNCTION__ String 出现的声明的名称。

您可以像这样在日志语句中使用这些:

println("error occurred on line \(__LINE__) in function \(__FUNCTION__)")

【讨论】:

  • 谢谢,我想我是在完成指南部分后才开始的。困扰我的是 FUNCTION 不包含该类,我必须修剪文件路径,并且在使用 FILE [ /Users 时只保留模块名称和文件名/joe.xxxx/Documents/Experimental/Logger/Logger/ViewController.swift].
【解决方案2】:

查看我刚刚发布的新库:https://github.com/DaveWoodCom/XCGLogger

这是一个 Swift 的调试日志库。

能够使用#function 宏的关键是将它们设置为日志记录函数的默认值。然后编译器将使用预期值填充它们。

func log(logMessage: String, functionName: String = #function) {
    print("\(functionName): \(logMessage)")
}

然后只需调用:

log("my message")

它按预期工作,为您提供如下内容:

whateverFunction(): my message

有关其工作原理的更多信息:https://www.cerebralgardens.com/blog/entry/2014/06/09/the-first-essential-swift-3rd-party-library-to-include-in-your-project

【讨论】:

  • __FUNCTION__ 的 bug 确实很严重,确认我在 Beta 1 中看到了它。如果我在后续的 beta 中看到它已修复,我会提供更新。
  • 我在github.com/DaveWoodCom/XCGLogger 中有一个解决__FUNCTION__ 错误的解决方法。由于错误总是在每次调用时将函数信息附加到末尾,如果你总是调用它两次,并且只使用附加的部分,那么你很高兴。
  • @DaveWood,Beta3 中仍然存在错误,仍然需要您的解决方法。
  • @DaveWood,Beta6,错误仍然存​​在。
  • 感谢图书馆!
【解决方案3】:

我会使用这样的东西:

func Log(message: String = "", _ path: String = __FILE__, _ function: String = __FUNCTION__) {
    let file = path.componentsSeparatedByString("/").last!.componentsSeparatedByString(".").first! // Sorry
    NSLog("\(file).\(function): \(message)")
}

与以前的答案相比的改进:

  • 使用 NSLog,而不是 print/println
  • 不再使用在字符串上不可用的 lastPathComponent
  • 日志消息是可选的

【讨论】:

    【解决方案4】:

    试试这个:

    class Log {
        class func msg(message: String,
            functionName:  String = __FUNCTION__, fileNameWithPath: String = __FILE__, lineNumber: Int = __LINE__ ) {
            // In the default arguments to this function:
            // 1) If I use a String type, the macros (e.g., __LINE__) don't expand at run time.
            //  "\(__FUNCTION__)\(__FILE__)\(__LINE__)"
            // 2) A tuple type, like,
            // typealias SMLogFuncDetails = (String, String, Int)
            //  SMLogFuncDetails = (__FUNCTION__, __FILE__, __LINE__) 
            //  doesn't work either.
            // 3) This String = __FUNCTION__ + __FILE__
            //  also doesn't work.
    
            var fileNameWithoutPath = fileNameWithPath.lastPathComponent
    
    #if DEBUG
            let output = "\(NSDate()): \(message) [\(functionName) in \(fileNameWithoutPath), line \(lineNumber)]"
            println(output)
    #endif
        }
    }
    

    日志使用:

    let x = 100
    Log.msg("My output message \(x)")
    

    【讨论】:

      【解决方案5】:

      这是我使用的:https://github.com/goktugyil/QorumLogs
      它类似于 XCGLogger,但更好。

      func myLog<T>(object: T, _ file: String = __FILE__, _ function: String = __FUNCTION__, _ line: Int = __LINE__) {
          let info = "\(file).\(function)[\(line)]:\(object)"
          print(info)
      }
      

      【讨论】:

        【解决方案6】:

        这只会在调试模式下打印:

        func debugLog(text: String,  fileName: String = __FILE__, function: String =  __FUNCTION__, line: Int = __LINE__) {
            debugPrint("[\((fileName as NSString).lastPathComponent), in \(function)() at line: \(line)]: \(text)")
        }
        

        结果:

        "[Book.swift, in addPage() at line: 33]: Page added with success"
        

        【讨论】:

          【解决方案7】:

          对于 Swift 3 及更高版本:

          print("\(#function)")
          

          【讨论】:

            【解决方案8】:

            从 Swift 2.2 开始,您可以使用 Literal Expressions 指定它,如 Swift Programming Language guide 中所述。

            因此,如果您有一个 Logger 结构,该结构有一个记录错误发生位置的函数,那么您可以这样调用它:

            Logger().log(message, fileName: #file, functionName: #function, atLine: #line)

            【讨论】:

              【解决方案9】:

              这是我的看法。

              func Log<T>(_ object: Shit, _ file: String = #file, _ function: String = #function, _ line: Int = #line) {
              
              var filename = (file as NSString).lastPathComponent
              filename = filename.components(separatedBy: ".")[0]
              
              let currentDate = Date()
              let df = DateFormatter()
              df.dateFormat = "HH:mm:ss.SSS"
              
              print("┌──────────────┬───────────────────────────────────────────────────────────────")
              print("│ \(df.string(from: currentDate)) │ \(filename).\(function) (\(line))")
              print("└──────────────┴───────────────────────────────────────────────────────────────")
              print("  \(object)\n")}
              

              希望你喜欢。

              【讨论】:

              • 我笑了!
              • 这是 h4x0r PR0 级别的日志记录
              【解决方案10】:

              Swift 4,基于所有这些很棒的答案。 ❤️

              /*
               That's how I protect my virginity.
              */
              
              import Foundation
              
              /// Based on [this SO question](https://stackoverflow.com/questions/24048430/logging-method-signature-using-swift).
              class Logger {
              
                  // MARK: - Lifecycle
              
                  private init() {} // Disallows direct instantiation e.g.: "Logger()"
              
                  // MARK: - Logging
              
                  class func log(_ message: Any = "",
                                 withEmoji: Bool = true,
                                 filename: String = #file,
                                 function: String =  #function,
                                 line: Int = #line) {
              
                      if withEmoji {
                          let body = emojiBody(filename: filename, function: function, line: line)
                          emojiLog(messageHeader: emojiHeader(), messageBody: body)
              
                      } else {
                          let body = regularBody(filename: filename, function: function, line: line)
                          regularLog(messageHeader: regularHeader(), messageBody: body)
                      }
              
                      let messageString = String(describing: message)
                      guard !messageString.isEmpty else { return }
                      print(" └ ? \(messageString)\n")
                  }
              }
              
              // MARK: - Private
              
              // MARK: Emoji
              
              private extension Logger {
              
                  class func emojiHeader() -> String {
                      return "⏱ \(formattedDate())"
                  }
              
                  class func emojiBody(filename: String, function: String, line: Int) -> String {
                      return "? \(filenameWithoutPath(filename: filename)), in ? \(function) at #️⃣ \(line)"
                  }
              
                  class func emojiLog(messageHeader: String, messageBody: String) {
                      print("\(messageHeader) │ \(messageBody)")
                  }
              }
              
              // MARK: Regular
              
              private extension Logger {
              
                  class func regularHeader() -> String {
                      return " \(formattedDate()) "
                  }
              
                  class func regularBody(filename: String, function: String, line: Int) -> String {
                      return " \(filenameWithoutPath(filename: filename)), in \(function) at \(line) "
                  }
              
                  class func regularLog(messageHeader: String, messageBody: String) {
                      let headerHorizontalLine = horizontalLine(for: messageHeader)
                      let bodyHorizontalLine = horizontalLine(for: messageBody)
              
                      print("┌\(headerHorizontalLine)┬\(bodyHorizontalLine)┐")
                      print("│\(messageHeader)│\(messageBody)│")
                      print("└\(headerHorizontalLine)┴\(bodyHorizontalLine)┘")
                  }
              
                  /// Returns a `String` composed by horizontal box-drawing characters (─) based on the given message length.
                  ///
                  /// For example:
                  ///
                  ///     " ViewController.swift, in viewDidLoad() at 26 " // Message
                  ///     "──────────────────────────────────────────────" // Returned String
                  ///
                  /// Reference: [U+250x Unicode](https://en.wikipedia.org/wiki/Box-drawing_character)
                  class func horizontalLine(for message: String) -> String {
                      return Array(repeating: "─", count: message.count).joined()
                  }
              }
              
              // MARK: Util
              
              private extension Logger {
              
                  /// "/Users/blablabla/Class.swift" becomes "Class.swift"
                  class func filenameWithoutPath(filename: String) -> String {
                      return URL(fileURLWithPath: filename).lastPathComponent
                  }
              
                  /// E.g. `15:25:04.749`
                  class func formattedDate() -> String {
                      let dateFormatter = DateFormatter()
                      dateFormatter.dateFormat = "HH:mm:ss.SSS"
                      return "\(dateFormatter.string(from: Date()))"
                  }
              }
              

              使用Logger.log() 通话--(表情符号默认开启):


              拨打Logger.log(withEmoji: false):


              更多使用示例:

              Logger.log()
              Logger.log(withEmoji: false)
              Logger.log("I'm a virgin.")
              Logger.log("I'm a virgin.", withEmoji: false)
              Logger.log(NSScreen.min.frame.maxX) // Can handle "Any" (not only String).
              

              【讨论】:

              • 有趣的是,你使用了 Logger 类,Apple 在 iOS 14 中引入了它(我也是 wrote about it)。 ;)
              • @PranavKasetti 确实如此。我实际上在Logger 周围创建了一个包。看看:github.com/backslash-f/apploggerLogger 太棒了。
              【解决方案11】:

              这将使您一次性获得类和函数名称:

              var name = NSStringFromClass(self.classForCoder) + "." + __FUNCTION__
              

              【讨论】:

              • 也许self.dynamicType 而不是self.classForCoder 会很简单?
              【解决方案12】:

              这似乎在 swift 3.1 中运行良好

              print("File: \((#file as NSString).lastPathComponent) Func: \(#function) Line: \(#line)")
              

              【讨论】:

                【解决方案13】:

                Swift 3 支持带有日期、函数名、文件名、行号的 debugLog 对象:

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

                【讨论】:

                  【解决方案14】:

                  ****** 可能已过时。 *******

                  如 cmets 中提到的 pranav,请使用 Logger for iOS 14+


                  我发布了一个新库:Printer

                  它有很多功能可以让你以不同的方式登录。

                  要记录成功消息:

                  Printer.log.success(details: "This is a Success message.")
                  

                  输出:

                  Printer ➞ [✅ Success] [⌚04-27-2017 10:53:28] ➞ ✹✹This is a Success message.✹✹
                  [Trace] ➞ ViewController.swift ➞ viewDidLoad() #58
                  

                  免责声明:这个库是我创建的。

                  【讨论】:

                  • 在 iOS 14+ 上使用 Logger
                  【解决方案15】:
                  func Log<T>(_ object: T, fileName: String = #file, function: String =  #function, line: Int = #line) {
                      NSLog("\((fileName as NSString).lastPathComponent), in \(function) at line: \(line): \(object)")
                  }
                  

                  【讨论】:

                  • 请添加您的答案的解释
                  • 我从下面的每个答案中提取了所有内容。例如,它不是字符串,而是对象,您不需要输入参数标签,因此 Log(object) 和 Log("test") 可以工作
                  【解决方案16】:

                  使用 os_log 的替代版本可能是:

                  func Log(_ msg: String = "", _ file: NSString = #file, _ function: String = #function) {
                      let baseName = file.lastPathComponent.replacingOccurrences(of: ".swift", with: "")
                      os_log("%{public}@:%{public}@: %@", type: .default, baseName, function, msg)
                  }
                  

                  还是重字符串处理,如果你负担不起,直接使用os_log就行了。

                  【讨论】:

                    猜你喜欢
                    • 2018-11-13
                    • 2022-06-10
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 2015-06-17
                    • 1970-01-01
                    • 2014-04-13
                    • 1970-01-01
                    相关资源
                    最近更新 更多