diff --git a/Clickstream.xcodeproj/project.pbxproj b/Clickstream.xcodeproj/project.pbxproj index e351748..4829ca0 100644 --- a/Clickstream.xcodeproj/project.pbxproj +++ b/Clickstream.xcodeproj/project.pbxproj @@ -13,6 +13,8 @@ 9A0BBA882BC3A59300F7DC01 /* URLRequest+Equality.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A0BBA872BC3A59300F7DC01 /* URLRequest+Equality.swift */; }; 9A0BBA892BC3A59300F7DC01 /* URLRequest+Equality.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A0BBA872BC3A59300F7DC01 /* URLRequest+Equality.swift */; }; 9A0BBA8A2BC3C98000F7DC01 /* SortedArray.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDE467EA2A5BD72000BFA976 /* SortedArray.swift */; }; + 9A0BBA8D2BC3CA5500F7DC01 /* PerformanceTracer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A0BBA8C2BC3CA5500F7DC01 /* PerformanceTracer.swift */; }; + 9A0BBA8E2BC3CA5500F7DC01 /* PerformanceTracer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A0BBA8C2BC3CA5500F7DC01 /* PerformanceTracer.swift */; }; BD6BDB6829FBC1360006B04A /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = BD6BDB3E29FBC1360006B04A /* Assets.xcassets */; }; BD6BDB6929FBC1360006B04A /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = BD6BDB3E29FBC1360006B04A /* Assets.xcassets */; }; BD6BDB6A29FBC1360006B04A /* RadioLabelTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD6BDB4129FBC1360006B04A /* RadioLabelTableViewCell.swift */; }; @@ -223,6 +225,7 @@ 68E7BD1B2456E6F10072549A /* ClickstreamTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ClickstreamTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 88F9204101C5E8D41805FD1A /* Pods-ClickstreamTests.production.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ClickstreamTests.production.xcconfig"; path = "Target Support Files/Pods-ClickstreamTests/Pods-ClickstreamTests.production.xcconfig"; sourceTree = ""; }; 9A0BBA872BC3A59300F7DC01 /* URLRequest+Equality.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "URLRequest+Equality.swift"; sourceTree = ""; }; + 9A0BBA8C2BC3CA5500F7DC01 /* PerformanceTracer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PerformanceTracer.swift; sourceTree = ""; }; 9D31FA91B32979CBBA2C3849 /* Pods-ClickstreamTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ClickstreamTests.debug.xcconfig"; path = "Target Support Files/Pods-ClickstreamTests/Pods-ClickstreamTests.debug.xcconfig"; sourceTree = ""; }; A870FFC059A664D0DF0292E5 /* Pods-ClickstreamTests.alpha.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ClickstreamTests.alpha.xcconfig"; path = "Target Support Files/Pods-ClickstreamTests/Pods-ClickstreamTests.alpha.xcconfig"; sourceTree = ""; }; A8F29FD3E426DD04C26B8560 /* Pods-ClickstreamTests.integration.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ClickstreamTests.integration.xcconfig"; path = "Target Support Files/Pods-ClickstreamTests/Pods-ClickstreamTests.integration.xcconfig"; sourceTree = ""; }; @@ -395,6 +398,14 @@ name = Products; sourceTree = ""; }; + 9A0BBA8B2BC3CA4000F7DC01 /* Performance */ = { + isa = PBXGroup; + children = ( + 9A0BBA8C2BC3CA5500F7DC01 /* PerformanceTracer.swift */, + ); + path = Performance; + sourceTree = ""; + }; 9AAFBB13247CBA15009BFDFB /* Recovered References */ = { isa = PBXGroup; children = ( @@ -491,6 +502,7 @@ BD6BDB5429FBC1360006B04A /* Tracker */ = { isa = PBXGroup; children = ( + 9A0BBA8B2BC3CA4000F7DC01 /* Performance */, BD6BDB5529FBC1360006B04A /* Health */, BD6BDB5929FBC1360006B04A /* Tracker.swift */, BD6BDB5A29FBC1360006B04A /* Utilities */, @@ -1221,6 +1233,7 @@ buildActionMask = 2147483647; files = ( BD6BDB7229FBC1360006B04A /* EventVisualizerLandingViewController.swift in Sources */, + 9A0BBA8D2BC3CA5500F7DC01 /* PerformanceTracer.swift in Sources */, BD6BDB7629FBC1360006B04A /* EventsListViewModel.swift in Sources */, BD6BDB7429FBC1360006B04A /* EventsListViewController.swift in Sources */, BDE468232A5BD72000BFA976 /* JSONStringDecoder.swift in Sources */, @@ -1323,6 +1336,7 @@ BDE4677A2A5BD24500BFA976 /* DispatchQueue+Detection.swift in Sources */, BD6BDB9B29FBC1360006B04A /* ClickStreamHealthConfigurations.swift in Sources */, BDE467882A5BD24500BFA976 /* DeviceStatusNotifierTests.swift in Sources */, + 9A0BBA8E2BC3CA5500F7DC01 /* PerformanceTracer.swift in Sources */, BDE467852A5BD24500BFA976 /* NetworkManagerDependenciesTests.swift in Sources */, BDE467822A5BD24500BFA976 /* EventProcessorDependenciesTests.swift in Sources */, BDE467872A5BD24500BFA976 /* KeepAliveServiceTests.swift in Sources */, diff --git a/Sources/Clickstream/NetworkManager/Infrastructure/Sockets/SocketHandler.swift b/Sources/Clickstream/NetworkManager/Infrastructure/Sockets/SocketHandler.swift index dbed456..94204a3 100644 --- a/Sources/Clickstream/NetworkManager/Infrastructure/Sockets/SocketHandler.swift +++ b/Sources/Clickstream/NetworkManager/Infrastructure/Sockets/SocketHandler.swift @@ -37,8 +37,9 @@ final class DefaultSocketHandler: SocketHandler { private var writeCallback: ((Result) -> Void)? /// Tracking time taken by the socket to establish a connection - /// TODO:Abhijeet to fix -// private var socketConnectionTimeTrace: Trace = Trace(name: ClickStreamDebugConstants.Traces.ClickstreamSocketConnectionTime.rawValue) + #if TRACKER_ENABLED + private var socketConnectionTimeTrace: Trace = Trace(name: TrackerConstant.Traces.ClickstreamSocketConnectionTime.rawValue) + #endif /// Provides the socket state var isConnected: Atomic = Atomic(false) @@ -104,8 +105,10 @@ final class DefaultSocketHandler: SocketHandler { print("socket-connecting") isConnectionRequestOpen = true connectionCallback?(.success(.connecting)) -// socketConnectionTimeTrace.attributes = [Constants.Strings.networkType: Reachability.getNetworkType().trackingId] -// socketConnectionTimeTrace.start() + #if TRACKER_ENABLED + socketConnectionTimeTrace.attributes = [Constants.Strings.networkType: Reachability.getNetworkType().trackingId] + socketConnectionTimeTrace.start() + #endif webSocket?.connect() lastConnectRequestTimestamp = Date() // recording time } @@ -124,9 +127,11 @@ final class DefaultSocketHandler: SocketHandler { } deinit { -// socketConnectionTimeTrace.attributes = [Constants.Strings.networkType: Reachability.getNetworkType().trackingId, -// Constants.Strings.status: Constants.Strings.failure] -// socketConnectionTimeTrace.stop() + #if TRACKER_ENABLED + socketConnectionTimeTrace.attributes = [Constants.Strings.networkType: Reachability.getNetworkType().trackingId, + Constants.Strings.status: Constants.Strings.failure] + socketConnectionTimeTrace.stop() + #endif print("socket-deinit") } } @@ -175,31 +180,34 @@ extension DefaultSocketHandler { } checkedSelf.isConnectionRequestOpen = false checkedSelf.connectionCallback?(.success(.connected)) -// checkedSelf.socketConnectionTimeTrace.attributes = [Constants.Strings.networkType: Reachability.getNetworkType().trackingId, -// Constants.Strings.status: Constants.Strings.success] -// checkedSelf.socketConnectionTimeTrace.stop() - case .disconnected(_, let code): + #if TRACKER_ENABLED + checkedSelf.socketConnectionTimeTrace.attributes = [Constants.Strings.networkType: Reachability.getNetworkType().trackingId, + Constants.Strings.status: Constants.Strings.success] + checkedSelf.socketConnectionTimeTrace.stop() + #endif + case .disconnected(let error, let code): + print("disconnected with error: \(error) errorCode: \(code)", .critical) Clickstream.connectionState = .closed -// checkedSelf.trackHealthEvent(eventName: .ClickstreamConnectionFailed, code: code) checkedSelf.isConnectionRequestOpen = false -// checkedSelf.socketConnectionTimeTrace.attributes = [Constants.Strings.networkType: Reachability.getNetworkType().trackingId, -// Constants.Strings.status: Constants.Strings.failure] -// checkedSelf.socketConnectionTimeTrace.stop() + #if TRACKER_ENABLED + checkedSelf.socketConnectionTimeTrace.attributes = [Constants.Strings.networkType: Reachability.getNetworkType().trackingId, + Constants.Strings.status: Constants.Strings.failure] + checkedSelf.socketConnectionTimeTrace.stop() + let errorObject = NSError(domain: "", code: Int(code), userInfo: [NSLocalizedDescriptionKey: error]) + checkedSelf.trackHealthEvent(eventName: .ClickstreamConnectionDropped, error: errorObject, code: code) + #endif case .text(let responseString): checkedSelf.writeCallback?(.success(responseString.data(using: .utf8))) case .error(let error): checkedSelf.isConnectionRequestOpen = false - // checkedSelf.socketConnectionTimeTrace.attributes = [Constants.Strings.networkType: Reachability.getNetworkType().trackingId, - // Constants.Strings.status: Constants.Strings.failure] - // checkedSelf.socketConnectionTimeTrace.stop() - checkedSelf.writeCallback?(.failure(.failed)) - #if TRACKER_ENABLED - if Tracker.debugMode { + checkedSelf.socketConnectionTimeTrace.attributes = [Constants.Strings.networkType: Reachability.getNetworkType().trackingId, + Constants.Strings.status: Constants.Strings.failure] + checkedSelf.socketConnectionTimeTrace.stop() let timeInterval = Date().timeIntervalSince(checkedSelf.lastConnectRequestTimestamp ?? Date()) self?.trackHealthEvent(eventName: .ClickstreamConnectionFailure, error: error, timeToConnection: ("\(timeInterval)")) - } #endif + checkedSelf.writeCallback?(.failure(.failed)) case .binary(let response): checkedSelf.writeCallback?(.success(response)) case .cancelled: @@ -272,6 +280,10 @@ extension DefaultSocketHandler { let event = HealthAnalysisEvent(eventName: eventName, reason: FailureReason.DuplicateID.rawValue) Tracker.sharedInstance?.record(event: event) + } else { + let event = HealthAnalysisEvent(eventName: eventName, + reason: error?.localizedDescription) + Tracker.sharedInstance?.record(event: event) } } #endif diff --git a/Sources/Tracker/Performance/PerformanceTracer.swift b/Sources/Tracker/Performance/PerformanceTracer.swift new file mode 100644 index 0000000..5619d2b --- /dev/null +++ b/Sources/Tracker/Performance/PerformanceTracer.swift @@ -0,0 +1,47 @@ +// +// PerformanceTracer.swift +// Clickstream +// +// Created by Abhijeet Mallick on 08/04/24. +// Copyright © 2024 Gojek. All rights reserved. +// + +import Foundation + +protocol Traceable { + mutating func start() + mutating func stop() +} + +struct Trace: Traceable { + + private var isRunning: Bool = false + + var name: String + var attributes: [String:Any] + + init(name: String, + attributes:[String:Any] = [:]) { + self.name = name + self.attributes = attributes + } + + mutating func start() { + if Tracker.debugMode && isRunning == false { + let notificationDict: [String:Any] = [TrackerConstant.traceName: name, + TrackerConstant.traceAttributes: attributes] + NotificationCenter.default.post(name: TrackerConstant.TraceStartNotification, object: notificationDict) + isRunning = true + } + } + + mutating func stop() { + if Tracker.debugMode && isRunning { + isRunning = false + let notificationDict: [String:Any] = [TrackerConstant.traceName: name, + TrackerConstant.traceAttributes: attributes] + NotificationCenter.default.post(name: TrackerConstant.TraceStopNotification, object: notificationDict) + } + } +} + diff --git a/Sources/Tracker/Utilities/TrackerConstants.swift b/Sources/Tracker/Utilities/TrackerConstants.swift index f9338a2..6c3776d 100644 --- a/Sources/Tracker/Utilities/TrackerConstants.swift +++ b/Sources/Tracker/Utilities/TrackerConstants.swift @@ -49,6 +49,14 @@ enum FailureReason: String, Codable, CaseIterable { } public struct TrackerConstant { + + internal enum Traces: String { + case ClickstreamSocketConnectionTime = "ClickstreamSocketConnectionTimeTrace" + } + + public static var traceName = "traceName" + public static var traceAttributes = "traceAttributes" + //TODO: Added Traces after merging Health tracking public static let DebugEventsNotification = NSNotification.Name(rawValue: "ClickstreamDebugNotifications") public static let TraceStartNotification = NSNotification.Name(rawValue: "ClickstreamTraceStartNotification")