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.
xavier@7578
     1
//
xavier@7578
     2
//  Logger.swift
xavier@7578
     3
//  pEp
xavier@7578
     4
//
xavier@7578
     5
//  Created by Dirk Zimmermann on 18.12.18.
xavier@7578
     6
//  Copyright © 2018 p≡p Security S.A. All rights reserved.
xavier@7578
     7
//
xavier@7578
     8
xavier@7578
     9
import Foundation
xavier@7578
    10
import os.log
xavier@7578
    11
dz@8979
    12
/**
dz@8979
    13
 Thin layer over `os_log` where not available.
dz@8979
    14
 */
dz@8979
    15
@available(iOS 10.0, macOS 10.12, tvOS 10.0, watchOS 3.0, *)
xavier@7578
    16
public class Logger {
dz@8979
    17
    /**
dz@8979
    18
     Map `os_log` levels.
dz@8979
    19
     */
dz@8979
    20
    public enum Severity {
dz@8979
    21
        /**
dz@8979
    22
         - Note: Not persisted by default, but will be written in case of errors.
dz@8979
    23
         */
dz@8979
    24
        case info
dz@8979
    25
dz@8979
    26
        /**
dz@8979
    27
         - Note: Not persisted by default, but will be written in case of errors.
dz@8979
    28
         */
dz@8979
    29
        case debug
dz@8979
    30
dz@8979
    31
        /**
dz@8979
    32
         This is the lowest priority that gets written to disk by default.
dz@8979
    33
         Used like WARN in this logger.
dz@8979
    34
         */
dz@8979
    35
        case `default`
dz@8979
    36
dz@8979
    37
        case error
dz@8979
    38
dz@8979
    39
        /**
dz@8979
    40
         - Note: As this is referring to inter-process problems, I don't see a use-case
dz@8979
    41
         for iOS.
dz@8979
    42
         */
dz@8979
    43
        case fault
dz@8979
    44
dz@8979
    45
        public func osLogType() -> OSLogType {
dz@8979
    46
            switch self {
dz@8979
    47
            case .info:
dz@8979
    48
                return .info
dz@8979
    49
            case .debug:
dz@8979
    50
                return .debug
dz@8979
    51
            case .default:
dz@8979
    52
                return .default
dz@8979
    53
            case .error:
dz@8979
    54
                return .error
dz@8979
    55
            case .fault:
dz@8979
    56
                return .fault
dz@8979
    57
            }
dz@8979
    58
        }
dz@8979
    59
    }
dz@8979
    60
xavier@7578
    61
    public init(subsystem: String = "security.pEp.app.iOS", category: String) {
xavier@7578
    62
        self.subsystem = subsystem
xavier@7578
    63
        self.category = category
andreas@7803
    64
        osLogger = OSLog(subsystem: subsystem, category: category)
xavier@7578
    65
    }
xavier@7578
    66
dz@8979
    67
    /**
dz@8979
    68
     Logs to default.
dz@8979
    69
     */
dz@8979
    70
    public func log(function: String = #function,
dz@8979
    71
                    filePath: String = #file,
dz@8979
    72
                    fileLine: Int = #line,
dz@8979
    73
                    _ message: StaticString,
dz@8979
    74
                    _ args: CVarArg...) {
dz@8979
    75
        saveLog(message: message,
dz@8979
    76
                severity: .default,
dz@8979
    77
                function: function,
dz@8979
    78
                filePath: filePath,
dz@8979
    79
                fileLine: fileLine,
dz@8979
    80
                args: args)
dz@8979
    81
    }
dz@8979
    82
dz@8979
    83
    /**
dz@8979
    84
     os_log doesn't have a warn per se, but default is coming close.
dz@8979
    85
     This is the same as log.
dz@8979
    86
     */
dz@8979
    87
    public func warn(function: String = #function,
dz@8979
    88
                     filePath: String = #file,
dz@8979
    89
                     fileLine: Int = #line,
dz@8979
    90
                     _ message: StaticString,
dz@8979
    91
                     _ args: CVarArg...) {
dz@8979
    92
        saveLog(message: message,
dz@8979
    93
                severity: .default,
dz@8979
    94
                function: function,
dz@8979
    95
                filePath: filePath,
dz@8979
    96
                fileLine: fileLine,
dz@8979
    97
                args: args)
dz@8979
    98
    }
dz@8979
    99
dz@8979
   100
    /**
dz@8979
   101
     Logs to info.
dz@8979
   102
     */
dz@8979
   103
    public func info(function: String = #function,
dz@8979
   104
                     filePath: String = #file,
dz@8979
   105
                     fileLine: Int = #line,
dz@8979
   106
                     _ message: StaticString,
dz@8979
   107
                     _ args: CVarArg...) {
dz@8979
   108
        saveLog(message: message,
dz@8979
   109
                severity: .info,
dz@8979
   110
                function: function,
dz@8979
   111
                filePath: filePath,
dz@8979
   112
                fileLine: fileLine,
dz@8979
   113
                args: args)
dz@8979
   114
    }
dz@8979
   115
dz@8979
   116
    /**
dz@8979
   117
     Logs to debug.
dz@8979
   118
     */
dz@8979
   119
    public func debug(function: String = #function,
dz@8979
   120
                      filePath: String = #file,
dz@8979
   121
                      fileLine: Int = #line,
dz@8979
   122
                      _ message: StaticString,
dz@8979
   123
                      _ args: CVarArg...) {
dz@8979
   124
        saveLog(message: message,
dz@8979
   125
                severity: .debug,
dz@8979
   126
                function: function,
dz@8979
   127
                filePath: filePath,
dz@8979
   128
                fileLine: fileLine,
dz@8979
   129
                args: args)
dz@8979
   130
    }
dz@8979
   131
dz@8979
   132
    /**
dz@8979
   133
     Logs to error.
dz@8979
   134
     */
dz@8979
   135
    public func error(function: String = #function,
dz@8979
   136
                      filePath: String = #file,
dz@8979
   137
                      fileLine: Int = #line,
dz@8979
   138
                      _ message: StaticString,
dz@8979
   139
                      _ args: CVarArg...) {
dz@8979
   140
        saveLog(message: message,
dz@8979
   141
                severity: .error,
dz@8979
   142
                function: function,
dz@8979
   143
                filePath: filePath,
dz@8979
   144
                fileLine: fileLine,
dz@8979
   145
                args: args)
dz@8979
   146
    }
dz@8979
   147
dz@8979
   148
    /**
dz@8979
   149
     Logs to fault.
dz@8979
   150
     */
dz@8979
   151
    public func fault(function: String = #function,
dz@8979
   152
                      filePath: String = #file,
dz@8979
   153
                      fileLine: Int = #line,
dz@8979
   154
                      _ message: StaticString,
dz@8979
   155
                      _ args: CVarArg...) {
dz@8979
   156
        saveLog(message: message,
dz@8979
   157
                severity: .fault,
dz@8979
   158
                function: function,
dz@8979
   159
                filePath: filePath,
dz@8979
   160
                fileLine: fileLine,
dz@8979
   161
                args: args)
dz@8979
   162
    }
dz@8979
   163
xavier@7578
   164
    public func errorAndCrash(function: String = #function,
xavier@7578
   165
                              filePath: String = #file,
xavier@7578
   166
                              fileLine: Int = #line,
dz@9013
   167
                              _ message: StaticString,
dz@9013
   168
                              _ args: CVarArg...) {
dz@9013
   169
        saveLog(message: message,
dz@9013
   170
                severity: .fault,
dz@9013
   171
                function: function,
dz@9013
   172
                filePath: filePath,
dz@9013
   173
                fileLine: fileLine,
dz@9013
   174
                args: args)
dz@9013
   175
dz@9013
   176
        SystemUtils.crash("\(filePath):\(function):\(fileLine) - \(message)")
dz@8919
   177
    }
dz@8918
   178
dz@8979
   179
    /**
dz@8979
   180
     Logs an error.
dz@8979
   181
     */
xavier@7578
   182
    public func log(function: String = #function,
xavier@7578
   183
                    filePath: String = #file,
xavier@7578
   184
                    fileLine: Int = #line,
xavier@7578
   185
                    error: Error) {
xavier@7578
   186
        // Error is not supported by "%@", because it doesn't conform to CVArg
xavier@7578
   187
        // and CVArg is only meant for internal types.
xavier@7578
   188
        // An alternative would be to use localizedDescription(),
xavier@7578
   189
        // but if they are indeed localized you end up with international
xavier@7578
   190
        // log messages.
xavier@7578
   191
        // So we wrap it into an NSError which does suppord CVArg.
xavier@7578
   192
        let nsErr = NSError(domain: subsystem, code: 0, userInfo: [NSUnderlyingErrorKey: error])
dz@8979
   193
dz@8979
   194
        saveLog(message: "%{public}@",
dz@8979
   195
                severity: .default,
dz@8979
   196
                function: function,
dz@8979
   197
                filePath: filePath,
dz@8979
   198
                fileLine: fileLine,
dz@8979
   199
                args: [nsErr])
xavier@7578
   200
    }
xavier@7578
   201
xavier@7578
   202
    /**
xavier@7578
   203
     Since this kind of logging is used so often in the codebase, it has its
xavier@7578
   204
     own method.
xavier@7578
   205
     */
xavier@7578
   206
    public func lostMySelf() {
dz@9001
   207
        errorAndCrash("Lost MySelf")
xavier@7578
   208
    }
xavier@7578
   209
xavier@7578
   210
    private let subsystem: String
xavier@7578
   211
    private let category: String
xavier@7578
   212
dz@8979
   213
    private let osLogger: Any?
dz@8979
   214
dz@8979
   215
    private func saveLog(message: StaticString,
dz@8979
   216
                         severity: Severity,
dz@8979
   217
                         function: String = #function,
dz@8979
   218
                         filePath: String = #file,
dz@8979
   219
                         fileLine: Int = #line,
dz@8979
   220
                         args: [CVarArg]) {
dz@8979
   221
        osLog(message: message,
dz@8979
   222
              severity: severity,
dz@8979
   223
              function: function,
dz@8979
   224
              filePath: filePath,
dz@8979
   225
              fileLine: fileLine,
dz@8979
   226
              args: args)
dz@8979
   227
    }
dz@8979
   228
dz@8979
   229
    /**
dz@9014
   230
     - Note: Logs in a format that is not pretty, but gets all the data across.
dz@9014
   231
       The restrictions are given to `os_log`'s implementation,
dz@9014
   232
       requiring a `StaticString`, which according to the documentation
dz@9014
   233
       must be known at compile time.
dz@8979
   234
     */
dz@8979
   235
    private func osLog(message: StaticString,
dz@8979
   236
                       severity: Severity,
dz@8979
   237
                       function: String = #function,
dz@8979
   238
                       filePath: String = #file,
dz@8979
   239
                       fileLine: Int = #line,
dz@8979
   240
                       args: [CVarArg]) {
dz@8979
   241
        let theLog = osLogger as! OSLog
dz@8979
   242
        let theType = severity.osLogType()
dz@8979
   243
dz@9014
   244
        os_log("%{public}@:%d %{public}@: %{public}@ (%{public}@)",
dz@8979
   245
               log: theLog,
dz@8979
   246
               type: theType,
dz@8979
   247
               filePath,
dz@8979
   248
               fileLine,
dz@9014
   249
               function,
dz@9015
   250
               "\(message)",
dz@9014
   251
               args)
dz@8979
   252
dz@8979
   253
        // We have to expand the array of arguments into positional ones.
dz@8979
   254
        // There is no attempt of trying to format the string on our side
dz@8979
   255
        // in order to make use of `os_log`'s fast 'offline' formatting
dz@8979
   256
        // (that is, the work is delayed until actual log display).
dz@8979
   257
        switch args.count {
dz@8979
   258
        case 0:
dz@8979
   259
            os_log(message,
dz@8979
   260
                   log: theLog,
dz@8979
   261
                   type: theType)
dz@8979
   262
        case 1:
dz@8979
   263
            os_log(message,
dz@8979
   264
                   log: theLog,
dz@8979
   265
                   type: theType,
dz@8979
   266
                   args[0])
dz@8979
   267
        case 2:
dz@8979
   268
            os_log(message,
dz@8979
   269
                   log: theLog,
dz@8979
   270
                   type: theType,
dz@8979
   271
                   args[0], args[1])
dz@8979
   272
        case 3:
dz@8979
   273
            os_log(message,
dz@8979
   274
                   log: theLog,
dz@8979
   275
                   type: theType,
dz@8979
   276
                   args[0], args[1], args[2])
dz@8979
   277
        case 4:
dz@8979
   278
            os_log(message,
dz@8979
   279
                   log: theLog,
dz@8979
   280
                   type: theType,
dz@8979
   281
                   args[0], args[1], args[2], args[3])
dz@8979
   282
        case 5:
dz@8979
   283
            os_log(message,
dz@8979
   284
                   log: theLog,
dz@8979
   285
                   type: theType,
dz@8979
   286
                   args[0], args[1], args[2], args[3], args[4])
dz@8979
   287
        case 6:
dz@8979
   288
            os_log(message,
dz@8979
   289
                   log: theLog,
dz@8979
   290
                   type: theType,
dz@8979
   291
                   args[0], args[1], args[2], args[3], args[4], args[5])
dz@8979
   292
        case 7:
dz@8979
   293
            os_log(message,
dz@8979
   294
                   log: theLog,
dz@8979
   295
                   type: theType,
dz@8979
   296
                   args[0], args[1], args[2], args[3], args[4], args[5], args[6])
dz@8979
   297
        case 8:
dz@8979
   298
            os_log(message,
dz@8979
   299
                   log: theLog,
dz@8979
   300
                   type: theType,
dz@8979
   301
                   args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7])
dz@8979
   302
        case 9:
dz@8979
   303
            os_log(message,
dz@8979
   304
                   log: theLog,
dz@8979
   305
                   type: theType,
dz@8979
   306
                   args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7],
dz@8979
   307
                   args[8])
dz@8979
   308
        case 10:
dz@8979
   309
            os_log(message,
dz@8979
   310
                   log: theLog,
dz@8979
   311
                   type: theType,
dz@8979
   312
                   args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7],
dz@8979
   313
                   args[8], args[9])
dz@8979
   314
        default:
dz@8979
   315
            os_log("Using more than 10 parameters",
dz@8979
   316
                   log: theLog,
dz@8979
   317
                   type: .error)
dz@8979
   318
            os_log(message,
dz@8979
   319
                   log: theLog,
dz@8979
   320
                   type: theType,
dz@8979
   321
                   args)
dz@8979
   322
        }
dz@8979
   323
    }
xavier@7578
   324
}