Loading components/lib/crash/src/main/java/mozilla/components/lib/crash/Breadcrumb.kt 0 → 100644 +94 −0 Original line number Diff line number Diff line /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ package mozilla.components.lib.crash import android.os.Parcelable import kotlinx.android.parcel.Parcelize /** * Represents a single crash breadcrumb. */ @Parcelize data class Breadcrumb( /** * Message of the crash breadcrumb. */ val message: String = "", /** * Data related to the crash breadcrumb. */ val data: Map<String, String> = emptyMap(), /** * Category of the crash breadcrumb. */ val category: String = "", /** * Level of the crash breadcrumb. */ val level: Level = Level.DEBUG, /** * Type of the crash breadcrumb. */ val type: Type = Type.DEFAULT ) : Parcelable { /** * Crash breadcrumb priority level. */ enum class Level { /** * DEBUG level. */ DEBUG, /** * INFO level. */ INFO, /** * WARNING level. */ WARNING, /** * ERROR level. */ ERROR, /** * CRITICAL level. */ CRITICAL } /** * Crash breadcrumb type. */ enum class Type { /** * DEFAULT type. */ DEFAULT, /** * HTTP type. */ HTTP, /** * NAVIGATION type. */ NAVIGATION, /** * USER type. */ USER } } components/lib/crash/src/main/java/mozilla/components/lib/crash/Crash.kt +16 −7 Original line number Diff line number Diff line Loading @@ -14,9 +14,12 @@ private const val INTENT_CRASH = "mozilla.components.lib.crash.CRASH" // Uncaught exception crash intent extras private const val INTENT_EXCEPTION = "exception" // Breadcrumbs intent extras private const val INTENT_BREADCRUMBS = "breadcrumbs" // Native code crash intent extras (Mirroring GeckoView values) private const val INTENT_MINIDUMP_PATH = "minidumpPath" private const val INTENT_EXTEAS_PATH = "extrasPath" private const val INTENT_EXTRAS_PATH = "extrasPath" private const val INTENT_MINIDUMP_SUCCESS = "minidumpSuccess" private const val INTENT_FATAL = "fatal" Loading @@ -30,15 +33,18 @@ sealed class Crash { * @property throwable The [Throwable] that caused the crash. */ data class UncaughtExceptionCrash( val throwable: Throwable val throwable: Throwable, val breadcrumbs: ArrayList<Breadcrumb> ) : Crash() { override fun toBundle() = Bundle().apply { putSerializable(INTENT_EXCEPTION, throwable as Serializable) putParcelableArrayList(INTENT_BREADCRUMBS, breadcrumbs) } companion object { internal fun fromBundle(bundle: Bundle) = UncaughtExceptionCrash( bundle.getSerializable(INTENT_EXCEPTION) as Throwable bundle.getSerializable(INTENT_EXCEPTION) as Throwable, bundle.getParcelableArrayList(INTENT_BREADCRUMBS) ?: arrayListOf() ) } } Loading @@ -60,21 +66,24 @@ sealed class Crash { val minidumpPath: String, val minidumpSuccess: Boolean, val extrasPath: String, val isFatal: Boolean val isFatal: Boolean, val breadcrumbs: ArrayList<Breadcrumb> ) : Crash() { override fun toBundle() = Bundle().apply { putString(INTENT_MINIDUMP_PATH, minidumpPath) putBoolean(INTENT_MINIDUMP_SUCCESS, minidumpSuccess) putString(INTENT_EXTEAS_PATH, extrasPath) putString(INTENT_EXTRAS_PATH, extrasPath) putBoolean(INTENT_FATAL, isFatal) putParcelableArrayList(INTENT_BREADCRUMBS, breadcrumbs) } companion object { internal fun fromBundle(bundle: Bundle) = NativeCodeCrash( bundle.getString(INTENT_MINIDUMP_PATH, ""), bundle.getBoolean(INTENT_MINIDUMP_SUCCESS, false), bundle.getString(INTENT_EXTEAS_PATH, ""), bundle.getBoolean(INTENT_FATAL, false) bundle.getString(INTENT_EXTRAS_PATH, ""), bundle.getBoolean(INTENT_FATAL, false), bundle.getParcelableArrayList(INTENT_BREADCRUMBS) ?: arrayListOf() ) } } Loading components/lib/crash/src/main/java/mozilla/components/lib/crash/CrashReporter.kt +20 −0 Original line number Diff line number Diff line Loading @@ -49,6 +49,7 @@ class CrashReporter( private val nonFatalCrashIntent: PendingIntent? = null ) { internal val logger = Logger("mozac/CrashReporter") internal val crashBreadcrumbs = arrayListOf<Breadcrumb>() init { if (services.isEmpty()) { Loading Loading @@ -85,6 +86,22 @@ class CrashReporter( logger.info("Crash report submitted to ${services.size} services") } /** * Add a crash breadcrumb to all registered services with breadcrumb support. * * ```Kotlin * crashReporter.recordCrashBreadcrumb( * Breadcrumb("Settings button clicked", data, "UI", Level.INFO, Type.USER) * ) * ``` */ fun recordCrashBreadcrumb(breadcrumb: Breadcrumb) { if (crashBreadcrumbs.size >= BREADCRUMB_MAX_NUM) { crashBreadcrumbs.removeAt(0) } crashBreadcrumbs.add(breadcrumb) } internal fun onCrash(context: Context, crash: Crash) { if (!enabled) { return Loading Loading @@ -174,6 +191,9 @@ class CrashReporter( ) companion object { @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) internal const val BREADCRUMB_MAX_NUM = 20 @Volatile private var instance: CrashReporter? = null Loading components/lib/crash/src/main/java/mozilla/components/lib/crash/handler/ExceptionHandler.kt +2 −1 Original line number Diff line number Diff line Loading @@ -27,7 +27,8 @@ class ExceptionHandler( try { crashing = true crashReporter.onCrash(context, Crash.UncaughtExceptionCrash(throwable)) crashReporter.onCrash(context, Crash.UncaughtExceptionCrash(throwable, crashReporter.crashBreadcrumbs)) defaultExceptionHandler?.uncaughtException(thread, throwable) } finally { Loading components/lib/crash/src/main/java/mozilla/components/lib/crash/service/SendCrashReportService.kt +3 −13 Original line number Diff line number Diff line Loading @@ -28,11 +28,6 @@ import kotlin.coroutines.EmptyCoroutineContext class SendCrashReportService : Service() { private val crashReporter: CrashReporter by lazy { CrashReporter.requireInstance } private val logger by lazy { CrashReporter .requireInstance .logger } private var reporterCoroutineContext: CoroutineContext = EmptyCoroutineContext override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int { Loading @@ -52,14 +47,9 @@ class SendCrashReportService : Service() { startForeground(notificationId, notification) } intent.extras?.let { extras -> val crash = Crash.NativeCodeCrash.fromBundle(extras) NotificationManagerCompat.from(this).cancel(this, NOTIFICATION_TAG) sendCrashReport(crash) { crashReporter.submitReport(Crash.fromIntent(intent)) stopSelf() } } ?: logger.error("Received intent with null extras") return START_NOT_STICKY } Loading Loading
components/lib/crash/src/main/java/mozilla/components/lib/crash/Breadcrumb.kt 0 → 100644 +94 −0 Original line number Diff line number Diff line /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ package mozilla.components.lib.crash import android.os.Parcelable import kotlinx.android.parcel.Parcelize /** * Represents a single crash breadcrumb. */ @Parcelize data class Breadcrumb( /** * Message of the crash breadcrumb. */ val message: String = "", /** * Data related to the crash breadcrumb. */ val data: Map<String, String> = emptyMap(), /** * Category of the crash breadcrumb. */ val category: String = "", /** * Level of the crash breadcrumb. */ val level: Level = Level.DEBUG, /** * Type of the crash breadcrumb. */ val type: Type = Type.DEFAULT ) : Parcelable { /** * Crash breadcrumb priority level. */ enum class Level { /** * DEBUG level. */ DEBUG, /** * INFO level. */ INFO, /** * WARNING level. */ WARNING, /** * ERROR level. */ ERROR, /** * CRITICAL level. */ CRITICAL } /** * Crash breadcrumb type. */ enum class Type { /** * DEFAULT type. */ DEFAULT, /** * HTTP type. */ HTTP, /** * NAVIGATION type. */ NAVIGATION, /** * USER type. */ USER } }
components/lib/crash/src/main/java/mozilla/components/lib/crash/Crash.kt +16 −7 Original line number Diff line number Diff line Loading @@ -14,9 +14,12 @@ private const val INTENT_CRASH = "mozilla.components.lib.crash.CRASH" // Uncaught exception crash intent extras private const val INTENT_EXCEPTION = "exception" // Breadcrumbs intent extras private const val INTENT_BREADCRUMBS = "breadcrumbs" // Native code crash intent extras (Mirroring GeckoView values) private const val INTENT_MINIDUMP_PATH = "minidumpPath" private const val INTENT_EXTEAS_PATH = "extrasPath" private const val INTENT_EXTRAS_PATH = "extrasPath" private const val INTENT_MINIDUMP_SUCCESS = "minidumpSuccess" private const val INTENT_FATAL = "fatal" Loading @@ -30,15 +33,18 @@ sealed class Crash { * @property throwable The [Throwable] that caused the crash. */ data class UncaughtExceptionCrash( val throwable: Throwable val throwable: Throwable, val breadcrumbs: ArrayList<Breadcrumb> ) : Crash() { override fun toBundle() = Bundle().apply { putSerializable(INTENT_EXCEPTION, throwable as Serializable) putParcelableArrayList(INTENT_BREADCRUMBS, breadcrumbs) } companion object { internal fun fromBundle(bundle: Bundle) = UncaughtExceptionCrash( bundle.getSerializable(INTENT_EXCEPTION) as Throwable bundle.getSerializable(INTENT_EXCEPTION) as Throwable, bundle.getParcelableArrayList(INTENT_BREADCRUMBS) ?: arrayListOf() ) } } Loading @@ -60,21 +66,24 @@ sealed class Crash { val minidumpPath: String, val minidumpSuccess: Boolean, val extrasPath: String, val isFatal: Boolean val isFatal: Boolean, val breadcrumbs: ArrayList<Breadcrumb> ) : Crash() { override fun toBundle() = Bundle().apply { putString(INTENT_MINIDUMP_PATH, minidumpPath) putBoolean(INTENT_MINIDUMP_SUCCESS, minidumpSuccess) putString(INTENT_EXTEAS_PATH, extrasPath) putString(INTENT_EXTRAS_PATH, extrasPath) putBoolean(INTENT_FATAL, isFatal) putParcelableArrayList(INTENT_BREADCRUMBS, breadcrumbs) } companion object { internal fun fromBundle(bundle: Bundle) = NativeCodeCrash( bundle.getString(INTENT_MINIDUMP_PATH, ""), bundle.getBoolean(INTENT_MINIDUMP_SUCCESS, false), bundle.getString(INTENT_EXTEAS_PATH, ""), bundle.getBoolean(INTENT_FATAL, false) bundle.getString(INTENT_EXTRAS_PATH, ""), bundle.getBoolean(INTENT_FATAL, false), bundle.getParcelableArrayList(INTENT_BREADCRUMBS) ?: arrayListOf() ) } } Loading
components/lib/crash/src/main/java/mozilla/components/lib/crash/CrashReporter.kt +20 −0 Original line number Diff line number Diff line Loading @@ -49,6 +49,7 @@ class CrashReporter( private val nonFatalCrashIntent: PendingIntent? = null ) { internal val logger = Logger("mozac/CrashReporter") internal val crashBreadcrumbs = arrayListOf<Breadcrumb>() init { if (services.isEmpty()) { Loading Loading @@ -85,6 +86,22 @@ class CrashReporter( logger.info("Crash report submitted to ${services.size} services") } /** * Add a crash breadcrumb to all registered services with breadcrumb support. * * ```Kotlin * crashReporter.recordCrashBreadcrumb( * Breadcrumb("Settings button clicked", data, "UI", Level.INFO, Type.USER) * ) * ``` */ fun recordCrashBreadcrumb(breadcrumb: Breadcrumb) { if (crashBreadcrumbs.size >= BREADCRUMB_MAX_NUM) { crashBreadcrumbs.removeAt(0) } crashBreadcrumbs.add(breadcrumb) } internal fun onCrash(context: Context, crash: Crash) { if (!enabled) { return Loading Loading @@ -174,6 +191,9 @@ class CrashReporter( ) companion object { @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) internal const val BREADCRUMB_MAX_NUM = 20 @Volatile private var instance: CrashReporter? = null Loading
components/lib/crash/src/main/java/mozilla/components/lib/crash/handler/ExceptionHandler.kt +2 −1 Original line number Diff line number Diff line Loading @@ -27,7 +27,8 @@ class ExceptionHandler( try { crashing = true crashReporter.onCrash(context, Crash.UncaughtExceptionCrash(throwable)) crashReporter.onCrash(context, Crash.UncaughtExceptionCrash(throwable, crashReporter.crashBreadcrumbs)) defaultExceptionHandler?.uncaughtException(thread, throwable) } finally { Loading
components/lib/crash/src/main/java/mozilla/components/lib/crash/service/SendCrashReportService.kt +3 −13 Original line number Diff line number Diff line Loading @@ -28,11 +28,6 @@ import kotlin.coroutines.EmptyCoroutineContext class SendCrashReportService : Service() { private val crashReporter: CrashReporter by lazy { CrashReporter.requireInstance } private val logger by lazy { CrashReporter .requireInstance .logger } private var reporterCoroutineContext: CoroutineContext = EmptyCoroutineContext override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int { Loading @@ -52,14 +47,9 @@ class SendCrashReportService : Service() { startForeground(notificationId, notification) } intent.extras?.let { extras -> val crash = Crash.NativeCodeCrash.fromBundle(extras) NotificationManagerCompat.from(this).cancel(this, NOTIFICATION_TAG) sendCrashReport(crash) { crashReporter.submitReport(Crash.fromIntent(intent)) stopSelf() } } ?: logger.error("Received intent with null extras") return START_NOT_STICKY } Loading