Submodules/pEpIOSToolbox/pEpIOSToolbox/Other/Logger.swift
author Dirk Zimmermann <dz@pep.security>
Tue, 11 Jun 2019 16:28:31 +0200
branchIOS-1540_GOOD
changeset 9015 4682a3245ca2
parent 9014 b6e5205bc054
child 9016 0d06277e56e3
permissions -rw-r--r--
IOS-1540 StaticString is not a CVarArg.
     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                               _ message: StaticString,
   168                               _ args: CVarArg...) {
   169         saveLog(message: message,
   170                 severity: .fault,
   171                 function: function,
   172                 filePath: filePath,
   173                 fileLine: fileLine,
   174                 args: args)
   175 
   176         SystemUtils.crash("\(filePath):\(function):\(fileLine) - \(message)")
   177     }
   178 
   179     /**
   180      Logs an error.
   181      */
   182     public func log(function: String = #function,
   183                     filePath: String = #file,
   184                     fileLine: Int = #line,
   185                     error: Error) {
   186         // Error is not supported by "%@", because it doesn't conform to CVArg
   187         // and CVArg is only meant for internal types.
   188         // An alternative would be to use localizedDescription(),
   189         // but if they are indeed localized you end up with international
   190         // log messages.
   191         // So we wrap it into an NSError which does suppord CVArg.
   192         let nsErr = NSError(domain: subsystem, code: 0, userInfo: [NSUnderlyingErrorKey: error])
   193 
   194         saveLog(message: "%{public}@",
   195                 severity: .default,
   196                 function: function,
   197                 filePath: filePath,
   198                 fileLine: fileLine,
   199                 args: [nsErr])
   200     }
   201 
   202     /**
   203      Since this kind of logging is used so often in the codebase, it has its
   204      own method.
   205      */
   206     public func lostMySelf() {
   207         errorAndCrash("Lost MySelf")
   208     }
   209 
   210     private let subsystem: String
   211     private let category: String
   212 
   213     private let osLogger: Any?
   214 
   215     private func saveLog(message: StaticString,
   216                          severity: Severity,
   217                          function: String = #function,
   218                          filePath: String = #file,
   219                          fileLine: Int = #line,
   220                          args: [CVarArg]) {
   221         osLog(message: message,
   222               severity: severity,
   223               function: function,
   224               filePath: filePath,
   225               fileLine: fileLine,
   226               args: args)
   227     }
   228 
   229     /**
   230      - Note: Logs in a format that is not pretty, but gets all the data across.
   231        The restrictions are given to `os_log`'s implementation,
   232        requiring a `StaticString`, which according to the documentation
   233        must be known at compile time.
   234      */
   235     private func osLog(message: StaticString,
   236                        severity: Severity,
   237                        function: String = #function,
   238                        filePath: String = #file,
   239                        fileLine: Int = #line,
   240                        args: [CVarArg]) {
   241         let theLog = osLogger as! OSLog
   242         let theType = severity.osLogType()
   243 
   244         os_log("%{public}@:%d %{public}@: %{public}@ (%{public}@)",
   245                log: theLog,
   246                type: theType,
   247                filePath,
   248                fileLine,
   249                function,
   250                "\(message)",
   251                args)
   252 
   253         // We have to expand the array of arguments into positional ones.
   254         // There is no attempt of trying to format the string on our side
   255         // in order to make use of `os_log`'s fast 'offline' formatting
   256         // (that is, the work is delayed until actual log display).
   257         switch args.count {
   258         case 0:
   259             os_log(message,
   260                    log: theLog,
   261                    type: theType)
   262         case 1:
   263             os_log(message,
   264                    log: theLog,
   265                    type: theType,
   266                    args[0])
   267         case 2:
   268             os_log(message,
   269                    log: theLog,
   270                    type: theType,
   271                    args[0], args[1])
   272         case 3:
   273             os_log(message,
   274                    log: theLog,
   275                    type: theType,
   276                    args[0], args[1], args[2])
   277         case 4:
   278             os_log(message,
   279                    log: theLog,
   280                    type: theType,
   281                    args[0], args[1], args[2], args[3])
   282         case 5:
   283             os_log(message,
   284                    log: theLog,
   285                    type: theType,
   286                    args[0], args[1], args[2], args[3], args[4])
   287         case 6:
   288             os_log(message,
   289                    log: theLog,
   290                    type: theType,
   291                    args[0], args[1], args[2], args[3], args[4], args[5])
   292         case 7:
   293             os_log(message,
   294                    log: theLog,
   295                    type: theType,
   296                    args[0], args[1], args[2], args[3], args[4], args[5], args[6])
   297         case 8:
   298             os_log(message,
   299                    log: theLog,
   300                    type: theType,
   301                    args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7])
   302         case 9:
   303             os_log(message,
   304                    log: theLog,
   305                    type: theType,
   306                    args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7],
   307                    args[8])
   308         case 10:
   309             os_log(message,
   310                    log: theLog,
   311                    type: theType,
   312                    args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7],
   313                    args[8], args[9])
   314         default:
   315             os_log("Using more than 10 parameters",
   316                    log: theLog,
   317                    type: .error)
   318             os_log(message,
   319                    log: theLog,
   320                    type: theType,
   321                    args)
   322         }
   323     }
   324 }