Submodules/pEpIOSToolbox/pEpIOSToolbox/Other/Logger.swift
author Dirk Zimmermann <dz@pep.security>
Tue, 11 Jun 2019 16:28:31 +0200
branchIOS-1540_GOOD
changeset 9026 30c7f02bdc5f
parent 9024 b11f7271fd69
child 9027 919fcd1cda19
permissions -rw-r--r--
IOS-1540 Better Logger.errorAndCrash.
     1 //
     2 //  Logger.swift
     3 //  pEp
     4 //
     5 //  Created by Dirk Zimmermann on 18.12.18.
     6 //  Copyright © 2018 p≡p Security S.A. All rights reserved.
     7 //
     8 
     9 import Foundation
    10 import os.log
    11 
    12 /**
    13  Thin layer over `os_log` where not available.
    14  */
    15 @available(iOS 10.0, macOS 10.12, tvOS 10.0, watchOS 3.0, *)
    16 public class Logger {
    17     /**
    18      Map `os_log` levels.
    19      */
    20     public enum Severity {
    21         /**
    22          - Note: Not persisted by default, but will be written in case of errors.
    23          */
    24         case info
    25 
    26         /**
    27          - Note: Not persisted by default, but will be written in case of errors.
    28          */
    29         case debug
    30 
    31         /**
    32          This is the lowest priority that gets written to disk by default.
    33          Used like WARN in this logger.
    34          */
    35         case `default`
    36 
    37         case error
    38 
    39         /**
    40          - Note: As this is referring to inter-process problems, I don't see a use-case
    41          for iOS.
    42          */
    43         case fault
    44 
    45         public func osLogType() -> OSLogType {
    46             switch self {
    47             case .info:
    48                 return .info
    49             case .debug:
    50                 return .debug
    51             case .default:
    52                 return .default
    53             case .error:
    54                 return .error
    55             case .fault:
    56                 return .fault
    57             }
    58         }
    59     }
    60 
    61     public init(subsystem: String = "security.pEp.app.iOS", category: String) {
    62         self.subsystem = subsystem
    63         self.category = category
    64         osLogger = OSLog(subsystem: subsystem, category: category)
    65     }
    66 
    67     /**
    68      Logs to default.
    69      */
    70     public func log(function: String = #function,
    71                     filePath: String = #file,
    72                     fileLine: Int = #line,
    73                     _ message: StaticString,
    74                     _ args: CVarArg...) {
    75         saveLog(message: message,
    76                 severity: .default,
    77                 function: function,
    78                 filePath: filePath,
    79                 fileLine: fileLine,
    80                 args: args)
    81     }
    82 
    83     /**
    84      os_log doesn't have a warn per se, but default is coming close.
    85      This is the same as log.
    86      */
    87     public func warn(function: String = #function,
    88                      filePath: String = #file,
    89                      fileLine: Int = #line,
    90                      _ message: StaticString,
    91                      _ args: CVarArg...) {
    92         saveLog(message: message,
    93                 severity: .default,
    94                 function: function,
    95                 filePath: filePath,
    96                 fileLine: fileLine,
    97                 args: args)
    98     }
    99 
   100     /**
   101      Logs to info.
   102      */
   103     public func info(function: String = #function,
   104                      filePath: String = #file,
   105                      fileLine: Int = #line,
   106                      _ message: StaticString,
   107                      _ args: CVarArg...) {
   108         saveLog(message: message,
   109                 severity: .info,
   110                 function: function,
   111                 filePath: filePath,
   112                 fileLine: fileLine,
   113                 args: args)
   114     }
   115 
   116     /**
   117      Logs to debug.
   118      */
   119     public func debug(function: String = #function,
   120                       filePath: String = #file,
   121                       fileLine: Int = #line,
   122                       _ message: StaticString,
   123                       _ args: CVarArg...) {
   124         saveLog(message: message,
   125                 severity: .debug,
   126                 function: function,
   127                 filePath: filePath,
   128                 fileLine: fileLine,
   129                 args: args)
   130     }
   131 
   132     /**
   133      Logs to error.
   134      */
   135     public func error(function: String = #function,
   136                       filePath: String = #file,
   137                       fileLine: Int = #line,
   138                       _ message: StaticString,
   139                       _ args: CVarArg...) {
   140         saveLog(message: message,
   141                 severity: .error,
   142                 function: function,
   143                 filePath: filePath,
   144                 fileLine: fileLine,
   145                 args: args)
   146     }
   147 
   148     /**
   149      Logs to fault.
   150      */
   151     public func fault(function: String = #function,
   152                       filePath: String = #file,
   153                       fileLine: Int = #line,
   154                       _ message: StaticString,
   155                       _ args: CVarArg...) {
   156         saveLog(message: message,
   157                 severity: .fault,
   158                 function: function,
   159                 filePath: filePath,
   160                 fileLine: fileLine,
   161                 args: args)
   162     }
   163 
   164     public func errorAndCrash(function: String = #function,
   165                               filePath: String = #file,
   166                               fileLine: Int = #line,
   167                               error: Error) {
   168         os_log("*** errorAndCrash: %@ (%@:%d %@)",
   169                log: osLogger as! OSLog,
   170                type: .fault,
   171                "\(error)",
   172                filePath,
   173                fileLine,
   174                function)
   175 
   176         SystemUtils.crash("*** errorAndCrash: \(error) (\(filePath):\(fileLine) \(function))")
   177     }
   178 
   179     public func errorAndCrash(function: String = #function,
   180                               filePath: String = #file,
   181                               fileLine: Int = #line,
   182                               message: String) {
   183         os_log("*** errorAndCrash: %@ (%@:%d %@)",
   184                log: osLogger as! OSLog,
   185                type: .fault,
   186                message,
   187                filePath,
   188                fileLine,
   189                function)
   190 
   191         SystemUtils.crash("*** errorAndCrash: \(message) (\(filePath):\(fileLine) \(function))")
   192     }
   193 
   194     public func errorAndCrash(function: String = #function,
   195                               filePath: String = #file,
   196                               fileLine: Int = #line,
   197                               _ message: StaticString,
   198                               _ args: CVarArg...) {
   199         saveLog(message: message,
   200                 severity: .fault,
   201                 function: function,
   202                 filePath: filePath,
   203                 fileLine: fileLine,
   204                 args: args)
   205 
   206         SystemUtils.crash("*** errorAndCrash: \(message) (\(filePath):\(fileLine) \(function))")
   207     }
   208 
   209     /**
   210      Logs an error.
   211      */
   212     public func log(function: String = #function,
   213                     filePath: String = #file,
   214                     fileLine: Int = #line,
   215                     error: Error) {
   216         // Error is not supported by "%@", because it doesn't conform to CVArg
   217         // and CVArg is only meant for internal types.
   218         // An alternative would be to use localizedDescription(),
   219         // but if they are indeed localized you end up with international
   220         // log messages.
   221         // So we wrap it into an NSError which does suppord CVArg.
   222         let nsErr = NSError(domain: subsystem, code: 0, userInfo: [NSUnderlyingErrorKey: error])
   223 
   224         saveLog(message: "%@",
   225                 severity: .default,
   226                 function: function,
   227                 filePath: filePath,
   228                 fileLine: fileLine,
   229                 args: [nsErr])
   230     }
   231 
   232     /**
   233      Since this kind of logging is used so often in the codebase, it has its
   234      own method.
   235      */
   236     public func lostMySelf() {
   237         errorAndCrash("Lost MySelf")
   238     }
   239 
   240     private let subsystem: String
   241     private let category: String
   242 
   243     private let osLogger: Any?
   244 
   245     private func saveLog(message: StaticString,
   246                          severity: Severity,
   247                          function: String = #function,
   248                          filePath: String = #file,
   249                          fileLine: Int = #line,
   250                          args: [CVarArg]) {
   251         osLog(message: message,
   252               severity: severity,
   253               function: function,
   254               filePath: filePath,
   255               fileLine: fileLine,
   256               args: args)
   257     }
   258 
   259     /**
   260      - Note: Wrapping `os_log` causes all kinds of problems, so until
   261         there is an official version of it that accepts `[CVarArg]` (os_logv?),
   262         interpolation is handled by us.
   263      */
   264     private func osLog(message: StaticString,
   265                        severity: Severity,
   266                        function: String = #function,
   267                        filePath: String = #file,
   268                        fileLine: Int = #line,
   269                        args: [CVarArg]) {
   270         var shouldLog = false
   271 
   272         #if DEBUG
   273         shouldLog = true
   274         #else
   275         if severity == .error || severity == .fault || severity == .default {
   276             shouldLog = true
   277         } else {
   278             shouldLog = false
   279         }
   280         #endif
   281 
   282         if shouldLog {
   283             let theLog = osLogger as! OSLog
   284             let theType = severity.osLogType()
   285 
   286             let ourString = String(format: "\(message)", arguments: args)
   287 
   288             os_log("%@ (%@:%d %@)",
   289                    log: theLog,
   290                    type: theType,
   291                    ourString,
   292                    filePath,
   293                    fileLine,
   294                    function)
   295         }
   296     }
   297 }