Skip to content

Commit

Permalink
Added delegate to the logger + improvements (#164)
Browse files Browse the repository at this point in the history
  • Loading branch information
kober32 authored Jul 8, 2024
1 parent 466f443 commit a2955d6
Show file tree
Hide file tree
Showing 7 changed files with 113 additions and 50 deletions.
115 changes: 90 additions & 25 deletions WultraMobileTokenSDK/Common/WMTLogger.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -374,7 +374,7 @@ class WMTOperationsImpl<T: WMTUserOperation>: 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()
}
Expand All @@ -393,7 +393,7 @@ class WMTOperationsImpl<T: WMTUserOperation>: WMTOperations, WMTService {
}
pollingTimer = nil
timer.invalidate()
D.print("Operations polling stopped")
D.info("Operations polling stopped")
}

// MARK: - private functions
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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()
}
Expand Down Expand Up @@ -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()
}
Expand All @@ -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
}

Expand All @@ -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 {
Expand All @@ -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)
}
}
Expand Down
2 changes: 1 addition & 1 deletion WultraMobileTokenSDKTests/IntegrationTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ class IntegrationTests: XCTestCase {

override func setUp() {
super.setUp()
WMTLogger.verboseLevel = .all
WMTLogger.verboseLevel = .debug
proxy = IntegrationProxy()

let exp = XCTestExpectation(description: "setup expectation")
Expand Down
1 change: 1 addition & 0 deletions docs/Changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
- Added resultTexts to UserOperation [(#160)](https://github.com/wultra/mtoken-sdk-ios/pull/160)
- Extended PushParser to support parsing of inbox notifications [(#158)](https://github.com/wultra/mtoken-sdk-ios/pull/158)
- Added statusReason to UserOperation [(#156)](https://github.com/wultra/mtoken-sdk-ios/pull/156)
- Improved logging options [(#164)](https://github.com/wultra/mtoken-sdk-ios/pull/164)

## 1.10.0 (Apr 18, 2024)

Expand Down
27 changes: 12 additions & 15 deletions docs/Logging.md
Original file line number Diff line number Diff line change
@@ -1,26 +1,23 @@
# Logging

For logging purposes, WMT uses `WMTLogger` class that prints to the console.
You can set up logging for the library using the `WMTLogger` class.

<!-- begin box info -->
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.
Networking traffic is logged in its own logger described in the [networking library documentation](https://github.com/wultra/networking-apple).
<!-- end -->

### Verbosity Level

You can limit the amount of logged information via `verboseLevel` property.
You can limit the amount of logged information via the `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. |
| 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. |
| `debug` | All messages will be logged. |

Example configuration:
### Logger Delegate

```swift
import WultraMobileTokenSDK

WMTLogger.verboseLevel = .all
```
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`.

0 comments on commit a2955d6

Please sign in to comment.