Есть ли альтернатива 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
Я использую github.com/goktugyil/QorumLogs
Thellimist
1
Я думаю, что ваш стиль ведения журнала должен быть определением «красивой функции». Спасибо, что поделился.
HuaTham

Ответы:

101

Swift имеет #file, #function, #line и #column. Из языка программирования Swift :

#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) Имя объявления, в котором оно появляется.

Из языка программирования Swift (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
Этот ответ должен быть отмечен как текущий правильный ответ.
Дэнни Браво,
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)")
Джон Доу
Круто, хорошо, что работает. Было бы также здорово иметь возможность каким-то образом передать в него класс / объект (один из вариантов - использовать явный аргумент self). Спасибо.
Морское побережье Тибета
Проблемы с вашим подходом: - Эта функция не является потокобезопасной. Если вы вызываете его из разных потоков одновременно, будьте готовы к неприятным сюрпризам - Использование глобальных функций - плохая практика
Кароли Нийзтор
9

Что касается XCode beta 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";
}
ZYiOS
источник
На самом деле здесь вам не нужна универсальная функция, потому что 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 () (с параметрами, разделенными запятыми) ..
Гунтис Треуландс
«LogW нельзя использовать точно так же, как print () (с параметрами, разделенными запятыми» Я думал добавить эту поддержку, но обнаружил, что она мне не нужна ». LogW (« Ошибка подключения к сокету: (ошибка) другая информация : (otherInfo) ")"
Дэниел Райан,
1
Правда. Ну, я повозился и нашел только другое решение - использовать extra () для хранения оператора, чтобы сделать его максимально похожим на print (). Использовал ваш ответ для создания этого github.com/GuntisTreulands/ColorLogger-Swift В любом случае, большое спасибо! :)
Guntis Treulands
Очень полезно! Начиная со Swift 2.2,__FUNCTION__ becomes #function, __FILE__ becomes #file, and __LINE__ becomes #line.
Карл Смит
У нас были проблемы с новыми ценностями. Мы подождем до Swift 3, пока не обновим нашу кодовую базу.
Дэниел Райан
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 * /

user3620768
источник
0

Я использую, это все, что требуется в быстром файле, все остальные файлы заберут его (как глобальная функция). Если вы хотите выпустить приложение, просто закомментируйте строку.

import UIKit

func logFunctionName(file:NSString = __FILE__, fnc:String = __FUNCTION__){  
    println("\(file.lastPathComponent):\(fnc)")
}
iCyberPaul
источник
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
источник
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
Хеманг
источник
0

Другой способ записать вызов функции:

NSLog("\(type(of:self)): %@", #function)
Ако
источник