Skip to content

Commit c3a2ac3

Browse files
committed
refactor(logging): extract BreadcrumbSink and ErrorReporter interfaces
Remove Bugsnag dependency from libs/logging by introducing BreadcrumbSink and ErrorReporter interfaces, with concrete Bugsnag implementations in the app module. Logging now guards calls behind an initialized check instead of crashing on early use.
1 parent 0dc720a commit c3a2ac3

14 files changed

Lines changed: 108 additions & 94 deletions

File tree

apps/flipcash/app/build.gradle.kts

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import com.bugsnag.gradle.dsl.debug
22
import com.bugsnag.gradle.dsl.release
3-
import com.google.firebase.crashlytics.buildtools.gradle.CrashlyticsExtension
43
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
54

65
plugins {
@@ -12,7 +11,6 @@ plugins {
1211
alias(libs.plugins.navigation.safeargs)
1312
id("dagger.hilt.android.plugin")
1413
alias(libs.plugins.google.services)
15-
alias(libs.plugins.firebase.crashlytics)
1614
alias(libs.plugins.firebase.perf)
1715
alias(libs.plugins.bugsnag.gradle)
1816
alias(libs.plugins.secrets)
@@ -65,9 +63,6 @@ android {
6563
isShrinkResources = true
6664
proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
6765

68-
configure<CrashlyticsExtension> {
69-
mappingFileUploadEnabled = true
70-
}
7166
}
7267
getByName("debug") {
7368
applicationIdSuffix = ".dev"
@@ -85,9 +80,6 @@ android {
8580
)
8681
}
8782

88-
configure<CrashlyticsExtension> {
89-
mappingFileUploadEnabled = tryReadProperty(rootProject.rootDir, "DEBUG_CRASHLYTICS_UPLOAD", "false").toBooleanStrictOrNull() ?: false
90-
}
9183
}
9284
}
9385

@@ -256,7 +248,6 @@ dependencies {
256248

257249
implementation(platform(libs.firebase.bom))
258250
implementation(libs.firebase.analytics)
259-
implementation(libs.firebase.crashlytics)
260251
implementation(libs.firebase.messaging)
261252

262253
implementation(libs.mixpanel)

apps/flipcash/app/src/main/kotlin/com/flipcash/app/MainActivity.kt

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,6 @@ import com.getcode.utils.CurrencyUtils
5555
import com.getcode.utils.LocalCurrencyUtils
5656
import com.getcode.utils.network.LocalNetworkObserver
5757
import com.getcode.utils.network.NetworkConnectivityListener
58-
import com.google.firebase.crashlytics.FirebaseCrashlytics
5958
import dagger.hilt.android.AndroidEntryPoint
6059
import dev.bmcreations.tipkit.engines.TipsEngine
6160
import dev.theolm.rinku.compose.ext.Rinku
@@ -187,8 +186,6 @@ private fun Activity.handleUncaughtException() {
187186
Thread.setDefaultUncaughtExceptionHandler { _, throwable ->
188187
if (BuildConfig.DEBUG) throw throwable
189188

190-
FirebaseCrashlytics.getInstance().recordException(throwable)
191-
192189
val intent = Intent(this, MainActivity::class.java).apply {
193190
putExtra(crashedKey, true)
194191
addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_NEW_TASK)
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package com.flipcash.app.internal.startup
2+
3+
import com.bugsnag.android.BreadcrumbType
4+
import com.bugsnag.android.Bugsnag
5+
import com.getcode.utils.BreadcrumbSink
6+
import com.getcode.utils.TraceType
7+
8+
class BugsnagBreadcrumbSink : BreadcrumbSink {
9+
override fun record(message: String, metadata: Map<String, Any>, type: TraceType) {
10+
if (!Bugsnag.isStarted()) return
11+
val breadcrumbType = type.toBugsnagBreadcrumbType() ?: return
12+
Bugsnag.leaveBreadcrumb(message, metadata, breadcrumbType)
13+
}
14+
}
15+
16+
private fun TraceType.toBugsnagBreadcrumbType(): BreadcrumbType? {
17+
return when (this) {
18+
TraceType.Silent -> null
19+
TraceType.Error -> BreadcrumbType.ERROR
20+
TraceType.Log -> BreadcrumbType.LOG
21+
TraceType.Navigation -> BreadcrumbType.NAVIGATION
22+
TraceType.Network -> BreadcrumbType.REQUEST
23+
TraceType.Process -> BreadcrumbType.PROCESS
24+
TraceType.StateChange -> BreadcrumbType.STATE
25+
TraceType.User -> BreadcrumbType.USER
26+
}
27+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package com.flipcash.app.internal.startup
2+
3+
import com.bugsnag.android.Bugsnag
4+
import com.getcode.utils.ErrorReporter
5+
6+
class BugsnagErrorReporter : ErrorReporter {
7+
override fun report(error: Throwable, cause: Throwable, isNotifiable: Boolean) {
8+
if (!Bugsnag.isStarted()) return
9+
Bugsnag.notify(error) { event ->
10+
if (isNotifiable) {
11+
event.addMetadata("alert", "slack_notify", true)
12+
event.addMetadata("alert", "error_type", cause.javaClass.simpleName)
13+
event.addMetadata(
14+
"alert", "error_family",
15+
cause.javaClass.enclosingClass?.simpleName ?: "Unknown"
16+
)
17+
}
18+
true
19+
}
20+
}
21+
}

apps/flipcash/app/src/main/kotlin/com/flipcash/app/internal/startup/FirebaseInitializer.kt

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,7 @@ package com.flipcash.app.internal.startup
22

33
import android.content.Context
44
import androidx.startup.Initializer
5-
import com.flipcash.app.android.BuildConfig
65
import com.google.firebase.Firebase
7-
import com.google.firebase.crashlytics.crashlytics
86
import com.google.firebase.initialize
97
import kotlinx.coroutines.CoroutineScope
108
import kotlinx.coroutines.Dispatchers
@@ -14,7 +12,6 @@ class FirebaseInitializer: Initializer<Unit> {
1412
override fun create(context: Context) {
1513
CoroutineScope(Dispatchers.IO).launch {
1614
Firebase.initialize(context)
17-
Firebase.crashlytics.isCrashlyticsCollectionEnabled = BuildConfig.NOTIFY_ERRORS || !BuildConfig.DEBUG
1815
}
1916
}
2017

apps/flipcash/app/src/main/kotlin/com/flipcash/app/internal/startup/TraceInitializer.kt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import com.bugsnag.android.Configuration
77
import com.flipcash.app.android.BuildConfig
88
import com.flipcash.app.internal.debug.FlipcashDebugTree
99
import com.flipcash.app.internal.debug.FlipcashErrorCallback
10+
import com.getcode.utils.ErrorUtils
1011
import com.getcode.utils.TraceManager
1112
import kotlinx.coroutines.CoroutineScope
1213
import kotlinx.coroutines.Dispatchers
@@ -24,6 +25,12 @@ class TraceInitializer: Initializer<Unit> {
2425
val config = Configuration.load(context)
2526
config.addOnError(FlipcashErrorCallback)
2627
Bugsnag.start(context, config)
28+
29+
ErrorUtils.addReporter(BugsnagErrorReporter())
30+
TraceManager.addSink(BugsnagBreadcrumbSink())
31+
TraceManager.setOnUserIdChanged { id ->
32+
Bugsnag.setUser(id, null, null)
33+
}
2734
}
2835
}
2936
}

libs/logging/build.gradle.kts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,8 @@ dependencies {
2828
testImplementation(libs.robolectric)
2929

3030
api(libs.timber)
31-
implementation(libs.bugsnag)
31+
implementation(libs.androidx.annotation)
3232
implementation(libs.rxjava)
33-
implementation(platform(libs.firebase.bom))
34-
implementation(libs.firebase.crashlytics)
3533
implementation(libs.grpc.kotlin)
3634
implementation(project(":libs:messaging"))
3735
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package com.getcode.utils
2+
3+
interface BreadcrumbSink {
4+
fun record(
5+
message: String,
6+
metadata: Map<String, Any>,
7+
type: TraceType,
8+
)
9+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package com.getcode.utils
2+
3+
interface ErrorReporter {
4+
fun report(
5+
error: Throwable,
6+
cause: Throwable,
7+
isNotifiable: Boolean,
8+
)
9+
}

libs/logging/src/main/kotlin/com/getcode/utils/ErrorUtils.kt

Lines changed: 8 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
package com.getcode.utils
22

33
import android.database.SQLException
4-
import com.bugsnag.android.Bugsnag
5-
import com.google.firebase.crashlytics.FirebaseCrashlytics
64
import com.getcode.libs.logging.BuildConfig
75
import com.getcode.manager.TopBarManager
86
import io.grpc.Status
@@ -17,6 +15,10 @@ import java.util.concurrent.TimeoutException
1715

1816
object ErrorUtils {
1917
private var isDisplayErrors = false
18+
private val reporters = mutableListOf<ErrorReporter>()
19+
20+
fun addReporter(reporter: ErrorReporter) { reporters.add(reporter) }
21+
fun removeReporter(reporter: ErrorReporter) { reporters.remove(reporter) }
2022

2123
fun setDisplayErrors(isDisplayErrors: Boolean) {
2224
ErrorUtils.isDisplayErrors = isDisplayErrors
@@ -63,34 +65,11 @@ object ErrorUtils {
6365
ignoredErrors.none { it.isInstance(throwable) } &&
6466
ignoredErrors.none { it.isInstance(throwableCause) }
6567
) {
66-
FirebaseCrashlytics.getInstance().recordException(throwable)
67-
if (Bugsnag.isStarted()) {
68-
Bugsnag.notify(throwable) { event ->
69-
val isNotifiable = throwable is NotifiableError
70-
|| throwableCause is NotifiableError
71-
|| throwableCause !is CodeServerError
72-
if (isNotifiable) {
73-
event.addMetadata("alert", "slack_notify", true)
74-
event.addMetadata("alert", "error_type", throwableCause.javaClass.simpleName)
75-
event.addMetadata("alert", "error_family", throwableCause.javaClass.enclosingClass?.simpleName ?: "Unknown")
76-
}
77-
true
78-
}
79-
}
80-
}
81-
}
68+
val isNotifiable = throwable is NotifiableError
69+
|| throwableCause is NotifiableError
70+
|| throwableCause !is CodeServerError
8271

83-
fun notifyUnexpected(throwable: Throwable, context: String? = null) {
84-
if (!BuildConfig.NOTIFY_ERRORS) return
85-
Timber.e(throwable)
86-
FirebaseCrashlytics.getInstance().recordException(throwable)
87-
if (Bugsnag.isStarted()) {
88-
Bugsnag.notify(throwable) { event ->
89-
event.addMetadata("alert", "slack_notify", true)
90-
event.addMetadata("alert", "error_type", throwable.javaClass.simpleName)
91-
context?.let { event.addMetadata("alert", "context", it) }
92-
true
93-
}
72+
reporters.forEach { it.report(throwable, throwableCause, isNotifiable) }
9473
}
9574
}
9675

0 commit comments

Comments
 (0)