Skip to content

Commit 1ac4c51

Browse files
authored
feat: add diagnostics client (#346)
* feat: add diagnostics client * refactor with actor * fix ut * fix * update * update * use xxhash32 for sampling * update * update * add RestrictedAmplitudeFeature * update * make isInSample public
1 parent 849b8b3 commit 1ac4c51

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+4298
-624
lines changed

android/src/main/java/com/amplitude/android/Amplitude.kt

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package com.amplitude.android
22

33
import android.app.Application
44
import android.content.Context
5+
import com.amplitude.android.diagnostics.AndroidDiagnosticsContextProvider
56
import com.amplitude.android.migration.MigrationManager
67
import com.amplitude.android.plugins.AnalyticsConnectorIdentityPlugin
78
import com.amplitude.android.plugins.AnalyticsConnectorPlugin
@@ -11,6 +12,7 @@ import com.amplitude.android.plugins.AndroidNetworkConnectivityCheckerPlugin
1112
import com.amplitude.android.storage.AndroidStorageContextV3
1213
import com.amplitude.android.utilities.ActivityLifecycleObserver
1314
import com.amplitude.core.State
15+
import com.amplitude.core.diagnostics.DiagnosticsContextProvider
1416
import com.amplitude.core.platform.plugins.AmplitudeDestination
1517
import com.amplitude.core.platform.plugins.GetAmpliExtrasPlugin
1618
import com.amplitude.id.IdentityConfiguration
@@ -20,6 +22,7 @@ import kotlinx.coroutines.Deferred
2022
import kotlinx.coroutines.SupervisorJob
2123
import kotlinx.coroutines.asCoroutineDispatcher
2224
import kotlinx.coroutines.launch
25+
import java.io.File
2326
import java.util.concurrent.Executors
2427
import com.amplitude.core.Amplitude as CoreAmplitude
2528

@@ -111,6 +114,16 @@ open class Amplitude internal constructor(
111114
(timeline as Timeline).start()
112115
}
113116

117+
override fun diagnosticsContextProvider(): DiagnosticsContextProvider? {
118+
val configuration = configuration as Configuration
119+
return AndroidDiagnosticsContextProvider(configuration.context)
120+
}
121+
122+
override fun diagnosticsStorageDirectory(): File {
123+
val configuration = configuration as Configuration
124+
return configuration.getStorageDirectory()
125+
}
126+
114127
/**
115128
* Reset identity:
116129
* - reset userId to "null"

android/src/main/java/com/amplitude/android/AutocaptureOptions.kt

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,3 +125,29 @@ class AutocaptureOptionsBuilder {
125125
* @return Set of autocapture options.
126126
*/
127127
fun autocaptureOptions(init: AutocaptureOptionsBuilder.() -> Unit): Set<AutocaptureOption> = AutocaptureOptionsBuilder().apply(init).build()
128+
129+
fun Set<AutocaptureOption>.stringRepresentation(): String {
130+
if (isEmpty()) return "none"
131+
132+
val options = mutableListOf<String>()
133+
if (contains(AutocaptureOption.SESSIONS)) {
134+
options.add("sessions")
135+
}
136+
if (contains(AutocaptureOption.APP_LIFECYCLES)) {
137+
options.add("appLifecycles")
138+
}
139+
if (contains(AutocaptureOption.DEEP_LINKS)) {
140+
options.add("deepLinks")
141+
}
142+
if (contains(AutocaptureOption.SCREEN_VIEWS)) {
143+
options.add("screenViews")
144+
}
145+
if (contains(AutocaptureOption.ELEMENT_INTERACTIONS)) {
146+
options.add("elementInteractions")
147+
}
148+
if (contains(AutocaptureOption.FRUSTRATION_INTERACTIONS)) {
149+
options.add("frustrationInteractions")
150+
}
151+
152+
return options.joinToString(separator = ",")
153+
}

android/src/main/java/com/amplitude/android/Configuration.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ open class Configuration(
5050
override var sessionId: Long? = null,
5151
override var httpClient: HttpClientInterface? = null,
5252
var interactionsOptions: InteractionsOptions = InteractionsOptions(),
53+
override var enableDiagnostics: Boolean = true,
5354
) : Configuration(
5455
apiKey,
5556
flushQueueSize,
@@ -74,6 +75,7 @@ open class Configuration(
7475
deviceId,
7576
sessionId,
7677
httpClient,
78+
enableDiagnostics = enableDiagnostics,
7779
) {
7880
companion object {
7981
const val MIN_TIME_BETWEEN_SESSIONS_MILLIS: Long = 300000
@@ -118,6 +120,7 @@ open class Configuration(
118120
sessionId: Long? = null,
119121
httpClient: HttpClientInterface? = null,
120122
interactionsOptions: InteractionsOptions = InteractionsOptions(),
123+
enableDiagnostics: Boolean = true,
121124
) : this(
122125
apiKey,
123126
context,
@@ -154,6 +157,7 @@ open class Configuration(
154157
sessionId,
155158
httpClient,
156159
interactionsOptions,
160+
enableDiagnostics,
157161
) {
158162
if (!trackingSessionEvents) {
159163
defaultTracking.sessions = false
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package com.amplitude.android.diagnostics
2+
3+
import android.content.Context
4+
import android.os.Build
5+
import com.amplitude.core.diagnostics.DiagnosticsContextInfo
6+
import com.amplitude.core.diagnostics.DiagnosticsContextProvider
7+
8+
class AndroidDiagnosticsContextProvider(
9+
private val context: Context,
10+
) : DiagnosticsContextProvider {
11+
override fun getContextInfo(): DiagnosticsContextInfo {
12+
return DiagnosticsContextInfo(
13+
manufacturer = Build.MANUFACTURER,
14+
model = Build.MODEL,
15+
osName = "Android",
16+
osVersion = Build.VERSION.RELEASE,
17+
platform = "Android",
18+
appVersion = fetchVersionName(),
19+
)
20+
}
21+
22+
private fun fetchVersionName(): String? {
23+
return try {
24+
val info = context.packageManager.getPackageInfo(context.packageName, 0)
25+
info.versionName
26+
} catch (_: Exception) {
27+
null
28+
}
29+
}
30+
}

android/src/main/java/com/amplitude/android/plugins/AndroidContextPlugin.kt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,12 @@ import com.amplitude.android.Configuration
55
import com.amplitude.android.TrackingOptions
66
import com.amplitude.common.android.AndroidContextProvider
77
import com.amplitude.core.Amplitude
8+
import com.amplitude.core.RestrictedAmplitudeFeature
89
import com.amplitude.core.events.BaseEvent
910
import com.amplitude.core.platform.Plugin
1011
import java.util.UUID
1112

13+
@OptIn(RestrictedAmplitudeFeature::class)
1214
open class AndroidContextPlugin : Plugin {
1315
override val type: Plugin.Type = Plugin.Type.Before
1416
override lateinit var amplitude: Amplitude
@@ -25,6 +27,11 @@ open class AndroidContextPlugin : Plugin {
2527
configuration.trackingOptions.shouldTrackAppSetId(),
2628
)
2729
initializeDeviceId(configuration)
30+
31+
amplitude.diagnosticsClient.setTag(
32+
name = "sdk.${SDK_LIBRARY}.version",
33+
value = SDK_VERSION,
34+
)
2835
}
2936

3037
override fun execute(event: BaseEvent): BaseEvent? {

android/src/main/java/com/amplitude/android/plugins/AndroidLifecyclePlugin.kt

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,17 +13,19 @@ import com.amplitude.android.GuardedAmplitudeFeature
1313
import com.amplitude.android.InteractionType.DeadClick
1414
import com.amplitude.android.InteractionType.RageClick
1515
import com.amplitude.android.internal.gestures.WindowCallbackManager
16+
import com.amplitude.android.stringRepresentation
1617
import com.amplitude.android.utilities.ActivityCallbackType
1718
import com.amplitude.android.utilities.ActivityLifecycleObserver
1819
import com.amplitude.android.utilities.DefaultEventUtils
1920
import com.amplitude.core.Amplitude
21+
import com.amplitude.core.RestrictedAmplitudeFeature
2022
import com.amplitude.core.platform.Plugin
2123
import kotlinx.coroutines.Dispatchers
2224
import kotlinx.coroutines.Job
2325
import kotlinx.coroutines.launch
2426
import com.amplitude.android.Amplitude as AndroidAmplitude
2527

26-
@OptIn(GuardedAmplitudeFeature::class)
28+
@OptIn(GuardedAmplitudeFeature::class, RestrictedAmplitudeFeature::class)
2729
class AndroidLifecyclePlugin(
2830
private val activityLifecycleObserver: ActivityLifecycleObserver,
2931
) : Application.ActivityLifecycleCallbacks,
@@ -58,6 +60,12 @@ class AndroidLifecyclePlugin(
5860
androidConfiguration.interactionsOptions,
5961
)
6062

63+
// Set autocapture state to diagnostics client
64+
amplitude.diagnosticsClient.setTag(
65+
name = "autocapture.enabled",
66+
value = androidConfiguration.autocapture.stringRepresentation(),
67+
)
68+
6169
val application = androidConfiguration.context as Application
6270

6371
// Initialize frustration interactions detector if rage or dead click is enabled

android/src/main/java/com/amplitude/android/storage/AndroidStorageContextV1.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ import com.amplitude.android.Amplitude
55
import com.amplitude.android.Configuration
66
import com.amplitude.android.migration.AndroidStorageMigration
77
import com.amplitude.android.migration.IdentityStorageMigration
8+
import com.amplitude.core.RestrictedAmplitudeFeature
9+
import com.amplitude.core.diagnostics.DiagnosticsClientProvider
810
import com.amplitude.core.utilities.FileStorage
911
import com.amplitude.id.FileIdentityStorage
1012
import com.amplitude.id.IdentityConfiguration
@@ -23,6 +25,7 @@ import java.io.File
2325
* /shared_prefs
2426
* /amplitude-android-{api_key}.xml
2527
*/
28+
@OptIn(RestrictedAmplitudeFeature::class)
2629
internal class AndroidStorageContextV1(
2730
private val amplitude: Amplitude,
2831
configuration: Configuration,
@@ -83,6 +86,7 @@ internal class AndroidStorageContextV1(
8386
sharedPreferences,
8487
storageDirectory,
8588
amplitude.diagnostics,
89+
DiagnosticsClientProvider { amplitude.diagnosticsClient },
8690
)
8791
}
8892

android/src/main/java/com/amplitude/android/storage/AndroidStorageContextV2.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ import com.amplitude.android.Amplitude
55
import com.amplitude.android.Configuration
66
import com.amplitude.android.migration.AndroidStorageMigration
77
import com.amplitude.android.migration.IdentityStorageMigration
8+
import com.amplitude.core.RestrictedAmplitudeFeature
9+
import com.amplitude.core.diagnostics.DiagnosticsClientProvider
810
import com.amplitude.core.utilities.FileStorage
911
import com.amplitude.id.FileIdentityStorage
1012
import com.amplitude.id.IdentityConfiguration
@@ -23,6 +25,7 @@ import java.io.File
2325
* /shared_prefs
2426
* /amplitude-android-{instance_name}.xml
2527
*/
28+
@OptIn(RestrictedAmplitudeFeature::class)
2629
internal class AndroidStorageContextV2(
2730
private val amplitude: Amplitude,
2831
configuration: Configuration,
@@ -100,6 +103,7 @@ internal class AndroidStorageContextV2(
100103
sharedPreferences,
101104
storageDirectory,
102105
amplitude.diagnostics,
106+
DiagnosticsClientProvider { amplitude.diagnosticsClient },
103107
)
104108
}
105109

0 commit comments

Comments
 (0)