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