Skip to content

Commit 5034522

Browse files
authored
fix(logger): add support for multiple clients (#407)
Fix logger to support multiple clients (sdkKeys). - full thread-safety for additional concurrency requirements - no resource conflicts for multiple sdkKeys support
1 parent 47ad0ae commit 5034522

File tree

4 files changed

+205
-32
lines changed

4 files changed

+205
-32
lines changed

Sources/Customization/DefaultLogger.swift

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -26,21 +26,21 @@ open class DefaultLogger: OPTLogger {
2626
set (newLevel) {
2727
if _logLevel == nil {
2828
_logLevel = newLevel
29-
return
3029
}
3130
}
3231
}
33-
34-
var osLogUsed = false
35-
36-
required public init() {
37-
}
32+
33+
required public init() {}
3834

3935
open func log(level: OptimizelyLogLevel, message: String) {
4036
if level > DefaultLogger.logLevel {
4137
return
4238
}
4339

40+
clog(level: level, message: message)
41+
}
42+
43+
func clog(level: OptimizelyLogLevel, message: String) {
4444
var osLogType: OSLogType
4545

4646
switch level {
@@ -51,7 +51,12 @@ open class DefaultLogger: OPTLogger {
5151
}
5252

5353
os_log("[%{public}@] %{public}@", log: .optimizely, type: osLogType, level.name, message)
54-
osLogUsed = true
54+
}
55+
56+
// test support
57+
58+
static func setLogLevel(_ level: OptimizelyLogLevel) {
59+
_logLevel = level
5560
}
5661
}
5762

Tests/OptimizelyTests-Common/LoggerTests.swift

Lines changed: 147 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -18,36 +18,141 @@ import XCTest
1818

1919
class LoggerTests: XCTestCase {
2020

21-
func testDebugLog() {
22-
CustomLogger.logCount = 0
23-
let logger = CustomLogger()
24-
CustomLogger.logLevel = .debug
21+
let logger = TestLogger()
22+
23+
func testOPTLogger_DefaultMethods() {
24+
// String messages
25+
26+
let message = "Log Message"
27+
28+
logger.e(message)
29+
verifyLogger(.error, message)
30+
logger.w(message)
31+
verifyLogger(.warning, message)
32+
logger.i(message)
33+
verifyLogger(.info, message)
34+
logger.d(message)
35+
verifyLogger(.debug, message)
36+
37+
// LogMessage
38+
39+
let logMessage = LogMessage.experimentNotRunning("key")
40+
41+
logger.e(logMessage)
42+
verifyLogger(.error, logMessage.description)
43+
logger.w(logMessage)
44+
verifyLogger(.warning, logMessage.description)
45+
logger.i(logMessage)
46+
verifyLogger(.info, logMessage.description)
47+
logger.d(logMessage)
48+
verifyLogger(.debug, logMessage.description)
49+
50+
// OptimizelyError
51+
52+
let error = OptimizelyError.sdkNotReady
53+
let errorMessage = "(src) " + error.reason
54+
55+
logger.e(error, source: "src")
56+
verifyLogger(.error, errorMessage)
57+
logger.w(error, source: "src")
58+
verifyLogger(.warning, errorMessage)
59+
logger.i(error, source: "src")
60+
verifyLogger(.info, errorMessage)
61+
logger.d(error, source: "src")
62+
verifyLogger(.debug, errorMessage)
63+
}
64+
65+
func testOPTLogger_DebugLog() {
66+
TestLogger.logLevel = .debug
2567
logger.d { () -> String in
26-
return "Log Message"
68+
return "message"
2769
}
28-
XCTAssertTrue(CustomLogger.logCount == 1)
29-
30-
CustomLogger.logCount = 0
31-
CustomLogger.logLevel = .info
70+
XCTAssertTrue(logger.logCount == 1)
71+
XCTAssertEqual(logger.getMessages(.debug), ["message"])
72+
logger.clearMessages()
73+
74+
TestLogger.logLevel = .info
3275
logger.d { () -> String in
33-
return "Log Message"
76+
return "message"
3477
}
35-
XCTAssertTrue(CustomLogger.logCount == 0)
78+
XCTAssertTrue(logger.logCount == 0)
3679
}
3780

3881
// MARK: - DefaultLogger Tests
3982

40-
func testLog_UseOSLog() {
41-
let logger = DefaultLogger()
42-
logger.i("Log Message")
83+
func testDefaultLogger_DebugLevel() {
84+
let logger = TestDefaultLogger()
85+
DefaultLogger.setLogLevel(.debug)
4386

44-
XCTAssertTrue(logger.osLogUsed)
87+
logger.e("message")
88+
XCTAssert(logger.logCount > 0); logger.logCount = 0
89+
logger.w("message")
90+
XCTAssert(logger.logCount > 0); logger.logCount = 0
91+
logger.i("message")
92+
XCTAssert(logger.logCount > 0); logger.logCount = 0
93+
logger.d("message")
94+
XCTAssert(logger.logCount > 0); logger.logCount = 0
95+
}
96+
97+
func testDefaultLogger_InfoLevel() {
98+
let logger = TestDefaultLogger()
99+
DefaultLogger.setLogLevel(.info)
100+
101+
logger.e("message")
102+
XCTAssert(logger.logCount > 0); logger.logCount = 0
103+
logger.w("message")
104+
XCTAssert(logger.logCount > 0); logger.logCount = 0
105+
logger.i("message")
106+
XCTAssert(logger.logCount > 0); logger.logCount = 0
107+
logger.d("message")
108+
XCTAssert(logger.logCount == 0)
109+
}
110+
111+
func testDefaultLogger_WarningLevel() {
112+
let logger = TestDefaultLogger()
113+
DefaultLogger.setLogLevel(.warning)
114+
115+
logger.e("message")
116+
XCTAssert(logger.logCount > 0); logger.logCount = 0
117+
logger.w("message")
118+
XCTAssert(logger.logCount > 0); logger.logCount = 0
119+
logger.i("message")
120+
XCTAssert(logger.logCount == 0)
121+
logger.d("message")
122+
XCTAssert(logger.logCount == 0)
123+
}
124+
125+
func testDefaultLogger_ErrorLevel() {
126+
let logger = TestDefaultLogger()
127+
DefaultLogger.setLogLevel(.error)
128+
129+
logger.e("message")
130+
XCTAssert(logger.logCount > 0); logger.logCount = 0
131+
logger.w("message")
132+
XCTAssert(logger.logCount == 0)
133+
logger.i("message")
134+
XCTAssert(logger.logCount == 0)
135+
logger.d("message")
136+
XCTAssert(logger.logCount == 0)
137+
}
138+
139+
}
140+
141+
// MARK: - Utils
142+
143+
extension LoggerTests {
144+
145+
func verifyLogger(_ logLevel: OptimizelyLogLevel, _ message: String) {
146+
XCTAssertTrue(logger.logCount == 1)
147+
XCTAssertEqual(logger.getMessages(logLevel), [message])
148+
logger.clearMessages()
45149
}
46150

47151
}
48152

49-
private class CustomLogger: OPTLogger {
50-
public static var logCount = 0
153+
// MARK: - Mock Loggers
154+
155+
class TestLogger: OPTLogger {
51156
private static var _logLevel: OptimizelyLogLevel?
52157
public static var logLevel: OptimizelyLogLevel {
53158
get {
@@ -59,10 +164,34 @@ private class CustomLogger: OPTLogger {
59164
}
60165

61166
required public init() {
167+
clearMessages()
62168
}
63169

64170
func log(level: OptimizelyLogLevel, message: String) {
65-
CustomLogger.logCount += 1
171+
logMessages[level.rawValue].append(message)
172+
}
173+
174+
// Utils
175+
176+
var logMessages = [[String]]()
177+
var logCount: Int {
178+
return logMessages.reduce(0) { $0 + $1.count }
179+
}
180+
181+
func getMessages(_ level: OptimizelyLogLevel) -> [String] {
182+
return logMessages[level.rawValue]
183+
}
184+
185+
func clearMessages() {
186+
logMessages = [[String]](repeating: [], count: OptimizelyLogLevel.debug.rawValue + 1)
66187
}
67188
}
68189

190+
class TestDefaultLogger: DefaultLogger {
191+
var logCount = 0
192+
override func clog(level: OptimizelyLogLevel, message: String) {
193+
logCount += 1
194+
}
195+
}
196+
197+

Tests/OptimizelyTests-MultiClients/HandlerRegistryServiceTests_MultiClients.swift

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,6 @@ import XCTest
1818

1919
class HandlerRegistryServiceTests_MultiClients: XCTestCase {
2020

21-
override func setUpWithError() throws {
22-
}
23-
24-
override func tearDownWithError() throws {
25-
}
26-
2721
func testConcurrentAccess_Singleton() {
2822
// this type used for all handlers except for logger
2923

Tests/OptimizelyTests-MultiClients/LoggerTests_MultiClients.swift

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,54 @@ import XCTest
1919
class LoggerTests_MultiClients: XCTestCase {
2020

2121
override func setUpWithError() throws {
22+
DefaultLogger.logLevel = .debug
2223
}
2324

24-
override func tearDownWithError() throws {
25+
func testConcurrentLogging() {
26+
let logger = DefaultLogger()
27+
28+
let numThreads = 10
29+
let numEventsPerThread = 100
30+
31+
let result = OTUtils.runConcurrent(count: numThreads) { item in
32+
for i in 0..<numEventsPerThread {
33+
logger.e("error-level: \(item)-\(i)")
34+
logger.w("warning-level: \(item)-\(i)")
35+
logger.i("info-level: \(item)-\(i)")
36+
logger.d("debug-level: \(item)-\(i)")
37+
38+
logger.e(.attributeFormatInvalid, source: String(item))
39+
logger.w(.attributeFormatInvalid, source: String(item))
40+
logger.i(.attributeFormatInvalid, source: String(item))
41+
logger.d(.attributeFormatInvalid, source: String(item))
42+
}
43+
}
44+
45+
XCTAssertTrue(result, "Concurrent tasks timed out")
46+
}
47+
48+
func testConcurrentLogging_MultipleLoggerInstances() {
49+
50+
let numThreads = 10
51+
let numEventsPerThread = 100
52+
53+
let result = OTUtils.runConcurrent(count: numThreads) { item in
54+
let logger = DefaultLogger()
55+
56+
for i in 0..<numEventsPerThread {
57+
logger.e("error-level: \(item)-\(i)")
58+
logger.w("warning-level: \(item)-\(i)")
59+
logger.i("info-level: \(item)-\(i)")
60+
logger.d("debug-level: \(item)-\(i)")
61+
62+
logger.e(.attributeFormatInvalid, source: String(item))
63+
logger.w(.attributeFormatInvalid, source: String(item))
64+
logger.i(.attributeFormatInvalid, source: String(item))
65+
logger.d(.attributeFormatInvalid, source: String(item))
66+
}
67+
}
68+
69+
XCTAssertTrue(result, "Concurrent tasks timed out")
2570
}
2671

2772
}

0 commit comments

Comments
 (0)