Skip to content

Instantly share code, notes, and snippets.

@ferologics
Last active July 2, 2018 20:29
Show Gist options
  • Save ferologics/4ee40ee362cfb76a670d1795ca17fea8 to your computer and use it in GitHub Desktop.
Save ferologics/4ee40ee362cfb76a670d1795ca17fea8 to your computer and use it in GitHub Desktop.
Simple logger; to be modularised in to a framework
import Foundation
fileprivate let logName = "debug.log"
fileprivate var debugLog = "" {
didSet {
var _debugLog = debugLog
guard let rangeOfOldCharacetrs = _debugLog.range(of: oldValue) else { return }
_debugLog.removeSubrange(rangeOfOldCharacetrs)
let directory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first
guard let url = directory?.appendingPathComponent(logName) else { return }
let path = url.path
do {
try _debugLog.appendToURL(fileURL: url)
} catch {
do {
try debugLog.write(toFile: path, atomically: true, encoding: String.Encoding.utf8)
} catch {
Logger.log(("Failed to create file with error: \(error)", .fatal))
}
}
}
}
class Logger {
typealias Log = (message: String, event: LogEvent)
static var dateFormat = "yyyy-MM-dd hh:mm:ssSSS"
static var dateFormatter: DateFormatter {
let formatter = DateFormatter()
formatter.dateFormat = dateFormat
formatter.locale = Locale.current
formatter.timeZone = TimeZone.current
return formatter
}
enum LogEvent: String {
case success = "" // success
case info = "ℹ️" // info
case debug = "💬" // debug
case verbose = "🔬" // verbose
case error = "🚨" // error
case warning = "⚠️" // warning
case fatal = "🔥" // fatal
}
enum LogLevel {
case hidden
case vital
case `default`
case debug
case verbose
var allowedLogEvents: Set<LogEvent> {
get {
let vitalLogEvents: [LogEvent] = [.success, .fatal, .error]
switch self {
case .hidden: return Set([])
case .vital: return Set(vitalLogEvents)
case .default: return Set(vitalLogEvents + [.info, .warning])
case .debug: return Set(vitalLogEvents + [.info, .warning, .debug])
case .verbose: return Set(vitalLogEvents + [.info, .warning, .debug, .verbose])
}
}
}
}
static var logLevel: LogLevel = {
#if DEBUG
return .default
#else
return .hidden
#endif
}()
class func log
(
_ log: @autoclosure () -> Log,
fileName: String = #file,
line: Int = #line,
column: Int = #column,
funcName: String = #function
)
{
let (message, event) = log()
let logMessage = "\(Date().toString()) \(event.rawValue) \(sourceFileName(filePath: fileName)) \(line):\(column) \(funcName) \(message)"
debugLog += logMessage + "\n"
// print logs only for the set log level
guard logLevel.allowedLogEvents.contains(event) else { return }
print(logMessage)
}
// MARK: - Helper
private class func sourceFileName(filePath: String) -> String {
let components = filePath.components(separatedBy: "/")
return components.isEmpty ? "" : components.last!.without(".swift")
}
}
extension Date {
func toString() -> String {
return Logger.dateFormatter.string(from: self as Date)
}
}
extension String {
func without(_ s: String) -> String {
guard let removalRange = self.range(of: s) else { return self }
var a = self
a.removeSubrange(removalRange)
return a
}
func appendLineToURL(fileURL: URL) throws {
try (self + "\n").appendToURL(fileURL: fileURL)
}
func appendToURL(fileURL: URL) throws {
do {
let data = try self.data(using: String.Encoding.utf8).unwrapOrThrow(CommonError.unwrapError)
try data.append(fileURL: fileURL)
} catch {
throw error
}
}
}
extension Data {
func append(fileURL: URL) throws {
if let fileHandle = FileHandle(forWritingAtPath: fileURL.path) {
defer {
fileHandle.closeFile()
}
fileHandle.seekToEndOfFile()
fileHandle.write(self)
}
else {
try write(to: fileURL, options: .atomic)
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment