Skip to content

Commit

Permalink
Replace Sentry with BugSnag for error reporting (#313)
Browse files Browse the repository at this point in the history
* Add bugsnag Android dependency

* Remove Sentry and BuildKonfig usage

* Add CrashKiOS bugsnag dependency

* Remove `sentry.dsn` from `gradle.properties`

* Add BugSnag API key in the Android app manifest using manifest placeholders

* Enable BugSnag for non-debug builds on Android

* Report background worker errors to BugSnag

* Enable BugSnag for non-debug version on iOS

* Upload dSYM to BugSnag instead of Sentry in when making iOS prod release

* Reporting handled errors in common code using BugSnag

* Remove unused Sentry usages

* Add bugsnag as a pod

* Update build archive command in iOS prod release workflow

* Fix curl command for upload dSYM to Bugsnag

* Change dSYM path in upload to Bugsnag step
  • Loading branch information
msasikanth authored Feb 14, 2024
1 parent e8e74b0 commit 8fff78f
Show file tree
Hide file tree
Showing 20 changed files with 84 additions and 160 deletions.
4 changes: 0 additions & 4 deletions .github/workflows/android_prod_release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,6 @@ jobs:
TERM: dumb
ORG_GRADLE_PROJECT_READER_KEYSTORE_PASSWORD: ${{ secrets.READER_KEYSTORE_PASSWORD }}
ORG_GRADLE_PROJECT_READER_KEY_PASSWORD: ${{ secrets.READER_KEY_PASSWORD }}
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
SENTRY_PROJECT: ${{ secrets.SENTRY_PROJECT }}
SENTRY_ORG: ${{ secrets.SENTRY_ORG }}
SENTRY_DSN: ${{ secrets.SENTRY_DSN }}
steps:
- name: Configure Tramline
id: tramline
Expand Down
16 changes: 7 additions & 9 deletions .github/workflows/ios_prod_release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ jobs:
runs-on: macos-14
env:
TERM: dumb
SENTRY_DSN: ${{ secrets.SENTRY_DSN }}
BUGSNAG_API_KEY: ${{ secrets.BUGSNAG_API_KEY }}
steps:
- name: Configure Tramline
id: tramline
Expand Down Expand Up @@ -78,6 +78,7 @@ jobs:
-destination generic/platform=iOS \
DEVELOPMENT_TEAM=6XCS8KZXDA \
PROVISIONING_PROFILE=${{ secrets.PROVISION_PROFILE_ID }} \
BUGSNAG_API_KEY=${BUGSNAG_API_KEY} \
clean archive
CODE_SIGN_IDENTITY="iPhone Distribution: Sasi Kanth (6XCS8KZXDA)"
Expand All @@ -89,15 +90,12 @@ jobs:
echo -n "$EXPORT_OPTIONS_PLIST" | base64 --decode -o $EXPORT_OPTS_PATH
xcodebuild -exportArchive -archivePath $RUNNER_TEMP/twine.xcarchive -exportOptionsPlist $EXPORT_OPTS_PATH -exportPath $RUNNER_TEMP/build
- name: Upload debug symbols to Sentry
- name: Upload debug symbols to Bugsnag
run: |
curl -sL https://sentry.io/get-cli/ | SENTRY_CLI_VERSION=2.21.2 bash;
sentry-cli debug-files upload --auth-token ${{ secrets.SENTRY_AUTH_TOKEN }} \
--include-sources \
--org ${{ secrets.SENTRY_ORG }} \
--project ${{ secrets.SENTRY_PROJECT }} \
$RUNNER_TEMP/twine.xcarchive/dSYMs
cd $RUNNER_TEMP/twine.xcarchive/dSYMs/;
curl --http1.1 https://upload.bugsnag.com/ \
-F apiKey=${BUGSNAG_API_KEY} \
-F [email protected]/Contents/Resources/DWARF/Twine \
- name: Clean up keychain and provisioning profile
if: ${{ always() }}
Expand Down
8 changes: 4 additions & 4 deletions androidApp/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,8 @@ plugins {
alias(libs.plugins.android.application)
alias(libs.plugins.compose)
alias(libs.plugins.ksp)
alias(libs.plugins.sentry.android)
}

sentry { tracingInstrumentation { enabled = false } }

kotlin {
jvmToolchain(20)

Expand All @@ -41,6 +38,9 @@ android {
minSdk = libs.versions.android.sdk.min.get().toInt()
targetSdk = libs.versions.android.sdk.target.get().toInt()

val keyBugsnagAPIKey = "BUGSNAG_API_KEY"
manifestPlaceholders[keyBugsnagAPIKey] = System.getenv(keyBugsnagAPIKey).orEmpty()

versionCode =
if (project.properties["VERSION_CODE"] != null) {
(project.properties["VERSION_CODE"] as String).toInt()
Expand Down Expand Up @@ -95,7 +95,7 @@ dependencies {
implementation(libs.kotlininject.runtime)
ksp(libs.kotlininject.compiler)
implementation(libs.androidx.work)
implementation(libs.sentry)
coreLibraryDesugaring(libs.desugarJdk)
implementation(libs.kotlinx.datetime)
implementation(libs.bugsnag)
}
4 changes: 4 additions & 0 deletions androidApp/src/androidMain/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@
android:dataExtractionRules="@xml/data_extraction_rules"
android:enableOnBackInvokedCallback="true"
tools:targetApi="tiramisu">

<meta-data android:name="com.bugsnag.android.API_KEY"
android:value="${BUGSNAG_API_KEY}"/>

<activity
android:name="dev.sasikanth.rss.reader.MainActivity"
android:exported="true"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,10 @@ import androidx.work.NetworkType
import androidx.work.PeriodicWorkRequest
import androidx.work.PeriodicWorkRequestBuilder
import androidx.work.WorkerParameters
import co.touchlab.crashkios.bugsnag.BugsnagKotlin
import com.bugsnag.android.Bugsnag
import dev.sasikanth.rss.reader.refresh.LastUpdatedAt
import dev.sasikanth.rss.reader.repository.RssRepository
import io.sentry.kotlin.multiplatform.Sentry
import io.sentry.kotlin.multiplatform.SentryLevel
import io.sentry.kotlin.multiplatform.protocol.Breadcrumb
import java.lang.Exception
import java.time.Duration
import kotlinx.coroutines.CancellationException
Expand Down Expand Up @@ -64,9 +63,8 @@ class FeedsRefreshWorker(
} catch (e: CancellationException) {
Result.failure()
} catch (e: Exception) {
Sentry.captureException(e) {
it.addBreadcrumb(Breadcrumb(level = SentryLevel.INFO, category = "Background"))
}
Bugsnag.leaveBreadcrumb("Background Worker")
BugsnagKotlin.sendFatalException(e)
Result.failure()
}
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,11 @@ import androidx.work.NetworkType
import androidx.work.PeriodicWorkRequest
import androidx.work.PeriodicWorkRequestBuilder
import androidx.work.WorkerParameters
import co.touchlab.crashkios.bugsnag.BugsnagKotlin
import com.bugsnag.android.Bugsnag
import dev.sasikanth.rss.reader.repository.RssRepository
import dev.sasikanth.rss.reader.repository.SettingsRepository
import dev.sasikanth.rss.reader.utils.calculateInstantBeforePeriod
import io.sentry.Sentry
import java.time.Duration
import kotlin.coroutines.cancellation.CancellationException

Expand Down Expand Up @@ -67,7 +68,8 @@ class PostsCleanUpWorker(
} catch (e: CancellationException) {
// no-op
} catch (e: Exception) {
Sentry.captureException(e)
Bugsnag.leaveBreadcrumb("Background Worker")
BugsnagKotlin.sendFatalException(e)
}

return Result.failure()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ import androidx.work.ListenableWorker
import androidx.work.WorkManager
import androidx.work.WorkerFactory
import androidx.work.WorkerParameters
import co.touchlab.crashkios.bugsnag.enableBugsnag
import com.bugsnag.android.Bugsnag
import dev.sasikanth.rss.reader.di.ApplicationComponent
import dev.sasikanth.rss.reader.di.create

Expand Down Expand Up @@ -79,6 +81,12 @@ class ReaderApplication : Application(), Configuration.Provider {

override fun onCreate() {
super.onCreate()

if (!BuildConfig.DEBUG) {
Bugsnag.start(this)
enableBugsnag()
}

enqueuePeriodicFeedsRefresh()
enqueuePeriodicPostsCleanUp()

Expand Down
3 changes: 1 addition & 2 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,9 @@ plugins {
alias(libs.plugins.android.library).apply(false)
alias(libs.plugins.compose).apply(false)
alias(libs.plugins.spotless).apply(false)
alias(libs.plugins.buildKonfig).apply(false)
alias(libs.plugins.sentry.android).apply(false)
alias(libs.plugins.kotlin.parcelize).apply(false)
alias(libs.plugins.kotlin.serialization).apply(false)
alias(libs.plugins.bugsnag).apply(false)
}

allprojects {
Expand Down
1 change: 0 additions & 1 deletion core/network/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@ kotlin {
implementation(libs.ksoup)
// TODO: Extract logging abstraction into separate module
implementation(libs.napier)
implementation(libs.sentry)
implementation(libs.ktxml)
}
commonTest.dependencies { implementation(libs.kotlin.test) }
Expand Down
3 changes: 0 additions & 3 deletions gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,3 @@ org.jetbrains.compose.experimental.uikit.enabled=true
android.useAndroidX=true
android.nonTransitiveRClass=true
android.nonFinalResIds=false
#Sentry
# do_not_change_here
sentry.dsn=
12 changes: 6 additions & 6 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,6 @@ kotlininject = "0.6.3"
ksp = "1.9.22-1.0.17"
material_color_utilities = "1.0.0-alpha01"
ksoup = "0.1.2"
sentry = "0.3.0"
sentry_android = "4.3.0"
buildKonfig = "0.15.1"
sqliteAndroid = "3.45.0"
windowSizeClass = "0.3.2"
desugarJdk = "2.0.4"
Expand All @@ -48,6 +45,9 @@ ktxml = "0.2.3"
uri = "0.0.16"
webview = "1.8.8"
uuid = "0.8.2"
bugsnag-plugin = "8.1.0"
bugsnag = "6.2.0"
crashkios-bugsnag = "0.8.6"

[libraries]
compose_runtime = { module = "org.jetbrains.compose.runtime:runtime", version.ref = "compose" }
Expand Down Expand Up @@ -96,7 +96,6 @@ kotlininject-compiler = { module = 'me.tatarka.inject:kotlin-inject-compiler-ksp
kotlininject-runtime = { module = 'me.tatarka.inject:kotlin-inject-runtime', version.ref = 'kotlininject' }
material_color_utilities = { module = "dev.sasikanth:material-color-utilities", version.ref = "material_color_utilities" }
ksoup = { module = "com.fleeksoft.ksoup:ksoup", version.ref = "ksoup" }
sentry = { module = "io.sentry:sentry-kotlin-multiplatform", version.ref = "sentry" }
sqliteAndroid = { module = "com.github.requery:sqlite-android", version.ref = "sqliteAndroid" }
windowSizeClass = { module = "dev.chrisbanes.material3:material3-window-size-class-multiplatform", version.ref = "windowSizeClass" }
desugarJdk = { module = "com.android.tools:desugar_jdk_libs", version.ref = "desugarJdk" }
Expand All @@ -113,6 +112,8 @@ ktxml = { module = "org.kobjects.ktxml:core", version.ref = "ktxml" }
uri = { module = "com.eygraber:uri-kmp", version.ref = "uri" }
webview = { module = "io.github.kevinnzou:compose-webview-multiplatform", version.ref = "webview" }
uuid = { module = "com.benasher44:uuid", version.ref = "uuid" }
bugsnag = { module = "com.bugsnag:bugsnag-android", version.ref = "bugsnag" }
crashkios-bugsnag = { module = "co.touchlab.crashkios:bugsnag", version.ref = "crashkios-bugsnag" }

[plugins]
android_application = { id = "com.android.application", version.ref = "android_gradle_plugin" }
Expand All @@ -126,8 +127,7 @@ compose = { id = "org.jetbrains.compose", version.ref = "compose" }
sqldelight = { id = "app.cash.sqldelight", version.ref = "sqldelight" }
spotless = { id = "com.diffplug.spotless", version.ref = "spotless" }
ksp = { id = 'com.google.devtools.ksp', version.ref = 'ksp' }
buildKonfig = { id = "com.codingfeline.buildkonfig", version.ref = "buildKonfig" }
sentry_android = { id = "io.sentry.android.gradle", version.ref = "sentry_android" }
bugsnag = { id = "com.bugsnag.android.gradle", version.ref = "bugsnag-plugin" }

[bundles]
compose = [ "compose_runtime", "compose_foundation", "compose_material", "compose_material3", "compose_resources", "compose_ui", "compose_ui_util", "compose_material_icons_extended" ]
Expand Down
22 changes: 11 additions & 11 deletions iosApp/iosApp.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@
7555FF79242A565900829871 /* Resources */,
F85CB1118929364A9C6EFABC /* Frameworks */,
2134C13603D0B299603D9F49 /* [CP] Copy Pods Resources */,
0E668490121D39D1C4498DE3 /* [CP] Embed Pods Frameworks */,
599E3F199C42E8E830BDD65E /* [CP] Embed Pods Frameworks */,
);
buildRules = (
);
Expand Down Expand Up @@ -180,38 +180,38 @@
/* End PBXResourcesBuildPhase section */

/* Begin PBXShellScriptBuildPhase section */
0E668490121D39D1C4498DE3 /* [CP] Embed Pods Frameworks */ = {
2134C13603D0B299603D9F49 /* [CP] Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-iosApp/Pods-iosApp-frameworks-${CONFIGURATION}-input-files.xcfilelist",
"${PODS_ROOT}/Target Support Files/Pods-iosApp/Pods-iosApp-resources-${CONFIGURATION}-input-files.xcfilelist",
);
name = "[CP] Embed Pods Frameworks";
name = "[CP] Copy Pods Resources";
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-iosApp/Pods-iosApp-frameworks-${CONFIGURATION}-output-files.xcfilelist",
"${PODS_ROOT}/Target Support Files/Pods-iosApp/Pods-iosApp-resources-${CONFIGURATION}-output-files.xcfilelist",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-iosApp/Pods-iosApp-frameworks.sh\"\n";
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-iosApp/Pods-iosApp-resources.sh\"\n";
showEnvVarsInLog = 0;
};
2134C13603D0B299603D9F49 /* [CP] Copy Pods Resources */ = {
599E3F199C42E8E830BDD65E /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-iosApp/Pods-iosApp-resources-${CONFIGURATION}-input-files.xcfilelist",
"${PODS_ROOT}/Target Support Files/Pods-iosApp/Pods-iosApp-frameworks-${CONFIGURATION}-input-files.xcfilelist",
);
name = "[CP] Copy Pods Resources";
name = "[CP] Embed Pods Frameworks";
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-iosApp/Pods-iosApp-resources-${CONFIGURATION}-output-files.xcfilelist",
"${PODS_ROOT}/Target Support Files/Pods-iosApp/Pods-iosApp-frameworks-${CONFIGURATION}-output-files.xcfilelist",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-iosApp/Pods-iosApp-resources.sh\"\n";
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-iosApp/Pods-iosApp-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
98D614C51D2DA07C614CC46E /* [CP] Check Pods Manifest.lock */ = {
Expand Down
30 changes: 12 additions & 18 deletions iosApp/iosApp/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import UIKit
import shared
import BackgroundTasks
import Sentry
import Bugsnag

class AppDelegate: NSObject, UIApplicationDelegate {
let rootHolder: RootHolder = RootHolder()
Expand All @@ -19,6 +19,11 @@ class AppDelegate: NSObject, UIApplicationDelegate {
)

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
#if !DEBUG
Bugsnag.start()
let config = BugsnagConfiguration.loadConfig()
BugsnagConfigKt.startBugsnag(config: config)
#endif

applicationComponent.initializers
.compactMap { ($0 as! any Initializer) }
Expand Down Expand Up @@ -49,6 +54,8 @@ class AppDelegate: NSObject, UIApplicationDelegate {
}

func cleanUpPosts(task: BGProcessingTask) {
Bugsnag.leaveBreadcrumb(withMessage: "Background Processing")

// Schedule next clean up task 24 hours in future
scheduleCleanUpPosts(earliest: Date(timeIntervalSinceNow: 60 * 60 * 24))

Expand All @@ -63,15 +70,7 @@ class AppDelegate: NSObject, UIApplicationDelegate {
}
task.setTaskCompleted(success: true)
} catch {
let breadcrumb = Breadcrumb()
breadcrumb.level = .info
breadcrumb.category = "Background"

let scope = Scope()
scope.addBreadcrumb(breadcrumb)

SentrySDK.capture(error: error, scope: scope)

Bugsnag.notifyError(error)
task.setTaskCompleted(success: false)
}
}
Expand All @@ -90,6 +89,8 @@ class AppDelegate: NSObject, UIApplicationDelegate {
}

func refreshFeeds(task: BGProcessingTask) {
Bugsnag.leaveBreadcrumb(withMessage: "Background Processing")

scheduledRefreshFeeds(earliest: Date(timeIntervalSinceNow: 60 * 60)) // 1 hour
Task(priority: .background) {
do {
Expand All @@ -101,14 +102,7 @@ class AppDelegate: NSObject, UIApplicationDelegate {

task.setTaskCompleted(success: true)
} catch {
let breadcrumb = Breadcrumb()
breadcrumb.level = .info
breadcrumb.category = "Background"

let scope = Scope()
scope.addBreadcrumb(breadcrumb)

SentrySDK.capture(error: error, scope: scope)
Bugsnag.notifyError(error)
task.setTaskCompleted(success: false)
}
}
Expand Down
5 changes: 5 additions & 0 deletions iosApp/iosApp/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>bugsnag</key>
<dict>
<key>apiKey</key>
<string>$(BUGSNAG_API_KEY)</string>
</dict>
<key>BGTaskSchedulerPermittedIdentifiers</key>
<array>
<string>dev.sasikanth.reader.posts_cleanup</string>
Expand Down
Loading

0 comments on commit 8fff78f

Please sign in to comment.