Last active
January 25, 2021 10:00
-
-
Save IuriiIaremenko/ea459665cc4c7a90c0ba8031d0b41ba0 to your computer and use it in GitHub Desktop.
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
// | |
// Logger.swift | |
// | |
// Created by Iurii Iaremenko on 7/1/19. | |
// | |
import Foundation | |
import os | |
import FirebaseCrashlytics | |
public struct LoggerOptions: OptionSet { | |
public init(rawValue: Int) { | |
self.rawValue = rawValue | |
} | |
public let rawValue: Int | |
static let verbose = LoggerOptions(rawValue: 1 << 0) | |
static let debug = LoggerOptions(rawValue: 1 << 1) | |
static let info = LoggerOptions(rawValue: 1 << 2) | |
static let warning = LoggerOptions(rawValue: 1 << 3) | |
static let error = LoggerOptions(rawValue: 1 << 4) | |
static let all: LoggerOptions = [.verbose, .debug, .info, .warning, .error] | |
} | |
public struct LoggerDestinations: OptionSet { | |
public init(rawValue: Int) { | |
self.rawValue = rawValue | |
} | |
public let rawValue: Int | |
static let console = LoggerDestinations(rawValue: 1 << 0) | |
static let crashlytics = LoggerDestinations(rawValue: 1 << 1) | |
static let crashlyticsNonFatals = LoggerDestinations(rawValue: 1 << 2) | |
static let all: LoggerDestinations = [.console, .crashlytics] | |
} | |
public struct Logger { | |
private enum Level: Int { | |
case verbose = 0 | |
case debug | |
case info | |
case warning | |
case error | |
var prefix: StaticString { | |
switch self { | |
case .verbose: return "⚪️ [VERBOSE] %s" | |
case .debug: return "⚫️ [DEBUG] %s" | |
case .info: return "🔵 [INFO] %s" | |
case .warning: return "⭕️ [WARNING] %s" | |
case .error: return "🔴 [ERROR] %s" | |
} | |
} | |
} | |
private init() {} | |
static public var logLevel: LoggerOptions = .all | |
static public var destinations: LoggerDestinations = .console | |
static public func verbose(_ message: @autoclosure () -> String, file: String = #file, function: String = #function, line: Int = #line) { | |
guard logLevel.contains(.verbose) else { return } | |
log(level: .verbose, message: message(), file: file, function: function, line: line) | |
} | |
static public func debug(_ message: @autoclosure () -> String, file: String = #file, function: String = #function, line: Int = #line) { | |
guard logLevel.contains(.debug) else { return } | |
log(level: .debug, message: message(), file: file, function: function, line: line) | |
} | |
static public func info(_ message: @autoclosure () -> String, file: String = #file, function: String = #function, line: Int = #line) { | |
guard logLevel.contains(.info) else { return } | |
log(level: .info, message: message(), file: file, function: function, line: line) | |
} | |
static public func warning(_ message: @autoclosure () -> String, file: String = #file, function: String = #function, line: Int = #line) { | |
guard logLevel.contains(.warning) else { return } | |
log(level: .warning, message: message(), file: file, function: function, line: line) | |
} | |
static public func error(_ message: @autoclosure () -> String, file: String = #file, function: String = #function, line: Int = #line) { | |
guard logLevel.contains(.error) else { return } | |
log(level: .error, message: message(), file: file, function: function, line: line) | |
} | |
static private func log(level: Logger.Level, message: @autoclosure () -> String, file: String, function: String, line: Int) { | |
guard !destinations.isEmpty else { return } | |
let formattedString = getFormattedString(file: file, function: function, line: line, message: message()) | |
if destinations.contains(.console) { | |
os_log(level.prefix, formattedString) | |
} | |
if destinations.contains(.crashlytics) { | |
Crashlytics.crashlytics().log(formattedString.nonPrivacySensativeString) | |
} | |
if destinations.contains(.crashlyticsNonFatals) && level == .error { | |
Crashlytics.crashlytics().record(error: NSError(domain: function, code: line)) | |
} | |
} | |
static private func getFormattedString(file: String, function: String, line: Int, message: String) -> String { | |
return "\(file.fileName)::\(function.functonName):\(line) \(message)" | |
} | |
} | |
private extension String { | |
var nonPrivacySensativeString: String { | |
let string = self.lowercased() | |
for forbiddenString in GlobalConstants.forbiddenStrings where string.contains(forbiddenString) { | |
return "\(forbiddenString): ***" | |
} | |
return self | |
} | |
var fileName: String { | |
return self.components(separatedBy: "/").last?.components(separatedBy: ".").first ?? "" | |
} | |
var functonName: String { | |
return String(prefix(while: { return $0 != "(" })) | |
} | |
} | |
struct GlobalConstants { | |
static let forbiddenStrings: [String] = ["password", "certificate", "token"] | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment