Skip to content

Commit 8d8cd4e

Browse files
authored
feat: beforeSend / fingerprinting (#70)
* add initial jvm impl * implement hook for apple * add more SentryEvent fields * format code * add SentryId to event * remove prints * fix setting context for Apple * improve consistency * consistency * add minor docs * ref: non-null init for Apple SentryEvent * add changelog * document thread-safety and improve null-safety * remove boilerplate this * remove boilerplate this * add more unit tests * remove this * make breadcrumbs, tags, fingerprints and exceptions mutable * use internal set for context * fix tests * change to non null collections and improve docs * remove this scope from extension fun * fix warnings
1 parent fab4a6e commit 8d8cd4e

File tree

25 files changed

+760
-46
lines changed

25 files changed

+760
-46
lines changed

CHANGELOG.md

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

55
### Features
66

7+
- feat: beforeSend / fingerprinting ([#70](https://github.com/getsentry/sentry-kotlin-multiplatform/pull/70))
78
- feat: configuring http client errors for Apple targets ([#76](https://github.com/getsentry/sentry-kotlin-multiplatform/pull/76))
89
- feat: improve Objc/Swift experience with @HiddenFromObjc ([#62](https://github.com/getsentry/sentry-kotlin-multiplatform/pull/62))
910
- feat: add view hierarchy ([#53](https://github.com/getsentry/sentry-kotlin-multiplatform/pull/53))

gradle.properties

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
# Kotlin
2-
kotlin.version=1.8.0
32
kotlin.incremental.multiplatform=true
43
kotlin.code.style=official
54
kotlin.mpp.stability.nowarn=true
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
package io.sentry.kotlin.multiplatform
2+
3+
import io.sentry.kotlin.multiplatform.extensions.toKmpBreadcrumb
4+
import io.sentry.kotlin.multiplatform.extensions.toKmpMessage
5+
import io.sentry.kotlin.multiplatform.extensions.toKmpSentryException
6+
import io.sentry.kotlin.multiplatform.extensions.toKmpSentryLevel
7+
import io.sentry.kotlin.multiplatform.extensions.toKmpUser
8+
import io.sentry.kotlin.multiplatform.protocol.Message
9+
import io.sentry.kotlin.multiplatform.protocol.SentryException
10+
import io.sentry.kotlin.multiplatform.protocol.SentryId
11+
import io.sentry.kotlin.multiplatform.protocol.User
12+
13+
public actual class SentryEvent actual constructor() : SentryBaseEvent() {
14+
public actual var level: SentryLevel? = null
15+
public actual var message: Message? = null
16+
public actual var logger: String? = null
17+
public actual var fingerprint: MutableList<String> = mutableListOf()
18+
public actual var exceptions: MutableList<SentryException> = mutableListOf()
19+
public override var release: String? = null
20+
public override var environment: String? = null
21+
public override var platform: String? = null
22+
public override var user: User? = null
23+
public override var serverName: String? = null
24+
public override var dist: String? = null
25+
26+
public constructor(cocoaSentryEvent: CocoaSentryEvent) : this() {
27+
eventId = SentryId(cocoaSentryEvent.eventId.toString())
28+
level = cocoaSentryEvent.level?.toKmpSentryLevel()
29+
message = cocoaSentryEvent.message?.toKmpMessage()
30+
logger = cocoaSentryEvent.logger
31+
release = cocoaSentryEvent.releaseName
32+
environment = cocoaSentryEvent.environment
33+
platform = cocoaSentryEvent.platform
34+
user = cocoaSentryEvent.user?.toKmpUser()
35+
serverName = cocoaSentryEvent.serverName
36+
dist = cocoaSentryEvent.dist
37+
38+
val cocoaFingerprint =
39+
cocoaSentryEvent.fingerprint()?.toMutableList() as? MutableList<String>
40+
val cocoaSentryExceptions =
41+
cocoaSentryEvent.exceptions?.map { (it as CocoaSentryException).toKmpSentryException() }
42+
?.toMutableList()
43+
val cocoaContexts =
44+
cocoaSentryEvent.context?.mapKeys { it.key as String }?.mapValues { it.value as Any }
45+
val cocoaBreadcrumbs = cocoaSentryEvent.breadcrumbs?.mapNotNull { it as? CocoaBreadcrumb }
46+
?.map { it.toKmpBreadcrumb() }?.toMutableList()
47+
val cocoaTags =
48+
cocoaSentryEvent.tags?.mapKeys { it.key as String }?.mapValues { it.value as String }
49+
?.toMutableMap()
50+
51+
cocoaFingerprint?.let { fingerprint = it }
52+
cocoaSentryExceptions?.let { exceptions = it }
53+
cocoaContexts?.let { contexts = it }
54+
cocoaBreadcrumbs?.let { breadcrumbs = it }
55+
cocoaTags?.let { tags = it }
56+
}
57+
}

sentry-kotlin-multiplatform/src/commonAppleMain/kotlin/io/sentry/kotlin/multiplatform/TypeAliases.kt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,11 @@ package io.sentry.kotlin.multiplatform
22

33
import cocoapods.Sentry.SentryAttachment
44
import cocoapods.Sentry.SentryBreadcrumb
5+
import cocoapods.Sentry.SentryEvent
6+
import cocoapods.Sentry.SentryException
57
import cocoapods.Sentry.SentryId
68
import cocoapods.Sentry.SentryLevel
9+
import cocoapods.Sentry.SentryMessage
710
import cocoapods.Sentry.SentryOptions
811
import cocoapods.Sentry.SentryScope
912
import cocoapods.Sentry.SentryUser
@@ -17,3 +20,6 @@ internal typealias CocoaSentryId = SentryId
1720
internal typealias CocoaSentryLevel = SentryLevel
1821
internal typealias CocoaAttachment = SentryAttachment
1922
internal typealias CocoaUserFeedback = SentryUserFeedback
23+
internal typealias CocoaSentryEvent = SentryEvent
24+
internal typealias CocoaMessage = SentryMessage
25+
internal typealias CocoaSentryException = SentryException
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package io.sentry.kotlin.multiplatform.extensions
2+
3+
import io.sentry.kotlin.multiplatform.CocoaMessage
4+
import io.sentry.kotlin.multiplatform.protocol.Message
5+
6+
internal fun CocoaMessage.toKmpMessage() = Message(
7+
message = message,
8+
params = params as? List<String>,
9+
formatted = formatted
10+
)
11+
12+
internal fun Message.toCocoaMessage(): CocoaMessage {
13+
val scope = this@toCocoaMessage
14+
val cocoaMessage = scope.formatted?.let { CocoaMessage(it) } ?: CocoaMessage()
15+
return cocoaMessage.apply {
16+
message = scope.message
17+
params = scope.params
18+
}
19+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package io.sentry.kotlin.multiplatform.extensions
2+
3+
import cocoapods.Sentry.SentryId
4+
import io.sentry.kotlin.multiplatform.CocoaSentryEvent
5+
import io.sentry.kotlin.multiplatform.SentryEvent
6+
7+
internal fun CocoaSentryEvent.applyKmpEvent(kmpEvent: SentryEvent): CocoaSentryEvent {
8+
kmpEvent.level?.let { level = it.toCocoaSentryLevel() }
9+
kmpEvent.platform?.let { platform = it }
10+
message = kmpEvent.message?.toCocoaMessage()
11+
logger = kmpEvent.logger
12+
fingerprint = kmpEvent.fingerprint
13+
releaseName = kmpEvent.release
14+
environment = kmpEvent.environment
15+
user = kmpEvent.user?.toCocoaUser()
16+
serverName = kmpEvent.serverName
17+
dist = kmpEvent.dist
18+
breadcrumbs = kmpEvent.breadcrumbs?.map { it.toCocoaBreadcrumb() }?.toMutableList()
19+
tags = kmpEvent.tags?.toMutableMap()
20+
eventId = SentryId(kmpEvent.eventId.toString())
21+
return this
22+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package io.sentry.kotlin.multiplatform.extensions
2+
3+
import io.sentry.kotlin.multiplatform.CocoaSentryException
4+
import io.sentry.kotlin.multiplatform.protocol.SentryException
5+
6+
internal fun CocoaSentryException.toKmpSentryException() = SentryException(
7+
type = type,
8+
value = value,
9+
module = module,
10+
threadId = threadId as Long?
11+
)

sentry-kotlin-multiplatform/src/commonAppleMain/kotlin/io/sentry/kotlin/multiplatform/extensions/SentryOptionsExtensions.kt

Lines changed: 21 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
package io.sentry.kotlin.multiplatform.extensions
22

33
import PrivateSentrySDKOnly.Sentry.PrivateSentrySDKOnly
4-
import cocoapods.Sentry.SentryEvent
54
import cocoapods.Sentry.SentryHttpStatusCodeRange
65
import io.sentry.kotlin.multiplatform.BuildKonfig
6+
import io.sentry.kotlin.multiplatform.CocoaSentryEvent
77
import io.sentry.kotlin.multiplatform.CocoaSentryOptions
8+
import io.sentry.kotlin.multiplatform.SentryEvent
89
import io.sentry.kotlin.multiplatform.SentryOptions
910
import io.sentry.kotlin.multiplatform.nsexception.dropKotlinCrashEvent
1011
import kotlinx.cinterop.convert
@@ -19,19 +20,19 @@ internal fun SentryOptions.toCocoaOptionsConfiguration(): (CocoaSentryOptions?)
1920
* This avoids code duplication for init on iOS.
2021
*/
2122
internal fun CocoaSentryOptions.applyCocoaBaseOptions(options: SentryOptions) {
22-
this.dsn = options.dsn
23-
this.attachStacktrace = options.attachStackTrace
24-
this.dist = options.dist
23+
dsn = options.dsn
24+
attachStacktrace = options.attachStackTrace
25+
dist = options.dist
2526
options.environment?.let {
26-
this.environment = it
27+
environment = it
2728
}
28-
this.releaseName = options.release
29-
this.debug = options.debug
30-
this.sessionTrackingIntervalMillis = options.sessionTrackingIntervalMillis.convert()
31-
this.enableAutoSessionTracking = options.enableAutoSessionTracking
32-
this.maxAttachmentSize = options.maxAttachmentSize.convert()
33-
this.maxBreadcrumbs = options.maxBreadcrumbs.convert()
34-
this.beforeSend = { event ->
29+
releaseName = options.release
30+
debug = options.debug
31+
sessionTrackingIntervalMillis = options.sessionTrackingIntervalMillis.convert()
32+
enableAutoSessionTracking = options.enableAutoSessionTracking
33+
maxAttachmentSize = options.maxAttachmentSize.convert()
34+
maxBreadcrumbs = options.maxBreadcrumbs.convert()
35+
beforeSend = { event ->
3536
val cocoaName = BuildKonfig.SENTRY_COCOA_PACKAGE_NAME
3637
val cocoaVersion = BuildKonfig.SENTRY_COCOA_VERSION
3738

@@ -49,14 +50,20 @@ internal fun CocoaSentryOptions.applyCocoaBaseOptions(options: SentryOptions) {
4950
sdk?.set("packages", packages)
5051

5152
event?.sdk = sdk
52-
dropKotlinCrashEvent(event as NSExceptionSentryEvent?) as SentryEvent?
53+
54+
val modifiedEvent = event?.let { SentryEvent(it) }?.let { unwrappedEvent ->
55+
val result = options.beforeSend?.invoke(unwrappedEvent)
56+
result?.let { event.applyKmpEvent(it) }
57+
}
58+
59+
dropKotlinCrashEvent(modifiedEvent as NSExceptionSentryEvent?) as CocoaSentryEvent?
5360
}
5461

5562
val sdkName = options.sdk?.name ?: BuildKonfig.SENTRY_KMP_COCOA_SDK_NAME
5663
val sdkVersion = options.sdk?.version ?: BuildKonfig.VERSION_NAME
5764
PrivateSentrySDKOnly.setSdkName(sdkName, sdkVersion)
5865

59-
this.beforeBreadcrumb = { cocoaBreadcrumb ->
66+
beforeBreadcrumb = { cocoaBreadcrumb ->
6067
cocoaBreadcrumb?.toKmpBreadcrumb()
6168
?.let { options.beforeBreadcrumb?.invoke(it) }?.toCocoaBreadcrumb()
6269
}

sentry-kotlin-multiplatform/src/commonAppleMain/kotlin/io/sentry/kotlin/multiplatform/extensions/UserExtensions.kt

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,8 @@ internal fun User.toCocoaUser() = CocoaUser().apply {
1313

1414
internal fun CocoaUser.toKmpUser() = User().apply {
1515
val scope = this@toKmpUser
16-
id = scope.userId.toString()
17-
username = scope.username.toString()
18-
email = scope.email.toString()
19-
ipAddress = scope.ipAddress.toString()
20-
setData(scope.data)
16+
id = scope.userId
17+
username = scope.username
18+
email = scope.email
19+
ipAddress = scope.ipAddress
2120
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
package io.sentry.kotlin.multiplatform
2+
3+
import io.sentry.kotlin.multiplatform.extensions.toKmpBreadcrumb
4+
import io.sentry.kotlin.multiplatform.extensions.toKmpMessage
5+
import io.sentry.kotlin.multiplatform.extensions.toKmpSentryException
6+
import io.sentry.kotlin.multiplatform.extensions.toKmpSentryLevel
7+
import io.sentry.kotlin.multiplatform.extensions.toKmpUser
8+
import io.sentry.kotlin.multiplatform.protocol.Message
9+
import io.sentry.kotlin.multiplatform.protocol.SentryException
10+
import io.sentry.kotlin.multiplatform.protocol.SentryId
11+
import io.sentry.kotlin.multiplatform.protocol.User
12+
13+
public actual class SentryEvent actual constructor() : SentryBaseEvent() {
14+
public actual var level: SentryLevel? = null
15+
public actual var message: Message? = null
16+
public actual var logger: String? = null
17+
public actual var fingerprint: MutableList<String> = mutableListOf()
18+
public actual var exceptions: MutableList<SentryException> = mutableListOf()
19+
public override var release: String? = null
20+
public override var environment: String? = null
21+
public override var platform: String? = null
22+
public override var user: User? = null
23+
public override var serverName: String? = null
24+
public override var dist: String? = null
25+
26+
public constructor(jvmSentryEvent: JvmSentryEvent) : this() {
27+
eventId = SentryId(jvmSentryEvent.eventId.toString())
28+
level = jvmSentryEvent.level?.toKmpSentryLevel()
29+
message = jvmSentryEvent.message?.toKmpMessage()
30+
logger = jvmSentryEvent.logger
31+
release = jvmSentryEvent.release
32+
environment = jvmSentryEvent.environment
33+
platform = jvmSentryEvent.platform
34+
user = jvmSentryEvent.user?.toKmpUser()
35+
serverName = jvmSentryEvent.serverName
36+
dist = jvmSentryEvent.dist
37+
contexts = jvmSentryEvent.contexts
38+
jvmSentryEvent.fingerprints?.let { fingerprint = it }
39+
jvmSentryEvent.exceptions?.let { exceptions = it.map { it.toKmpSentryException() }.toMutableList() }
40+
jvmSentryEvent.breadcrumbs?.let { breadcrumbs = it.map { it.toKmpBreadcrumb() }.toMutableList() }
41+
jvmSentryEvent.tags?.let { tags = it }
42+
}
43+
}

sentry-kotlin-multiplatform/src/commonJvmMain/kotlin/io/sentry/kotlin/multiplatform/TypeAliases.kt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,13 @@ package io.sentry.kotlin.multiplatform
33
import io.sentry.Attachment
44
import io.sentry.Breadcrumb
55
import io.sentry.Scope
6+
import io.sentry.SentryEvent
67
import io.sentry.SentryLevel
78
import io.sentry.SentryOptions
89
import io.sentry.UserFeedback
10+
import io.sentry.protocol.Contexts
11+
import io.sentry.protocol.Message
12+
import io.sentry.protocol.SentryException
913
import io.sentry.protocol.SentryId
1014
import io.sentry.protocol.User
1115

@@ -17,3 +21,7 @@ internal typealias JvmSentryId = SentryId
1721
internal typealias JvmSentryOptions = SentryOptions
1822
internal typealias JvmAttachment = Attachment
1923
internal typealias JvmUserFeedback = UserFeedback
24+
internal typealias JvmSentryEvent = SentryEvent
25+
internal typealias JvmMessage = Message
26+
internal typealias JvmSentryException = SentryException
27+
internal typealias JvmContexts = Contexts
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package io.sentry.kotlin.multiplatform.extensions
2+
3+
import io.sentry.kotlin.multiplatform.JvmMessage
4+
import io.sentry.kotlin.multiplatform.protocol.Message
5+
6+
internal fun JvmMessage.toKmpMessage() = Message(
7+
message = message,
8+
params = params,
9+
formatted = formatted
10+
)
11+
12+
internal fun Message.toJvmMessage() = JvmMessage().apply {
13+
val scope = this@toJvmMessage
14+
message = scope.message
15+
params = scope.params
16+
formatted = scope.formatted
17+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package io.sentry.kotlin.multiplatform.extensions
2+
3+
import io.sentry.kotlin.multiplatform.JvmSentryEvent
4+
import io.sentry.kotlin.multiplatform.JvmSentryId
5+
import io.sentry.kotlin.multiplatform.SentryEvent
6+
7+
internal fun JvmSentryEvent.applyKmpEvent(kmpEvent: SentryEvent): JvmSentryEvent {
8+
level = kmpEvent.level?.toJvmSentryLevel()
9+
message = kmpEvent.message?.toJvmMessage()
10+
logger = kmpEvent.logger
11+
fingerprints = kmpEvent.fingerprint
12+
release = kmpEvent.release
13+
environment = kmpEvent.environment
14+
platform = kmpEvent.platform
15+
user = kmpEvent.user?.toJvmUser()
16+
serverName = kmpEvent.serverName
17+
dist = kmpEvent.dist
18+
breadcrumbs = kmpEvent.breadcrumbs?.map { it.toJvmBreadcrumb() }
19+
eventId = JvmSentryId(kmpEvent.eventId.toString())
20+
tags = kmpEvent.tags
21+
return this
22+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package io.sentry.kotlin.multiplatform.extensions
2+
3+
import io.sentry.kotlin.multiplatform.JvmSentryException
4+
import io.sentry.kotlin.multiplatform.protocol.SentryException
5+
6+
internal fun JvmSentryException.toKmpSentryException() = SentryException(
7+
type = type,
8+
value = value,
9+
module = module,
10+
threadId = threadId
11+
)

0 commit comments

Comments
 (0)