From 9fc534f14c1b0cf095702c59f49dd531c76c1c8f Mon Sep 17 00:00:00 2001 From: Giancarlo Buenaflor Date: Wed, 3 Dec 2025 21:44:48 +0100 Subject: [PATCH 1/2] Update --- .../multiplatform/SentryBridge.apple.kt | 18 +++++++---- .../nsexception/SentryUnhandledExceptions.kt | 30 +++++++++++++------ 2 files changed, 34 insertions(+), 14 deletions(-) diff --git a/sentry-kotlin-multiplatform/src/appleMain/kotlin/io/sentry/kotlin/multiplatform/SentryBridge.apple.kt b/sentry-kotlin-multiplatform/src/appleMain/kotlin/io/sentry/kotlin/multiplatform/SentryBridge.apple.kt index 933ddae0..4eb6737c 100644 --- a/sentry-kotlin-multiplatform/src/appleMain/kotlin/io/sentry/kotlin/multiplatform/SentryBridge.apple.kt +++ b/sentry-kotlin-multiplatform/src/appleMain/kotlin/io/sentry/kotlin/multiplatform/SentryBridge.apple.kt @@ -1,11 +1,12 @@ package io.sentry.kotlin.multiplatform import Internal.Sentry.PrivateSentrySDKOnly +import Internal.Sentry.kSentryLevelError import cocoapods.Sentry.SentrySDK import io.sentry.kotlin.multiplatform.extensions.toCocoaBreadcrumb import io.sentry.kotlin.multiplatform.extensions.toCocoaUser import io.sentry.kotlin.multiplatform.extensions.toCocoaUserFeedback -import io.sentry.kotlin.multiplatform.nsexception.asNSException +import io.sentry.kotlin.multiplatform.nsexception.asSentryEvent import io.sentry.kotlin.multiplatform.nsexception.dropKotlinCrashEvent import io.sentry.kotlin.multiplatform.protocol.Breadcrumb import io.sentry.kotlin.multiplatform.protocol.SentryId @@ -75,15 +76,22 @@ internal actual class SentryBridge actual constructor(private val sentryInstance } actual fun captureException(throwable: Throwable): SentryId { - val cocoaSentryId = SentrySDK.captureException(throwable.asNSException(true)) + val event = throwable.asSentryEvent( + level = kSentryLevelError, + isHandled = true, + markThreadAsCrashed = false + ) + val cocoaSentryId = SentrySDK.captureEvent(event) return SentryId(cocoaSentryId.toString()) } actual fun captureException(throwable: Throwable, scopeCallback: ScopeCallback): SentryId { - val cocoaSentryId = SentrySDK.captureException( - throwable.asNSException(true), - configureScopeCallback(scopeCallback) + val event = throwable.asSentryEvent( + level = kSentryLevelError, + isHandled = true, + markThreadAsCrashed = false ) + val cocoaSentryId = SentrySDK.captureEvent(event, configureScopeCallback(scopeCallback)) return SentryId(cocoaSentryId.toString()) } diff --git a/sentry-kotlin-multiplatform/src/appleMain/kotlin/io/sentry/kotlin/multiplatform/nsexception/SentryUnhandledExceptions.kt b/sentry-kotlin-multiplatform/src/appleMain/kotlin/io/sentry/kotlin/multiplatform/nsexception/SentryUnhandledExceptions.kt index 1039b2bc..040d2213 100644 --- a/sentry-kotlin-multiplatform/src/appleMain/kotlin/io/sentry/kotlin/multiplatform/nsexception/SentryUnhandledExceptions.kt +++ b/sentry-kotlin-multiplatform/src/appleMain/kotlin/io/sentry/kotlin/multiplatform/nsexception/SentryUnhandledExceptions.kt @@ -16,6 +16,7 @@ package io.sentry.kotlin.multiplatform.nsexception import Internal.Sentry.NSExceptionKt_SentryCrashStackCursorFromNSException import Internal.Sentry.kSentryLevelFatal +import io.sentry.kotlin.multiplatform.CocoaSentryLevel import kotlinx.cinterop.invoke import platform.Foundation.NSException import platform.Foundation.NSNumber @@ -96,19 +97,29 @@ private fun Throwable.asSentryEnvelope(): CocoapodsSentryEnvelope { /** * Converts `this` [Throwable] to a [cocoapods.Sentry.SentryEvent]. + * + * @param level The Sentry level (e.g., kSentryLevelFatal for crashes, kSentryLevelError for handled) + * @param isHandled Whether this is a handled exception (false for crashes, true for captured exceptions) + * @param markThreadAsCrashed Whether to mark the current thread as crashed (true for crashes, false for handled) */ @Suppress("UnnecessaryOptInAnnotation") -private fun Throwable.asSentryEvent(): CocoapodsSentryEvent = - CocoapodsSentryEvent(kSentryLevelFatal).apply { +internal fun Throwable.asSentryEvent( + level: CocoaSentryLevel = kSentryLevelFatal, + isHandled: Boolean = false, + markThreadAsCrashed: Boolean = true +): CocoapodsSentryEvent = + CocoapodsSentryEvent(level).apply { @Suppress("UNCHECKED_CAST") val threads = threadInspector?.getCurrentThreadsWithStackTrace() as List? this.threads = threads val currentThread = threads?.firstOrNull { it.current?.boolValue ?: false }?.apply { - setCrashed(NSNumber(true)) - // Crashed threads shouldn't have a stacktrace, the thread_id should be set on the exception instead - // https://develop.sentry.dev/sdk/event-payloads/threads/ - stacktrace = null + if (markThreadAsCrashed) { + setCrashed(NSNumber(true)) + // Crashed threads shouldn't have a stacktrace, the thread_id should be set on the exception instead + // https://develop.sentry.dev/sdk/event-payloads/threads/ + stacktrace = null + } } debugMeta = threads?.let { InternalSentryDependencyContainer.sharedInstance().debugImageProvider.getDebugImagesForThreads( @@ -117,18 +128,19 @@ private fun Throwable.asSentryEvent(): CocoapodsSentryEvent = } exceptions = this@asSentryEvent .let { throwable -> throwable.causes.asReversed() + throwable } - .map { it.asNSException().asSentryException(currentThread?.threadId) } + .map { it.asNSException().asSentryException(currentThread?.threadId, isHandled) } } /** * Converts `this` [NSException] to a [io.sentry.kotlin.multiplatform.protocol.SentryException]. */ private fun NSException.asSentryException( - threadId: NSNumber? + threadId: NSNumber?, + isHandled: Boolean = false ): CocoapodsSentryException = CocoapodsSentryException(reason ?: "", name ?: "Throwable").apply { this.threadId = threadId mechanism = CocoapodsSentryMechanism("generic").apply { - setHandled(NSNumber(false)) + setHandled(NSNumber(isHandled)) } stacktrace = threadInspector?.stacktraceBuilder?.let { stacktraceBuilder -> val cursor = NSExceptionKt_SentryCrashStackCursorFromNSException(this@asSentryException) From 2c07cc95cdee00509a55ab48e24c6c0bb0cf58e7 Mon Sep 17 00:00:00 2001 From: Giancarlo Buenaflor Date: Wed, 10 Dec 2025 00:47:38 +0100 Subject: [PATCH 2/2] Update CHANGELOG.md to reflect upcoming release changes, including improvements to iOS crash reports and stacktrace handling. Mark the release as "Unreleased" and add warnings regarding potential impacts on issue grouping for iOS events. --- CHANGELOG.md | 49 +++++++++++++++++++++++++++++-------------------- 1 file changed, 29 insertions(+), 20 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6a752709..d232401d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,11 +1,17 @@ # Changelog -## 0.22.0 +## Unreleased + +⚠️ This release will affect issue grouping for iOS events as Sentry now captures correct stacktraces for manually captured and crashed iOS events. ### Features - Improve iOS crash reports by adding scope data ([#491](https://github.com/getsentry/sentry-kotlin-multiplatform/pull/491)) - - ⚠️ This change will most likely affect issue grouping as Sentry now properly symbolicates Kotlin iOS crashes +- Improve stacktrace of manually captured exceptions on iOS ([#493](https://github.com/getsentry/sentry-kotlin-multiplatform/pull/493)) + +## 0.22.0 + +### Features ### Dependencies @@ -169,6 +175,7 @@ Potentially breaking: this release bumps the used Kotlin version to `2.1.21`. ### Features - Add experimental session replay options to common code ([#275](https://github.com/getsentry/sentry-kotlin-multiplatform/pull/275)) + ```kotlin Sentry.init { options -> // Adjust these values for production @@ -176,6 +183,7 @@ Sentry.init { options -> options.sessionReplay.sessionSampleRate = 1.0 } ``` + - Add `Sentry.isEnabled()` API to common code ([#273](https://github.com/getsentry/sentry-kotlin-multiplatform/pull/273)) - Add `enableWatchdogTerminationTracking` in common options ([#281](https://github.com/getsentry/sentry-kotlin-multiplatform/pull/281)) - Add `diagnosticLevel` in common options ([#287](https://github.com/getsentry/sentry-kotlin-multiplatform/pull/287)) @@ -213,12 +221,13 @@ Sentry.init { options -> - Enables auto installing of the required Sentry Cocoa SDK with Cocoapods (if Cocoapods plugin is enabled) - Configures linking for SPM (needed if you want to compile a dynamic framework with `isStatic = false`) - Configure via the `sentryKmp` configuration block in your build file + ```kotlin // Example configuration in build.gradle.kts sentryKmp { // Disable auto installing the KMP SDK to commonMain autoInstall.commonMain.enabled = false -} +} ``` ### Dependencies @@ -236,7 +245,7 @@ sentryKmp { ### Features - New Sentry KMP Gradle plugin ([#230](https://github.com/getsentry/sentry-kotlin-multiplatform/pull/230)) - - Install via `plugins { id("io.sentry.kotlin.multiplatform.gradle") version "{version}" }` + - Install via `plugins { id("io.sentry.kotlin.multiplatform.gradle") version "{version}" }` - Enables auto installing of the KMP SDK to commonMain (if all targets are supported) - Enables auto installing of the required Sentry Cocoa SDK with Cocoapods (if Cocoapods plugin is enabled) - Configures linking for SPM (needed if you want to compile a dynamic framework) @@ -266,6 +275,7 @@ sentryKmp { - This allows you to initialize the SDK with platform-specific options that may not be available in the common code of the KMP SDK yet. Usage: + ```kotlin // build.gradle.kts kotlin { @@ -284,7 +294,7 @@ fun init() { expect fun platformOptionsConfiguration(): PlatformOptionsConfiguration // iOS -actual fun createPlatformOptions(): PlatformOptionsConfiguration = { +actual fun createPlatformOptions(): PlatformOptionsConfiguration = { dsn = "your_dsn" release = "1.0.0" // ... @@ -366,8 +376,8 @@ pod("Sentry") { - [changelog](https://github.com/getsentry/sentry-java/blob/main/CHANGELOG.md#710) - [diff](https://github.com/getsentry/sentry-java/compare/6.33.1...7.1.0) - Bump Cocoa SDK from v8.4.0 to v8.17.1 ([#158](https://github.com/getsentry/sentry-kotlin-multiplatform/pull/163)) - - [changelog](https://github.com/getsentry/sentry-cocoa/blob/main/CHANGELOG.md#8172) - - [diff](https://github.com/getsentry/sentry-cocoa/compare/8.4.0...8.17.2) + - [changelog](https://github.com/getsentry/sentry-cocoa/blob/main/CHANGELOG.md#8172) + - [diff](https://github.com/getsentry/sentry-cocoa/compare/8.4.0...8.17.2) - Bump Kotlin version from v1.8.0 to v1.9.21 ([#146](https://github.com/getsentry/sentry-kotlin-multiplatform/pull/146) ## 0.3.0 @@ -405,7 +415,7 @@ pod("Sentry") { ## 0.1.1 -### Fixes +### Fixes - fix: beforeSend dropping events if not set in options ([#79](https://github.com/getsentry/sentry-kotlin-multiplatform/pull/79)) @@ -442,15 +452,14 @@ pod("Sentry") { ### Features - - JVM, Android, iOS, macOS, watchOS, tvOS integration - - Sentry init and close - - Capture Message - - Capture Exception with proper stack traces - - Custom unhandled exception handler on Cocoa to properly catch crashes and the stacktrace - - Scope configuration globally and locally - - User Feedback - - Attachments to Scope - - Screenshots option for Android and iOS - - Add beforeBreadcrumb hook - - Kotlin Multiplatform Sample project - +- JVM, Android, iOS, macOS, watchOS, tvOS integration +- Sentry init and close +- Capture Message +- Capture Exception with proper stack traces +- Custom unhandled exception handler on Cocoa to properly catch crashes and the stacktrace +- Scope configuration globally and locally +- User Feedback +- Attachments to Scope +- Screenshots option for Android and iOS +- Add beforeBreadcrumb hook +- Kotlin Multiplatform Sample project