Last active
September 20, 2023 08:04
-
-
Save irace/57723a68d367ea7400b623f2f4f9ff92 to your computer and use it in GitHub Desktop.
Simple Swift logger
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Foundation | |
final class ConsoleLogDestination: LogDestination { | |
func log(statement: String) { | |
#if DEBUG | |
print(statement) | |
#endif | |
} | |
func error(error: Error) { | |
#if DEBUG | |
print(error) | |
#endif | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Foundation | |
protocol LogDestination { | |
func log(statement: String) | |
func error(error: Error) | |
} | |
/** | |
An abstraction on top of all different types of logging! | |
For now, this allows you to log a string, along with an optional `Error` instance (for `error` and `severe` levels | |
only). | |
In the future, we may want to add support for event and impression tracking, or we may prefer to keep those | |
separate. Not sure yet! | |
Some inspiration taken from [SwiftyBeaver](https://swiftybeaver.com), as well as | |
[this blog post](https://medium.com/@sauvik_dolui/developing-a-tiny-logger-in-swift-7221751628e6). | |
*/ | |
final class Logger { | |
private enum Level: CustomStringConvertible { | |
case debug | |
case info | |
case verbose | |
case warn | |
case error | |
case severe | |
var description: String { | |
switch self { | |
case .debug: return "💬 DEBUG" | |
case .info: return "ℹ️ INFO" | |
case .verbose: return "🔬 VERBOSE" | |
case .warn: return "⚠️ WARN" | |
case .error: return "‼️ ERROR" | |
case .severe: return "🔥 SEVERE" | |
} | |
} | |
} | |
// MARK: - State | |
private static var dateFormatter = DateFormatter().then { | |
$0.dateFormat = "hh:mm:ss" | |
$0.locale = Locale.current | |
$0.timeZone = TimeZone.current | |
} | |
// MARK: - Inputs | |
private let destinations: [LogDestination] | |
// MARK: - Initialization | |
init(destinations: [LogDestination]) { | |
self.destinations = destinations | |
} | |
// MARK: - Public | |
func debug(_ message: String, filePath: String = #file, line: Int = #line, functionName: String = #function) { | |
log(event: .debug, message: message, filePath: filePath, line: line, functionName: functionName) | |
} | |
func info(_ message: String, filePath: String = #file, line: Int = #line, functionName: String = #function) { | |
log(event: .info, message: message, filePath: filePath, line: line, functionName: functionName) | |
} | |
func verbose(_ message: String, filePath: String = #file, line: Int = #line, functionName: String = #function) { | |
log(event: .verbose, message: message, filePath: filePath, line: line, functionName: functionName) | |
} | |
func warn(_ message: String, filePath: String = #file, line: Int = #line, functionName: String = #function) { | |
log(event: .warn, message: message, filePath: filePath, line: line, functionName: functionName) | |
} | |
func error(_ message: String, error: Error? = nil, filePath: String = #file, line: Int = #line, functionName: String = #function) { | |
log(event: .error, message: message, error: error, filePath: filePath, line: line, functionName: functionName) | |
} | |
func severe(_ message: String, error: Error? = nil, filePath: String = #file, line: Int = #line, functionName: String = #function) { | |
log(event: .severe, message: message, error: error, filePath: filePath, line: line, functionName: functionName) | |
} | |
// MARK: - Private | |
private func log(event: Level, message: String, error: Error? = nil, filePath: String, line: Int, functionName: String) { | |
let statement = Logger.statement(event: event, message: message, filePath: filePath, line: line, functionName: functionName) | |
for destination in destinations { | |
destination.log(statement: statement) | |
if let error = error { | |
destination.error(error: error) | |
} | |
} | |
} | |
private static func statement(event: Level, message: String, filePath: String, line: Int, functionName: String) -> String { | |
return [ | |
Logger.dateFormatter.string(from: Date()), | |
event.description, | |
Logger.functionCall(filePath: filePath, functionName: functionName, line: line), | |
"-", | |
message | |
].joined(separator: " ") | |
} | |
private static func functionCall(filePath: String, functionName: String, line: Int) -> String { | |
return "\(fileName(path: filePath)).\(functionName):\(line)" | |
} | |
private static func fileName(path: String) -> String { | |
return path | |
.components(separatedBy: "/") | |
.last? | |
.components(separatedBy: ".") | |
.first ?? "" | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment