From e98230289f3b06e5bd0be4367e56e24498c01aee Mon Sep 17 00:00:00 2001 From: Nino van Hooff Date: Thu, 7 Mar 2024 10:06:49 +0100 Subject: [PATCH 1/2] WIP fix crashlytics log grouping crash-logging --- .../template/logging/CrashlyticsAntilog.kt | 33 +++++++++++++++++-- .../presentation/home/HomeViewModel.kt | 1 + 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/app/src/main/kotlin/nl/q42/template/logging/CrashlyticsAntilog.kt b/app/src/main/kotlin/nl/q42/template/logging/CrashlyticsAntilog.kt index f9301eb..f5664fa 100644 --- a/app/src/main/kotlin/nl/q42/template/logging/CrashlyticsAntilog.kt +++ b/app/src/main/kotlin/nl/q42/template/logging/CrashlyticsAntilog.kt @@ -35,15 +35,44 @@ class CrashlyticsAntilog : Antilog() { val limitedMessage = message?.take(MAX_CHARS_IN_LOG) ?: "(no message)" // to avoid OutOfMemoryError's + // at least one of message or throwable is not null if (priority < LogLevel.ERROR) { - // at least one of message or throwable is not null val errorMessage = throwable?.let { " with error: ${throwable}: ${throwable.message}".take(MAX_CHARS_IN_LOG) } ?: "" Firebase.crashlytics.log(limitedMessage + errorMessage) } else { Firebase.crashlytics.log("recordException with message: $limitedMessage") - Firebase.crashlytics.recordException(throwable ?: Exception(message)) + Firebase.crashlytics.recordException(throwable ?: buildCrashlyticsSyntheticException(limitedMessage)) } } + + /** Strip the stacktrace so that the calls implementing error logging are removed and the actual + * code that called Napier.e() remains. + * + * This is a workaround for the fact that Crashlytics groups errors by stacktrace + * [https://stackoverflow.com/a/59779764](https://stackoverflow.com/a/59779764) + */ + private fun buildCrashlyticsSyntheticException(message: String): Exception { + val stackTrace = Thread.currentThread().stackTrace + val numToRemove = 4 + val lastToRemove = stackTrace[numToRemove - 1] + if (lastToRemove.className != "io.github.aakira.napier.Napier" || lastToRemove.methodName != "e\$default"){ + logcatAntilog.log(priority = LogLevel.ERROR, tag = null, throwable = null, + message = "Got unexpected stacktrace: class: ${lastToRemove.className}, method: ${lastToRemove.methodName}" + ) + } + val abbreviatedStackTrace = stackTrace.takeLast(stackTrace.size - numToRemove).toTypedArray() + return SyntheticException("Synthetic Exception: $message", abbreviatedStackTrace) + } + +} + +class SyntheticException( + message: String, + private val abbreviatedStackTrace: Array +) : Exception(message) { + override fun getStackTrace(): Array { + return abbreviatedStackTrace + } } diff --git a/feature/home/src/main/kotlin/nl/q42/template/presentation/home/HomeViewModel.kt b/feature/home/src/main/kotlin/nl/q42/template/presentation/home/HomeViewModel.kt index 1bb6bd4..6739b8d 100644 --- a/feature/home/src/main/kotlin/nl/q42/template/presentation/home/HomeViewModel.kt +++ b/feature/home/src/main/kotlin/nl/q42/template/presentation/home/HomeViewModel.kt @@ -29,6 +29,7 @@ class HomeViewModel @Inject constructor( init { loadUser() + Napier.e("Nino test error") } fun onScreenResumed() { From 9a4ed7cb2e60476ca864ae0c9f036ab3b37fb28a Mon Sep 17 00:00:00 2001 From: Nino van Hooff Date: Fri, 17 May 2024 15:14:12 +0200 Subject: [PATCH 2/2] CHANGE Crashlyticslogger numToRemove to 9 --- .../main/kotlin/nl/q42/template/logging/CrashlyticsLogger.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/kotlin/nl/q42/template/logging/CrashlyticsLogger.kt b/app/src/main/kotlin/nl/q42/template/logging/CrashlyticsLogger.kt index 8a0a565..e32dbff 100644 --- a/app/src/main/kotlin/nl/q42/template/logging/CrashlyticsLogger.kt +++ b/app/src/main/kotlin/nl/q42/template/logging/CrashlyticsLogger.kt @@ -55,7 +55,7 @@ class CrashlyticsLogger : Antilog() { */ private fun buildCrashlyticsSyntheticException(message: String): Exception { val stackTrace = Thread.currentThread().stackTrace - val numToRemove = 4 + val numToRemove = 9 val lastToRemove = stackTrace[numToRemove - 1] if (lastToRemove.className != "io.github.aakira.napier.Napier" || lastToRemove.methodName != "e\$default"){ logcatLogger.log(priority = LogLevel.ERROR, tag = null, throwable = null,