Logger.swift 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331
  1. import os.log
  2. import os.signpost
  3. import UIKit
  4. var LoggerTestMode = false
  5. private let baseReporter = TrioApp.resolver.resolve(GroupedIssueReporter.self)!
  6. private let router = TrioApp.resolver.resolve(Router.self)!
  7. let loggerLock = NSRecursiveLock()
  8. func debug(
  9. _ category: Logger.Category,
  10. _ message: @autoclosure () -> String,
  11. printToConsole: Bool = true,
  12. file: String = #file,
  13. function: String = #function,
  14. line: UInt = #line
  15. ) {
  16. let msg = message()
  17. DispatchWorkItem(qos: .background, flags: .enforceQoS) {
  18. loggerLock.perform {
  19. category.logger.debug(msg, printToConsole: printToConsole, file: file, function: function, line: line)
  20. }
  21. }.perform()
  22. }
  23. func info(
  24. _ category: Logger.Category,
  25. _ message: String,
  26. type: MessageType = .info,
  27. file: String = #file,
  28. function: String = #function,
  29. line: UInt = #line
  30. ) {
  31. DispatchWorkItem(qos: .background, flags: .enforceQoS) {
  32. loggerLock.perform {
  33. category.logger.info(message, type: type, file: file, function: function, line: line)
  34. }
  35. }.perform()
  36. }
  37. func warning(
  38. _ category: Logger.Category,
  39. _ message: String,
  40. description: String? = nil,
  41. error maybeError: Swift.Error? = nil,
  42. file: String = #file,
  43. function: String = #function,
  44. line: UInt = #line
  45. ) {
  46. DispatchWorkItem(qos: .background, flags: .enforceQoS) {
  47. loggerLock.perform {
  48. category.logger.warning(
  49. message,
  50. description: description,
  51. error: maybeError,
  52. file: file,
  53. function: function,
  54. line: line
  55. )
  56. }
  57. }.perform()
  58. }
  59. func error(
  60. _ category: Logger.Category,
  61. _ message: String,
  62. description: String? = nil,
  63. error maybeError: Swift.Error? = nil,
  64. file: String = #file,
  65. function: String = #function,
  66. line: UInt = #line
  67. ) -> Never {
  68. loggerLock.perform {
  69. category.logger.errorWithoutFatalError(
  70. message,
  71. description: description,
  72. error: maybeError,
  73. file: file,
  74. function: function,
  75. line: line
  76. )
  77. fatalError(
  78. "\(message) @ \(String(describing: description)) @ \(String(describing: maybeError)) @ \(file) @ \(function) @ \(line)"
  79. )
  80. }
  81. }
  82. func check(
  83. _ condition: @autoclosure () -> Bool,
  84. _ message: @autoclosure () -> String,
  85. description: @autoclosure () -> String? = nil,
  86. file: String = #file,
  87. function: String = #function,
  88. line: UInt = #line
  89. ) {
  90. guard !condition() else { return }
  91. let msg = message()
  92. let descr = description()
  93. loggerLock.perform {
  94. warning(.default, msg, description: descr, file: file.file, function: function, line: line)
  95. }
  96. }
  97. final class Logger {
  98. static let `default` = Logger(category: .default, reporter: baseReporter)
  99. static let service = Logger(category: .service, reporter: baseReporter)
  100. static let businessLogic = Logger(category: .businessLogic, reporter: baseReporter)
  101. static let openAPS = Logger(category: .openAPS, reporter: baseReporter)
  102. static let deviceManager = Logger(category: .deviceManager, reporter: baseReporter)
  103. static let apsManager = Logger(category: .apsManager, reporter: baseReporter)
  104. static let nightscout = Logger(category: .nightscout, reporter: baseReporter)
  105. static let remoteControl = Logger(category: .remoteControl, reporter: baseReporter)
  106. static let bolusState = Logger(category: .bolusState, reporter: baseReporter)
  107. static let watchManager = Logger(category: .watchManager, reporter: baseReporter)
  108. static let coreData = Logger(category: .coreData, reporter: baseReporter)
  109. static let storage = Logger(category: .storage, reporter: baseReporter)
  110. enum Category: String {
  111. case `default`
  112. case service
  113. case businessLogic
  114. case openAPS
  115. case deviceManager
  116. case apsManager
  117. case nightscout
  118. case remoteControl
  119. case bolusState
  120. case watchManager
  121. case coreData
  122. case storage
  123. var name: String {
  124. rawValue.capitalizingFirstLetter()
  125. }
  126. var logger: Logger {
  127. switch self {
  128. case .default: return .default
  129. case .service: return .service
  130. case .businessLogic: return .businessLogic
  131. case .openAPS: return .openAPS
  132. case .deviceManager: return .deviceManager
  133. case .apsManager: return .apsManager
  134. case .nightscout: return .nightscout
  135. case .remoteControl: return .remoteControl
  136. case .bolusState: return .bolusState
  137. case .watchManager: return .watchManager
  138. case .coreData: return .coreData
  139. case .storage: return .storage
  140. }
  141. }
  142. fileprivate var log: OSLog {
  143. let subsystem = Bundle.main.bundleIdentifier!
  144. switch self {
  145. case .default: return OSLog.default
  146. case .apsManager,
  147. .bolusState,
  148. .businessLogic,
  149. .coreData,
  150. .deviceManager,
  151. .nightscout,
  152. .openAPS,
  153. .remoteControl,
  154. .service,
  155. .storage,
  156. .watchManager:
  157. return OSLog(subsystem: subsystem, category: name)
  158. }
  159. }
  160. }
  161. fileprivate enum Error: Swift.Error {
  162. case error(String)
  163. case errorWithInnerError(String, Swift.Error)
  164. case errorWithDescription(String, String)
  165. case errorWithDescriptionAndInnerError(String, String, Swift.Error)
  166. private func domain() -> String {
  167. switch self {
  168. case let .error(domain),
  169. let .errorWithDescription(domain, _),
  170. let .errorWithDescriptionAndInnerError(domain, _, _),
  171. let .errorWithInnerError(domain, _):
  172. return domain
  173. }
  174. }
  175. private func innerError() -> Swift.Error? {
  176. switch self {
  177. case let .errorWithDescriptionAndInnerError(_, _, error),
  178. let .errorWithInnerError(_, error):
  179. return error
  180. default: return nil
  181. }
  182. }
  183. func asNSError() -> NSError {
  184. var info: [String: Any] = ["Description": String(describing: self)]
  185. if let error = innerError() {
  186. info["Error"] = String(describing: error)
  187. }
  188. return NSError(domain: domain(), code: -1, userInfo: info)
  189. }
  190. }
  191. private let category: Category
  192. private let reporter: IssueReporter
  193. let log: OSLog
  194. private init(category: Category, reporter: IssueReporter) {
  195. self.category = category
  196. self.reporter = reporter
  197. log = category.log
  198. }
  199. static func setup() {
  200. loggerLock.perform {
  201. baseReporter.setup()
  202. }
  203. }
  204. func debug(
  205. _ message: @autoclosure () -> String,
  206. printToConsole: Bool = true,
  207. file: String = #file,
  208. function: String = #function,
  209. line: UInt = #line
  210. ) {
  211. let message = "DEV: \(message())"
  212. if printToConsole {
  213. os_log("%@ - %@ - %d %{public}@", log: log, type: .debug, file.file, function, line, message)
  214. }
  215. reporter.log(category.name, message, file: file, function: function, line: line)
  216. }
  217. func info(
  218. _ message: String,
  219. type: MessageType = .info,
  220. file: String = #file,
  221. function: String = #function,
  222. line: UInt = #line
  223. ) {
  224. let printedMessage = "INFO: \(message)"
  225. os_log("%@ - %@ - %d %{public}@", log: log, type: .info, file.file, function, line, printedMessage)
  226. reporter.log(category.name, printedMessage, file: file, function: function, line: line)
  227. showAlert(message, type: type)
  228. }
  229. func warning(
  230. _ message: String,
  231. description: String? = nil,
  232. error maybeError: Swift.Error? = nil,
  233. file: String = #file,
  234. function: String = #function,
  235. line: UInt = #line
  236. ) {
  237. let loggerError = maybeError.loggerError(message: message, withDescription: description)
  238. let message = "WARN: \(String(describing: loggerError))"
  239. os_log("%@ - %@ - %d %{public}@", log: log, type: .default, file.file, function, line, message)
  240. reporter.log(category.name, message, file: file, function: function, line: line)
  241. if !LoggerTestMode, maybeError?.shouldReportNonFatalIssue ?? true {
  242. reporter.reportNonFatalIssue(withError: loggerError.asNSError())
  243. }
  244. }
  245. func error(
  246. _ message: String,
  247. description: String? = nil,
  248. error maybeError: Swift.Error? = nil,
  249. file: String = #file,
  250. function: String = #function,
  251. line: UInt = #line
  252. ) -> Never {
  253. errorWithoutFatalError(message, description: description, error: maybeError, file: file, function: function, line: line)
  254. fatalError(
  255. "\(message) @ \(String(describing: description)) @ \(String(describing: maybeError)) @ \(file) @ \(function) @ \(line)"
  256. )
  257. }
  258. private func showAlert(_ message: String, type: MessageType = .info) {
  259. DispatchQueue.main.async {
  260. let messageCont = MessageContent(content: message, type: type)
  261. router.alertMessage.send(messageCont)
  262. }
  263. }
  264. fileprivate func errorWithoutFatalError(
  265. _ message: String,
  266. description: String? = nil,
  267. error maybeError: Swift.Error? = nil,
  268. file: String = #file,
  269. function: String = #function,
  270. line: UInt = #line
  271. ) {
  272. let loggerError = maybeError.loggerError(message: message, withDescription: description)
  273. let message = "ERR: \(String(describing: loggerError))"
  274. os_log("%@ - %@ - %d %{public}@", log: log, type: .error, file.file, function, line, message)
  275. reporter.log(category.name, message, file: file, function: function, line: line)
  276. reporter.reportNonFatalIssue(withError: loggerError.asNSError())
  277. }
  278. }
  279. private extension Optional where Wrapped == Swift.Error {
  280. func loggerError(message: String, withDescription description: String?) -> Logger.Error {
  281. switch (description, self) {
  282. case (nil, nil):
  283. return .error(message)
  284. case let (descr?, nil):
  285. return .errorWithDescription(message, descr)
  286. case let (nil, error?):
  287. return .errorWithInnerError(message, error)
  288. case let (descr?, error?):
  289. return .errorWithDescriptionAndInnerError(message, descr, error)
  290. }
  291. }
  292. }
  293. private extension String {
  294. var file: String { components(separatedBy: "/").last ?? "" }
  295. }