diff --git a/WultraMobileTokenSDK/Common/WMTLogger.swift b/WultraMobileTokenSDK/Common/WMTLogger.swift index ab36922..ad8bd23 100644 --- a/WultraMobileTokenSDK/Common/WMTLogger.swift +++ b/WultraMobileTokenSDK/Common/WMTLogger.swift @@ -16,49 +16,67 @@ import Foundation -/// WMTLogger provides simple logging facility available for DEBUG build of the library. +/// WMTLogger provides simple logging facility. +/// +/// Note that HTTP logs are managed by the underlying Networking library (via `WPNLogger` class). public class WMTLogger { - /// Defines verbose level for this simple debugging facility. + /// Verbose level of the logger. public enum VerboseLevel: Int { /// Silences all messages. case off = 0 - /// Only errors will be printed to the debug console. + /// Only errors will be logged. case errors = 1 - /// Errors and warnings will be printed to the debug console. + /// Errors and warnings will be logged. case warnings = 2 - /// All messages will be printed to the debug console. - case all = 3 + /// Error, warning and info messages will be logged. + case info = 3 + /// All messages will logged - including debug messages + case debug = 4 } - /// Current verbose level. Note that value is ignored for non-DEBUG builds. + /// Logger delegate + public static weak var delegate: WMTLoggerDelegate? + + /// Current verbose level. `warnings` by default public static var verboseLevel: VerboseLevel = .warnings - /// Prints simple message to the debug console. - static func print(_ message: @autoclosure () -> String) { - #if DEBUG || WMT_ENABLE_LOGGING - if verboseLevel == .all { - Swift.print("[WMT] \(message())") - } - #endif + /// Prints simple message to the system console. + static func debug(_ message: @autoclosure () -> String) { + log(message(), level: .debug) + } + + /// Prints simple message to the system console. + static func info(_ message: @autoclosure () -> String) { + log(message(), level: .info) } - /// Prints warning message to the debug console. + /// Prints warning message to the system console. static func warning(_ message: @autoclosure () -> String) { - #if DEBUG || WMT_ENABLE_LOGGING - if verboseLevel.rawValue >= VerboseLevel.warnings.rawValue { - Swift.print("[WMT] WARNING: \(message())") - } - #endif + log(message(), level: .warning) } - /// Prints error message to the debug console. + /// Prints error message to the system console. static func error(_ message: @autoclosure () -> String) { - #if DEBUG || WMT_ENABLE_LOGGING - if verboseLevel != .off { - Swift.print("[WMT] ERROR: \(message())") + log(message(), level: .error) + } + + private static func log(_ message: @autoclosure () -> String, level: WMTLogLevel) { + let levelAllowed = level.minVerboseLevel.rawValue <= verboseLevel.rawValue + let forceReport = delegate?.wmtFollowVerboseLevel == false + guard levelAllowed || forceReport else { + // not logging + return + } + + let msg = message() + + if levelAllowed { + print("[WMT:\(level.logName)] \(msg)") + } + if levelAllowed || forceReport { + delegate?.wmtLog(message: msg, logLevel: level) } - #endif } #if DEBUG @@ -84,4 +102,51 @@ public class WMTLogger { #endif } +/// Delegate that can further process logs from the library +public protocol WMTLoggerDelegate: AnyObject { + + /// If the delegate should follow selected verbosity level. + /// + /// When set to true, then (for example) if `errors` is selected as a `verboseLevel`, only `error` logLevel will be called. + /// When set to false, all methods might be called no matter the selected `verboseLevel`. + var wmtFollowVerboseLevel: Bool { get } + + /// Log was recorded + /// - Parameters: + /// - message: Message of the log + /// - logLevel: Log level + func wmtLog(message: String, logLevel: WMTLogLevel) +} + +/// Level of the log +public enum WMTLogLevel { + /// Debug logs. Might contain sensitive data like body of the request etc. + /// You should only use this level during development. + case debug + /// Regular library logic logs + case info + /// Non-critical warning + case warning + /// Error happened + case error + + fileprivate var minVerboseLevel: WMTLogger.VerboseLevel { + return switch self { + case .debug: .debug + case .info: .info + case .warning: .warnings + case .error: .errors + } + } + + fileprivate var logName: String { + return switch self { + case .debug: "DEBUG" + case .info: "INFO" + case .warning: "WARNING" + case .error: "ERROR" + } + } +} + internal typealias D = WMTLogger diff --git a/WultraMobileTokenSDK/Operations/Model/UserOperation/WMTOperationFormData.swift b/WultraMobileTokenSDK/Operations/Model/UserOperation/WMTOperationFormData.swift index e4ad53c..b3b84db 100644 --- a/WultraMobileTokenSDK/Operations/Model/UserOperation/WMTOperationFormData.swift +++ b/WultraMobileTokenSDK/Operations/Model/UserOperation/WMTOperationFormData.swift @@ -59,7 +59,7 @@ public class WMTOperationFormData: Codable { } } } catch { - D.print("No attributes in WMTOperationFormData: \(error)") + D.error("No attributes in WMTOperationFormData: \(error)") } attributes = operationAttributes diff --git a/WultraMobileTokenSDK/Operations/Service/WMTOperationsImpl.swift b/WultraMobileTokenSDK/Operations/Service/WMTOperationsImpl.swift index 7b16cfd..5aed671 100644 --- a/WultraMobileTokenSDK/Operations/Service/WMTOperationsImpl.swift +++ b/WultraMobileTokenSDK/Operations/Service/WMTOperationsImpl.swift @@ -374,7 +374,7 @@ class WMTOperationsImpl: WMTOperations, WMTService { adjustedInterval = interval } - D.print("Operations polling started with \(adjustedInterval) seconds interval") + D.info("Operations polling started with \(adjustedInterval) seconds interval") pollingTimer = Timer.scheduledTimer(withTimeInterval: adjustedInterval, repeats: true) { [weak self] _ in self?.refreshOperations() } @@ -393,7 +393,7 @@ class WMTOperationsImpl: WMTOperations, WMTService { } pollingTimer = nil timer.invalidate() - D.print("Operations polling stopped") + D.info("Operations polling stopped") } // MARK: - private functions diff --git a/WultraMobileTokenSDK/Operations/Utils/WMTOperationExpirationWatcher.swift b/WultraMobileTokenSDK/Operations/Utils/WMTOperationExpirationWatcher.swift index deac2b5..d036410 100644 --- a/WultraMobileTokenSDK/Operations/Utils/WMTOperationExpirationWatcher.swift +++ b/WultraMobileTokenSDK/Operations/Utils/WMTOperationExpirationWatcher.swift @@ -134,7 +134,7 @@ public class WMTOperationExpirationWatcher { if opsToWatch.isEmpty { D.warning("WMTOperationExpirationWatcher: All operations are already watched") } else { - D.print("WMTOperationExpirationWatcher: Adding \(opsToWatch.count) operation to watch.") + D.debug("WMTOperationExpirationWatcher: Adding \(opsToWatch.count) operation to watch.") self.operationsToWatch.append(contentsOf: opsToWatch) self.prepareTimer() } @@ -175,10 +175,10 @@ public class WMTOperationExpirationWatcher { // when nil is provided, we consider it as "stop all" if let operations = operations { self.operationsToWatch.removeAll(where: { current in operations.contains(where: { toRemove in toRemove.equals(other: current) }) }) - D.print("WMTOperationExpirationWatcher: Stoped watching \(operations.count) operations.") + D.debug("WMTOperationExpirationWatcher: Stoped watching \(operations.count) operations.") } else { self.operationsToWatch.removeAll() - D.print("WMTOperationExpirationWatcher: Stoped watching all operations.") + D.debug("WMTOperationExpirationWatcher: Stoped watching all operations.") } self.prepareTimer() } @@ -194,7 +194,7 @@ public class WMTOperationExpirationWatcher { timer = nil guard operationsToWatch.isEmpty == false else { - D.print("WMTOperationExpirationWatcher: No operations to watch.") + D.debug("WMTOperationExpirationWatcher: No operations to watch.") return } @@ -211,7 +211,7 @@ public class WMTOperationExpirationWatcher { // The 0.1 addition is a correction of the Timer class which can fire slightly (in order of 0.000x seconds) earlier than scheduled. let interval = max(5, firstOp.operationExpires.timeIntervalSince1970 - self.currentDateProvider.currentDate.timeIntervalSince1970) + 0.1 - D.print("WMTOperationExpirationWatcher: Scheduling operation expire check in \(Int(interval)) seconds.") + D.debug("WMTOperationExpirationWatcher: Scheduling operation expire check in \(Int(interval)) seconds.") self.timer = Timer.scheduledTimer(withTimeInterval: interval, repeats: false) { [weak self] _ in guard let self = self else { @@ -230,7 +230,7 @@ public class WMTOperationExpirationWatcher { self.operationsToWatch.removeAll(where: { $0.isExpired(currentDate) }) self.prepareTimer() DispatchQueue.main.async { - D.print("WMTOperationExpirationWatcher: Reporting \(expiredOps.count) expired operations.") + D.info("WMTOperationExpirationWatcher: Reporting \(expiredOps.count) expired operations.") self.delegate?.operationsExpired(expiredOps) } } diff --git a/docs/Logging.md b/docs/Logging.md index 1312278..2d50059 100644 --- a/docs/Logging.md +++ b/docs/Logging.md @@ -1,26 +1,19 @@ # Logging -For logging purposes, WMT uses `WMTLogger` class that prints to the console. - - -Note that logging to the console is available only when the library is compiled in the `Debug` mode or with `WMT_ENABLE_LOGGING` Swift compile condition. - +You can set up logging for the library using the `WMTLogger` class. ### Verbosity Level -You can limit the amount of logged information via `verboseLevel` property. - -| Level | Description | -| --- | --- | -| `off` | Silences all messages. | -| `errors` | Only errors will be printed to the debug console. | -| `warnings` _(default)_ | Errors and warnings will be printed to the debug console. | -| `all` | All messages will be printed to the debug console. | +You can limit the amount of logged information via the `verboseLevel` property. -Example configuration: +| Level | Description | +| ---------------------- | ------------------------------------------------- | +| `off` | Silences all messages. | +| `errors` | Only errors will be logged. | +| `warnings` _(default)_ | Errors and warnings will be logged. | +| `info` | Error, warning and info messages will be logged. | +| `all` | All messages will be logged. | -```swift -import WultraMobileTokenSDK +### Logger Delegate -WMTLogger.verboseLevel = .all -``` \ No newline at end of file +In case you want to process logs on your own (for example log into a file or some cloud service), you can set `WMTLogger.delegate`.