Skip to content

Commit

Permalink
feat: add debuglogger to confidence (#144)
Browse files Browse the repository at this point in the history
* add debuglogger to confidence

* fix lint issues

* add logging to RemoteConfidenceClient

* add test coverage

* align with android

* liter fix

* adjust data logged

* document how logging works

---------

Co-authored-by: Nicklas Lundin <[email protected]>
  • Loading branch information
nickybondarenko and nicklasl authored Jun 28, 2024
1 parent 2ffc41d commit c8fe939
Show file tree
Hide file tree
Showing 9 changed files with 199 additions and 35 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ struct ConfidenceDemoApp: App {
var body: some Scene {
WindowGroup {
let secret = ProcessInfo.processInfo.environment["CLIENT_SECRET"] ?? ""
let confidence = Confidence.Builder(clientSecret: secret)
let confidence = Confidence.Builder(clientSecret: secret, loggerLevel: .TRACE)
.withContext(initialContext: ["targeting_key": ConfidenceValue(string: UUID.init().uuidString)])
.withRegion(region: .europe)
.build()
Expand Down
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,11 @@ To set context data to be appended to all tracked events, here as example:
confidence.putContext(context: ["os_version": ConfidenceValue(string: "17.0")])
```

### Logging
By default, the Confidence SDK will log errors and warnings. You can change the preferred log level by passing a `loggerLevel` to the `Confidence.Builder` constructor.

To turn off logging completely, you can pass `LoggingLevel.NONE` to the `Confidence.Builder`.

# OpenFeature Provider
If you want to use OpenFeature, an OpenFeature Provider for the [OpenFeature SDK](https://github.com/open-feature/kotlin-swift) is also available.

Expand Down
6 changes: 5 additions & 1 deletion Sources/Confidence/Apply/FlagApplierWithRetries.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,22 @@ final class FlagApplierWithRetries: FlagApplier {
private let options: ConfidenceClientOptions
private let cacheDataInteractor: CacheDataActor
private let metadata: ConfidenceMetadata
private let debugLogger: DebugLogger?

init(
httpClient: HttpClient,
storage: Storage,
options: ConfidenceClientOptions,
metadata: ConfidenceMetadata,
cacheDataInteractor: CacheDataActor? = nil,
triggerBatch: Bool = true
triggerBatch: Bool = true,
debugLogger: DebugLogger? = nil
) {
self.storage = storage
self.httpClient = httpClient
self.options = options
self.metadata = metadata
self.debugLogger = debugLogger

let storedData = try? storage.load(defaultValue: CacheData.empty())
self.cacheDataInteractor = cacheDataInteractor ?? CacheDataInteractor(cacheData: storedData ?? .empty())
Expand All @@ -48,6 +51,7 @@ final class FlagApplierWithRetries: FlagApplier {
return
}

debugLogger?.logFlags(action: "Apply", flag: flagName)
self.writeToFile(data: data)
await triggerBatch()
}
Expand Down
50 changes: 41 additions & 9 deletions Sources/Confidence/Confidence.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ public class Confidence: ConfidenceEventSender {
private var storage: Storage
private var cancellables = Set<AnyCancellable>()
private var currentFetchTask: Task<(), Never>?
private let debugLogger: DebugLogger?

// Internal for testing
internal let remoteFlagResolver: ConfidenceResolveClient
Expand All @@ -31,7 +32,8 @@ public class Confidence: ConfidenceEventSender {
storage: Storage,
context: ConfidenceStruct = [:],
parent: ConfidenceEventSender? = nil,
visitorId: String? = nil
visitorId: String? = nil,
debugLogger: DebugLogger?
) {
self.eventSenderEngine = eventSenderEngine
self.clientSecret = clientSecret
Expand All @@ -42,6 +44,7 @@ public class Confidence: ConfidenceEventSender {
self.storage = storage
self.flagApplier = flagApplier
self.remoteFlagResolver = remoteFlagResolver
self.debugLogger = debugLogger
if let visitorId {
putContext(context: ["visitor_id": ConfidenceValue.init(string: visitorId)])
}
Expand All @@ -57,7 +60,10 @@ public class Confidence: ConfidenceEventSender {
try await self.fetchAndActivate()
self.contextReconciliatedChanges.send(context.hash())
} catch {
// TODO: Log errors for debugging
debugLogger?.logMessage(
message: "\(error)",
isWarning: true
)
}
}
}
Expand All @@ -70,6 +76,7 @@ public class Confidence: ConfidenceEventSender {
public func activate() throws {
let savedFlags = try storage.load(defaultValue: FlagResolution.EMPTY)
self.cache = savedFlags
debugLogger?.logFlags(action: "Activate", flag: "")
}

/**
Expand All @@ -82,7 +89,10 @@ public class Confidence: ConfidenceEventSender {
do {
try await internalFetch()
} catch {
// TODO: Log errors for debugging
debugLogger?.logMessage(
message: "\(error)",
isWarning: true
)
}
try activate()
}
Expand All @@ -95,6 +105,7 @@ public class Confidence: ConfidenceEventSender {
flags: resolvedFlags.resolvedValues,
resolveToken: resolvedFlags.resolveToken ?? ""
)
debugLogger?.logFlags(action: "Fetch", flag: "")
try storage.save(data: resolution)
}

Expand All @@ -107,7 +118,10 @@ public class Confidence: ConfidenceEventSender {
do {
try await internalFetch()
} catch {
// TODO: Log errors for debugging
debugLogger?.logMessage(
message: "\(error )",
isWarning: true
)
}
}
}
Expand Down Expand Up @@ -209,6 +223,7 @@ public class Confidence: ConfidenceEventSender {
var map = confidence.contextSubject.value
map[key] = value
confidence.contextSubject.value = map
confidence.debugLogger?.logContext(action: "PutContext", context: confidence.contextSubject.value)
}
}

Expand All @@ -219,6 +234,7 @@ public class Confidence: ConfidenceEventSender {
map.updateValue(entry.value, forKey: entry.key)
}
confidence.contextSubject.value = map
confidence.debugLogger?.logContext(action: "PutContext", context: confidence.contextSubject.value)
}
}

Expand All @@ -232,6 +248,7 @@ public class Confidence: ConfidenceEventSender {
map.updateValue(entry.value, forKey: entry.key)
}
confidence.contextSubject.value = map
confidence.debugLogger?.logContext(action: "PutContext", context: confidence.contextSubject.value)
}
}

Expand All @@ -241,6 +258,7 @@ public class Confidence: ConfidenceEventSender {
map.removeValue(forKey: key)
confidence.contextSubject.value = map
confidence.removedContextKeys.insert(key)
confidence.debugLogger?.logContext(action: "RemoveContext", context: confidence.contextSubject.value)
}
}

Expand All @@ -256,7 +274,8 @@ public class Confidence: ConfidenceEventSender {
remoteFlagResolver: remoteFlagResolver,
storage: storage,
context: context,
parent: self)
parent: self,
debugLogger: debugLogger)
}
}

Expand All @@ -266,6 +285,7 @@ extension Confidence {
internal let clientSecret: String
internal let eventStorage: EventStorage
internal let visitorId = VisitorUtil().getId()
internal let loggerLevel: LoggerLevel

// Can be configured
internal var region: ConfidenceRegion = .global
Expand All @@ -280,13 +300,14 @@ extension Confidence {
/**
Initializes the builder with the given credentails.
*/
public init(clientSecret: String) {
public init(clientSecret: String, loggerLevel: LoggerLevel = .WARN) {
self.clientSecret = clientSecret
do {
eventStorage = try EventStorageImpl()
} catch {
eventStorage = EventStorageInMemory()
}
self.loggerLevel = loggerLevel
}

internal func withFlagResolverClient(flagResolver: ConfidenceResolveClient) -> Builder {
Expand Down Expand Up @@ -320,6 +341,13 @@ extension Confidence {
}

public func build() -> Confidence {
var debugLogger: DebugLogger?
if loggerLevel != LoggerLevel.NONE {
debugLogger = DebugLoggerImpl(loggerLevel: loggerLevel)
debugLogger?.logContext(action: "InitialContext", context: initialContext)
} else {
debugLogger = nil
}
let options = ConfidenceClientOptions(
credentials: ConfidenceClientCredentials.clientSecret(secret: clientSecret),
region: region)
Expand All @@ -335,7 +363,8 @@ extension Confidence {
httpClient: httpClient,
storage: DefaultStorage(filePath: "confidence.flags.apply"),
options: options,
metadata: metadata
metadata: metadata,
debugLogger: debugLogger
)
let flagResolver = flagResolver ?? RemoteConfidenceResolveClient(
options: options,
Expand All @@ -345,7 +374,9 @@ extension Confidence {
let eventSenderEngine = EventSenderEngineImpl(
clientSecret: clientSecret,
uploader: uploader,
storage: eventStorage)
storage: eventStorage,
debugLogger: debugLogger
)
return Confidence(
clientSecret: clientSecret,
region: region,
Expand All @@ -355,7 +386,8 @@ extension Confidence {
storage: storage ?? DefaultStorage(filePath: "confidence.flags.resolve"),
context: initialContext,
parent: nil,
visitorId: visitorId
visitorId: visitorId,
debugLogger: debugLogger
)
}
}
Expand Down
69 changes: 69 additions & 0 deletions Sources/Confidence/DebugLogger.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import Foundation
import OSLog

internal protocol DebugLogger {
func logEvent(action: String, event: ConfidenceEvent?)
func logMessage(message: String, isWarning: Bool)
func logFlags(action: String, flag: String)
func logContext(action: String, context: ConfidenceStruct)
}

private extension Logger {
private static var subsystem = Bundle.main.bundleIdentifier

static let confidence = Logger(subsystem: subsystem ?? "", category: "confidence")
}

internal class DebugLoggerImpl: DebugLogger {
private let loggerLevel: LoggerLevel

init(loggerLevel: LoggerLevel) {
self.loggerLevel = loggerLevel
}

func logMessage(message: String, isWarning: Bool = false) {
if isWarning {
log(messageLevel: .WARN, message: message)
} else {
log(messageLevel: .DEBUG, message: message)
}
}

func logEvent(action: String, event: ConfidenceEvent?) {
log(messageLevel: .DEBUG, message: "[\(action)] \(event?.name ?? "")")
}

func logFlags(action: String, flag: String) {
log(messageLevel: .TRACE, message: "[\(action)] \(flag)")
}

func logContext(action: String, context: ConfidenceStruct) {
log(messageLevel: .TRACE, message: "[\(action)] \(context)")
}

private func log(messageLevel: LoggerLevel, message: String) {
if messageLevel >= loggerLevel {
switch messageLevel {
case .TRACE:
Logger.confidence.trace("\(message)")
case .DEBUG:
Logger.confidence.debug("\(message)")
case .WARN:
Logger.confidence.warning("\(message)")
case .ERROR:
Logger.confidence.error("\(message)")
case .NONE:
// do nothing
break
}
}
}
}

public enum LoggerLevel: Comparable {
case TRACE
case DEBUG
case WARN
case ERROR
case NONE
}
29 changes: 22 additions & 7 deletions Sources/Confidence/EventSenderEngine.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,11 @@ protocol FlushPolicy {
}

protocol EventSenderEngine {
func emit(eventName: String, data: ConfidenceStruct, context: ConfidenceStruct) throws
func emit(
eventName: String,
data: ConfidenceStruct,
context: ConfidenceStruct
) throws
func shutdown()
func flush()
}
Expand All @@ -25,18 +29,21 @@ final class EventSenderEngineImpl: EventSenderEngine {
private let payloadMerger: PayloadMerger = PayloadMergerImpl()
private let semaphore = DispatchSemaphore(value: 1)
private let writeQueue: DispatchQueue
private let debugLogger: DebugLogger?

convenience init(
clientSecret: String,
uploader: ConfidenceClient,
storage: EventStorage
storage: EventStorage,
debugLogger: DebugLogger?
) {
self.init(
clientSecret: clientSecret,
uploader: uploader,
storage: storage,
flushPolicies: [SizeFlushPolicy(batchSize: 10)],
writeQueue: DispatchQueue(label: "ConfidenceWriteQueue")
writeQueue: DispatchQueue(label: "ConfidenceWriteQueue"),
debugLogger: debugLogger
)
}

Expand All @@ -45,13 +52,15 @@ final class EventSenderEngineImpl: EventSenderEngine {
uploader: ConfidenceClient,
storage: EventStorage,
flushPolicies: [FlushPolicy],
writeQueue: DispatchQueue
writeQueue: DispatchQueue,
debugLogger: DebugLogger?
) {
self.uploader = uploader
self.clientSecret = clientSecret
self.storage = storage
self.flushPolicies = flushPolicies + [ManualFlushPolicy()]
self.writeQueue = writeQueue
self.debugLogger = debugLogger

writeReqChannel
.receive(on: self.writeQueue)
Expand Down Expand Up @@ -119,16 +128,22 @@ final class EventSenderEngineImpl: EventSenderEngine {
semaphore.signal()
}

func emit(eventName: String, data: ConfidenceStruct, context: ConfidenceStruct) throws {
writeReqChannel.send(ConfidenceEvent(
func emit(
eventName: String,
data: ConfidenceStruct,
context: ConfidenceStruct
) throws {
let event = ConfidenceEvent(
name: eventName,
payload: try payloadMerger.merge(context: context, data: data),
eventTime: Date.backport.now)
)
writeReqChannel.send(event)
debugLogger?.logEvent(action: "Emitting event", event: event)
}

func flush() {
writeReqChannel.send(manualFlushEvent)
debugLogger?.logEvent(action: "Event flushed", event: nil)
}

func shutdown() {
Expand Down
Loading

0 comments on commit c8fe939

Please sign in to comment.