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


87

在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

1
我使用NSLog("Running %@ : %@",NSStringFromClass(self.dynamicType),__FUNCTION__)
Magster


1
我认为您的日志记录样式应该是“漂亮功能”的定义。感谢分享。
HuaTham

Answers:


101

斯威夫特有#file#function#line#column。从Swift编程语言

#file -字符串-出现在其中的文件的名称。

#line -整数-它出现的行号。

#column -Int-起始的列号。

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


11
可以肯定的是-这些都是C提出的。但是,这并没有回答关于的问题__PRETTY_FUNCTION__,这很难通过给定的选项创建。(有__CLASS__吗?如果是的话,那会有所帮助。)
Olie

10
在Swift 2.2中,应使用#function,#file等,如下所示:stackoverflow.com/a/35991392/1151916
Ramis

70

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

  • #file(字符串)显示文件的名称。
  • #line(Int)显示它的行号。
  • #column(Int)开头的列号。
  • #function(字符串)出现在其中的声明的名称。

来自894页的Swift编程语言(Swift 3.1)

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

1
应该将其标记为当前正确的答案。
丹尼·布拉沃

18

Swift 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上不可用。
primulaveris 2015年

1
好小子。Swift> 2.1的微小更改:“ println”已重命名为“ print”。print(“ file:(file.debugDescription)function:(function)line:(line)”)
John Doe,

很酷,很有效。能够以某种方式将类/对象传递给它也将很棒(一种选择是使用一个显式的self参数)。谢谢。
西藏沿海地区

您的方法存在的问题:-此函数不是线程安全的。如果您一次从不同的线程调用它,对于一些不良的惊喜准备-使用全局函数是不好的做法
卡罗利Nyisztor

9

从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

8

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

[快速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
arediver

5

这是更新的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)")

1
太好了 不过话又说回来.. LogW不能准确地使用相同的print()(与参数,以逗号分隔)..
冈蒂斯Treulands

“ LogW(”套接字连接错误:(错误)其他信息:(otherInfo)“)”
Daniel Ryan

1
真正。好吧,我修改了一下,发现的唯一其他解决方案是-使用extra()来保存该语句,使其尽可能与print()相似。用您的答案创建了这个github.com/GuntisTreulands/ColorLogger-Swift ,非常感谢!:)
Guntis Treulands '16

很有用!从Swift 2.2开始,__FUNCTION__ becomes #function, __FILE__ becomes #file, and __LINE__ becomes #line.
卡尔·史密斯

我们在使用新值时遇到了麻烦。我们将等到swift 3,直到更新我们的代码库。
丹尼尔·瑞安

0

或使用以下功能进行轻微的修改:

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 Product:init(type:name:year:price :):34 FirstViewController:viewDidLoad():15 AppDelegate:applicationDidBecomeActive:62 * /


0

我用的是,这是一个快速文件所需要的,其他所有文件都将它(作为全局函数)将其拾取。当您要发布应用程序时,只需注释掉该行。

import UIKit

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

0

斯威夫特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)")
}

0

雨燕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

0

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

NSLog("\(type(of:self)): %@", #function)
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.