Logging Method signature using swift
IosCmdSwiftIos8Ios 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
__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.
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):
> Calling with Logger.log(withEmoji: false)
:
> 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.swift ➞ viewDidLoad() #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.