Skip to content

Commit 0ea9f54

Browse files
Hongyan JiangGitHub Enterprise
authored andcommitted
add user_session_id (usi) to beacons
1 parent b845763 commit 0ea9f54

24 files changed

+273
-55
lines changed

Changelog.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
## 1.6.5
44
- Add crash to mobile feature list and send to Instana backend
5+
- Add user_session_id (usi) to beacons
56

67
## 1.6.4
78
- Fix issue for setCaptureHeaders method not capture http response headers

Sources/InstanaAgent/.swiftlint.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,3 +29,5 @@ identifier_name:
2929
error: 3 # only error
3030
excluded: # excluded via string array
3131
- id
32+
large_tuple: 3
33+

Sources/InstanaAgent/Beacons/CoreBeacon/CoreBeacon.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,13 @@ struct CoreBeacon: Codable {
8686
*/
8787
var sid: String
8888

89+
/**
90+
* User Session ID
91+
*
92+
* A unique ID that represents the device
93+
*/
94+
var usi: String?
95+
8996
/**
9097
* Beacon ID
9198
*

Sources/InstanaAgent/Beacons/CoreBeacon/CoreBeaconFactory.swift

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@ class CoreBeaconFactory {
2323
func map(_ beacon: Beacon) throws -> CoreBeacon {
2424
var cbeacon = CoreBeacon.createDefault(viewName: beacon.viewName, key: conf.key,
2525
timestamp: beacon.timestamp,
26-
sid: session.id, id: beacon.id,
27-
mobileFeatures: mobileFeatures)
26+
sid: session.id, usi: session.usi,
27+
id: beacon.id, mobileFeatures: mobileFeatures)
2828
cbeacon.append(properties)
2929
switch beacon {
3030
case let item as HTTPBeacon:
@@ -177,10 +177,12 @@ extension CoreBeacon {
177177
ec = String(1)
178178
}
179179

180+
// swiftlint:disable function_parameter_count
180181
static func createDefault(viewName: String?,
181182
key: String,
182183
timestamp: Instana.Types.Milliseconds,
183184
sid: UUID,
185+
usi: UUID?,
184186
id: UUID,
185187
mobileFeatures: String?,
186188
connection: NetworkUtility.ConnectionType = InstanaSystemUtils.networkUtility.connectionType,
@@ -190,6 +192,7 @@ extension CoreBeacon {
190192
k: key,
191193
ti: String(timestamp),
192194
sid: sid.uuidString,
195+
usi: usi?.uuidString,
193196
bid: id.uuidString,
194197
uf: mobileFeatures,
195198
bi: InstanaSystemUtils.applicationBundleIdentifier,

Sources/InstanaAgent/Configuration/Defines.swift

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,20 @@
44

55
import Foundation
66

7+
// Default user_session_id reresh time interval as negative number means never refresh
8+
// ie. keep the same user_session_id forever
9+
public let defaultUsiRefreshTimeIntervalInHrs: Double = -1.0
10+
11+
// Do not allow user_session_id tracking
12+
public let usiTrackingNotAllowed: Double = 0.0
13+
14+
let userSessionIDKey = "Instana_UserSessionIDKey"
15+
let usi_startTimeKey = "Instana_usiStartTimeKey"
16+
717
let ignoreZipReportingKey = "IgnoreZIPReporting"
818

19+
let maxSlowSendInterval: Instana.Types.Seconds = 3600.0
20+
921
let maxDaysToKeepCrashLog = 90
1022

1123
let sessionIDKey = "Instana_SessionIdKey"

Sources/InstanaAgent/Configuration/InstanaConfiguraton.swift

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,6 @@ import Foundation
1717

1818
// Use a reference type to avoid a copy when having concurrency
1919
class InstanaConfiguration {
20-
static let maxSlowSendInterval = 3600.0
21-
2220
enum SuspendReporting {
2321
/// Reporting is suspended while the device battery is low.
2422
case lowBattery
@@ -48,8 +46,6 @@ class InstanaConfiguration {
4846
}
4947

5048
struct Defaults {
51-
// 0 seconds means disable network probing
52-
static let slowSendInterval: Instana.Types.Seconds = 0.0
5349
static let reporterSendDebounce: Instana.Types.Seconds = 2.0
5450
static let reporterSendLowBatteryDebounce: Instana.Types.Seconds = 10.0
5551
static let gzipReport = ProcessInfo.ignoreZIPReporting ? false : true
@@ -67,6 +63,7 @@ class InstanaConfiguration {
6763
var suspendReporting: Set<SuspendReporting>
6864
var monitorTypes: Set<MonitorTypes>
6965
var slowSendInterval: Instana.Types.Seconds
66+
var usiRefreshTimeIntervalInHrs: Double
7067
var reporterSendDebounce: Instana.Types.Seconds
7168
var reporterSendLowBatteryDebounce: Instana.Types.Seconds
7269
var maxRetries: Int
@@ -78,7 +75,8 @@ class InstanaConfiguration {
7875
var isValid: Bool { !key.isEmpty && !reportingURL.absoluteString.isEmpty }
7976

8077
required init(reportingURL: URL, key: String, httpCaptureConfig: HTTPCaptureConfig,
81-
enableCrashReporting: Bool, slowSendInterval: Double) {
78+
enableCrashReporting: Bool, slowSendInterval: Instana.Types.Seconds,
79+
usiRefreshTimeIntervalInHrs: Double) {
8280
self.reportingURL = reportingURL
8381
self.key = key
8482
self.httpCaptureConfig = httpCaptureConfig
@@ -88,6 +86,7 @@ class InstanaConfiguration {
8886
monitorTypes.insert(.crash)
8987
}
9088
self.slowSendInterval = slowSendInterval
89+
self.usiRefreshTimeIntervalInHrs = usiRefreshTimeIntervalInHrs
9190
reporterSendDebounce = Defaults.reporterSendDebounce
9291
reporterSendLowBatteryDebounce = Defaults.reporterSendLowBatteryDebounce
9392
maxRetries = Defaults.maxRetries
@@ -99,8 +98,11 @@ class InstanaConfiguration {
9998
}
10099

101100
static func `default`(key: String, reportingURL: URL, httpCaptureConfig: HTTPCaptureConfig = .automatic,
102-
enableCrashReporting: Bool, slowSendInterval: Double = 0.0) -> InstanaConfiguration {
101+
enableCrashReporting: Bool, slowSendInterval: Instana.Types.Seconds = 0.0,
102+
usiRefreshTimeIntervalInHrs: Double = defaultUsiRefreshTimeIntervalInHrs)
103+
-> InstanaConfiguration {
103104
self.init(reportingURL: reportingURL, key: key, httpCaptureConfig: httpCaptureConfig,
104-
enableCrashReporting: enableCrashReporting, slowSendInterval: slowSendInterval)
105+
enableCrashReporting: enableCrashReporting, slowSendInterval: slowSendInterval,
106+
usiRefreshTimeIntervalInHrs: usiRefreshTimeIntervalInHrs)
105107
}
106108
}

Sources/InstanaAgent/Configuration/InstanaSession.swift

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,16 @@ class InstanaSession {
1616
/// The Session ID created on each app launch
1717
let id: UUID
1818

19+
/// A unique ID that represents the device
20+
private var userSessionID: UUID?
21+
var usiStartTime: TimeInterval?
22+
var usi: UUID? {
23+
if !isSessionValid() {
24+
(userSessionID, usiStartTime) = InstanaSession.usiNew(configuration)
25+
}
26+
return userSessionID
27+
}
28+
1929
/// Session information for previous app launch
2030
/// so as to assist metric kit payload analyse
2131
var previousSession: PreviousSession?
@@ -33,6 +43,78 @@ class InstanaSession {
3343

3444
previousSession = PreviousSession.readInPreviousSessionData()
3545
id = sessionID
46+
(userSessionID, usiStartTime) = InstanaSession.usiRetrieve(configuration)
3647
PreviousSession.persistSessionID(sid: sessionID)
3748
}
49+
50+
private func isSessionValid() -> Bool {
51+
// Do now allow user_session_id tracking
52+
if configuration.usiRefreshTimeIntervalInHrs == usiTrackingNotAllowed {
53+
return true
54+
}
55+
56+
// user_session_id never expires
57+
if configuration.usiRefreshTimeIntervalInHrs < 0 {
58+
return userSessionID != nil
59+
}
60+
61+
guard let usiStartTime = usiStartTime else { return false }
62+
let usiTimeElapse = Date().timeIntervalSince1970 - usiStartTime
63+
if usiTimeElapse > Double(configuration.usiRefreshTimeIntervalInHrs) * 3600.0 {
64+
return false
65+
}
66+
return true
67+
}
68+
69+
private static func usiRetrieve(_ config: InstanaConfiguration) -> (UUID?, TimeInterval?) {
70+
if config.usiRefreshTimeIntervalInHrs == usiTrackingNotAllowed {
71+
UserDefaults.standard.removeObject(forKey: userSessionIDKey)
72+
UserDefaults.standard.removeObject(forKey: usi_startTimeKey)
73+
return (nil, nil)
74+
}
75+
76+
var usiActive: UUID?
77+
var startTime: TimeInterval?
78+
79+
let idStr = UserDefaults.standard.string(forKey: userSessionIDKey)
80+
if idStr != nil {
81+
usiActive = UUID(uuidString: idStr!)
82+
if usiActive == nil {
83+
UserDefaults.standard.removeObject(forKey: userSessionIDKey)
84+
} else {
85+
let startTimeRead = UserDefaults.standard.double(forKey: usi_startTimeKey)
86+
let now = Date().timeIntervalSince1970
87+
if startTimeRead > 0, startTimeRead <= now {
88+
startTime = startTimeRead
89+
} else {
90+
usiActive = nil
91+
}
92+
}
93+
}
94+
95+
if usiActive == nil {
96+
return usiNew(config)
97+
}
98+
return (usiActive!, startTime!)
99+
}
100+
101+
private static func usiNew(_ config: InstanaConfiguration) -> (UUID?, TimeInterval?) {
102+
if config.usiRefreshTimeIntervalInHrs == usiTrackingNotAllowed {
103+
UserDefaults.standard.removeObject(forKey: userSessionIDKey)
104+
UserDefaults.standard.removeObject(forKey: usi_startTimeKey)
105+
return (nil, nil)
106+
}
107+
108+
let usiActive = UUID()
109+
UserDefaults.standard.setValue(usiActive.uuidString, forKey: userSessionIDKey)
110+
111+
var startTime: TimeInterval?
112+
if config.usiRefreshTimeIntervalInHrs > 0 {
113+
startTime = Date().timeIntervalSince1970
114+
UserDefaults.standard.setValue(startTime, forKey: usi_startTimeKey)
115+
} else {
116+
UserDefaults.standard.removeObject(forKey: usi_startTimeKey)
117+
}
118+
return (usiActive, startTime)
119+
}
38120
}

Sources/InstanaAgent/Configuration/PreviousSession.swift

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -27,19 +27,23 @@ struct PreviousSession: Codable {
2727
self.userName = userName
2828
}
2929

30+
static func cleanupPreviousSessionUserDefaults() {
31+
UserDefaults.standard.removeObject(forKey: sessionIDKey)
32+
UserDefaults.standard.removeObject(forKey: sessionStartTimeKey)
33+
UserDefaults.standard.removeObject(forKey: viewNameKey)
34+
UserDefaults.standard.removeObject(forKey: carrierKey)
35+
UserDefaults.standard.removeObject(forKey: connectionTypeKey)
36+
UserDefaults.standard.removeObject(forKey: userIDKey)
37+
UserDefaults.standard.removeObject(forKey: userNameKey)
38+
UserDefaults.standard.removeObject(forKey: userEmailKey)
39+
}
40+
3041
///
3142
/// Read in and parse saved previous session id, session start time etc.
3243
///
3344
static func readInPreviousSessionData() -> PreviousSession? {
3445
defer {
35-
UserDefaults.standard.removeObject(forKey: sessionIDKey)
36-
UserDefaults.standard.removeObject(forKey: sessionStartTimeKey)
37-
UserDefaults.standard.removeObject(forKey: viewNameKey)
38-
UserDefaults.standard.removeObject(forKey: carrierKey)
39-
UserDefaults.standard.removeObject(forKey: connectionTypeKey)
40-
UserDefaults.standard.removeObject(forKey: userIDKey)
41-
UserDefaults.standard.removeObject(forKey: userNameKey)
42-
UserDefaults.standard.removeObject(forKey: userEmailKey)
46+
cleanupPreviousSessionUserDefaults()
4347
}
4448

4549
guard let sidStr = UserDefaults.standard.string(forKey: sessionIDKey),

Sources/InstanaAgent/Instana.swift

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -85,30 +85,33 @@ import Foundation
8585
var collectionEnabled = true
8686
var enableCrashReporting = false
8787
var slowSendInterval = 0.0
88+
var usiRefreshTimeIntervalInHrs = defaultUsiRefreshTimeIntervalInHrs
8889
if let options = options {
8990
httpCaptureConfig = options.httpCaptureConfig
9091
collectionEnabled = options.collectionEnabled
9192
enableCrashReporting = options.enableCrashReporting
9293

9394
let debounce = InstanaConfiguration.Defaults.reporterSendDebounce
9495
if options.slowSendInterval != 0.0,
95-
options.slowSendInterval < debounce || options.slowSendInterval > InstanaConfiguration.maxSlowSendInterval {
96+
options.slowSendInterval < debounce || options.slowSendInterval > maxSlowSendInterval {
9697
// Illegal slowSendInterval. Expected value 2 ~ 3600 in seconds
9798
return false
9899
}
99100
slowSendInterval = options.slowSendInterval
101+
102+
usiRefreshTimeIntervalInHrs = options.usiRefreshTimeIntervalInHrs
100103
}
101104
let config = InstanaConfiguration.default(key: key, reportingURL: reportingURL,
102105
httpCaptureConfig: httpCaptureConfig,
103106
enableCrashReporting: enableCrashReporting,
104-
slowSendInterval: slowSendInterval)
107+
slowSendInterval: slowSendInterval,
108+
usiRefreshTimeIntervalInHrs: usiRefreshTimeIntervalInHrs)
105109
let session = InstanaSession(configuration: config, propertyHandler: InstanaPropertyHandler(),
106110
collectionEnabled: collectionEnabled)
107111
Instana.current = Instana(session: session)
108112
return true
109113
}
110114

111-
/// Deprecated! Use setup( ) with InstanaSetupOptions instead.
112115
/// Configures and sets up the Instana agent with the default configuration. (deprecated)
113116
/// - HTTP sessions will be captured automatically by default
114117
///
@@ -118,6 +121,7 @@ import Foundation
118121
/// - reportingURL: Reporting URL for the Instana backend.
119122
/// - enableCrashReporting: Subscribe to metricKit events so as to enable crash reporting.
120123
/// App must have explicitly asked user permission to subscribe before this call.
124+
@available(*, deprecated, message: "This method is deprecated. Use the setup() method with InstanaSetupOptions.")
121125
@objc
122126
public static func setup(key: String, reportingURL: URL, enableCrashReporting: Bool = false) {
123127
let config = InstanaConfiguration.default(key: key, reportingURL: reportingURL,
@@ -127,7 +131,6 @@ import Foundation
127131
Instana.current = Instana(session: session)
128132
}
129133

130-
/// Deprecated! Use setup( ) with InstanaSetupOptions instead.
131134
/// Configures and sets up the Instana agent with a custom HTTP capture configuration. (deprecated)
132135
///
133136
/// - Note: Should be called only once, as soon as posible. Preferably in `application(_:, didFinishLaunchingWithOptions:)`
@@ -138,6 +141,7 @@ import Foundation
138141
/// - collectionEnabled: Enable or disable collection (instrumentation) on setup. Can be changed later via the property `collectionEnabled` (Default: true)
139142
/// - enableCrashReporting: Subscribe to metricKit events so as to enable crash reporting.
140143
/// App must have explicitly asked user permission to subscribe before this call.
144+
@available(*, deprecated, message: "This method is deprecated. Use the setup() method with InstanaSetupOptions.")
141145
@objc
142146
public static func setup(key: String, reportingURL: URL,
143147
httpCaptureConfig: HTTPCaptureConfig = .automatic,

Sources/InstanaAgent/InstanaSetupOptions.swift

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import Foundation
99
public var collectionEnabled: Bool
1010
public var enableCrashReporting: Bool
1111
public var slowSendInterval: Instana.Types.Seconds
12+
public var usiRefreshTimeIntervalInHrs: Double
1213

1314
/// Instana custom configuration for setup.
1415
///
@@ -21,10 +22,12 @@ import Foundation
2122
@objc public
2223
init(httpCaptureConfig: HTTPCaptureConfig = .automatic,
2324
collectionEnabled: Bool = true, enableCrashReporting: Bool = false,
24-
slowSendInterval: Instana.Types.Seconds = 0.0) {
25+
slowSendInterval: Instana.Types.Seconds = 0.0,
26+
usiRefreshTimeIntervalInHrs: Double = defaultUsiRefreshTimeIntervalInHrs) {
2527
self.httpCaptureConfig = httpCaptureConfig
2628
self.collectionEnabled = collectionEnabled
2729
self.enableCrashReporting = enableCrashReporting
2830
self.slowSendInterval = slowSendInterval
31+
self.usiRefreshTimeIntervalInHrs = usiRefreshTimeIntervalInHrs
2932
}
3033
}

0 commit comments

Comments
 (0)