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
Edwin Vermeer
quelle
1
Ich benutzeNSLog("Running %@ : %@",NSStringFromClass(self.dynamicType),__FUNCTION__)
Magster
Ich benutze github.com/goktugyil/QorumLogs
Thellimist
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.

Kreiri
quelle
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
Ramis
quelle
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.

Pegpeg
quelle
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
Olie
quelle
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";
}
ZYiOS
quelle
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)")
Daniel Ryan
quelle
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 * /

user3620768
quelle
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)")
}
iCyberPaul
quelle
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)")
}
AleyRobotics
quelle
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
Hemang
quelle
0

Eine andere Möglichkeit, Funktionsaufrufe zu protokollieren:

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