diff --git a/platforms/swift/Sources/ShopifyAcceleratedCheckouts/ShopifyAcceleratedCheckouts.swift b/platforms/swift/Sources/ShopifyAcceleratedCheckouts/ShopifyAcceleratedCheckouts.swift index 15ccd698..da76fea0 100644 --- a/platforms/swift/Sources/ShopifyAcceleratedCheckouts/ShopifyAcceleratedCheckouts.swift +++ b/platforms/swift/Sources/ShopifyAcceleratedCheckouts/ShopifyAcceleratedCheckouts.swift @@ -8,13 +8,16 @@ public enum ShopifyAcceleratedCheckouts { /// The logging level for Accelerated Checkouts operations /// Default: .error - which will emit "error" and "fault" logs - public static var logLevel: LogLevel = .error { - didSet { - logger.logLevel = logLevel + public static var logLevel: LogLevel { + get { + logger.logLevel + } + set { + logger.logLevel = newValue } } /// Shared logger for ShopifyAcceleratedCheckouts /// To modify the logLevel - internal static var logger = OSLogger(prefix: name, logLevel: logLevel) + internal static let logger = OSLogger(prefix: name, logLevel: .error) } diff --git a/platforms/swift/Sources/ShopifyCheckoutKit/Logger.swift b/platforms/swift/Sources/ShopifyCheckoutKit/Logger.swift index 5107ee83..6081d9dd 100644 --- a/platforms/swift/Sources/ShopifyCheckoutKit/Logger.swift +++ b/platforms/swift/Sources/ShopifyCheckoutKit/Logger.swift @@ -10,21 +10,48 @@ public enum LogLevel: String, CaseIterable, Sendable { case none } -public class OSLogger { +public final class OSLogger: Sendable { private let logger = OSLog(subsystem: subsystem, category: OSLog.Category.pointsOfInterest) - private var prefix: String - package var logLevel: LogLevel + private let prefix: String + private let lockedLogLevel: LockedValue + private let sendToOSLogHandler: (@Sendable (String, OSLogType) -> Void)? - public static var shared = OSLogger() + package var logLevel: LogLevel { + get { + lockedLogLevel.get() + } + set { + lockedLogLevel.set(newValue) + } + } + + private static let lockedSharedLogger = LockedValue(OSLogger()) + + public static var shared: OSLogger { + get { + lockedSharedLogger.get() + } + set { + lockedSharedLogger.set(newValue) + } + } + + public convenience init() { + self.init(prefix: "ShopifyCheckoutKit", logLevel: ShopifyCheckoutKit.configuration.logLevel) + } - public init() { - prefix = "ShopifyCheckoutKit" - logLevel = ShopifyCheckoutKit.configuration.logLevel + public convenience init(prefix: String, logLevel: LogLevel) { + self.init(prefix: prefix, logLevel: logLevel, sendToOSLogHandler: nil) } - public init(prefix: String, logLevel: LogLevel) { + init( + prefix: String, + logLevel: LogLevel, + sendToOSLogHandler: (@Sendable (String, OSLogType) -> Void)? + ) { self.prefix = prefix - self.logLevel = logLevel + lockedLogLevel = LockedValue(logLevel) + self.sendToOSLogHandler = sendToOSLogHandler } public func debug(_ message: String) { @@ -54,15 +81,21 @@ public class OSLogger { /// Capturing `os_log` output is not possible /// This indirection lets us capture messages in `LoggerTests.swift` internal func sendToOSLog(_ message: String, type: OSLogType) { - os_log("%@", log: logger, type: type, message) + if let sendToOSLogHandler { + sendToOSLogHandler(message, type) + } else { + os_log("%@", log: logger, type: type, message) + } } private func shouldEmit(_ choice: LogLevel) -> Bool { - if logLevel == .none { + let currentLogLevel = logLevel + + if currentLogLevel == .none { return false } - return logLevel == .all || logLevel == choice + return currentLogLevel == .all || currentLogLevel == choice } } diff --git a/platforms/swift/Tests/ShopifyCheckoutKitTests/LoggerTests.swift b/platforms/swift/Tests/ShopifyCheckoutKitTests/LoggerTests.swift index fc0590cd..b4c22522 100644 --- a/platforms/swift/Tests/ShopifyCheckoutKitTests/LoggerTests.swift +++ b/platforms/swift/Tests/ShopifyCheckoutKitTests/LoggerTests.swift @@ -2,21 +2,44 @@ import os.log @testable import ShopifyCheckoutKit import XCTest -class TestableOSLogger: OSLogger { - private(set) var capturedMessages: [(message: String, type: OSLogType)] = [] - private let testPrefix: String - override init() { - testPrefix = "ShopifyCheckoutKit" - super.init() +final class TestableOSLogger: Sendable { + private let capturedMessagesStorage = LockedValue([(message: String, type: OSLogType)]()) + private let logger: OSLogger + + var capturedMessages: [(message: String, type: OSLogType)] { + capturedMessagesStorage.get() } - override init(prefix: String, logLevel: LogLevel) { - testPrefix = prefix - super.init(prefix: prefix, logLevel: logLevel) + convenience init() { + self.init(prefix: "ShopifyCheckoutKit", logLevel: ShopifyCheckoutKit.configuration.logLevel) } - override func sendToOSLog(_ message: String, type: OSLogType) { - capturedMessages.append((message: message, type: type)) + init(prefix: String, logLevel: LogLevel) { + logger = OSLogger( + prefix: prefix, + logLevel: logLevel, + sendToOSLogHandler: { [capturedMessagesStorage] message, type in + capturedMessagesStorage.update { + $0.append((message: message, type: type)) + } + } + ) + } + + func info(_ message: String) { + logger.info(message) + } + + func debug(_ message: String) { + logger.debug(message) + } + + func error(_ message: String) { + logger.error(message) + } + + func fault(_ message: String) { + logger.fault(message) } } @@ -37,9 +60,23 @@ final class OSLoggerTests: XCTestCase { XCTAssertNotNil(OSLogger.shared) } - func test_defaultInitializer_withNoParameters_shouldMaintainBackwardsCompatibility() { + func test_defaultInitializer_withNoParameters_shouldUseConfigurationLogLevel() { + ShopifyCheckoutKit.configure { $0.logLevel = .debug } + let logger = OSLogger() - XCTAssertNotNil(logger) + + XCTAssertEqual(logger.logLevel, .debug) + } + + func test_sharedLogger_canBeReplaced() { + let originalLogger = OSLogger.shared + defer { OSLogger.shared = originalLogger } + + let replacementLogger = OSLogger(prefix: "Replacement", logLevel: .debug) + + OSLogger.shared = replacementLogger + + XCTAssertTrue(OSLogger.shared === replacementLogger) } func test_logLevelNone_withAllLogCalls_shouldBlockAllLogging() { diff --git a/platforms/swift/api/ShopifyAcceleratedCheckouts.json b/platforms/swift/api/ShopifyAcceleratedCheckouts.json index 9bee51c1..d5daeb64 100644 --- a/platforms/swift/api/ShopifyAcceleratedCheckouts.json +++ b/platforms/swift/api/ShopifyAcceleratedCheckouts.json @@ -131,11 +131,6 @@ "mangledName": "$s27ShopifyAcceleratedCheckoutsAAO8logLevel0A11CheckoutKit03LogE0OvpZ", "moduleName": "ShopifyAcceleratedCheckouts", "static": true, - "declAttributes": [ - "HasInitialValue", - "HasStorage" - ], - "hasStorage": true, "accessors": [ { "kind": "Accessor", @@ -154,7 +149,6 @@ "mangledName": "$s27ShopifyAcceleratedCheckoutsAAO8logLevel0A11CheckoutKit03LogE0OvgZ", "moduleName": "ShopifyAcceleratedCheckouts", "static": true, - "implicit": true, "accessorKind": "get" }, { @@ -179,7 +173,6 @@ "mangledName": "$s27ShopifyAcceleratedCheckoutsAAO8logLevel0A11CheckoutKit03LogE0OvsZ", "moduleName": "ShopifyAcceleratedCheckouts", "static": true, - "implicit": true, "accessorKind": "set" } ] diff --git a/platforms/swift/api/ShopifyCheckoutKit.json b/platforms/swift/api/ShopifyCheckoutKit.json index 448a9a7b..8ca66d23 100644 --- a/platforms/swift/api/ShopifyCheckoutKit.json +++ b/platforms/swift/api/ShopifyCheckoutKit.json @@ -4602,9 +4602,8 @@ "moduleName": "ShopifyCheckoutKit", "isInternal": true, "declAttributes": [ - "HasStorage" + "Final" ], - "hasStorage": true, "accessors": [ { "kind": "Accessor", @@ -4622,10 +4621,9 @@ "usr": "s:18ShopifyCheckoutKit8OSLoggerC8logLevelAA03LogF0Ovg", "mangledName": "$s18ShopifyCheckoutKit8OSLoggerC8logLevelAA03LogF0Ovg", "moduleName": "ShopifyCheckoutKit", - "implicit": true, "isInternal": true, "declAttributes": [ - "Transparent" + "Final" ], "accessorKind": "get" }, @@ -4650,10 +4648,9 @@ "usr": "s:18ShopifyCheckoutKit8OSLoggerC8logLevelAA03LogF0Ovs", "mangledName": "$s18ShopifyCheckoutKit8OSLoggerC8logLevelAA03LogF0Ovs", "moduleName": "ShopifyCheckoutKit", - "implicit": true, "isInternal": true, "declAttributes": [ - "Transparent" + "Final" ], "accessorKind": "set" } @@ -4677,11 +4674,8 @@ "moduleName": "ShopifyCheckoutKit", "static": true, "declAttributes": [ - "HasInitialValue", - "Final", - "HasStorage" + "Final" ], - "hasStorage": true, "accessors": [ { "kind": "Accessor", @@ -4700,10 +4694,8 @@ "mangledName": "$s18ShopifyCheckoutKit8OSLoggerC6sharedACvgZ", "moduleName": "ShopifyCheckoutKit", "static": true, - "implicit": true, "declAttributes": [ - "Final", - "Transparent" + "Final" ], "accessorKind": "get" }, @@ -4729,10 +4721,8 @@ "mangledName": "$s18ShopifyCheckoutKit8OSLoggerC6sharedACvsZ", "moduleName": "ShopifyCheckoutKit", "static": true, - "implicit": true, "declAttributes": [ - "Final", - "Transparent" + "Final" ], "accessorKind": "set" } @@ -4754,7 +4744,7 @@ "usr": "s:18ShopifyCheckoutKit8OSLoggerCACycfc", "mangledName": "$s18ShopifyCheckoutKit8OSLoggerCACycfc", "moduleName": "ShopifyCheckoutKit", - "init_kind": "Designated" + "init_kind": "Convenience" }, { "kind": "Constructor", @@ -4784,7 +4774,7 @@ "usr": "s:18ShopifyCheckoutKit8OSLoggerC6prefix8logLevelACSS_AA03LogG0Otcfc", "mangledName": "$s18ShopifyCheckoutKit8OSLoggerC6prefix8logLevelACSS_AA03LogG0Otcfc", "moduleName": "ShopifyCheckoutKit", - "init_kind": "Designated" + "init_kind": "Convenience" }, { "kind": "Function", @@ -4807,6 +4797,9 @@ "usr": "s:18ShopifyCheckoutKit8OSLoggerC5debugyySSF", "mangledName": "$s18ShopifyCheckoutKit8OSLoggerC5debugyySSF", "moduleName": "ShopifyCheckoutKit", + "declAttributes": [ + "Final" + ], "funcSelfKind": "NonMutating" }, { @@ -4830,6 +4823,9 @@ "usr": "s:18ShopifyCheckoutKit8OSLoggerC4infoyySSF", "mangledName": "$s18ShopifyCheckoutKit8OSLoggerC4infoyySSF", "moduleName": "ShopifyCheckoutKit", + "declAttributes": [ + "Final" + ], "funcSelfKind": "NonMutating" }, { @@ -4853,6 +4849,9 @@ "usr": "s:18ShopifyCheckoutKit8OSLoggerC5erroryySSF", "mangledName": "$s18ShopifyCheckoutKit8OSLoggerC5erroryySSF", "moduleName": "ShopifyCheckoutKit", + "declAttributes": [ + "Final" + ], "funcSelfKind": "NonMutating" }, { @@ -4876,6 +4875,9 @@ "usr": "s:18ShopifyCheckoutKit8OSLoggerC5faultyySSF", "mangledName": "$s18ShopifyCheckoutKit8OSLoggerC5faultyySSF", "moduleName": "ShopifyCheckoutKit", + "declAttributes": [ + "Final" + ], "funcSelfKind": "NonMutating" } ], @@ -4883,7 +4885,25 @@ "usr": "s:18ShopifyCheckoutKit8OSLoggerC", "mangledName": "$s18ShopifyCheckoutKit8OSLoggerC", "moduleName": "ShopifyCheckoutKit", + "declAttributes": [ + "Final" + ], + "hasMissingDesignatedInitializers": true, "conformances": [ + { + "kind": "Conformance", + "name": "Sendable", + "printedName": "Sendable", + "usr": "s:s8SendableP", + "mangledName": "$ss8SendableP" + }, + { + "kind": "Conformance", + "name": "SendableMetatype", + "printedName": "SendableMetatype", + "usr": "s:s16SendableMetatypeP", + "mangledName": "$ss16SendableMetatypeP" + }, { "kind": "Conformance", "name": "Copyable",