Skip to content

Instantly share code, notes, and snippets.

@irace
Last active September 20, 2023 08:04
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save irace/57723a68d367ea7400b623f2f4f9ff92 to your computer and use it in GitHub Desktop.
Save irace/57723a68d367ea7400b623f2f4f9ff92 to your computer and use it in GitHub Desktop.
Simple Swift logger
import Foundation
final class ConsoleLogDestination: LogDestination {
func log(statement: String) {
#if DEBUG
print(statement)
#endif
}
func error(error: Error) {
#if DEBUG
print(error)
#endif
}
}
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