// retoor <retoor@molodetz.nl>
import Foundation
actor BotLogger {
enum Level: String {
case debug = "DEBUG"
case info = "INFO"
case warning = "WARN"
case error = "ERROR"
case spam = "SPAM"
}
private let dateFormatter: DateFormatter
private let logFile: FileHandle?
private let logToConsole: Bool
private let minLevel: Level
init(logPath: String? = nil, logToConsole: Bool = true, minLevel: Level = .info) {
self.dateFormatter = DateFormatter()
self.dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
self.logToConsole = logToConsole
self.minLevel = minLevel
if let path = logPath {
_ = FileManager.default.createFile(atPath: path, contents: nil, attributes: nil)
self.logFile = FileHandle(forWritingAtPath: path)
self.logFile?.seekToEndOfFile()
} else {
self.logFile = nil
}
}
deinit {
try? logFile?.close()
}
func log(_ level: Level, _ message: String) {
guard shouldLog(level) else { return }
let timestamp = dateFormatter.string(from: Date())
let formattedMessage = "[\(timestamp)] [\(level.rawValue)] \(message)"
if logToConsole {
print(formattedMessage)
}
if let fileHandle = logFile,
let data = (formattedMessage + "\n").data(using: .utf8) {
try? fileHandle.write(contentsOf: data)
}
}
func debug(_ message: String) {
log(.debug, message)
}
func info(_ message: String) {
log(.info, message)
}
func warning(_ message: String) {
log(.warning, message)
}
func error(_ message: String) {
log(.error, message)
}
func spam(_ message: String) {
log(.spam, message)
}
private func shouldLog(_ level: Level) -> Bool {
let levels: [Level] = [.debug, .info, .warning, .error, .spam]
guard let currentIndex = levels.firstIndex(of: minLevel),
let messageIndex = levels.firstIndex(of: level) else {
return true
}
return messageIndex >= currentIndex
}
}