Gibt es eine schnelle Alternative für NSLog (@ "% s", __PRETTY_FUNCTION__)


87

In Ziel C können Sie die aufgerufene Methode protokollieren, indem Sie:

NSLog(@"%s", __PRETTY_FUNCTION__)

Normalerweise wird dies von einem Protokollierungsmakro verwendet.

Obwohl Swift keine Makros unterstützt (glaube ich), möchte ich dennoch eine generische Protokollanweisung verwenden, die den Namen der aufgerufenen Funktion enthält. Ist das in Swift möglich?

Update: Ich verwende jetzt diese globale Funktion für die Protokollierung, die hier zu finden ist: https://github.com/evermeer/Stuff#print Und die Sie installieren können mit:

pod 'Stuff/Print'

Hier ist der Code:

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

Was Sie so verwenden können:

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)    

Was dazu führen wird:

✳️ .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
Ich benutzeNSLog("Running %@ : %@",NSStringFromClass(self.dynamicType),__FUNCTION__)
Magster


1
Ich denke, Ihr Protokollierungsstil sollte die Definition von "hübsche Funktion" sein. Danke für das Teilen.
HuaTham

Antworten:


100

Swift hat #file, #function, #line und #column. Aus der Swift-Programmiersprache :

#file - String - Der Name der Datei, in der sie angezeigt wird.

#line - Int - Die Zeilennummer, in der es angezeigt wird.

#column - Int - Die Spaltennummer, in der es beginnt.

#function - String - Der Name der Deklaration, in der sie erscheint.


11
Na klar - die alle kommen von C. Aber das hat die Frage nicht beantwortet __PRETTY_FUNCTION__, die aus den gegebenen Optionen nicht einfach zu erstellen ist. (Gibt es eine __CLASS__? Wenn ja, würde das helfen.)
Olie

10
In Swift 2.2 sollte #function, #file und andere wie hier gezeigt verwendet werden: stackoverflow.com/a/35991392/1151916
Ramis

70

Ab Swift 2.2 sollten wir verwenden:

  • #file (String) Der Name der Datei, in der sie angezeigt wird.
  • #line (Int) Die Zeilennummer, in der sie angezeigt wird.
  • #column (Int) Die Spaltennummer, in der es beginnt.
  • #function (String) Der Name der Deklaration, in der sie erscheint.

Aus der Swift-Programmiersprache (Swift 3.1) auf Seite 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
Dies sollte als die aktuell richtige Antwort markiert werden.
Danny Bravo

18

Swift 4
Hier ist mein Ansatz:

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

Machen Sie dies zu einer globalen Funktion und rufen Sie einfach auf

pretty_function()

Bonus: Sie sehen, dass der Thread ausgeführt wird, [T] für einen Hintergrund-Thread und [M] für den Haupt-Thread.


Die Deklaration der Datei muss von String in NSString geändert werden. lastPathComponent ist für String nicht verfügbar.
Primulaveris

1
Super Kerl. Kleine Änderung für Swift> 2.1: "println" wurde in "print" umbenannt. print ("file: (file.debugDescription) function: (function) line: (line)")
John Doe

Cool, gut, dass es funktioniert. Wäre auch toll, wenn man Klasse / Objekt irgendwie übergeben könnte (eine Option ist die Verwendung eines expliziten Selbstarguments). Vielen Dank.
Seeküste von Tibet

Probleme mit Ihrem Ansatz: - Diese Funktion ist nicht threadsicher. Wenn Sie es aus verschiedenen Threads gleichzeitig aufrufen, sollten Sie sich auf einige böse Überraschungen
einstellen

9

Ab XCode Beta 6 können Sie reflect(self).summaryden Klassennamen und __FUNCTION__den Funktionsnamen abrufen, aber die Dinge sind im Moment etwas verstümmelt. Hoffentlich finden sie eine bessere Lösung. Es könnte sich lohnen, ein #define zu verwenden, bis wir die Beta verlassen haben.

Dieser Code:

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

gibt Ergebnisse wie diese:

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

EDIT: Dies ist mehr Code, hat mich aber näher an das gebracht, was ich brauchte, was ich denke, was Sie wollten.

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

Es gibt Ausgabe wie folgt:

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

8

Ich bevorzuge es, eine globale Protokollfunktion zu definieren:

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

Die Ausgabe ist so etwas wie:

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

Sie benötigen hier eigentlich keine generische Funktion, da objectParameter als Anyanstelle von deklariert werden können T.
Werediver

5

Hier ist eine aktualisierte Swift 2-Antwort.

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

Anwendungsbeispiel:

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

1
Das ist großartig. Aber andererseits .. LogW kann nicht genau wie print () verwendet werden (mit durch Komma getrennten Parametern) ..
Guntis Treulands

"LogW kann nicht genau wie print () verwendet werden (mit durch Komma getrennten Parametern". Ich wollte diese Unterstützung hinzufügen, habe sie jedoch nicht benötigt. "LogW (" Socket-Verbindungsfehler: (Fehler) andere Informationen : (otherInfo) ")"
Daniel Ryan

1
Wahr. Nun, ich habe herumgebastelt und nur eine andere Lösung, die ich gefunden habe, war - extra () zu verwenden, um die Anweisung zu halten und sie so ähnlich wie print () wie möglich zu machen. Verwenden Sie Ihre Antwort, um diese zu erstellen. Github.com/GuntisTreulands/ColorLogger-Swift Wie auch immer, vielen Dank! :)
Guntis Treulands

Sehr hilfreich! Ab Swift 2.2,__FUNCTION__ becomes #function, __FILE__ becomes #file, and __LINE__ becomes #line.
Carl Smith

Wir hatten Probleme mit den neuen Werten. Wir werden bis schnell 3 warten, bis wir unsere Codebasis aktualisiert haben.
Daniel Ryan

0

Oder leichte Funktionsänderung mit:

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

}}

/ * erzeugt einen Ausführungs-Trace wie: AppDelegate: application (_: didFinishLaunchingWithOptions :): 18 Produkt: init (Typ: Name: Jahr: Preis :): 34 FirstViewController: viewDidLoad (): 15 AppDelegate: applicationDidBecomeActive: 62 * /


0

Ich benutze, das ist alles, was in einer schnellen Datei benötigt wird, alle anderen Dateien werden es aufnehmen (als globale Funktion). Wenn Sie die Anwendung freigeben möchten, kommentieren Sie einfach die Zeile aus.

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 +

Wenn Sie nicht den gesamten Dateinamen möchten, finden Sie hier eine schnelle Lösung dafür.

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

Eine andere Möglichkeit, Funktionsaufrufe zu protokollieren:

NSLog("\(type(of:self)): %@", #function)
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.