NSLog(@"%s", __PRETTY_FUNCTION__) 是不是有 Swift 替代方案

Posted

技术标签:

【中文标题】NSLog(@"%s", __PRETTY_FUNCTION__) 是不是有 Swift 替代方案【英文标题】:Is there a Swift alternative for NSLog(@"%s", __PRETTY_FUNCTION__)NSLog(@"%s", __PRETTY_FUNCTION__) 是否有 Swift 替代方案 【发布时间】:2014-06-25 07:21:38 【问题描述】:

在 Objective C 中,您可以使用以下方法记录正在调用的方法:

NSLog(@"%s", __PRETTY_FUNCTION__)

这通常用于日志记录宏。

虽然 Swift 不支持宏(我认为),但我仍然想使用包含被调用函数名称的通用日志语句。这在 Swift 中可行吗?

更新: 我现在使用这个全局函数进行日志记录,可以在这里找到: https://github.com/evermeer/Stuff#print 您可以使用以下方式安装:

pod 'Stuff/Print'

代码如下:

public class Stuff 

    public enum logLevel: Int 
        case info = 1
        case debug = 2
        case warn = 3
        case error = 4
        case fatal = 5
        case none = 6

        public func description() -> String 
            switch self 
            case .info:
                return "❓"
            case .debug:
                return "✳️"
            case .warn:
                return "⚠️"
            case .error:
                return "????"
            case .fatal:
                return "????"
            case .none:
                return ""
            
        
    

    public static var minimumLogLevel: logLevel = .info

    public static func print<T>(_ object: T, _ level: logLevel = .debug, filename: String = #file, line: Int = #line, funcname: String = #function) 
        if level.rawValue >= Stuff.minimumLogLevel.rawValue 
            let dateFormatter = DateFormatter()
            dateFormatter.dateFormat = "MM/dd/yyyy HH:mm:ss:SSS"
            let process = ProcessInfo.processInfo
            let threadId = "?"
            let file = URL(string: filename)?.lastPathComponent ?? ""
            Swift.print("\n\(level.description()) .\(level) ⏱ \(dateFormatter.string(from: Foundation.Date())) ???? \(process.processName) [\(process.processIdentifier):\(threadId)] ???? \(file)(\(line)) ⚙️ \(funcname) ➡️\r\t\(object)")
        
    

你可以这样使用:

Stuff.print("Just as the standard print but now with detailed information")
Stuff.print("Now it's a warning", .warn)
Stuff.print("Or even an error", .error)

Stuff.minimumLogLevel = .error
Stuff.print("Now you won't see normal log output")
Stuff.print("Only errors are shown", .error)

Stuff.minimumLogLevel = .none
Stuff.print("Or if it's disabled you won't see any log", .error)    

这将导致:

✳️ .debug ⏱ 02/13/2017 09:52:51:852 ???? xctest [18960:?] ???? PrintStuffTests.swift(15) ⚙️ testExample() ➡️
    Just as the standard print but now with detailed information

⚠️ .warn ⏱ 02/13/2017 09:52:51:855 ???? xctest [18960:?] ???? PrintStuffTests.swift(16) ⚙️ testExample() ➡️
    Now it's a warning

???? .error ⏱ 02/13/2017 09:52:51:855 ???? xctest [18960:?] ???? PrintStuffTests.swift(17) ⚙️ testExample() ➡️
    Or even an error

???? .error ⏱ 02/13/2017 09:52:51:855 ???? xctest [18960:?] ???? PrintStuffTests.swift(21) ⚙️ testExample() ➡️
    Only errors are shown

【问题讨论】:

我用NSLog("Running %@ : %@",NSStringFromClass(self.dynamicType),__FUNCTION__) 我用github.com/goktugyil/QorumLogs 我认为你的日志风格应该是“漂亮功能”的定义。感谢分享。 【参考方案1】:

Swift 有 #file#function#line#column。来自Swift Programming Language:

#file - 字符串 - 文件名。

#line - Int - 它出现的行号。

#column - Int - 它开始的列号。

#function - 字符串 - 出现的声明的名称。

【讨论】:

当然——这些都是从 C 开始的。但这并没有回答关于 __PRETTY_FUNCTION__ 的问题,它不容易从给定的选项中创建。 (有 __CLASS__ 吗?如果有,那会有所帮助。) 在 Swift 2.2 中应该使用 #function、#file 和其他,如下所示:***.com/a/35991392/1151916【参考方案2】:

从 Swift 2.2 开始,我们应该使用:

#file(字符串)文件名。 #line (Int) 出现的行号。 #column (Int) 开始的列号。 #function(字符串)它出现的声明的名称。

来自 The Swift Programming Language (Swift 3.1),第 894 页。

func specialLiterals() 
    print("#file literal from file: \(#file)")
    print("#function literal from function: \(#function)")
    print("#line: \(#line) -> #column: \(#column)")

// Output:
// #file literal from file: My.playground
// #function literal from function: specialLiterals()
// #line: 10 -> #column: 42

【讨论】:

这应该被标记为当前正确的答案。【参考方案3】:

斯威夫特 4 这是我的方法:

func pretty_function(_ file: String = #file, function: String = #function, line: Int = #line) 

    let fileString: NSString = NSString(string: file)

    if Thread.isMainThread 
        print("file:\(fileString.lastPathComponent) function:\(function) line:\(line) [M]")
     else 
        print("file:\(fileString.lastPathComponent) function:\(function) line:\(line) [T]")
    

将其设为全局函数,然后调用

pretty_function()

奖励:您将看到线程正在执行,[T] 用于后台线程,[M] 用于主线程。

【讨论】:

需要将文件的声明从String改为NSString。 lastPathComponent 在 String 上不可用。 很棒的家伙。 Swift > 2.1 的微小变化:“println”已重命名为“print”。 print("file:(file.debugDescription) function:(function) line:(line)") 酷,很好用。能够以某种方式将类/对象传递给它也会很棒(一种选择是使用显式的 self 参数)。谢谢。 您的方法存在问题: - 此函数不是线程安全的。如果您同时从不同的线程调用它,请为一些不好的意外做好准备 - 使用全局函数是不好的做法【参考方案4】:

从 XCode beta 6 开始,您可以使用 reflect(self).summary 获取类名并使用 __FUNCTION__ 获取函数名,但现在有些混乱。希望他们能提出更好的解决方案。在我们完成测试之前,使用#define 可能是值得的。

这段代码:

NSLog("[%@ %@]", reflect(self).summary, __FUNCTION__)

给出如下结果:

2014-08-24 08:46:26.606 SwiftLessons[427:16981938] [C12SwiftLessons24HelloWorldViewController (has 2 children) goodbyeActiongoodbyeAction]

编辑:这是更多的代码,但让我更接近我需要的,我认为这就是你想要的。

func intFromString(str: String) -> Int

    var result = 0;
    for chr in str.unicodeScalars
    
        if (chr.isDigit())
        
            let value = chr - "0";
            result *= 10;
            result += value;
        
        else
        
            break;
        
    

    return result;



@IBAction func flowAction(AnyObject)

    let cname = _stdlib_getTypeName(self)
    var parse = cname.substringFromIndex(1)                                 // strip off the "C"
    var count = self.intFromString(parse)
    var countStr = String(format: "%d", count)                              // get the number at the beginning
    parse = parse.substringFromIndex(countStr.lengthOfBytesUsingEncoding(NSUTF8StringEncoding))
    let appName = parse.substringToIndex(count)                             // pull the app name

    parse = parse.substringFromIndex(count);                                // now get the class name
    count = self.intFromString(parse)
    countStr = String(format: "%d", count)
    parse = parse.substringFromIndex(countStr.lengthOfBytesUsingEncoding(NSUTF8StringEncoding))
    let className = parse.substringToIndex(count)
    NSLog("app: %@ class: %@ func: %@", appName, className, __FUNCTION__)

它给出这样的输出:

2014-08-24 09:52:12.159 SwiftLessons[1397:17145716] app: SwiftLessons class: ViewController func: flowAction

【讨论】:

【参考方案5】:

我更喜欢定义一个全局日志函数:

[斯威夫特 3.1]

func ZYLog(_ object: Any?, filename: String = #file, line: Int = #line, funcname: String = #function) 
    #if DEBUG
    print("****\(Date()) \(filename)(\(line)) \(funcname):\r\(object ?? "nil")\n")
    #endif

[Swift 3.0]

func ZYLog<T>(_ object: T?, filename: String = #file, line: Int = #line, funcname: String = #function) 
    #if DEBUG
    print("****\(Date()) \(filename)(\(line)) \(funcname):\r\(object)\n")
    #endif

[Swift 2.0]

func ZYLog<T>(object: T, filename: String = __FILE__, line: Int = __LINE__, funcname: String = __FUNCTION__) 
    println("****\(filename.lastPathComponent)(\(line)) \(funcname):\r\(object)\n")

输出类似于:

****ZYHttpSessionManager.swift(78) POST(_:parameters:success:failure:):
[POST] user/login, 
    "auth_key" = xxx;
    "auth_type" = 0;
    pwd = xxx;
    user = "xxx";


****PointViewController.swift(162) loadData():
review/list [limit: 30, skip: 0]

****ZYHttpSessionManager.swift(66) GET(_:parameters:success:failure:):
[GET] review/list, 
    "auth_key" = xxx;
    uuid = "xxx";

【讨论】:

这里实际上不需要泛型函数,因为object 参数可以声明为Any 而不是T【参考方案6】:

这是一个更新的 Swift 2 答案。

func LogW(msg:String, function: String = __FUNCTION__, file: String = __FILE__, line: Int = __LINE__)
    print("[WARNING]\(makeTag(function, file: file, line: line)) : \(msg)")


private func makeTag(function: String, file: String, line: Int) -> String
    let url = NSURL(fileURLWithPath: file)
    let className:String! = url.lastPathComponent == nil ? file: url.lastPathComponent!
    return "\(className) \(function)[\(line)]"

使用示例:

LogW("Socket connection error: \(error)")

【讨论】:

这太棒了。但话又说回来.. LogW 不能与 print() 完全相同(带参数,用逗号分隔).. "LogW 不能和 print() 完全一样使用(带参数,用逗号隔开)我想添加这个支持,但发现不需要。"LogW("Socket连接错误:(错误)其他信息:(otherInfo)“)” 是的。好吧,我四处修补,我发现的唯一其他解决方案是 - 使用额外的 () 来保存语句,使其尽可能类似于 print() 。使用您的答案创建了这个github.com/GuntisTreulands/ColorLogger-Swift 无论如何,非常感谢! :) 非常有用!从 Swift 2.2 开始,__FUNCTION__ becomes #function, __FILE__ becomes #file, and __LINE__ becomes #line. 我们在使用新值时遇到了问题。我们将等到 swift 3,直到更新我们的代码库。【参考方案7】:

或轻微的功能修改:

func logFunctionName(file:String = __FILE__, fnc:String = __FUNCTION__, line:(Int)=__LINE__) 
    var className = file.lastPathComponent.componentsSeparatedByString(".")
    println("\(className[0]):\(fnc):\(line)")

/* 将产生如下执行跟踪: AppDelegate:application(_:didFinishLaunchingWithOptions:):18 产品:init(类型:名称:年份:价格:):34 FirstViewController:viewDidLoad():15 AppDelegate:applicationDidBecomeActive:62 */

【讨论】:

【参考方案8】:

我使用,这就是 swift 文件中所需的全部内容,所有其他文件都会选择它(作为全局函数)。 当您想要发布应用程序时,只需注释掉该行即可。

import UIKit

func logFunctionName(file:NSString = __FILE__, fnc:String = __FUNCTION__)  
    println("\(file.lastPathComponent):\(fnc)")

【讨论】:

【参考方案9】:

Swift 3.0

public func LogFunction<T>(object: T, filename: String = #file, line: Int = #line, funcname: String = #function) 
    let dateFormatter = DateFormatter()
    dateFormatter.dateFormat = "MM/dd/yyyy HH:mm:ss:SSS"
    let process = ProcessInfo.processInfo()
    let threadId = "?"
    print("\(dateFormatter.string(from:Date())) \(process.processName) [\(process.processIdentifier):\(threadId)] \(filename)(\(line)) \(funcname)::: \(object)")

【讨论】:

【参考方案10】:

Swift 3.x+

如果您不想要 整个 文件名,那么这里有一个快速解决方法。

func trace(fileName:String = #file, lineNumber:Int = #line, functionName:String = #function) -> Void 
    print("filename: \(fileName.components(separatedBy: "/").last!) function: \(functionName) line: #\(lineNumber)")


filename: ViewController.swift function: viewDidLoad() line: #42

【讨论】:

【参考方案11】:

另一种记录函数调用的方式:

NSLog("\(type(of:self)): %@", #function)

【讨论】:

以上是关于NSLog(@"%s", __PRETTY_FUNCTION__) 是不是有 Swift 替代方案的主要内容,如果未能解决你的问题,请参考以下文章

类似NSLog的ASL Log宏

是否有NSLog的Swift替代方案(@“%s”,__ PRETTY_FUNCTION__)

NSLog函数名文件名和行号

NSLog演化

自定义的打印语句NSLog在控制台输出不完整的完美解决

用于详细调试输出的宏