Logging Method signature using swift

IosCmdSwiftIos8

Ios Problem Overview


I am trying to rewrite my logging class and I would like to know how to substitute PRETTY_FUNCTION or NSStringFromSelector(_cmd) in a swift file in order to track the method calls?

Ios Solutions


Solution 1 - Ios

Special literals in swift are as follows (from [the swift guide]

#file String The name of the file in which it appears.

#line Int The line number on which it appears.

#column Int The column number in which it begins.

#function String The name of the declaration in which it appears.


Prior to Swift 2.2b4, these were

(https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/Expressions.html)):

__FILE__ String The name of the file in which it appears.

__LINE__ Int The line number on which it appears.

__COLUMN__ Int The column number in which it begins.

__FUNCTION__ String The name of the declaration in which it appears.

You can use these in logging statements like so:

println("error occurred on line \(__LINE__) in function \(__FUNCTION__)")

Solution 2 - Ios

Check out a new library I've just published: https://github.com/DaveWoodCom/XCGLogger

It's a debug logging library for Swift.

The key to being able to use the #function macros, is to set them as default values to your logging function. The compiler will then fill them in using the expected values.

func log(logMessage: String, functionName: String = #function) {
    print("\(functionName): \(logMessage)")
}

Then just call:

log("my message")

And it works as expected giving you something like:

whateverFunction(): my message

More info on how this works: https://www.cerebralgardens.com/blog/entry/2014/06/09/the-first-essential-swift-3rd-party-library-to-include-in-your-project

Solution 3 - Ios

I'd use something like this:

func Log(message: String = "", _ path: String = __FILE__, _ function: String = __FUNCTION__) {
    let file = path.componentsSeparatedByString("/").last!.componentsSeparatedByString(".").first! // Sorry
    NSLog("\(file).\(function): \(message)")
}

Improvements compared to previous answers:

  • Uses NSLog, not print/println
  • Does not use lastPathComponent which is not available on Strings anymore
  • The log message is optional

Solution 4 - Ios

Try this:

class Log {
    class func msg(message: String,
        functionName:  String = __FUNCTION__, fileNameWithPath: String = __FILE__, lineNumber: Int = __LINE__ ) {
        // In the default arguments to this function:
        // 1) If I use a String type, the macros (e.g., __LINE__) don't expand at run time.
        //  "\(__FUNCTION__)\(__FILE__)\(__LINE__)"
        // 2) A tuple type, like,
        // typealias SMLogFuncDetails = (String, String, Int)
        //  SMLogFuncDetails = (__FUNCTION__, __FILE__, __LINE__) 
        //  doesn't work either.
        // 3) This String = __FUNCTION__ + __FILE__
        //  also doesn't work.
            
        var fileNameWithoutPath = fileNameWithPath.lastPathComponent
            
#if DEBUG
        let output = "\(NSDate()): \(message) [\(functionName) in \(fileNameWithoutPath), line \(lineNumber)]"
        println(output)
#endif
    }
}

Log using:

let x = 100
Log.msg("My output message \(x)")

Solution 5 - Ios

Here is what I used in: https://github.com/goktugyil/QorumLogs
Its like XCGLogger but better.

func myLog<T>(object: T, _ file: String = __FILE__, _ function: String = __FUNCTION__, _ line: Int = __LINE__) {
    let info = "\(file).\(function)[\(line)]:\(object)"
    print(info)
}

Solution 6 - Ios

This will print only in debug mode:

func debugLog(text: String,  fileName: String = __FILE__, function: String =  __FUNCTION__, line: Int = __LINE__) {
    debugPrint("[\((fileName as NSString).lastPathComponent), in \(function)() at line: \(line)]: \(text)")
}

Result:

"[Book.swift, in addPage() at line: 33]: Page added with success"

Solution 7 - Ios

For Swift 3 and above:

print("\(#function)")

Solution 8 - Ios

As of Swift 2.2, you can specify it using Literal Expressions, as described at the Swift Programming Language guide.

So if you had a Logger struct that had a function that logged where the error happened, then you would call it like this:

Logger().log(message, fileName: #file, functionName: #function, atLine: #line)

Solution 9 - Ios

Here's my take on it.

func Log<T>(_ object: Shit, _ file: String = #file, _ function: String = #function, _ line: Int = #line) {

var filename = (file as NSString).lastPathComponent
filename = filename.components(separatedBy: ".")[0]

let currentDate = Date()
let df = DateFormatter()
df.dateFormat = "HH:mm:ss.SSS"

print("┌──────────────┬───────────────────────────────────────────────────────────────")
print("│ \(df.string(from: currentDate))\(filename).\(function) (\(line))")
print("└──────────────┴───────────────────────────────────────────────────────────────")
print("  \(object)\n")}

Hope you enjoy.

enter image description here

Solution 10 - Ios

Swift 4, based on all these awesome answers. ❤️

/*
 That's how I protect my virginity.
*/

import Foundation

/// Based on [this SO question](https://stackoverflow.com/questions/24048430/logging-method-signature-using-swift).
class Logger {

    // MARK: - Lifecycle

    private init() {} // Disallows direct instantiation e.g.: "Logger()"

    // MARK: - Logging

    class func log(_ message: Any = "",
                   withEmoji: Bool = true,
                   filename: String = #file,
                   function: String =  #function,
                   line: Int = #line) {

        if withEmoji {
            let body = emojiBody(filename: filename, function: function, line: line)
            emojiLog(messageHeader: emojiHeader(), messageBody: body)

        } else {
            let body = regularBody(filename: filename, function: function, line: line)
            regularLog(messageHeader: regularHeader(), messageBody: body)
        }

        let messageString = String(describing: message)
        guard !messageString.isEmpty else { return }
        print(" └ 📣 \(messageString)\n")
    }
}

// MARK: - Private

// MARK: Emoji

private extension Logger {

    class func emojiHeader() -> String {
        return "⏱ \(formattedDate())"
    }

    class func emojiBody(filename: String, function: String, line: Int) -> String {
        return "🗂 \(filenameWithoutPath(filename: filename)), in 🔠 \(function) at #️⃣ \(line)"
    }

    class func emojiLog(messageHeader: String, messageBody: String) {
        print("\(messageHeader)\(messageBody)")
    }
}

// MARK: Regular

private extension Logger {

    class func regularHeader() -> String {
        return " \(formattedDate()) "
    }

    class func regularBody(filename: String, function: String, line: Int) -> String {
        return " \(filenameWithoutPath(filename: filename)), in \(function) at \(line) "
    }

    class func regularLog(messageHeader: String, messageBody: String) {
        let headerHorizontalLine = horizontalLine(for: messageHeader)
        let bodyHorizontalLine = horizontalLine(for: messageBody)

        print("┌\(headerHorizontalLine)\(bodyHorizontalLine)┐")
        print("│\(messageHeader)\(messageBody)│")
        print("└\(headerHorizontalLine)\(bodyHorizontalLine)┘")
    }

    /// Returns a `String` composed by horizontal box-drawing characters (─) based on the given message length.
    ///
    /// For example:
    ///
    ///     " ViewController.swift, in viewDidLoad() at 26 " // Message
    ///     "──────────────────────────────────────────────" // Returned String
    ///
    /// Reference: [U+250x Unicode](https://en.wikipedia.org/wiki/Box-drawing_character)
    class func horizontalLine(for message: String) -> String {
        return Array(repeating: "─", count: message.count).joined()
    }
}

// MARK: Util

private extension Logger {

    /// "/Users/blablabla/Class.swift" becomes "Class.swift"
    class func filenameWithoutPath(filename: String) -> String {
        return URL(fileURLWithPath: filename).lastPathComponent
    }

    /// E.g. `15:25:04.749`
    class func formattedDate() -> String {
        let dateFormatter = DateFormatter()
        dateFormatter.dateFormat = "HH:mm:ss.SSS"
        return "\(dateFormatter.string(from: Date()))"
    }
}

> Calling with Logger.log() -- (emoji is on by default):

emojiLog


> Calling with Logger.log(withEmoji: false):

regularLog


> More usage examples:

Logger.log()
Logger.log(withEmoji: false)
Logger.log("I'm a virgin.")
Logger.log("I'm a virgin.", withEmoji: false)
Logger.log(NSScreen.min.frame.maxX) // Can handle "Any" (not only String).

Solution 11 - Ios

This will get you the class and the function name in one go:

var name = NSStringFromClass(self.classForCoder) + "." + __FUNCTION__

Solution 12 - Ios

this seems to work fine in swift 3.1

print("File: \((#file as NSString).lastPathComponent) Func: \(#function) Line: \(#line)")

Solution 13 - Ios

Swift 3 support debugLog object with date, function name, file name, line number:

public func debugLog(object: Any, functionName: String = #function, fileName: String = #file, lineNumber: Int = #line) {
    let className = (fileName as NSString).lastPathComponent
    print("\(NSDate()): <\(className)> \(functionName) [#\(lineNumber)]| \(object)\n")
}

Solution 14 - Ios

****** Possibly outdated. *******

As pranav mentioned in the comments please use Logger for iOS 14+


There's a new library I have published: Printer.

It has many functions to let you log in different ways.

To log a success message:

Printer.log.success(details: "This is a Success message.")

Output:

Printer[✅ Success] [⌚04-27-2017 10:53:28] ➞ ✹✹This is a Success message.✹✹
[Trace]ViewController.swiftviewDidLoad() #58

Disclaimer: This library has been created by me.

Solution 15 - Ios

func Log<T>(_ object: T, fileName: String = #file, function: String =  #function, line: Int = #line) {
    NSLog("\((fileName as NSString).lastPathComponent), in \(function) at line: \(line): \(object)")
}

Solution 16 - Ios

An alternative version, using os_log, could be:

func Log(_ msg: String = "", _ file: NSString = #file, _ function: String = #function) {
    let baseName = file.lastPathComponent.replacingOccurrences(of: ".swift", with: "")
    os_log("%{public}@:%{public}@: %@", type: .default, baseName, function, msg)
}

Still heavy on string processing, if you can't afford that, just use os_log directly.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionEssa A. HaddadView Question on Stackoverflow
Solution 1 - IosNickView Answer on Stackoverflow
Solution 2 - IosDave WoodView Answer on Stackoverflow
Solution 3 - IospipacsView Answer on Stackoverflow
Solution 4 - IosChris PrinceView Answer on Stackoverflow
Solution 5 - IosEsqarrouthView Answer on Stackoverflow
Solution 6 - IosnsinvocationView Answer on Stackoverflow
Solution 7 - IosYasir AliView Answer on Stackoverflow
Solution 8 - IosluizParreiraView Answer on Stackoverflow
Solution 9 - IosdeeJView Answer on Stackoverflow
Solution 10 - Iosbackslash-fView Answer on Stackoverflow
Solution 11 - IosfloohhView Answer on Stackoverflow
Solution 12 - IoslozflanView Answer on Stackoverflow
Solution 13 - IosChanraksmeyView Answer on Stackoverflow
Solution 14 - IosHemangView Answer on Stackoverflow
Solution 15 - IoshariszamanView Answer on Stackoverflow
Solution 16 - IospipacsView Answer on Stackoverflow