|
|
@@ -0,0 +1,305 @@
|
|
|
+import os.log
|
|
|
+import os.signpost
|
|
|
+import UIKit
|
|
|
+
|
|
|
+var LoggerTestMode = false
|
|
|
+
|
|
|
+private let baseReporter = FreeAPSApp.resolver.resolve(GroupedIssueReporter.self)!
|
|
|
+private let router = FreeAPSApp.resolver.resolve(Router.self)!
|
|
|
+
|
|
|
+let loggerLock = NSRecursiveLock()
|
|
|
+
|
|
|
+func debug(
|
|
|
+ _ category: Logger.Category,
|
|
|
+ _ message: @autoclosure () -> String,
|
|
|
+ printToConsole: Bool = true,
|
|
|
+ file: String = #file,
|
|
|
+ function: String = #function,
|
|
|
+ line: UInt = #line
|
|
|
+) {
|
|
|
+ let msg = message()
|
|
|
+ DispatchWorkItem(qos: .userInteractive, flags: .enforceQoS) {
|
|
|
+ loggerLock.perform {
|
|
|
+ category.logger.debug(msg, printToConsole: printToConsole, file: file, function: function, line: line)
|
|
|
+ }
|
|
|
+ }.perform()
|
|
|
+}
|
|
|
+
|
|
|
+func info(
|
|
|
+ _ category: Logger.Category,
|
|
|
+ _ message: String,
|
|
|
+ file: String = #file,
|
|
|
+ function: String = #function,
|
|
|
+ line: UInt = #line
|
|
|
+) {
|
|
|
+ DispatchWorkItem(qos: .userInteractive, flags: .enforceQoS) {
|
|
|
+ loggerLock.perform {
|
|
|
+ category.logger.info(message, file: file, function: function, line: line)
|
|
|
+ }
|
|
|
+ }.perform()
|
|
|
+}
|
|
|
+
|
|
|
+func warning(
|
|
|
+ _ category: Logger.Category,
|
|
|
+ _ message: String,
|
|
|
+ description: String? = nil,
|
|
|
+ error maybeError: Swift.Error? = nil,
|
|
|
+ file: String = #file,
|
|
|
+ function: String = #function,
|
|
|
+ line: UInt = #line
|
|
|
+) {
|
|
|
+ DispatchWorkItem(qos: .userInteractive, flags: .enforceQoS) {
|
|
|
+ loggerLock.perform {
|
|
|
+ category.logger.warning(
|
|
|
+ message,
|
|
|
+ description: description,
|
|
|
+ error: maybeError,
|
|
|
+ file: file,
|
|
|
+ function: function,
|
|
|
+ line: line
|
|
|
+ )
|
|
|
+ }
|
|
|
+ }.perform()
|
|
|
+}
|
|
|
+
|
|
|
+func error(
|
|
|
+ _ category: Logger.Category,
|
|
|
+ _ message: String,
|
|
|
+ description: String? = nil,
|
|
|
+ error maybeError: Swift.Error? = nil,
|
|
|
+ file: String = #file,
|
|
|
+ function: String = #function,
|
|
|
+ line: UInt = #line
|
|
|
+) -> Never {
|
|
|
+ loggerLock.perform {
|
|
|
+ category.logger.errorWithoutFatalError(
|
|
|
+ message,
|
|
|
+ description: description,
|
|
|
+ error: maybeError,
|
|
|
+ file: file,
|
|
|
+ function: function,
|
|
|
+ line: line
|
|
|
+ )
|
|
|
+
|
|
|
+ fatalError(
|
|
|
+ "\(message) @ \(String(describing: description)) @ \(String(describing: maybeError)) @ \(file) @ \(function) @ \(line)"
|
|
|
+ )
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+func check(
|
|
|
+ _ condition: @autoclosure () -> Bool,
|
|
|
+ _ message: @autoclosure () -> String,
|
|
|
+ description: @autoclosure () -> String? = nil,
|
|
|
+ file: String = #file,
|
|
|
+ function: String = #function,
|
|
|
+ line: UInt = #line
|
|
|
+) {
|
|
|
+ guard !condition() else { return }
|
|
|
+ let msg = message()
|
|
|
+ let descr = description()
|
|
|
+ loggerLock.perform {
|
|
|
+ warning(.default, msg, description: descr, file: file.file, function: function, line: line)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+final class Logger {
|
|
|
+ static let `default` = Logger(category: .default, reporter: baseReporter)
|
|
|
+ static let service = Logger(category: .service, reporter: baseReporter)
|
|
|
+ static let businessLogic = Logger(category: .businessLogic, reporter: baseReporter)
|
|
|
+ static let openAPS = Logger(category: .openAPS, reporter: baseReporter)
|
|
|
+ static let deviceManager = Logger(category: .openAPS, reporter: baseReporter)
|
|
|
+ static let apsManager = Logger(category: .openAPS, reporter: baseReporter)
|
|
|
+
|
|
|
+ enum Category: String {
|
|
|
+ case `default`
|
|
|
+ case service
|
|
|
+ case businessLogic
|
|
|
+ case openAPS
|
|
|
+ case deviceManager
|
|
|
+ case apsManager
|
|
|
+
|
|
|
+ var name: String {
|
|
|
+ rawValue.capitalized
|
|
|
+ }
|
|
|
+
|
|
|
+ var logger: Logger {
|
|
|
+ switch self {
|
|
|
+ case .default: return .default
|
|
|
+ case .service: return .service
|
|
|
+ case .businessLogic: return .businessLogic
|
|
|
+ case .openAPS: return .openAPS
|
|
|
+ case .deviceManager: return .deviceManager
|
|
|
+ case .apsManager: return .apsManager
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ fileprivate var log: OSLog {
|
|
|
+ let subsystem = Bundle.main.bundleIdentifier!
|
|
|
+ switch self {
|
|
|
+ case .default: return OSLog.default
|
|
|
+ case .apsManager,
|
|
|
+ .businessLogic,
|
|
|
+ .deviceManager,
|
|
|
+ .openAPS,
|
|
|
+ .service:
|
|
|
+ return OSLog(subsystem: subsystem, category: name)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ fileprivate enum Error: Swift.Error {
|
|
|
+ case error(String)
|
|
|
+ case errorWithInnerError(String, Swift.Error)
|
|
|
+ case errorWithDescription(String, String)
|
|
|
+ case errorWithDescriptionAndInnerError(String, String, Swift.Error)
|
|
|
+
|
|
|
+ private func domain() -> String {
|
|
|
+ switch self {
|
|
|
+ case let .error(domain),
|
|
|
+ let .errorWithDescription(domain, _),
|
|
|
+ let .errorWithDescriptionAndInnerError(domain, _, _),
|
|
|
+ let .errorWithInnerError(domain, _):
|
|
|
+ return domain
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private func innerError() -> Swift.Error? {
|
|
|
+ switch self {
|
|
|
+ case let .errorWithDescriptionAndInnerError(_, _, error),
|
|
|
+ let .errorWithInnerError(_, error):
|
|
|
+ return error
|
|
|
+ default: return nil
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ func asNSError() -> NSError {
|
|
|
+ var info: [String: Any] = ["Description": String(describing: self)]
|
|
|
+
|
|
|
+ if let error = innerError() {
|
|
|
+ info["Error"] = String(describing: error)
|
|
|
+ }
|
|
|
+
|
|
|
+ return NSError(domain: domain(), code: -1, userInfo: info)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private let category: Category
|
|
|
+ private let reporter: IssueReporter
|
|
|
+ let log: OSLog
|
|
|
+
|
|
|
+ private init(category: Category, reporter: IssueReporter) {
|
|
|
+ self.category = category
|
|
|
+ self.reporter = reporter
|
|
|
+ log = category.log
|
|
|
+ }
|
|
|
+
|
|
|
+ static func setup() {
|
|
|
+ loggerLock.perform {
|
|
|
+ baseReporter.setup()
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ func debug(
|
|
|
+ _ message: @autoclosure () -> String,
|
|
|
+ printToConsole: Bool = true,
|
|
|
+ file: String = #file,
|
|
|
+ function: String = #function,
|
|
|
+ line: UInt = #line
|
|
|
+ ) {
|
|
|
+ let message = "DEV: \(message())"
|
|
|
+ if printToConsole {
|
|
|
+ os_log("%@ - %@ - %d %{public}@", log: log, type: .debug, file.file, function, line, message)
|
|
|
+ }
|
|
|
+ reporter.log(category.name, message, file: file, function: function, line: line)
|
|
|
+ }
|
|
|
+
|
|
|
+ func info(
|
|
|
+ _ message: String,
|
|
|
+ file: String = #file,
|
|
|
+ function: String = #function,
|
|
|
+ line: UInt = #line
|
|
|
+ ) {
|
|
|
+ let message = "INFO: \(message)"
|
|
|
+ os_log("%@ - %@ - %d %{public}@", log: log, type: .info, file.file, function, line, message)
|
|
|
+ reporter.log(category.name, message, file: file, function: function, line: line)
|
|
|
+
|
|
|
+ showAlert(message)
|
|
|
+ }
|
|
|
+
|
|
|
+ func warning(
|
|
|
+ _ message: String,
|
|
|
+ description: String? = nil,
|
|
|
+ error maybeError: Swift.Error? = nil,
|
|
|
+ file: String = #file,
|
|
|
+ function: String = #function,
|
|
|
+ line: UInt = #line
|
|
|
+ ) {
|
|
|
+ let loggerError = maybeError.loggerError(message: message, withDescription: description)
|
|
|
+ let message = "WARN: \(String(describing: loggerError))"
|
|
|
+
|
|
|
+ os_log("%@ - %@ - %d %{public}@", log: log, type: .default, file.file, function, line, message)
|
|
|
+ reporter.log(category.name, message, file: file, function: function, line: line)
|
|
|
+ if !LoggerTestMode, maybeError?.shouldReportNonFatalIssue ?? true {
|
|
|
+ reporter.reportNonFatalIssue(withError: loggerError.asNSError())
|
|
|
+ showAlert(message)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ func error(
|
|
|
+ _ message: String,
|
|
|
+ description: String? = nil,
|
|
|
+ error maybeError: Swift.Error? = nil,
|
|
|
+ file: String = #file,
|
|
|
+ function: String = #function,
|
|
|
+ line: UInt = #line
|
|
|
+ ) -> Never {
|
|
|
+ errorWithoutFatalError(message, description: description, error: maybeError, file: file, function: function, line: line)
|
|
|
+
|
|
|
+ fatalError(
|
|
|
+ "\(message) @ \(String(describing: description)) @ \(String(describing: maybeError)) @ \(file) @ \(function) @ \(line)"
|
|
|
+ )
|
|
|
+ }
|
|
|
+
|
|
|
+ private func showAlert(_ message: String) {
|
|
|
+// let category = self.category
|
|
|
+ DispatchQueue.main.async {
|
|
|
+ router.alertMessage.send(message)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ fileprivate func errorWithoutFatalError(
|
|
|
+ _ message: String,
|
|
|
+ description: String? = nil,
|
|
|
+ error maybeError: Swift.Error? = nil,
|
|
|
+ file: String = #file,
|
|
|
+ function: String = #function,
|
|
|
+ line: UInt = #line
|
|
|
+ ) {
|
|
|
+ let loggerError = maybeError.loggerError(message: message, withDescription: description)
|
|
|
+ let message = "ERR: \(String(describing: loggerError))"
|
|
|
+
|
|
|
+ os_log("%@ - %@ - %d %{public}@", log: log, type: .error, file.file, function, line, message)
|
|
|
+ reporter.log(category.name, message, file: file, function: function, line: line)
|
|
|
+ reporter.reportNonFatalIssue(withError: loggerError.asNSError())
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+private extension Optional where Wrapped == Swift.Error {
|
|
|
+ func loggerError(message: String, withDescription description: String?) -> Logger.Error {
|
|
|
+ switch (description, self) {
|
|
|
+ case (nil, nil):
|
|
|
+ return .error(message)
|
|
|
+ case let (descr?, nil):
|
|
|
+ return .errorWithDescription(message, descr)
|
|
|
+ case let (nil, error?):
|
|
|
+ return .errorWithInnerError(message, error)
|
|
|
+ case let (descr?, error?):
|
|
|
+ return .errorWithDescriptionAndInnerError(message, descr, error)
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+private extension String {
|
|
|
+ var file: String { components(separatedBy: "/").last ?? "" }
|
|
|
+}
|