Skip to content

Commit 2220929

Browse files
authored
feat: adds ability to provide a OSLog instance via the Config.logger property (#78)
**Related issues** SC-245493 **Describe the solution you've provided** Added Config.logger property that is only available on platforms supporting OSLog. **Describe alternatives you've considered** Creating a LD specific protocol for logging and exposing that. Customer's would then have to implement an adapter for this. This was rejected as adding more logging flavors to the ecosystem is probably a worse outcome. **Additional context** Logging was previously a no-op on Linux and Windows, and that continues to be the case.
2 parents 23560ce + 1098609 commit 2220929

File tree

2 files changed

+67
-30
lines changed

2 files changed

+67
-30
lines changed

Source/LDSwiftEventSource.swift

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,12 @@ import Foundation
44
import FoundationNetworking
55
#endif
66

7+
#if canImport(os)
8+
// os_log is not supported on some platforms, but we want to use it for most of our customer's
9+
// use cases that use Apple OSs
10+
import os.log
11+
#endif
12+
713
/**
814
Provides an EventSource client for consuming Server-Sent Events.
915

@@ -51,12 +57,18 @@ public class EventSource {
5157
public var method: String = "GET"
5258
/// Optional HTTP body to be included in the API request.
5359
public var body: Data?
54-
/// An initial value for the last-event-id header to be sent on the initial request
55-
public var lastEventId: String = ""
5660
/// Additional HTTP headers to be set on the request
5761
public var headers: [String: String] = [:]
5862
/// Transform function to allow dynamically configuring the headers on each API request.
5963
public var headerTransform: HeaderTransform = { $0 }
64+
/// An initial value for the last-event-id header to be sent on the initial request
65+
public var lastEventId: String = ""
66+
67+
#if canImport(os)
68+
/// Configure the logger that will be used.
69+
public var logger: OSLog = OSLog(subsystem: "com.launchdarkly.swift-eventsource", category: "LDEventSource")
70+
#endif
71+
6072
/// The minimum amount of time to wait before reconnecting after a failure
6173
public var reconnectTime: TimeInterval = 1.0
6274
/// The maximum amount of time to wait before reconnecting after a failure
@@ -152,8 +164,9 @@ class ReconnectionTimer {
152164
// MARK: EventSourceDelegate
153165
class EventSourceDelegate: NSObject, URLSessionDataDelegate {
154166
private let delegateQueue: DispatchQueue = DispatchQueue(label: "ESDelegateQueue")
155-
private let logger = Logs()
156-
167+
168+
public var logger: InternalLogging
169+
157170
private let config: EventSource.Config
158171

159172
private var readyState: ReadyState = .raw {
@@ -170,6 +183,14 @@ class EventSourceDelegate: NSObject, URLSessionDataDelegate {
170183

171184
init(config: EventSource.Config) {
172185
self.config = config
186+
187+
#if canImport(os)
188+
self.logger = OSLogAdapter(osLog: config.logger)
189+
#else
190+
self.logger = NoOpLogging()
191+
#endif
192+
193+
173194
self.eventParser = EventParser(handler: config.handler,
174195
initialEventId: config.lastEventId,
175196
initialRetry: config.reconnectTime)
@@ -277,7 +298,8 @@ class EventSourceDelegate: NSObject, URLSessionDataDelegate {
277298

278299
readyState = .closed
279300
let sleep = reconnectionTimer.reconnectDelay(baseDelay: currentRetry)
280-
logger.log(.info, "Waiting %.3f seconds before reconnecting...", sleep)
301+
// this formatting shenanigans is to workaround String not implementing CVarArg on Swift<5.4 on Linux
302+
logger.log(.info, "Waiting %@ seconds before reconnecting...", String(format: "%.3f", sleep))
281303
delegateQueue.asyncAfter(deadline: .now() + sleep) { [weak self] in
282304
self?.connect()
283305
}
@@ -305,7 +327,8 @@ class EventSourceDelegate: NSObject, URLSessionDataDelegate {
305327
config.handler.onOpened()
306328
completionHandler(.allow)
307329
} else {
308-
logger.log(.info, "Unsuccessful response: %d", statusCode)
330+
// this formatting shenanigans is to workaround String not implementing CVarArg on Swift<5.4 on Linux
331+
logger.log(.info, "Unsuccessful response: %@", String(format: "%d", statusCode))
309332
if dispatchError(error: UnsuccessfulResponseError(responseCode: statusCode)) == .shutdown {
310333
logger.log(.info, "Connection has been explicitly shut down by error handler")
311334
readyState = .shutdown

Source/Logs.swift

Lines changed: 38 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,52 @@
11
import Foundation
22

3-
#if !os(Linux) && !os(Windows)
3+
#if canImport(os)
44
import os.log
55
#endif
66

7-
class Logs {
8-
enum Level {
9-
case debug, info, warn, error
7+
protocol InternalLogging {
8+
func log(_ level: Level, _ staticMsg: StaticString)
9+
func log(_ level: Level, _ staticMsg: StaticString, _ arg: String)
10+
func log(_ level: Level, _ staticMsg: StaticString, _ arg1: String, _ arg2: String)
11+
}
1012

11-
#if !os(Linux) && !os(Windows)
12-
private static let osLogTypes = [ Level.debug: OSLogType.debug,
13-
Level.info: OSLogType.info,
14-
Level.warn: OSLogType.default,
15-
Level.error: OSLogType.error]
16-
var osLogType: OSLogType { Level.osLogTypes[self]! }
17-
#endif
18-
}
13+
enum Level {
14+
case debug, info, warn, error
1915

20-
#if !os(Linux) && !os(Windows)
21-
private let logger: OSLog = OSLog(subsystem: "com.launchdarkly.swift-eventsource", category: "LDEventSource")
16+
#if canImport(os)
17+
private static let osLogTypes = [ Level.debug: OSLogType.debug,
18+
Level.info: OSLogType.info,
19+
Level.warn: OSLogType.default,
20+
Level.error: OSLogType.error]
21+
var osLogType: OSLogType { Level.osLogTypes[self]! }
22+
#endif
23+
}
2224

25+
#if canImport(os)
26+
class OSLogAdapter: InternalLogging {
27+
28+
private let osLog: OSLog
29+
30+
init(osLog: OSLog) {
31+
self.osLog = osLog
32+
}
33+
2334
func log(_ level: Level, _ staticMsg: StaticString) {
24-
os_log(staticMsg, log: logger, type: level.osLogType)
35+
os_log(staticMsg, log: self.osLog, type: level.osLogType)
2536
}
26-
27-
func log(_ level: Level, _ staticMsg: StaticString, _ arg: CVarArg) {
28-
os_log(staticMsg, log: logger, type: level.osLogType, arg)
37+
38+
func log(_ level: Level, _ staticMsg: StaticString, _ arg: String) {
39+
os_log(staticMsg, log: self.osLog, type: level.osLogType, arg)
2940
}
30-
31-
func log(_ level: Level, _ staticMsg: StaticString, _ arg1: CVarArg, _ arg2: CVarArg) {
32-
os_log(staticMsg, log: logger, type: level.osLogType, arg1, arg2)
41+
42+
func log(_ level: Level, _ staticMsg: StaticString, _ arg1: String, _ arg2: String) {
43+
os_log(staticMsg, log: self.osLog, type: level.osLogType, arg1, arg2)
3344
}
34-
#else
35-
// We use Any over CVarArg here, because on Linux prior to Swift 5.4 String does not conform to CVarArg
36-
func log(_ level: Level, _ staticMsg: StaticString, _ args: Any...) { }
45+
}
3746
#endif
47+
48+
class NoOpLogging: InternalLogging {
49+
func log(_ level: Level, _ staticMsg: StaticString) {}
50+
func log(_ level: Level, _ staticMsg: StaticString, _ arg: String) {}
51+
func log(_ level: Level, _ staticMsg: StaticString, _ arg1: String, _ arg2: String) {}
3852
}

0 commit comments

Comments
 (0)