Есть ли альтернатива 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

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

На самом деле здесь вам не нужна универсальная функция, потому что 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 * /


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

0

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

NSLog("\(type(of:self)): %@", #function)
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.