Skip to content

Commit 41c921a

Browse files
feat: add app hang / anr (#187)
* add options * Add test * add test * Format code * Update CHANGELOG * Update tests * add api * Fix detekt --------- Co-authored-by: Sentry Github Bot <bot+github-bot@sentry.io>
1 parent 618a235 commit 41c921a

File tree

14 files changed

+448
-4
lines changed

14 files changed

+448
-4
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@
44

55
### Features
66

7+
- Add App Hang Tracking / ANR options ([#187](https://github.com/getsentry/sentry-kotlin-multiplatform/pull/187))
8+
- Use `isAnrEnabled` and `anrTimeoutIntervalMillis` to configure ANR tracking for Android
9+
- Use `enableAppHangTracking` and `appHangTimeoutIntervalMillis` to configure App Hang tracking for iOS
10+
- Both options are enabled by default
711
- Add isCrashedLastRun ([#186](https://github.com/getsentry/sentry-kotlin-multiplatform/pull/186))
812
- You can use it with `Sentry.isCrashedLastRun()`
913

sentry-kotlin-multiplatform/api/android/sentry-kotlin-multiplatform.api

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,8 @@ public final class io/sentry/kotlin/multiplatform/SentryLevel : java/lang/Enum {
158158

159159
public class io/sentry/kotlin/multiplatform/SentryOptions {
160160
public fun <init> ()V
161+
public final fun getAnrTimeoutIntervalMillis ()J
162+
public final fun getAppHangTimeoutIntervalMillis ()J
161163
public final fun getAttachScreenshot ()Z
162164
public final fun getAttachStackTrace ()Z
163165
public final fun getAttachThreads ()Z
@@ -167,6 +169,7 @@ public class io/sentry/kotlin/multiplatform/SentryOptions {
167169
public final fun getDebug ()Z
168170
public final fun getDist ()Ljava/lang/String;
169171
public final fun getDsn ()Ljava/lang/String;
172+
public final fun getEnableAppHangTracking ()Z
170173
public final fun getEnableAutoSessionTracking ()Z
171174
public final fun getEnableCaptureFailedRequests ()Z
172175
public final fun getEnvironment ()Ljava/lang/String;
@@ -179,6 +182,10 @@ public class io/sentry/kotlin/multiplatform/SentryOptions {
179182
public final fun getSdk ()Lio/sentry/kotlin/multiplatform/protocol/SdkVersion;
180183
public final fun getSessionTrackingIntervalMillis ()J
181184
public final fun getTracesSampleRate ()Ljava/lang/Double;
185+
public final fun isAnrEnabled ()Z
186+
public final fun setAnrEnabled (Z)V
187+
public final fun setAnrTimeoutIntervalMillis (J)V
188+
public final fun setAppHangTimeoutIntervalMillis (J)V
182189
public final fun setAttachScreenshot (Z)V
183190
public final fun setAttachStackTrace (Z)V
184191
public final fun setAttachThreads (Z)V
@@ -188,6 +195,7 @@ public class io/sentry/kotlin/multiplatform/SentryOptions {
188195
public final fun setDebug (Z)V
189196
public final fun setDist (Ljava/lang/String;)V
190197
public final fun setDsn (Ljava/lang/String;)V
198+
public final fun setEnableAppHangTracking (Z)V
191199
public final fun setEnableAutoSessionTracking (Z)V
192200
public final fun setEnableCaptureFailedRequests (Z)V
193201
public final fun setEnvironment (Ljava/lang/String;)V

sentry-kotlin-multiplatform/api/jvm/sentry-kotlin-multiplatform.api

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,8 @@ public final class io/sentry/kotlin/multiplatform/SentryLevel : java/lang/Enum {
155155

156156
public class io/sentry/kotlin/multiplatform/SentryOptions {
157157
public fun <init> ()V
158+
public final fun getAnrTimeoutIntervalMillis ()J
159+
public final fun getAppHangTimeoutIntervalMillis ()J
158160
public final fun getAttachScreenshot ()Z
159161
public final fun getAttachStackTrace ()Z
160162
public final fun getAttachThreads ()Z
@@ -164,6 +166,7 @@ public class io/sentry/kotlin/multiplatform/SentryOptions {
164166
public final fun getDebug ()Z
165167
public final fun getDist ()Ljava/lang/String;
166168
public final fun getDsn ()Ljava/lang/String;
169+
public final fun getEnableAppHangTracking ()Z
167170
public final fun getEnableAutoSessionTracking ()Z
168171
public final fun getEnableCaptureFailedRequests ()Z
169172
public final fun getEnvironment ()Ljava/lang/String;
@@ -176,6 +179,10 @@ public class io/sentry/kotlin/multiplatform/SentryOptions {
176179
public final fun getSdk ()Lio/sentry/kotlin/multiplatform/protocol/SdkVersion;
177180
public final fun getSessionTrackingIntervalMillis ()J
178181
public final fun getTracesSampleRate ()Ljava/lang/Double;
182+
public final fun isAnrEnabled ()Z
183+
public final fun setAnrEnabled (Z)V
184+
public final fun setAnrTimeoutIntervalMillis (J)V
185+
public final fun setAppHangTimeoutIntervalMillis (J)V
179186
public final fun setAttachScreenshot (Z)V
180187
public final fun setAttachStackTrace (Z)V
181188
public final fun setAttachThreads (Z)V
@@ -185,6 +192,7 @@ public class io/sentry/kotlin/multiplatform/SentryOptions {
185192
public final fun setDebug (Z)V
186193
public final fun setDist (Ljava/lang/String;)V
187194
public final fun setDsn (Ljava/lang/String;)V
195+
public final fun setEnableAppHangTracking (Z)V
188196
public final fun setEnableAutoSessionTracking (Z)V
189197
public final fun setEnableCaptureFailedRequests (Z)V
190198
public final fun setEnvironment (Ljava/lang/String;)V

sentry-kotlin-multiplatform/build.gradle.kts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,14 @@ kotlin {
122122
macosMain.get().dependsOn(commonTvWatchMacOsMain)
123123
watchosMain.get().dependsOn(commonTvWatchMacOsMain)
124124

125+
val commonTvWatchMacOsTest by creating {
126+
dependsOn(appleTest.get())
127+
}
128+
129+
tvosTest.get().dependsOn(commonTvWatchMacOsTest)
130+
macosTest.get().dependsOn(commonTvWatchMacOsTest)
131+
watchosTest.get().dependsOn(commonTvWatchMacOsTest)
132+
125133
cocoapods {
126134
summary = "Official Sentry SDK Kotlin Multiplatform"
127135
homepage = "https://github.com/getsentry/sentry-kotlin-multiplatform"

sentry-kotlin-multiplatform/sentry_kotlin_multiplatform.podspec

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,4 +50,4 @@ Pod::Spec.new do |spec|
5050
}
5151
]
5252

53-
end
53+
end
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ internal fun SentryOptions.toAndroidSentryOptionsCallback(): (SentryAndroidOptio
1212
// Apply Android specific options
1313
it.isAttachScreenshot = this.attachScreenshot
1414
it.isAttachViewHierarchy = this.attachViewHierarchy
15+
it.isAnrEnabled = this.isAnrEnabled
16+
it.anrTimeoutIntervalMillis = this.anrTimeoutIntervalMillis
1517

1618
it.sdkVersion?.name = this.sdk?.name ?: BuildKonfig.SENTRY_KMP_ANDROID_SDK_NAME
1719
it.sdkVersion?.version = this.sdk?.version ?: BuildKonfig.VERSION_NAME
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
package io.sentry.kotlin.multiplatform
2+
3+
import io.sentry.android.core.SentryAndroidOptions
4+
import io.sentry.kotlin.multiplatform.extensions.toAndroidSentryOptionsCallback
5+
import kotlin.test.assertEquals
6+
7+
actual interface PlatformOptions : CommonPlatformOptions {
8+
val isAnrEnabled: Boolean
9+
val anrTimeoutIntervalMillis: Long
10+
val attachScreenshot: Boolean
11+
val attachViewHierarchy: Boolean
12+
}
13+
14+
class SentryAndroidOptionsWrapper(private val androidOptions: SentryAndroidOptions) :
15+
PlatformOptions {
16+
override val dsn: String?
17+
get() = androidOptions.dsn
18+
19+
override val attachStackTrace: Boolean
20+
get() = androidOptions.isAttachStacktrace
21+
22+
override val release: String?
23+
get() = androidOptions.release
24+
25+
override val debug: Boolean
26+
get() = androidOptions.isDebug
27+
28+
override val environment: String?
29+
get() = androidOptions.environment
30+
31+
override val dist: String?
32+
get() = androidOptions.dist
33+
34+
override val enableAutoSessionTracking: Boolean
35+
get() = androidOptions.isEnableAutoSessionTracking
36+
37+
override val sessionTrackingIntervalMillis: Long
38+
get() = androidOptions.sessionTrackingIntervalMillis
39+
40+
override val maxBreadcrumbs: Int
41+
get() = androidOptions.maxBreadcrumbs
42+
43+
override val maxAttachmentSize: Long
44+
get() = androidOptions.maxAttachmentSize
45+
46+
override val sampleRate: Double?
47+
get() = androidOptions.sampleRate
48+
49+
override val tracesSampleRate: Double?
50+
get() = androidOptions.tracesSampleRate
51+
52+
override val isAnrEnabled: Boolean
53+
get() = androidOptions.isAnrEnabled
54+
55+
override val anrTimeoutIntervalMillis: Long
56+
get() = androidOptions.anrTimeoutIntervalMillis
57+
58+
override val attachScreenshot: Boolean
59+
get() = androidOptions.isAttachScreenshot
60+
61+
override val attachViewHierarchy: Boolean
62+
get() = androidOptions.isAttachViewHierarchy
63+
64+
override fun applyFromOptions(options: SentryOptions) {
65+
options.toAndroidSentryOptionsCallback().invoke(androidOptions)
66+
}
67+
}
68+
69+
actual fun createPlatformOptions(): PlatformOptions =
70+
SentryAndroidOptionsWrapper(SentryAndroidOptions())
71+
72+
actual fun PlatformOptions.assertPlatformSpecificOptions(options: SentryOptions) {
73+
assertEquals(attachScreenshot, options.attachScreenshot)
74+
assertEquals(attachViewHierarchy, options.attachViewHierarchy)
75+
assertEquals(isAnrEnabled, options.isAnrEnabled)
76+
assertEquals(anrTimeoutIntervalMillis, options.anrTimeoutIntervalMillis)
77+
}

sentry-kotlin-multiplatform/src/appleMain/kotlin/io/sentry/kotlin/multiplatform/extensions/SentryOptionsExtensions.apple.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ internal fun CocoaSentryOptions.applyCocoaBaseOptions(options: SentryOptions) {
3333
enableAutoSessionTracking = options.enableAutoSessionTracking
3434
maxAttachmentSize = options.maxAttachmentSize.convert()
3535
maxBreadcrumbs = options.maxBreadcrumbs.convert()
36+
enableAppHangTracking = options.enableAppHangTracking
37+
appHangTimeoutInterval = options.appHangTimeoutIntervalMillis.toDouble()
3638
options.sampleRate?.let {
3739
sampleRate = NSNumber(double = it)
3840
}

sentry-kotlin-multiplatform/src/commonMain/kotlin/io/sentry/kotlin/multiplatform/SentryOptions.kt

Lines changed: 54 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,11 @@ package io.sentry.kotlin.multiplatform
33
import io.sentry.kotlin.multiplatform.protocol.Breadcrumb
44
import io.sentry.kotlin.multiplatform.protocol.SdkVersion
55

6-
private const val DEFAULT_MAX_BREADCRUMBS = 100
7-
private const val DEFAULT_MAX_ATTACHMENT_SIZE = 20 * 1024 * 1024L
8-
private const val DEFAULT_SESSION_INTERVAL_MILLIS = 30000L
6+
internal const val DEFAULT_MAX_BREADCRUMBS = 100
7+
internal const val DEFAULT_MAX_ATTACHMENT_SIZE = 20 * 1024 * 1024L
8+
internal const val DEFAULT_SESSION_INTERVAL_MILLIS = 30000L
9+
internal const val DEFAULT_APPHANG_TIMEOUT_INTERVAL_MILLIS = 2000L
10+
internal const val DEFAULT_ANR_TIMEOUT_INTERVAL_MILLIS = 5000L
911

1012
/** Sentry options that can be used to configure the SDK. */
1113
public open class SentryOptions {
@@ -120,4 +122,53 @@ public open class SentryOptions {
120122
* transactions will be sent. Transactions are picked randomly. Default is null (disabled)
121123
*/
122124
public var tracesSampleRate: Double? = null
125+
126+
/**
127+
* Controls the tracking of App Hangs capturing moments when the application
128+
* becomes unresponsive for a set duration.
129+
*
130+
* **Default**: Enabled.
131+
*
132+
* **Platform Availability**: Cocoa.
133+
*
134+
* For more information, refer to the Cocoa documentation on App Hangs:
135+
* [Cocoa App Hangs](https://docs.sentry.io/platforms/apple/configuration/app-hangs/)
136+
*/
137+
public var enableAppHangTracking: Boolean = true
138+
139+
/**
140+
* Defines the timeout interval in milliseconds for detecting App Hangs.
141+
*
142+
* **Default**: 2000 milliseconds (2 seconds).
143+
*
144+
* **Platform Availability**: Cocoa.
145+
*
146+
* For configuration details and best practices, see:
147+
* [Cocoa App Hangs](https://docs.sentry.io/platforms/apple/configuration/app-hangs/)
148+
*/
149+
public var appHangTimeoutIntervalMillis: Long = DEFAULT_APPHANG_TIMEOUT_INTERVAL_MILLIS
150+
151+
/**
152+
* Enables or disables ANR (Application Not Responding) tracking.
153+
*
154+
* **Default**: Enabled.
155+
*
156+
* **Platform Availability**: Android.
157+
*
158+
* Detailed documentation on ANR tracking can be found here:
159+
* [Android ANR](https://docs.sentry.io/platforms/android/configuration/app-not-respond/)
160+
*/
161+
public var isAnrEnabled: Boolean = true
162+
163+
/**
164+
* Sets the timeout interval in milliseconds for considering an application as not responding (ANR).
165+
*
166+
* **Default**: 5000 milliseconds (5 seconds).
167+
*
168+
* **Platform Availability**: Android.
169+
*
170+
* For more information on configuring ANR detection, visit:
171+
* [Android ANR](https://docs.sentry.io/platforms/android/configuration/app-not-respond/)
172+
*/
173+
public var anrTimeoutIntervalMillis: Long = DEFAULT_ANR_TIMEOUT_INTERVAL_MILLIS
123174
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package io.sentry.kotlin.multiplatform
2+
3+
interface CommonPlatformOptions {
4+
val dsn: String?
5+
val attachStackTrace: Boolean
6+
val release: String?
7+
val debug: Boolean
8+
val environment: String?
9+
val dist: String?
10+
val enableAutoSessionTracking: Boolean
11+
val sessionTrackingIntervalMillis: Long
12+
val maxBreadcrumbs: Int
13+
val maxAttachmentSize: Long
14+
val sampleRate: Double?
15+
val tracesSampleRate: Double?
16+
17+
fun applyFromOptions(options: SentryOptions)
18+
}
19+
20+
expect interface PlatformOptions : CommonPlatformOptions
21+
22+
expect fun createPlatformOptions(): PlatformOptions

sentry-kotlin-multiplatform/src/commonTest/kotlin/io/sentry/kotlin/multiplatform/SentryOptionsTest.kt

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@ import io.sentry.kotlin.multiplatform.protocol.Breadcrumb
44
import io.sentry.kotlin.multiplatform.utils.fakeDsn
55
import kotlin.test.Test
66
import kotlin.test.assertEquals
7+
import kotlin.test.assertFalse
8+
import kotlin.test.assertNull
9+
import kotlin.test.assertTrue
710

811
class SentryOptionsTest : BaseSentryTest() {
912
@Test
@@ -93,4 +96,79 @@ class SentryOptionsTest : BaseSentryTest() {
9396

9497
assertEquals(null, modifiedBreadcrumb)
9598
}
99+
100+
@Test
101+
fun `GIVEN SentryOptions THEN default values are set`() {
102+
val options = SentryOptions()
103+
104+
assertNull(options.dsn)
105+
assertTrue(options.attachStackTrace)
106+
assertTrue(options.attachThreads)
107+
assertNull(options.release)
108+
assertFalse(options.debug)
109+
assertNull(options.environment)
110+
assertNull(options.dist)
111+
assertTrue(options.enableAutoSessionTracking)
112+
assertEquals(DEFAULT_SESSION_INTERVAL_MILLIS, options.sessionTrackingIntervalMillis)
113+
assertFalse(options.attachScreenshot)
114+
assertNull(options.beforeBreadcrumb)
115+
assertNull(options.beforeSend)
116+
assertNull(options.sdk)
117+
assertEquals(DEFAULT_MAX_BREADCRUMBS, options.maxBreadcrumbs)
118+
assertEquals(DEFAULT_MAX_ATTACHMENT_SIZE, options.maxAttachmentSize)
119+
assertFalse(options.attachViewHierarchy)
120+
assertTrue(options.enableCaptureFailedRequests)
121+
assertEquals(listOf(HttpStatusCodeRange()), options.failedRequestStatusCodes)
122+
assertEquals(listOf(".*"), options.failedRequestTargets)
123+
assertNull(options.sampleRate)
124+
assertNull(options.tracesSampleRate)
125+
assertTrue(options.enableAppHangTracking)
126+
assertEquals(2000L, options.appHangTimeoutIntervalMillis)
127+
assertTrue(options.isAnrEnabled)
128+
assertEquals(5000L, options.anrTimeoutIntervalMillis)
129+
}
130+
131+
@Test
132+
fun `GIVEN SentryOptions WHEN applyFromOptions THEN applies values to native options`() {
133+
val options = SentryOptions().apply {
134+
dsn = fakeDsn
135+
attachStackTrace = false
136+
release = "release"
137+
debug = true
138+
environment = "environment"
139+
dist = "dist"
140+
enableAutoSessionTracking = false
141+
sessionTrackingIntervalMillis = 1000L
142+
maxBreadcrumbs = 10
143+
maxAttachmentSize = 100L
144+
sampleRate = 0.5
145+
tracesSampleRate = 0.5
146+
attachScreenshot = true
147+
attachViewHierarchy = true
148+
enableAppHangTracking = false
149+
appHangTimeoutIntervalMillis = 1000L
150+
isAnrEnabled = false
151+
anrTimeoutIntervalMillis = 1000L
152+
}
153+
154+
val platformOptions = createPlatformOptions()
155+
platformOptions.applyFromOptions(options)
156+
157+
assertEquals(fakeDsn, platformOptions.dsn)
158+
assertFalse(platformOptions.attachStackTrace)
159+
assertEquals("release", platformOptions.release)
160+
assertTrue(platformOptions.debug)
161+
assertEquals("environment", platformOptions.environment)
162+
assertEquals("dist", platformOptions.dist)
163+
assertFalse(platformOptions.enableAutoSessionTracking)
164+
assertEquals(1000L, platformOptions.sessionTrackingIntervalMillis)
165+
assertEquals(10, platformOptions.maxBreadcrumbs)
166+
assertEquals(100L, platformOptions.maxAttachmentSize)
167+
assertEquals(0.5, platformOptions.sampleRate)
168+
assertEquals(0.5, platformOptions.tracesSampleRate)
169+
170+
platformOptions.assertPlatformSpecificOptions(options)
171+
}
96172
}
173+
174+
expect fun PlatformOptions.assertPlatformSpecificOptions(options: SentryOptions)

0 commit comments

Comments
 (0)