มีทางเลือกอื่นของ Swift สำหรับ NSLog (@“% s”, __PRETTY_FUNCTION__) หรือไม่


88

ใน 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

คำตอบ:


101

สวิฟท์มี#file, #function, และ#line #columnจากSwift Programming Language :

#file - สตริง - ชื่อของไฟล์ที่ปรากฏ

#line - Int - หมายเลขบรรทัดที่ปรากฏ

#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 (String) ชื่อของไฟล์ที่ปรากฏ
  • #line (Int) หมายเลขบรรทัดที่ปรากฏ
  • #column (Int) หมายเลขคอลัมน์ที่เริ่มต้น
  • #function (String) ชื่อของการประกาศที่ปรากฏ

จาก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

1
ควรทำเครื่องหมายว่าเป็นคำตอบที่ถูกต้องในปัจจุบัน
Danny Bravo

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

1
สุดยอดครับ การเปลี่ยนแปลงเล็กน้อยสำหรับ Swift> 2.1: "println" ถูกเปลี่ยนชื่อเป็น "print" print ("file: (file.debugDescription) function: (function) line: (line)")
John Doe

เจ๋งดีที่ได้ผล จะเป็นการดีที่จะสามารถส่งคลาส / อ็อบเจ็กต์เข้าไปได้ (ทางเลือกหนึ่งคือใช้อาร์กิวเมนต์ที่ชัดเจน) ขอบคุณ.
ชายฝั่งทะเลทิเบต

ปัญหาเกี่ยวกับแนวทางของคุณ: - ฟังก์ชันนี้ไม่ปลอดภัยต่อเธรด หากคุณเรียกมันจากเธรดที่แตกต่างกันในคราวเดียวโปรดเตรียมพร้อมสำหรับความประหลาดใจที่ไม่ดี - การใช้ฟังก์ชันทั่วโลกเป็นการปฏิบัติที่ไม่ดี
Karoly Nyisztor

9

สำหรับ XCode เบต้า 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

ฉันต้องการกำหนดฟังก์ชันบันทึกส่วนกลาง:

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

ผลลัพธ์เป็นดังนี้:

****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ได้
werediver

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 () (ที่มีพารามิเตอร์คั่นด้วยลูกน้ำ) ..
Guntis Treulands

"ไม่สามารถใช้ LogW เหมือนกับ print () (ที่มีพารามิเตอร์คั่นด้วยเครื่องหมายจุลภาค" ฉันคิดว่าจะเพิ่มการสนับสนุนนี้ แต่พบว่าไม่ต้องการ "LogW (" ข้อผิดพลาดในการเชื่อมต่อซ็อกเก็ต: (ข้อผิดพลาด) ข้อมูลอื่น ๆ : (otherInfo) ")"
Daniel Ryan

1
จริง. ฉันปรับแต่งรอบ ๆ และมีเพียงวิธีแก้ปัญหาอื่น ๆ ที่ฉันพบคือ - ใช้ extra () เพื่อเก็บคำสั่งเพื่อให้คล้ายกับ print () มากที่สุด ใช้คำตอบของคุณเพื่อสร้างgithub.com/GuntisTreulands/ColorLogger-Swift Anyways ขอบคุณมาก! :)
Guntis Treulands

มีประโยชน์มาก! ณ Swift 2.2__FUNCTION__ becomes #function, __FILE__ becomes #file, and __LINE__ becomes #line.
Carl Smith

เรามีปัญหากับค่านิยมใหม่ เราจะรอจนกว่า swift 3 จนกว่าจะอัปเดตฐานรหัสของเรา
Daniel Ryan

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

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 +

หากคุณไม่ต้องการชื่อไฟล์ทั้งหมดนี่คือการแก้ไขด่วนสำหรับสิ่งนั้น

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

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.