Skip to content

Commit 9c05e65

Browse files
authored
Telemetry Metrics (#214)
Telemetry to record some basic usage data for the purpose of guiding development towards features that get used. Must be enabled to send data.
1 parent f82c56c commit 9c05e65

File tree

29 files changed

+687
-32
lines changed

29 files changed

+687
-32
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,4 @@
1414
.cxx
1515
local.properties
1616
.idea
17+
/core/bin

android/src/androidTest/java/com/segment/analytics/ExampleInstrumentedTest.kt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package com.segment.analytics
22

33
import androidx.test.platform.app.InstrumentationRegistry
44
import androidx.test.ext.junit.runners.AndroidJUnit4
5+
import com.segment.analytics.kotlin.core.Telemetry
56

67
import org.junit.Test
78
import org.junit.runner.RunWith
@@ -15,6 +16,10 @@ import org.junit.Assert.*
1516
*/
1617
@RunWith(AndroidJUnit4::class)
1718
class ExampleInstrumentedTest {
19+
init {
20+
Telemetry.enable = false
21+
}
22+
1823
@Test
1924
fun useAppContext() {
2025
// Context of the app under test.

android/src/test/java/com/segment/analytics/kotlin/android/AndroidAnalyticsKtTest.kt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,16 @@
11
package com.segment.analytics.kotlin.android
22

3+
import com.segment.analytics.kotlin.core.Telemetry
34
import org.junit.jupiter.api.Assertions.*
45

56
import org.junit.jupiter.api.Test
67
import org.junit.jupiter.api.assertThrows
78

89
internal class AndroidAnalyticsKtTest {
10+
init {
11+
Telemetry.enable = false
12+
}
13+
914
@Test
1015
fun `jvm initializer in android platform should failed`() {
1116
val exception = assertThrows<Exception> {

android/src/test/java/com/segment/analytics/kotlin/android/AndroidContextCollectorTests.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,10 @@ class AndroidContextCollectorTests {
3131
private val testDispatcher = UnconfinedTestDispatcher()
3232
private val testScope = TestScope(testDispatcher)
3333

34+
init {
35+
Telemetry.enable = false
36+
}
37+
3438
@Before
3539
fun setUp() {
3640
appContext = spyk(InstrumentationRegistry.getInstrumentation().targetContext)

android/src/test/java/com/segment/analytics/kotlin/android/AndroidLifecyclePluginTests.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ class AndroidLifecyclePluginTests {
4141
private val testScope = TestScope(testDispatcher)
4242

4343
init {
44+
Telemetry.enable = false
4445
val packageInfo = PackageInfo()
4546
packageInfo.versionCode = 100
4647
packageInfo.versionName = "1.0.0"

android/src/test/java/com/segment/analytics/kotlin/android/EventsFileTests.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import com.segment.analytics.kotlin.core.emptyJsonObject
77
import com.segment.analytics.kotlin.core.utilities.EncodeDefaultsJson
88
import com.segment.analytics.kotlin.core.utilities.EventsFileManager
99
import com.segment.analytics.kotlin.core.utilities.SegmentInstant
10+
import com.segment.analytics.kotlin.core.Telemetry
1011
import io.mockk.every
1112
import io.mockk.mockkObject
1213
import kotlinx.coroutines.test.runTest
@@ -32,6 +33,7 @@ class EventsFileTests {
3233
private val directory = File("/tmp/analytics-android-test/")
3334

3435
init {
36+
Telemetry.enable = false
3537
mockkObject(SegmentInstant)
3638
every { SegmentInstant.now() } returns Date(0).toInstant().toString()
3739
}

android/src/test/java/com/segment/analytics/kotlin/android/StorageTests.kt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import com.segment.analytics.kotlin.core.Configuration
99
import com.segment.analytics.kotlin.core.Settings
1010
import com.segment.analytics.kotlin.core.Storage
1111
import com.segment.analytics.kotlin.core.System
12+
import com.segment.analytics.kotlin.core.Telemetry
1213
import com.segment.analytics.kotlin.core.TrackEvent
1314
import com.segment.analytics.kotlin.core.UserInfo
1415
import com.segment.analytics.kotlin.core.emptyJsonObject
@@ -43,6 +44,10 @@ class StorageTests {
4344
private lateinit var androidStorage: AndroidStorage
4445
private var mockContext: Context = mockContext()
4546

47+
init {
48+
Telemetry.enable = false
49+
}
50+
4651
@BeforeEach
4752
fun setup() = runTest {
4853
clearPersistentStorage()

core/src/main/java/com/segment/analytics/kotlin/core/Analytics.kt

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -93,9 +93,11 @@ open class Analytics protected constructor(
9393
object : CoroutineConfiguration {
9494
override val store = Store()
9595
val exceptionHandler = CoroutineExceptionHandler { _, t ->
96-
Analytics.segmentLog(
97-
"Caught Exception in Analytics Scope: ${t}"
98-
)
96+
reportErrorWithMetrics(null, t,"Caught Exception in Analytics Scope",
97+
Telemetry.INVOKE_ERROR_METRIC, t.stackTraceToString()) {
98+
it["error"] = t.toString()
99+
it["message"] = "Exception in Analytics Scope"
100+
}
99101
}
100102
override val analyticsScope = CoroutineScope(SupervisorJob() + exceptionHandler)
101103
override val analyticsDispatcher: CloseableCoroutineDispatcher =
@@ -114,6 +116,14 @@ open class Analytics protected constructor(
114116
add(ContextPlugin())
115117
add(UserInfoPlugin())
116118

119+
Telemetry.increment(Telemetry.INVOKE_METRIC) {
120+
it["message"] = "configured"
121+
it["apihost"] = configuration.apiHost
122+
it["cdnhost"] = configuration.cdnHost
123+
it["flush"] =
124+
"at:${configuration.flushAt} int:${configuration.flushInterval} pol:${configuration.flushPolicies.count()}"
125+
}
126+
117127
// Setup store
118128
analyticsScope.launch(analyticsDispatcher) {
119129
store.also {
@@ -123,6 +133,7 @@ open class Analytics protected constructor(
123133

124134
// subscribe to store after state is provided
125135
storage.subscribeToStore()
136+
Telemetry.subscribe(store)
126137
}
127138

128139
if (configuration.autoAddSegmentDestination) {

core/src/main/java/com/segment/analytics/kotlin/core/Errors.kt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,16 @@ fun Analytics.reportInternalError(error: Throwable) {
1010
Analytics.reportInternalError(error)
1111
}
1212

13+
fun reportErrorWithMetrics(analytics: Analytics?, error: Throwable,
14+
message: String, metric:String,
15+
log: String, buildTags: (MutableMap<String, String>) -> Unit) {
16+
analytics?.configuration?.errorHandler?.invoke(error)
17+
var fullMessage = message
18+
error.message?.let { fullMessage += ": $it"}
19+
Analytics.segmentLog(fullMessage)
20+
Telemetry.error(metric, log, buildTags)
21+
}
22+
1323
fun Analytics.Companion.reportInternalError(error: Throwable) {
1424
error.message?.let {
1525
Analytics.segmentLog(it)

core/src/main/java/com/segment/analytics/kotlin/core/HTTPClient.kt

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,12 @@ class HTTPClient(
3434
URL(url)
3535
} catch (e: MalformedURLException) {
3636
val error = IOException("Attempted to use malformed url: $url", e)
37-
Analytics.reportInternalError(error)
37+
reportErrorWithMetrics(null, e,"Attempted to use malformed url: $url",
38+
Telemetry.INVOKE_ERROR_METRIC, e.stackTraceToString()) {
39+
it["error"] = e.toString()
40+
it["writekey"] = writeKey
41+
it["message"] = "Malformed url"
42+
}
3843
throw error
3944
}
4045
val connection = requestedURL.openConnection() as HttpURLConnection
@@ -105,7 +110,7 @@ internal fun HttpURLConnection.createPostConnection(): Connection {
105110
inputStream?.close()
106111
}
107112
throw HTTPException(
108-
responseCode, connection.responseMessage, responseBody
113+
responseCode, connection.responseMessage, responseBody, connection.headerFields
109114
)
110115
}
111116
} finally {
@@ -119,7 +124,8 @@ internal fun HttpURLConnection.createPostConnection(): Connection {
119124
internal class HTTPException(
120125
val responseCode: Int,
121126
val responseMessage: String,
122-
val responseBody: String?
127+
val responseBody: String?,
128+
val responseHeaders: MutableMap<String, MutableList<String>>
123129
) :
124130
IOException("HTTP $responseCode: $responseMessage. Response: ${responseBody ?: "No response"}") {
125131
fun is4xx(): Boolean {

0 commit comments

Comments
 (0)