使用 swift 记录方法签名

Posted

技术标签:

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

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

【问题讨论】:

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

【讨论】:

以上是关于使用 swift 记录方法签名的主要内容,如果未能解决你的问题,请参考以下文章

Objective-C 方法签名在 Swift 转换后是一样的

在 Swift 中,如何测试一个对象是不是实现了一个可选的协议方法,该方法在签名上有所不同,而无需实际调用该方法?

java中的签名----转载

WinCC的电子签名与审计追踪

用于向 Google API (Swift) 验证服务帐户的 JWT 签名无效

带有数字签名的 Swift 签名字符串