Có sự thay thế Swift cho NSLog không (@ “% s”, __PRETTY_FUNCTION__)


88

Trong Objective C, bạn có thể ghi lại phương thức đang được gọi bằng cách sử dụng:

NSLog(@"%s", __PRETTY_FUNCTION__)

Thông thường điều này được sử dụng từ macro ghi nhật ký.

Mặc dù Swift không hỗ trợ macro (tôi nghĩ), tôi vẫn muốn sử dụng một câu lệnh nhật ký chung bao gồm tên của hàm đã được gọi. Điều đó có khả thi trong Swift không?

Cập nhật: Bây giờ tôi sử dụng chức năng toàn cầu này để ghi nhật ký có thể tìm thấy tại đây: https://github.com/evermeer/Stuff#print Và bạn có thể cài đặt bằng cách sử dụng:

pod 'Stuff/Print'

Đây là mã:

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)")
        }
    }
}

Bạn có thể sử dụng như thế này:

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)    

Điều này sẽ dẫn đến:

✳️ .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
Tôi sử dụngNSLog("Running %@ : %@",NSStringFromClass(self.dynamicType),__FUNCTION__)
Magster Ngày


1
Tôi nghĩ rằng phong cách ghi nhật ký của bạn nên là định nghĩa của "chức năng khá". Cám ơn vì đã chia sẻ.
HuaTham 17/02/17

Câu trả lời:


101

Swift có #file, #function, #line#column. Từ ngôn ngữ lập trình Swift :

#file - Chuỗi - Tên của tệp mà nó xuất hiện.

#line - Int - Số dòng mà nó xuất hiện.

#column - Int - Số cột mà nó bắt đầu.

#function - Chuỗi - Tên của khai báo mà nó xuất hiện.


11
Chắc chắn rồi - tất cả đều chuyển tiếp từ C. Nhưng điều đó không trả lời được câu hỏi về __PRETTY_FUNCTION__điều này không dễ dàng tạo ra từ các tùy chọn đã cho. (Có không __CLASS__? Nếu vậy, điều đó sẽ giúp ích.)
Olie

10
Trong Swift 2.2 nên sử dụng # function, #file và những thứ khác như được hiển thị ở đây: stackoverflow.com/a/35991392/1151916
Ramis

70

Bắt đầu từ Swift 2.2, chúng ta nên sử dụng:

  • #file (Chuỗi) Tên của tệp mà nó xuất hiện.
  • #line (Int) Số dòng mà nó xuất hiện.
  • #column (Int) Số cột mà nó bắt đầu.
  • # function (Chuỗi) Tên của khai báo mà nó xuất hiện.

Từ Ngôn ngữ Lập trình Swift (Swift 3.1) tại trang 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

1
Đây sẽ được đánh dấu là câu trả lời hiện tại đúng.
Danny Bravo

18

Swift 4
Đây là cách tiếp cận của tôi:

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]")
    }
}

Đặt chức năng này thành một chức năng toàn cục và chỉ cần gọi

pretty_function()

Phần thưởng: Bạn sẽ thấy luồng được thực thi trên, [T] cho luồng nền và [M] cho luồng chính.


Cần thay đổi phần khai báo của tệp từ String thành NSString. lastPathComponent không có sẵn trên String.
primulaveris

1
Anh bạn tuyệt vời. Thay đổi nhỏ cho Swift> 2.1: "println" đã được đổi tên thành "print". print ("file: (file.debugDescription) function: (function) line: (line)")
John Doe

Tuyệt, tốt là nó hoạt động. Cũng sẽ rất tuyệt nếu có thể truyền lớp / đối tượng vào nó bằng cách nào đó (một tùy chọn là sử dụng một tự đối số rõ ràng). Cảm ơn.
Sea Coast of Tibet

Các vấn đề với cách tiếp cận của bạn: - Chức năng này không an toàn theo chuỗi. Nếu bạn gọi nó từ chủ đề khác nhau cùng một lúc, hãy chuẩn bị cho một số bất ngờ xấu - Sử dụng chức năng toàn cầu là xấu thực hành
Karoly Nyisztor

9

Kể từ phiên bản beta 6 của XCode, bạn có thể sử dụng reflect(self).summaryđể lấy tên lớp và __FUNCTION__lấy tên hàm, nhưng mọi thứ đang bị xáo trộn một chút, ngay bây giờ. Hy vọng rằng họ sẽ đưa ra một giải pháp tốt hơn. Có thể đáng giá nếu sử dụng #define cho đến khi chúng tôi hết phiên bản beta.

Mã này:

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

cho kết quả như thế này:

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

BIÊN TẬP: Đây là mã nhiều hơn, nhưng đã giúp tôi đến gần hơn với những gì tôi cần, mà tôi nghĩ là những gì bạn muốn.

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__)
}

Nó cho kết quả như thế này:

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

8

Tôi thích xác định một hàm nhật ký toàn cục:

[Swift 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")
}

đầu ra là một cái gì đó như:

****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";
}

Bạn không thực sự cần một hàm chung chung ở đây, vì objecttham số có thể được khai báo Anythay vì T.
werediver

5

Đây là câu trả lời Swift 2 được cập nhật.

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)]"
}

Ví dụ sử dụng:

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

1
Điều này là tuyệt vời. Nhưng sau đó một lần nữa .. LogW có thể không được sử dụng chính xác giống như in () (với các thông số, cách nhau bằng dấu phẩy) ..
Guntis Treulands

"LogW không thể được sử dụng chính xác như print () (với các tham số, được phân tách bằng dấu phẩy" Tôi đã nghĩ đến việc thêm hỗ trợ này nhưng tôi thấy mình không cần nó. "LogW (" Lỗi kết nối ổ cắm: (lỗi) thông tin khác : (otherInfo) ")"
Daniel Ryan

1
Thật. Tôi đã mày mò và chỉ có giải pháp khác mà tôi tìm thấy là - sử dụng hàm extra () để giữ câu lệnh, để làm cho nó giống với print () nhất có thể. Đã sử dụng câu trả lời của bạn để tạo github.com/GuntisTreulands/ColorLogger-Swift Anyways, cảm ơn rất nhiều! :)
Guntis Treulands

Rất hữu dụng! Kể từ Swift 2.2,__FUNCTION__ becomes #function, __FILE__ becomes #file, and __LINE__ becomes #line.
Carl Smith,

Chúng tôi đã gặp khó khăn với các giá trị mới. Chúng tôi sẽ đợi đến swift 3 cho đến khi cập nhật cơ sở mã của chúng tôi.
Daniel Ryan,

0

Hoặc sửa đổi chức năng nhẹ với:

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

}

/ * sẽ tạo ra một dấu vết thực thi như: AppDelegate: application (_: didFinishLaunchingWithOptions :): 18 Product: init (type: name: year: price :): 34 FirstViewController: viewDidLoad (): 15 AppDelegate: applicationDidBecomeActive: 62 * /


0

Tôi sử dụng, đây là tất cả những gì được yêu cầu trong một tệp nhanh, tất cả các tệp khác sẽ chọn nó (như một hàm toàn cầu). Khi bạn muốn phát hành ứng dụng chỉ cần bình luận ra dòng.

import UIKit

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

0

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)")
}

0

Swift 3.x +

Nếu bạn không muốn toàn bộ tên tệp thì đây là một bản sửa lỗi nhanh cho điều đó.

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

Một cách khác để ghi nhật ký cuộc gọi hàm:

NSLog("\(type(of:self)): %@", #function)
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.