Чи існує швидка альтернатива NSLog (@ “% s”, __PRETTY_FUNCTION__)


88

У цілі C ви можете реєструвати метод, який викликається, використовуючи:

NSLog(@"%s", __PRETTY_FUNCTION__)

Зазвичай це використовується з макросу реєстрації.

Хоча 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 24.03.15

Я використовую 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__? Якщо так, це допомогло б.)
Олі,

10
У Swift 2.2 слід використовувати # function, #file та інші, як показано тут: stackoverflow.com/a/35991392/1151916
Раміс,

70

Починаючи з Swift 2.2, ми повинні використовувати:

  • # файл (String) Ім'я файлу, в якому він відображається.
  • # лінія (Int) Номер рядка, на якому він відображається.
  • # колонка (Int) Номер стовпця, з якого він починається.
  • # функція (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

Свіфт 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 недоступний у рядку.
primulaveris

1
Чудовий чувак. Крихітна зміна для Swift> 2.1: "println" перейменовано на "print". print ("файл: (file.debugDescription) функція: (функція) рядок: (рядок)")
Джон Доу

Класно, добре, що це працює. Також було б чудово мати можливість якось передати в нього клас / об’єкт (один із варіантів - використовувати явний аргумент self). Дякую.
Морське узбережжя Тибету

Проблеми з вашим підходом: - Ця функція не захищена від потоків. Якщо ви дзвоните йому відразу з різних потоків, будьте готові до якихось поганих сюрпризів - Використання глобальних функцій - погана практика
Karoly Nyisztor

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]

EDIT: Це більше код, але я наблизив мене до того, що мені потрібно, і я думаю, що це те, що ви хотіли.

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

Я вважаю за краще визначати глобальну функцію журналу:

[Свіфт 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
}

[Свіфт 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
}

[Свіфт 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 () (з параметрами, розділеними комами) ..
Guntis Treulands

«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.
Карл Сміт

У нас були проблеми з новими цінностями. Ми зачекаємо до швидкого 3, поки не оновимо нашу базу коду.
Daniel Ryan

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

Свіфт 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

Свіфт 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.