Skip to content

Commit ab444d0

Browse files
authored
Apply sampling rate settings from configs (#10647)
1 parent 35ba5f3 commit ab444d0

14 files changed

+354
-507
lines changed

FirebaseSessions/Sources/FirebaseSessions.swift

Lines changed: 15 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ protocol SessionsProvider {
3838
/// Top-level Classes in the Sessions SDK
3939
private let coordinator: SessionCoordinator
4040
private let initiator: SessionInitiator
41-
private let identifiers: Identifiers
41+
private let sessionGenerator: SessionGenerator
4242
private let appInfo: ApplicationInfo
4343
private let settings: SessionsSettings
4444

@@ -54,35 +54,34 @@ protocol SessionsProvider {
5454

5555
let fireLogger = EventGDTLogger(googleDataTransport: googleDataTransport!)
5656

57-
let identifiers = Identifiers()
58-
let coordinator = SessionCoordinator(
59-
identifiers: identifiers,
60-
installations: installations,
61-
fireLogger: fireLogger,
62-
sampler: SessionSampler()
63-
)
64-
6557
let appInfo = ApplicationInfo(appID: appID)
6658
let settings = SessionsSettings(
6759
appInfo: appInfo,
6860
installations: installations
6961
)
62+
63+
let sessionGenerator = SessionGenerator(settings: settings)
64+
let coordinator = SessionCoordinator(
65+
installations: installations,
66+
fireLogger: fireLogger
67+
)
68+
7069
let initiator = SessionInitiator(settings: settings)
7170

7271
self.init(appID: appID,
73-
identifiers: identifiers,
72+
sessionGenerator: sessionGenerator,
7473
coordinator: coordinator,
7574
initiator: initiator,
7675
appInfo: appInfo,
7776
settings: settings)
7877
}
7978

8079
// Initializes the SDK and begines the process of listening for lifecycle events and logging events
81-
init(appID: String, identifiers: Identifiers, coordinator: SessionCoordinator,
80+
init(appID: String, sessionGenerator: SessionGenerator, coordinator: SessionCoordinator,
8281
initiator: SessionInitiator, appInfo: ApplicationInfo, settings: SessionsSettings) {
8382
self.appID = appID
8483

85-
self.identifiers = identifiers
84+
self.sessionGenerator = sessionGenerator
8685
self.coordinator = coordinator
8786
self.initiator = initiator
8887
self.appInfo = appInfo
@@ -93,10 +92,10 @@ protocol SessionsProvider {
9392
self.initiator.beginListening {
9493
// On each session start, first update Settings if expired
9594
self.settings.updateSettings()
96-
self.identifiers.generateNewSessionID()
97-
// Generate a session start event only when session data collection is enabled.
98-
if self.settings.sessionsEnabled {
99-
let event = SessionStartEvent(identifiers: self.identifiers, appInfo: self.appInfo)
95+
let session = self.sessionGenerator.generateNewSession()
96+
// Generate a session start event only when session data collection is enabled and if the session is allowed to dispatch events
97+
if self.settings.sessionsEnabled, session.shouldDispatchEvents {
98+
let event = SessionStartEvent(sessionInfo: session, appInfo: self.appInfo)
10099
DispatchQueue.global().async {
101100
self.coordinator.attemptLoggingSessionStart(event: event) { result in
102101
}

FirebaseSessions/Sources/Identifiers.swift

Lines changed: 0 additions & 59 deletions
This file was deleted.

FirebaseSessions/Sources/SessionCoordinator.swift

Lines changed: 23 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -19,19 +19,13 @@ import Foundation
1919
/// involved with sending a Session Start event.
2020
///
2121
class SessionCoordinator {
22-
let identifiers: IdentifierProvider
2322
let installations: InstallationsProtocol
2423
let fireLogger: EventGDTLoggerProtocol
25-
let sampler: SessionSamplerProtocol
2624

27-
init(identifiers: IdentifierProvider,
28-
installations: InstallationsProtocol,
29-
fireLogger: EventGDTLoggerProtocol,
30-
sampler: SessionSamplerProtocol) {
31-
self.identifiers = identifiers
25+
init(installations: InstallationsProtocol,
26+
fireLogger: EventGDTLoggerProtocol) {
3227
self.installations = installations
3328
self.fireLogger = fireLogger
34-
self.sampler = sampler
3529
}
3630

3731
// Begins the process of logging a SessionStartEvent to FireLog, while taking into account Data Collection, Sampling, and fetching Settings
@@ -41,38 +35,30 @@ class SessionCoordinator {
4135
/// 1. Check if the session can be sent. If yes, move to 2. Else, drop the event.
4236
/// 2. Fetch the installations Id. If successful, move to 3. Else, drop sending the event.
4337
/// 3. Log the event. If successful, all is good. Else, log the message with error.
44-
if sampler.shouldSendEventForSession(sessionId: identifiers.sessionID) {
45-
installations.installationID { result in
46-
switch result {
47-
case let .success(fiid):
48-
event.setInstallationID(installationId: fiid)
49-
self.fireLogger.logEvent(event: event) { logResult in
50-
switch logResult {
51-
case .success():
52-
Logger.logInfo("Successfully logged Session Start event to GoogleDataTransport")
53-
callback(.success(()))
54-
case let .failure(error):
55-
Logger
56-
.logError(
57-
"Error logging Session Start event to GoogleDataTransport: \(error)."
58-
)
59-
callback(.failure(error))
60-
}
38+
installations.installationID { result in
39+
switch result {
40+
case let .success(fiid):
41+
event.setInstallationID(installationId: fiid)
42+
self.fireLogger.logEvent(event: event) { logResult in
43+
switch logResult {
44+
case .success():
45+
Logger.logInfo("Successfully logged Session Start event to GoogleDataTransport")
46+
callback(.success(()))
47+
case let .failure(error):
48+
Logger
49+
.logError(
50+
"Error logging Session Start event to GoogleDataTransport: \(error)."
51+
)
52+
callback(.failure(error))
6153
}
62-
case let .failure(error):
63-
Logger
64-
.logError(
65-
"Error getting Firebase Installation ID: \(error). Skip sending event."
66-
)
67-
callback(.failure(FirebaseSessionsError.SessionInstallationsError))
6854
}
55+
case let .failure(error):
56+
Logger
57+
.logError(
58+
"Error getting Firebase Installation ID: \(error). Skip sending event."
59+
)
60+
callback(.failure(FirebaseSessionsError.SessionInstallationsError))
6961
}
70-
} else {
71-
Logger
72-
.logInfo(
73-
"Session event dropped due to sampling."
74-
)
75-
callback(.failure(FirebaseSessionsError.SessionSamplingError))
7662
}
7763
}
7864
}
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
//
2+
// Copyright 2022 Google LLC
3+
//
4+
// Licensed under the Apache License, Version 2.0 (the "License");
5+
// you may not use this file except in compliance with the License.
6+
// You may obtain a copy of the License at
7+
//
8+
// http://www.apache.org/licenses/LICENSE-2.0
9+
//
10+
// Unless required by applicable law or agreed to in writing, software
11+
// distributed under the License is distributed on an "AS IS" BASIS,
12+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
// See the License for the specific language governing permissions and
14+
// limitations under the License.
15+
16+
import Foundation
17+
18+
@_implementationOnly import FirebaseInstallations
19+
20+
struct SessionInfo {
21+
let sessionId: String
22+
let previousSessionId: String?
23+
let shouldDispatchEvents: Bool
24+
25+
init(sessionId: String, previousSessionId: String?, dispatchEvents: Bool) {
26+
self.sessionId = sessionId
27+
self.previousSessionId = previousSessionId
28+
shouldDispatchEvents = dispatchEvents
29+
}
30+
}
31+
32+
///
33+
/// Generator is responsible for:
34+
/// 1) Generating the Session ID
35+
/// 2) Persisting and reading the Session ID from the last session
36+
/// (Maybe) 3) Persisting, reading, and incrementing an increasing index
37+
///
38+
class SessionGenerator {
39+
private var thisSession: SessionInfo?
40+
private var settings: SessionsSettings
41+
42+
init(settings: SessionsSettings) {
43+
self.settings = settings
44+
}
45+
46+
// Generates a new Session ID. If there was already a generated Session ID
47+
// from the last session during the app's lifecycle, it will also set the last Session ID
48+
func generateNewSession() -> SessionInfo {
49+
let newSessionId = UUID().uuidString.replacingOccurrences(of: "-", with: "").lowercased()
50+
51+
var collectEvents = true
52+
let randomValue = Double.random(in: 0 ... 1)
53+
54+
if randomValue > settings.samplingRate {
55+
collectEvents = false
56+
}
57+
58+
let newSession = SessionInfo(sessionId: newSessionId,
59+
previousSessionId: thisSession?.sessionId,
60+
dispatchEvents: collectEvents)
61+
thisSession = newSession
62+
return newSession
63+
}
64+
65+
func currentSession() -> SessionInfo? {
66+
return thisSession
67+
}
68+
}

FirebaseSessions/Sources/SessionSampler.swift

Lines changed: 0 additions & 43 deletions
This file was deleted.

FirebaseSessions/Sources/SessionStartEvent.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,15 +35,15 @@ import Foundation
3535
class SessionStartEvent: NSObject, GDTCOREventDataObject {
3636
var proto: firebase_appquality_sessions_SessionEvent
3737

38-
init(identifiers: IdentifierProvider, appInfo: ApplicationInfoProtocol,
38+
init(sessionInfo: SessionInfo, appInfo: ApplicationInfoProtocol,
3939
time: TimeProvider = Time()) {
4040
proto = firebase_appquality_sessions_SessionEvent()
4141

4242
super.init()
4343

4444
proto.event_type = firebase_appquality_sessions_EventType_SESSION_START
45-
proto.session_data.session_id = makeProtoString(identifiers.sessionID)
46-
proto.session_data.previous_session_id = makeProtoStringOrNil(identifiers.previousSessionID)
45+
proto.session_data.session_id = makeProtoString(sessionInfo.sessionId)
46+
proto.session_data.previous_session_id = makeProtoStringOrNil(sessionInfo.previousSessionId)
4747
proto.session_data.event_timestamp_us = time.timestampUS
4848

4949
proto.application_info.app_id = makeProtoString(appInfo.appID)

FirebaseSessions/Sources/Settings/LocalOverrideSettings.swift

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,23 +22,34 @@ class LocalOverrideSettings: SettingsProvider, SettingsProtocol {
2222
static let PlistKey_sessions_samplingRate = "FirebaseSessionsSampingRate"
2323

2424
var sessionsEnabled: Bool? {
25-
return plistValueForConfig(configName: LocalOverrideSettings.PlistKey_sessions_enabled) as? Bool
25+
let session_enabled = plistValueForConfig(configName: LocalOverrideSettings
26+
.PlistKey_sessions_enabled) as? Bool
27+
if session_enabled != nil {
28+
return Bool(session_enabled!)
29+
}
30+
return nil
2631
}
2732

2833
var sessionTimeout: TimeInterval? {
29-
return
30-
plistValueForConfig(configName: LocalOverrideSettings
31-
.PlistKey_sessions_timeout) as? TimeInterval
34+
let timeout = plistValueForConfig(configName: LocalOverrideSettings
35+
.PlistKey_sessions_timeout) as? String
36+
if timeout != nil {
37+
return Double(timeout!)
38+
}
39+
return nil
3240
}
3341

3442
var samplingRate: Double? {
35-
return
36-
plistValueForConfig(configName: LocalOverrideSettings
37-
.PlistKey_sessions_samplingRate) as? Double
43+
let rate = plistValueForConfig(configName: LocalOverrideSettings
44+
.PlistKey_sessions_samplingRate) as? String
45+
if rate != nil {
46+
return Double(rate!)
47+
}
48+
return nil
3849
}
3950

4051
private func plistValueForConfig(configName: String) -> Any? {
41-
return Bundle.main.object(forInfoDictionaryKey: configName)
52+
return Bundle.main.infoDictionary?[configName]
4253
}
4354
}
4455

0 commit comments

Comments
 (0)