Apakah ada alternatif Swift untuk NSLog (@ “% s”, __PRETTY_FUNCTION__)

88

Di Objective C Anda bisa mencatat metode yang dipanggil menggunakan:

NSLog(@"%s", __PRETTY_FUNCTION__)

Biasanya ini digunakan dari makro logging.

Meskipun Swift tidak mendukung makro (menurut saya), saya tetap ingin menggunakan pernyataan log umum yang menyertakan nama fungsi yang dipanggil. Apakah itu mungkin di Swift?

Pembaruan: Sekarang saya menggunakan fungsi global untuk logging yang dapat ditemukan di sini: https://github.com/evermeer/Stuff#print Dan yang dapat Anda instal menggunakan:

pod 'Stuff/Print'

Ini kodenya:

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

Yang bisa Anda gunakan seperti ini:

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)    

Yang akan menghasilkan:

✳️ .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
Edwin Vermeer
sumber
1
Saya menggunakanNSLog("Running %@ : %@",NSStringFromClass(self.dynamicType),__FUNCTION__)
Magster
Saya menggunakan github.com/goktugyil/QorumLogs
Thellimist
1
Saya pikir gaya logging Anda harus berupa definisi "fungsi cantik". Terima kasih telah berbagi.
HuaTham

Jawaban:

101

Swift memiliki #file, #function, #line dan #column. Dari Bahasa Pemrograman Swift :

#file - String - Nama file di mana file itu muncul.

#line - Int - Nomor baris yang muncul.

#column - Int - Nomor kolom di mana itu dimulai.

#function - String - Nama deklarasi di mana ia muncul.

Kreiri
sumber
11
Baiklah - semua itu maju dari C. Tapi itu tidak menjawab pertanyaan tentang __PRETTY_FUNCTION__, yang tidak mudah dibuat dari opsi yang diberikan. (Apakah ada __CLASS__? Jika demikian, itu akan membantu.)
Olie
10
Di Swift 2.2 harus menggunakan #fungsi, #file dan lainnya seperti yang ditunjukkan di sini: stackoverflow.com/a/35991392/1151916
Ramis
70

Mulai dari Swift 2.2 kita harus menggunakan:

  • #file (String) Nama berkas tempat ia muncul.
  • #line (Int) Nomor baris yang muncul.
  • #column (Int) Nomor kolom tempat kolom dimulai.
  • #fungsi (String) Nama deklarasi di mana ia muncul.

Dari The Swift Programming Language (Swift 3.1) di halaman 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
Ramis
sumber
1
Ini harus ditandai sebagai jawaban yang benar saat ini.
Danny Bravo
18

Swift 4
Inilah pendekatan saya:

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

Jadikan ini fungsi global dan panggil saja

pretty_function()

Bonus: Anda akan melihat utas dijalankan pada, [T] untuk utas latar belakang dan [M] untuk utas Utama.

pasak
sumber
Perlu mengubah deklarasi file dari String ke NSString. lastPathComponent tidak tersedia di String.
primulaveris
1
Bung mengagumkan. Perubahan kecil untuk Swift> 2.1: "println" telah diganti namanya menjadi "print". print ("file: (file.debugDescription) function: (function) line: (line)")
John Doe
Keren, bagus itu berhasil. Akan sangat bagus juga untuk bisa melewatkan kelas / objek ke dalamnya entah bagaimana (salah satu pilihan adalah menggunakan argumen diri eksplisit). Terima kasih.
Pantai Laut Tibet
Masalah dengan pendekatan Anda: - Fungsi ini tidak aman untuk thread. Jika Anda memanggilnya dari utas yang berbeda sekaligus, bersiaplah untuk beberapa kejutan buruk - Menggunakan fungsi global adalah praktik yang buruk
Karoly Nyisztor
9

Pada XCode beta 6, Anda dapat menggunakan reflect(self).summaryuntuk mendapatkan nama kelas dan __FUNCTION__untuk mendapatkan nama fungsi, tetapi hal-hal agak rusak, sekarang. Mudah-mudahan, mereka akan menemukan solusi yang lebih baik. Mungkin ada gunanya menggunakan #define hingga kami keluar dari beta.

Kode ini:

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

memberikan hasil seperti ini:

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

EDIT: Ini lebih banyak kode, tetapi membuat saya lebih dekat dengan apa yang saya butuhkan, yang menurut saya adalah apa yang Anda inginkan.

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

Ini memberikan keluaran seperti ini:

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

Saya lebih suka mendefinisikan fungsi log global:

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

outputnya seperti ini:

****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";
}
ZYiOS
sumber
Anda tidak benar-benar membutuhkan fungsi generik di sini, karena objectparameter dapat dinyatakan sebagai Anybukan T.
werediver
5

Ini adalah jawaban Swift 2 yang diperbarui.

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

Contoh penggunaan:

LogW("Socket connection error: \(error)")
Daniel Ryan
sumber
1
Ini luar biasa. Tapi sekali lagi .. LogW tidak dapat digunakan persis sama dengan print () (dengan parameter, dipisahkan dengan koma) ..
Guntis Treulands
"LogW tidak dapat digunakan persis sama dengan print () (dengan parameter, dipisahkan dengan koma" Saya berpikir untuk menambahkan dukungan ini tetapi ternyata saya tidak membutuhkannya. "LogW (" Kesalahan sambungan soket: (kesalahan) info lainnya : (otherInfo) ")"
Daniel Ryan
1
Benar. Saya mengotak-atik dan hanya solusi lain yang saya temukan adalah - menggunakan extra () untuk menahan pernyataan tersebut, agar semirip mungkin dengan print (). Gunakan jawaban Anda untuk membuat yang satu ini github.com/GuntisTreulands/ColorLogger-Swift . Terima kasih banyak! :)
Guntis Treulands
Sangat berguna! Pada Swift 2.2,__FUNCTION__ becomes #function, __FILE__ becomes #file, and __LINE__ becomes #line.
Carl Smith
Kami mengalami masalah dengan nilai-nilai baru. Kami akan menunggu hingga swift 3 hingga memperbarui basis kode kami.
Daniel Ryan
0

Atau sedikit modifikasi fungsi dengan:

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

}

/ * akan menghasilkan jejak eksekusi seperti: AppDelegate: application (_: didFinishLaunchingWithOptions :): 18 Product: init (type: name: year: price :): 34 FirstViewController: viewDidLoad (): 15 AppDelegate: applicationDidBecomeActive: 62 * /

pengguna3620768
sumber
0

Saya menggunakan, ini semua yang diperlukan dalam file swift, semua file lain akan mengambilnya (sebagai fungsi global). Ketika Anda ingin merilis aplikasi, cukup beri komentar.

import UIKit

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

Swift 3.0.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)")
}
AleyRobotics
sumber
0

Cepat 3.x +

Jika Anda tidak menginginkan seluruh nama file maka berikut adalah perbaikan cepat untuk itu.

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
Hemang
sumber
0

Cara lain untuk mencatat panggilan fungsi:

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