Submodules/pEpIOSToolbox/pEpIOSToolbox/Other/Logger.swift
author Dirk Zimmermann <dz@pep.security>
Tue, 11 Jun 2019 16:28:31 +0200
branchIOS-1540_GOOD
changeset 9028 fd7e84e7e135
parent 9027 919fcd1cda19
child 9029 dccf4c72edb5
permissions -rw-r--r--
IOS-1540 Internal osLog switching to String.
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
dz@9017
   164
    public func errorAndCrash(function: String = #function,
dz@9017
   165
                              filePath: String = #file,
dz@9017
   166
                              fileLine: Int = #line,
dz@9017
   167
                              error: Error) {
dz@9026
   168
        os_log("*** errorAndCrash: %@ (%@:%d %@)",
dz@9017
   169
               log: osLogger as! OSLog,
dz@9017
   170
               type: .fault,
dz@9027
   171
               error as CVarArg,
dz@9017
   172
               filePath,
dz@9017
   173
               fileLine,
dz@9026
   174
               function)
dz@9017
   175
dz@9026
   176
        SystemUtils.crash("*** errorAndCrash: \(error) (\(filePath):\(fileLine) \(function))")
dz@9016
   177
    }
dz@9016
   178
dz@9017
   179
    public func errorAndCrash(function: String = #function,
dz@9017
   180
                              filePath: String = #file,
dz@9017
   181
                              fileLine: Int = #line,
dz@9017
   182
                              message: String) {
dz@9026
   183
        os_log("*** errorAndCrash: %@ (%@:%d %@)",
dz@9017
   184
               log: osLogger as! OSLog,
dz@9017
   185
               type: .fault,
dz@9026
   186
               message,
dz@9017
   187
               filePath,
dz@9017
   188
               fileLine,
dz@9026
   189
               function)
dz@9017
   190
dz@9026
   191
        SystemUtils.crash("*** errorAndCrash: \(message) (\(filePath):\(fileLine) \(function))")
dz@9016
   192
    }
dz@9016
   193
xavier@7578
   194
    public func errorAndCrash(function: String = #function,
xavier@7578
   195
                              filePath: String = #file,
xavier@7578
   196
                              fileLine: Int = #line,
dz@9013
   197
                              _ message: StaticString,
dz@9013
   198
                              _ args: CVarArg...) {
dz@9013
   199
        saveLog(message: message,
dz@9013
   200
                severity: .fault,
dz@9013
   201
                function: function,
dz@9013
   202
                filePath: filePath,
dz@9013
   203
                fileLine: fileLine,
dz@9013
   204
                args: args)
dz@9013
   205
dz@9026
   206
        SystemUtils.crash("*** errorAndCrash: \(message) (\(filePath):\(fileLine) \(function))")
dz@8919
   207
    }
dz@8918
   208
dz@8979
   209
    /**
dz@8979
   210
     Logs an error.
dz@8979
   211
     */
xavier@7578
   212
    public func log(function: String = #function,
xavier@7578
   213
                    filePath: String = #file,
xavier@7578
   214
                    fileLine: Int = #line,
xavier@7578
   215
                    error: Error) {
xavier@7578
   216
        // Error is not supported by "%@", because it doesn't conform to CVArg
xavier@7578
   217
        // and CVArg is only meant for internal types.
xavier@7578
   218
        // An alternative would be to use localizedDescription(),
xavier@7578
   219
        // but if they are indeed localized you end up with international
xavier@7578
   220
        // log messages.
xavier@7578
   221
        // So we wrap it into an NSError which does suppord CVArg.
xavier@7578
   222
        let nsErr = NSError(domain: subsystem, code: 0, userInfo: [NSUnderlyingErrorKey: error])
dz@8979
   223
dz@9023
   224
        saveLog(message: "%@",
dz@8979
   225
                severity: .default,
dz@8979
   226
                function: function,
dz@8979
   227
                filePath: filePath,
dz@8979
   228
                fileLine: fileLine,
dz@8979
   229
                args: [nsErr])
xavier@7578
   230
    }
xavier@7578
   231
xavier@7578
   232
    /**
xavier@7578
   233
     Since this kind of logging is used so often in the codebase, it has its
xavier@7578
   234
     own method.
xavier@7578
   235
     */
xavier@7578
   236
    public func lostMySelf() {
dz@9001
   237
        errorAndCrash("Lost MySelf")
xavier@7578
   238
    }
xavier@7578
   239
xavier@7578
   240
    private let subsystem: String
xavier@7578
   241
    private let category: String
xavier@7578
   242
dz@8979
   243
    private let osLogger: Any?
dz@8979
   244
dz@8979
   245
    private func saveLog(message: StaticString,
dz@8979
   246
                         severity: Severity,
dz@8979
   247
                         function: String = #function,
dz@8979
   248
                         filePath: String = #file,
dz@8979
   249
                         fileLine: Int = #line,
dz@8979
   250
                         args: [CVarArg]) {
dz@9028
   251
        osLog(message: "\(message)",
dz@8979
   252
              severity: severity,
dz@8979
   253
              function: function,
dz@8979
   254
              filePath: filePath,
dz@8979
   255
              fileLine: fileLine,
dz@8979
   256
              args: args)
dz@8979
   257
    }
dz@8979
   258
dz@8979
   259
    /**
dz@9020
   260
     - Note: Wrapping `os_log` causes all kinds of problems, so until
dz@9020
   261
        there is an official version of it that accepts `[CVarArg]` (os_logv?),
dz@9020
   262
        interpolation is handled by us.
dz@8979
   263
     */
dz@9028
   264
    private func osLog(message: String,
dz@8979
   265
                       severity: Severity,
dz@8979
   266
                       function: String = #function,
dz@8979
   267
                       filePath: String = #file,
dz@8979
   268
                       fileLine: Int = #line,
dz@8979
   269
                       args: [CVarArg]) {
dz@9021
   270
        var shouldLog = false
dz@8979
   271
dz@9021
   272
        #if DEBUG
dz@9021
   273
        shouldLog = true
dz@9021
   274
        #else
dz@9021
   275
        if severity == .error || severity == .fault || severity == .default {
dz@9021
   276
            shouldLog = true
dz@9022
   277
        } else {
dz@9022
   278
            shouldLog = false
dz@9021
   279
        }
dz@9021
   280
        #endif
dz@9018
   281
dz@9021
   282
        if shouldLog {
dz@9021
   283
            let theLog = osLogger as! OSLog
dz@9021
   284
            let theType = severity.osLogType()
dz@9021
   285
dz@9023
   286
            let ourString = String(format: "\(message)", arguments: args)
dz@9021
   287
dz@9024
   288
            os_log("%@ (%@:%d %@)",
dz@9021
   289
                   log: theLog,
dz@9021
   290
                   type: theType,
dz@9024
   291
                   ourString,
dz@9021
   292
                   filePath,
dz@9021
   293
                   fileLine,
dz@9024
   294
                   function)
dz@9021
   295
        }
dz@8979
   296
    }
xavier@7578
   297
}