Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added delegate to the logger + improvements #164

Merged
merged 4 commits into from
Jul 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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:/wultra/mtoken-sdk-ios/pull/160)
- Extended PushParser to support parsing of inbox notifications [(#158)](https:/wultra/mtoken-sdk-ios/pull/158)
- Added statusReason to UserOperation [(#156)](https:/wultra/mtoken-sdk-ios/pull/156)
- Improved logging options [(#164)](https:/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:/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`.
Loading