Skip to content

Commit f28b137

Browse files
Merge pull request #27 from checkout/PRISM-11093-log-the-breakdown-of-latency-metrics-by-operations-in-the-respective-events-on-risk-i-os
PRISM-11093 - Add latency metrics to logs
2 parents 912697c + 9c654ff commit f28b137

8 files changed

+99
-38
lines changed

Sources/Risk/Logging/LoggerService.swift

+33-6
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,18 @@ enum RiskEvent: String, Codable {
1717
case loadFailure = "riskLoadFailure"
1818
}
1919

20+
struct Elapsed {
21+
let block: Double? // time to retrieve configuration or loadFailure
22+
let deviceDataPersist: Double? // time to persist data
23+
let fpload: Double? // time to load fingerprint
24+
let fppublish: Double? // time to publish fingerprint data
25+
let total: Double? // total time
26+
27+
private enum CodingKeys: String, CodingKey {
28+
case block = "Block", deviceDataPersist = "DeviceDataPersist", fpload = "FpLoad", fppublish = "FpPublish", total = "Total"
29+
}
30+
}
31+
2032
struct RiskLogError {
2133
let reason: String // service method
2234
let message: String // description of error
@@ -30,11 +42,11 @@ struct RiskLogError {
3042

3143
protocol LoggerServiceProtocol {
3244
init(internalConfig: RiskSDKInternalConfig)
33-
func log(riskEvent: RiskEvent, deviceSessionId: String?, requestId: String?, error: RiskLogError?)
45+
func log(riskEvent: RiskEvent, blockTime: Double?, deviceDataPersistTime: Double?, fpLoadTime: Double?, fpPublishTime: Double?, deviceSessionId: String?, requestId: String?, error: RiskLogError?)
3446
}
3547

3648
extension LoggerServiceProtocol {
37-
func formatEvent(internalConfig: RiskSDKInternalConfig, riskEvent: RiskEvent, deviceSessionId: String?, requestId: String?, error: RiskLogError?) -> Event {
49+
func formatEvent(internalConfig: RiskSDKInternalConfig, riskEvent: RiskEvent, deviceSessionId: String?, requestId: String?, error: RiskLogError?, latencyMetric: Elapsed) -> Event {
3850
let maskedPublicKey = getMaskedPublicKey(publicKey: internalConfig.merchantPublicKey)
3951
let ddTags = getDDTags(environment: internalConfig.environment.rawValue)
4052
var monitoringLevel: MonitoringLevel
@@ -56,17 +68,27 @@ extension LoggerServiceProtocol {
5668
switch riskEvent {
5769
case .published, .collected:
5870
properties = [
71+
"Block": AnyCodable(latencyMetric.block),
72+
"DeviceDataPersist": AnyCodable(latencyMetric.deviceDataPersist),
73+
"FpLoad": AnyCodable(latencyMetric.fpload),
74+
"FpPublish": AnyCodable(latencyMetric.fppublish),
75+
"Total": AnyCodable(latencyMetric.total),
5976
"EventType": AnyCodable(riskEvent.rawValue),
6077
"FramesMode": AnyCodable(internalConfig.framesMode),
6178
"MaskedPublicKey": AnyCodable(maskedPublicKey),
6279
"ddTags": AnyCodable(ddTags),
6380
"RiskSDKVersion": AnyCodable(Constants.riskSdkVersion),
6481
"Timezone": AnyCodable(TimeZone.current.identifier),
65-
"RequestId": AnyCodable(requestId),
82+
"FpRequestId": AnyCodable(requestId),
6683
"DeviceSessionId": AnyCodable(deviceSessionId),
6784
]
6885
case .publishFailure, .loadFailure, .publishDisabled:
6986
properties = [
87+
"Block": AnyCodable(latencyMetric.block),
88+
"DeviceDataPersist": AnyCodable(latencyMetric.deviceDataPersist),
89+
"FpLoad": AnyCodable(latencyMetric.fpload),
90+
"FpPublish": AnyCodable(latencyMetric.fppublish),
91+
"Total": AnyCodable(latencyMetric.total),
7092
"EventType": AnyCodable(riskEvent.rawValue),
7193
"FramesMode": AnyCodable(internalConfig.framesMode),
7294
"MaskedPublicKey": AnyCodable(maskedPublicKey),
@@ -144,11 +166,16 @@ struct LoggerService: LoggerServiceProtocol {
144166

145167
}
146168

147-
func log(riskEvent: RiskEvent, deviceSessionId: String? = nil, requestId: String? = nil, error: RiskLogError? = nil) {
148-
let event = formatEvent(internalConfig: internalConfig, riskEvent: riskEvent, deviceSessionId: deviceSessionId, requestId: requestId, error: error)
169+
func log(riskEvent: RiskEvent, blockTime: Double? = nil, deviceDataPersistTime: Double? = nil, fpLoadTime: Double? = nil, fpPublishTime: Double? = nil, deviceSessionId: String? = nil, requestId: String? = nil, error: RiskLogError? = nil) {
170+
171+
let totalLatency = (blockTime ?? 0.00) + (deviceDataPersistTime ?? 0.00) + (fpLoadTime ?? 0.00) + (fpPublishTime ?? 0.00)
172+
173+
let latencyMetric = Elapsed(block: blockTime, deviceDataPersist: deviceDataPersistTime, fpload: fpLoadTime, fppublish: fpPublishTime, total: totalLatency)
174+
175+
let event = formatEvent(internalConfig: internalConfig, riskEvent: riskEvent, deviceSessionId: deviceSessionId, requestId: requestId, error: error, latencyMetric: latencyMetric)
149176
logger.log(event: event)
150177
}
151-
178+
152179
private func getDeviceModel() -> String {
153180
#if targetEnvironment(simulator)
154181
if let identifier = ProcessInfo().environment["SIMULATOR_MODEL_IDENTIFIER"] {

Sources/Risk/Risk.swift

+6-5
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,8 @@ public final class Risk {
3030
self.fingerprintService = FingerprintService(
3131
fingerprintPublicKey: configuration.publicKey,
3232
internalConfig: self.internalConfig,
33-
loggerService: self.loggerService
33+
loggerService: self.loggerService,
34+
blockTime: configuration.blockTime
3435
)
3536
completion(.success(()))
3637

@@ -50,17 +51,17 @@ public final class Risk {
5051
guard let self = self else { return }
5152

5253
switch fpResult {
53-
case .success(let requestId):
54-
self.persistFpData(cardToken: cardToken, fingerprintRequestId: requestId, completion: completion)
54+
case .success(let response):
55+
self.persistFpData(cardToken: cardToken, fingerprintRequestId: response.requestId, fpLoadTime: response.fpLoadTime, fpPublishTime: response.fpPublishTime, completion: completion)
5556

5657
case .failure(let error):
5758
completion(.failure(error))
5859
}
5960
}
6061
}
6162

62-
private func persistFpData(cardToken: String?, fingerprintRequestId: String, completion: @escaping (Result<PublishRiskData, RiskError.Publish>) -> Void) {
63-
self.deviceDataService.persistFpData(fingerprintRequestId: fingerprintRequestId, cardToken: cardToken) { result in
63+
private func persistFpData(cardToken: String?, fingerprintRequestId: String, fpLoadTime: Double, fpPublishTime: Double, completion: @escaping (Result<PublishRiskData, RiskError.Publish>) -> Void) {
64+
self.deviceDataService.persistFpData(fingerprintRequestId: fingerprintRequestId, fpLoadTime: fpLoadTime, fpPublishTime: fpPublishTime, cardToken: cardToken) { result in
6465
switch result {
6566
case .success(let response):
6667
completion(.success(PublishRiskData(deviceSessionId: response.deviceSessionId)))

Sources/Risk/Services/DeviceDataService.swift

+18-10
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
//
88

99
import Foundation
10+
import QuartzCore
1011

1112
struct FingerprintIntegration: Decodable, Equatable {
1213
let enabled: Bool
@@ -15,6 +16,7 @@ struct FingerprintIntegration: Decodable, Equatable {
1516

1617
struct FingerprintConfiguration: Equatable {
1718
let publicKey: String
19+
let blockTime: Double
1820
}
1921

2022
struct DeviceDataConfiguration: Decodable, Equatable {
@@ -42,46 +44,50 @@ struct PersistDeviceDataResponse: Decodable, Equatable {
4244

4345
protocol DeviceDataServiceProtocol {
4446
func getConfiguration(completion: @escaping (Result<FingerprintConfiguration, RiskError.Configuration>) -> Void)
45-
func persistFpData(fingerprintRequestId: String, cardToken: String?, completion: @escaping (Result<PersistDeviceDataResponse, RiskError.Publish>) -> Void)
47+
func persistFpData(fingerprintRequestId: String, fpLoadTime: Double, fpPublishTime: Double, cardToken: String?, completion: @escaping (Result<PersistDeviceDataResponse, RiskError.Publish>) -> Void)
4648
}
4749

48-
struct DeviceDataService: DeviceDataServiceProtocol {
50+
final class DeviceDataService: DeviceDataServiceProtocol {
4951
let config: RiskSDKInternalConfig
5052
let apiService: APIServiceProtocol
5153
let loggerService: LoggerServiceProtocol
54+
var blockTime: Double
5255

5356
init(config: RiskSDKInternalConfig, apiService: APIServiceProtocol = APIService(), loggerService: LoggerServiceProtocol) {
5457
self.config = config
5558
self.apiService = apiService
5659
self.loggerService = loggerService
60+
self.blockTime = 0.00
5761
}
5862

5963
func getConfiguration(completion: @escaping (Result<FingerprintConfiguration, RiskError.Configuration>) -> Void) {
64+
let startBlockTime = CACurrentMediaTime()
6065
let endpoint = "\(config.deviceDataEndpoint)/configuration?integrationType=\(config.integrationType.rawValue)&riskSdkVersion=\(Constants.riskSdkVersion)&timezone=\(TimeZone.current.identifier)"
6166
let authToken = config.merchantPublicKey
6267

6368
apiService.getJSONFromAPIWithAuthorization(endpoint: endpoint, authToken: authToken, responseType: DeviceDataConfiguration.self) {
6469
result in
6570
switch result {
6671
case .success(let configuration):
67-
72+
let endBlockTime = CACurrentMediaTime()
73+
self.blockTime = (endBlockTime - startBlockTime) * 1000
6874
guard configuration.fingerprintIntegration.enabled, let fingerprintPublicKey = configuration.fingerprintIntegration.publicKey else {
69-
loggerService.log(riskEvent: .publishDisabled, deviceSessionId: nil, requestId: nil, error: RiskLogError(reason: "getConfiguration", message: RiskError.Configuration.integrationDisabled.localizedDescription, status: nil, type: "Error"))
75+
self.loggerService.log(riskEvent: .publishDisabled, blockTime: self.blockTime, deviceDataPersistTime: nil, fpLoadTime: nil, fpPublishTime: nil, deviceSessionId: nil, requestId: nil, error: RiskLogError(reason: "getConfiguration", message: RiskError.Configuration.integrationDisabled.localizedDescription, status: nil, type: "Error"))
7076

7177
return completion(.failure(.integrationDisabled))
7278
}
7379

7480
completion(.success(
75-
FingerprintConfiguration.init(publicKey: fingerprintPublicKey)))
81+
FingerprintConfiguration.init(publicKey: fingerprintPublicKey, blockTime: self.blockTime)))
7682
case .failure(let error):
77-
78-
loggerService.log(riskEvent: .loadFailure, deviceSessionId: nil, requestId: nil, error: RiskLogError(reason: "getConfiguration", message: error.localizedDescription, status: nil, type: "Error"))
83+
self.loggerService.log(riskEvent: .loadFailure, blockTime: nil, deviceDataPersistTime: nil, fpLoadTime: nil, fpPublishTime: nil, deviceSessionId: nil, requestId: nil, error: RiskLogError(reason: "getConfiguration", message: error.localizedDescription, status: nil, type: "Error"))
7984
return completion(.failure(.couldNotRetrieveConfiguration))
8085
}
8186
}
8287
}
8388

84-
func persistFpData(fingerprintRequestId: String, cardToken: String?, completion: @escaping (Result<PersistDeviceDataResponse, RiskError.Publish>) -> Void) {
89+
func persistFpData(fingerprintRequestId: String, fpLoadTime: Double, fpPublishTime: Double, cardToken: String?, completion: @escaping (Result<PersistDeviceDataResponse, RiskError.Publish>) -> Void) {
90+
let startPersistTime = CACurrentMediaTime()
8591
let endpoint = "\(config.deviceDataEndpoint)/fingerprint?riskSdkVersion=\(Constants.riskSdkVersion)"
8692
let authToken = config.merchantPublicKey
8793
let integrationType = config.integrationType
@@ -96,11 +102,13 @@ struct DeviceDataService: DeviceDataServiceProtocol {
96102

97103
switch result {
98104
case .success(let response):
99-
loggerService.log(riskEvent: .published, deviceSessionId: response.deviceSessionId, requestId: fingerprintRequestId, error: nil)
105+
let endPersistTime = CACurrentMediaTime()
106+
let persistTime = (endPersistTime - startPersistTime) * 1000
107+
self.loggerService.log(riskEvent: .published, blockTime: self.blockTime, deviceDataPersistTime: persistTime, fpLoadTime: fpLoadTime, fpPublishTime: fpPublishTime, deviceSessionId: response.deviceSessionId, requestId: fingerprintRequestId, error: nil)
100108

101109
completion(.success(response))
102110
case .failure(let error):
103-
loggerService.log(riskEvent: .publishFailure, deviceSessionId: nil, requestId: nil, error: RiskLogError(reason: "persistFpData", message: error.localizedDescription, status: nil, type: "Error"))
111+
self.loggerService.log(riskEvent: .publishFailure, blockTime: self.blockTime, deviceDataPersistTime: nil, fpLoadTime: fpLoadTime, fpPublishTime: fpPublishTime, deviceSessionId: nil, requestId: nil, error: RiskLogError(reason: "persistFpData", message: error.localizedDescription, status: nil, type: "Error"))
104112

105113
completion(.failure(.couldNotPersisRiskData))
106114
}

Sources/Risk/Services/FingerprintService.swift

+27-7
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,10 @@
77

88
import FingerprintPro
99
import Foundation
10+
import QuartzCore
1011

1112
protocol FingerprintServiceProtocol {
12-
func publishData(completion: @escaping (Result<String, RiskError.Publish>) -> Void)
13+
func publishData(completion: @escaping (Result<FpPublishData, RiskError.Publish>) -> Void)
1314
}
1415

1516
extension FingerprintServiceProtocol {
@@ -22,24 +23,41 @@ extension FingerprintServiceProtocol {
2223
}
2324
}
2425

26+
struct FpPublishData {
27+
let requestId: String
28+
let fpLoadTime: Double
29+
let fpPublishTime: Double
30+
}
31+
2532
final class FingerprintService: FingerprintServiceProtocol {
2633
private var requestId: String?
2734
private let client: FingerprintClientProviding
2835
private let internalConfig: RiskSDKInternalConfig
2936
private let loggerService: LoggerServiceProtocol
37+
private let fpLoadTime: Double
38+
private var fpPublishTime: Double
39+
private let blockTime: Double
3040

31-
init(fingerprintPublicKey: String, internalConfig: RiskSDKInternalConfig, loggerService: LoggerServiceProtocol) {
41+
init(fingerprintPublicKey: String, internalConfig: RiskSDKInternalConfig, loggerService: LoggerServiceProtocol, blockTime: Double) {
42+
43+
let startBlockTime = CACurrentMediaTime()
44+
3245
let customDomain: Region = .custom(domain: internalConfig.fingerprintEndpoint)
3346
let configuration = Configuration(apiKey: fingerprintPublicKey, region: customDomain)
3447
client = FingerprintProFactory.getInstance(configuration)
48+
let endBlockTime = CACurrentMediaTime()
49+
self.fpLoadTime = (endBlockTime - startBlockTime) * 1000
50+
self.fpPublishTime = 0.00
51+
self.blockTime = blockTime
3552
self.internalConfig = internalConfig
3653
self.loggerService = loggerService
3754
}
3855

39-
func publishData(completion: @escaping (Result<String, RiskError.Publish>) -> Void) {
56+
func publishData(completion: @escaping (Result<FpPublishData, RiskError.Publish>) -> Void) {
57+
let startFpPublishTime = CACurrentMediaTime()
4058

4159
guard requestId == nil else {
42-
return completion(.success(requestId!))
60+
return completion(.success(FpPublishData(requestId: requestId!, fpLoadTime: self.fpLoadTime, fpPublishTime: self.fpPublishTime)))
4361
}
4462

4563
let metadata = createMetadata(sourceType: internalConfig.sourceType.rawValue)
@@ -48,14 +66,16 @@ final class FingerprintService: FingerprintServiceProtocol {
4866

4967
switch result {
5068
case .failure(let error):
51-
self?.loggerService.log(riskEvent: .publishFailure, deviceSessionId: nil, requestId: nil, error: RiskLogError(reason: "publishData", message: error.localizedDescription, status: nil, type: "Error"))
69+
self?.loggerService.log(riskEvent: .publishFailure, blockTime: self?.blockTime, deviceDataPersistTime: nil, fpLoadTime: self?.fpLoadTime, fpPublishTime: nil, deviceSessionId: nil, requestId: nil, error: RiskLogError(reason: "publishData", message: error.localizedDescription, status: nil, type: "Error"))
5270

5371
return completion(.failure(.couldNotPublishRiskData))
5472
case let .success(response):
55-
self?.loggerService.log(riskEvent: .collected, deviceSessionId: nil, requestId: response.requestId, error: nil)
73+
let endFpPublishTime = CACurrentMediaTime()
74+
self?.fpPublishTime = (endFpPublishTime - startFpPublishTime) * 1000
75+
self?.loggerService.log(riskEvent: .collected, blockTime: self?.blockTime, deviceDataPersistTime: nil, fpLoadTime: self?.fpLoadTime, fpPublishTime: self?.fpPublishTime, deviceSessionId: nil, requestId: response.requestId, error: nil)
5676
self?.requestId = response.requestId
5777

58-
completion(.success(response.requestId))
78+
completion(.success(FpPublishData(requestId: response.requestId, fpLoadTime: self?.fpLoadTime ?? 0.00, fpPublishTime: self?.fpPublishTime ?? 0.00)))
5979
}
6080
}
6181
}

Tests/Mocks/MockDeviceDataService.swift

+3-3
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,14 @@ class MockDeviceDataService: DeviceDataServiceProtocol {
1414

1515
func getConfiguration(completion: @escaping (Result<FingerprintConfiguration, RiskError.Configuration>) -> Void) {
1616
if shouldReturnConfiguration {
17-
let configuration = FingerprintConfiguration(publicKey: "mocked_public_key")
17+
let configuration = FingerprintConfiguration(publicKey: "mocked_public_key", blockTime: 123.00)
1818
completion(.success(configuration))
1919
} else {
2020
completion(.failure(.couldNotRetrieveConfiguration))
2121
}
2222
}
23-
24-
func persistFpData(fingerprintRequestId: String, cardToken: String?, completion: @escaping (Result<PersistDeviceDataResponse, RiskError.Publish>) -> Void) {
23+
24+
func persistFpData(fingerprintRequestId: String, fpLoadTime: Double, fpPublishTime: Double, cardToken: String?, completion: @escaping (Result<PersistDeviceDataResponse, RiskError.Publish>) -> Void) {
2525
if shouldSucceedPersistFpData {
2626
let response = PersistDeviceDataResponse(deviceSessionId: "mocked_device_session_id")
2727
completion(.success(response))

Tests/Mocks/MockFingerprintService.swift

+3-3
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,13 @@ class MockFingerprintService: FingerprintServiceProtocol {
1212
var shouldSucceed: Bool = true
1313
var requestId: String?
1414

15-
func publishData(completion: @escaping (Result<String, RiskError.Publish>) -> Void) {
15+
func publishData(completion: @escaping (Result<FpPublishData, RiskError.Publish>) -> Void) {
1616
if shouldSucceed {
1717
if let requestId = requestId {
18-
completion(.success(requestId))
18+
completion(.success(FpPublishData(requestId: requestId, fpLoadTime: 123.00, fpPublishTime: 321.00)))
1919
} else {
2020
let fakeRequestId = "fakeRequestId"
21-
completion(.success(fakeRequestId))
21+
completion(.success(FpPublishData(requestId: fakeRequestId, fpLoadTime: 123.00, fpPublishTime: 321.00)))
2222
}
2323
} else {
2424
completion(.failure(.couldNotPublishRiskData))

Tests/Mocks/MockLoggerService.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,5 @@ struct MockLoggerService: LoggerServiceProtocol {
88

99
init(internalConfig: RiskSDKInternalConfig) {}
1010

11-
func log(riskEvent: RiskEvent, deviceSessionId: String?, requestId: String?, error: RiskLogError?) {}
11+
func log(riskEvent: RiskEvent, blockTime: Double?, deviceDataPersistTime: Double?, fpLoadTime: Double?, fpPublishTime: Double?, deviceSessionId: String?, requestId: String?, error: RiskLogError?) {}
1212
}

0 commit comments

Comments
 (0)