From 7818abb58b0813ca04805f34aad1926339b746b1 Mon Sep 17 00:00:00 2001 From: jhnstn Date: Wed, 6 Mar 2024 11:14:27 -0500 Subject: [PATCH 1/8] Add Js exception data class --- .../tracks/crashlogging/JsException.kt | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 AutomatticTracks/src/main/java/com/automattic/android/tracks/crashlogging/JsException.kt diff --git a/AutomatticTracks/src/main/java/com/automattic/android/tracks/crashlogging/JsException.kt b/AutomatticTracks/src/main/java/com/automattic/android/tracks/crashlogging/JsException.kt new file mode 100644 index 00000000..947beb95 --- /dev/null +++ b/AutomatticTracks/src/main/java/com/automattic/android/tracks/crashlogging/JsException.kt @@ -0,0 +1,23 @@ +package com.automattic.android.tracks.crashlogging + + +data class JsException( + val type: String, + val message: String, + var stackTrace: List, + val context: Map, + val tags: Map, + val isHandled: Boolean, + val handledBy: String, +) + +data class JsExceptionStackTraceElement ( + val fileName: String, + val lineNumber: Int, + val colNumber: Int, + val function: String, +) + +interface JsExceptionCallback { + fun onReportSent(sent:Boolean) +} From fe59b92981312d3e2aef96663fb68e278e1e5676 Mon Sep 17 00:00:00 2001 From: jhnstn Date: Wed, 6 Mar 2024 11:15:33 -0500 Subject: [PATCH 2/8] Add send JS report function --- .../tracks/crashlogging/CrashLogging.kt | 6 +++- .../internal/SentryCrashLogging.kt | 34 ++++++++++++++++++- 2 files changed, 38 insertions(+), 2 deletions(-) diff --git a/AutomatticTracks/src/main/java/com/automattic/android/tracks/crashlogging/CrashLogging.kt b/AutomatticTracks/src/main/java/com/automattic/android/tracks/crashlogging/CrashLogging.kt index d7b09c19..66a14f4a 100644 --- a/AutomatticTracks/src/main/java/com/automattic/android/tracks/crashlogging/CrashLogging.kt +++ b/AutomatticTracks/src/main/java/com/automattic/android/tracks/crashlogging/CrashLogging.kt @@ -1,7 +1,6 @@ package com.automattic.android.tracks.crashlogging interface CrashLogging { - /** * Records a breadcrumb during the app lifecycle but doesn't report an event. This basically * adds more context for the next reports created and sent by [sendReport] or an unhandled @@ -40,4 +39,9 @@ interface CrashLogging { tags: Map = emptyMap(), message: String? = null ) + + fun sendJavaScriptReport( + jsException: JsException, + callback: JsExceptionCallback + ) } diff --git a/AutomatticTracks/src/main/java/com/automattic/android/tracks/crashlogging/internal/SentryCrashLogging.kt b/AutomatticTracks/src/main/java/com/automattic/android/tracks/crashlogging/internal/SentryCrashLogging.kt index d30870de..e7e10d3c 100644 --- a/AutomatticTracks/src/main/java/com/automattic/android/tracks/crashlogging/internal/SentryCrashLogging.kt +++ b/AutomatticTracks/src/main/java/com/automattic/android/tracks/crashlogging/internal/SentryCrashLogging.kt @@ -5,6 +5,8 @@ import com.automattic.android.tracks.crashlogging.CrashLogging import com.automattic.android.tracks.crashlogging.CrashLoggingDataProvider import com.automattic.android.tracks.crashlogging.CrashLoggingUser import com.automattic.android.tracks.crashlogging.ExtraKnownKey +import com.automattic.android.tracks.crashlogging.JsException +import com.automattic.android.tracks.crashlogging.JsExceptionCallback import com.automattic.android.tracks.crashlogging.PerformanceMonitoringConfig.Disabled import com.automattic.android.tracks.crashlogging.PerformanceMonitoringConfig.Enabled import com.automattic.android.tracks.crashlogging.eventLevel @@ -24,7 +26,6 @@ internal class SentryCrashLogging constructor( private val sentryWrapper: SentryErrorTrackerWrapper, applicationScope: CoroutineScope ) : CrashLogging { - init { sentryWrapper.initialize(application) { options -> @@ -134,6 +135,37 @@ internal class SentryCrashLogging constructor( sentryWrapper.captureEvent(event) } + override fun sendJavaScriptReport( + jsException: JsException, + callback: JsExceptionCallback + ) { + val exception = Throwable(jsException.message) + + // @TODO we need to figure out how to send a stack trace element with the column number + exception.stackTrace = jsException.stackTrace.map { + StackTraceElement( + it.fileName, // We probably don't have a class name so we use the file name + it.function, + it.fileName, + it.lineNumber + ) + }.toTypedArray() + + val event = SentryEvent(exception).apply { + this.message = Message().apply { this.message = message } + this.level = SentryLevel.FATAL + this.platform = "javascript" + this.appendTags(jsException.tags) + } + + // @TODO figure out mechanism to add isHandled and handledBy + + // @TODO add context i.e. context["react_native_context"] = jsException.context; + + sentryWrapper.captureEvent(event) + callback.onReportSent(true) + } + private fun SentryEvent.appendTags(tags: Map) { for ((key, value) in tags) { this.setTag(key, value) From 447aba350dc6eb9b2022f9e53febf44f7a91cb2c Mon Sep 17 00:00:00 2001 From: jhnstn Date: Wed, 6 Mar 2024 15:23:45 -0500 Subject: [PATCH 3/8] Add send JS report function to demo app --- .../example/sampletracksapp/MainActivity.kt | 30 ++++++++++++++++++- .../src/main/res/layout/activity_main.xml | 6 ++++ 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/sampletracksapp/src/main/java/com/example/sampletracksapp/MainActivity.kt b/sampletracksapp/src/main/java/com/example/sampletracksapp/MainActivity.kt index 6fb3bd16..7bfc9190 100644 --- a/sampletracksapp/src/main/java/com/example/sampletracksapp/MainActivity.kt +++ b/sampletracksapp/src/main/java/com/example/sampletracksapp/MainActivity.kt @@ -1,6 +1,7 @@ package com.example.sampletracksapp import android.os.Bundle +import android.util.Log import androidx.appcompat.app.AppCompatActivity import com.automattic.android.tracks.crashlogging.CrashLoggingDataProvider import com.automattic.android.tracks.crashlogging.CrashLoggingOkHttpInterceptorProvider @@ -8,6 +9,9 @@ import com.automattic.android.tracks.crashlogging.CrashLoggingProvider import com.automattic.android.tracks.crashlogging.CrashLoggingUser import com.automattic.android.tracks.crashlogging.EventLevel import com.automattic.android.tracks.crashlogging.ExtraKnownKey +import com.automattic.android.tracks.crashlogging.JsException +import com.automattic.android.tracks.crashlogging.JsExceptionCallback +import com.automattic.android.tracks.crashlogging.JsExceptionStackTraceElement import com.automattic.android.tracks.crashlogging.PerformanceMonitoringConfig import com.automattic.android.tracks.crashlogging.RequestFormatter import com.automattic.android.tracks.crashlogging.performance.PerformanceMonitoringRepositoryProvider @@ -26,7 +30,6 @@ import java.io.IOException import java.util.Locale class MainActivity : AppCompatActivity() { - val transactionRepository: PerformanceTransactionRepository = PerformanceMonitoringRepositoryProvider.createInstance() @@ -89,6 +92,31 @@ class MainActivity : AppCompatActivity() { crashLogging.sendReport(exception = Exception("Exception from Tracks test app")) } + sendReportWithJavaScriptException.setOnClickListener { + val callback = object : JsExceptionCallback { + override fun onReportSent(sent: Boolean) { + Log.d("JsExceptionCallback", "onReportSent: $sent") + } + } + val jsException = JsException( + type = "Error", + message = "JavaScript exception from Tracks test app", + stackTrace = listOf( + JsExceptionStackTraceElement( + fileName = "file.js", + lineNumber = 1, + colNumber = 1, + function = "function" + ) + ), + context = mapOf("context" to "value"), + tags = mapOf("tag" to "SomeTag"), + isHandled = true, + handledBy = "SomeHandler" + ) + crashLogging.sendJavaScriptReport(jsException, callback) + } + recordBreadcrumbWithMessage.setOnClickListener { crashLogging.recordEvent( message = "Custom breadcrumb", diff --git a/sampletracksapp/src/main/res/layout/activity_main.xml b/sampletracksapp/src/main/res/layout/activity_main.xml index d537954d..77c396a0 100644 --- a/sampletracksapp/src/main/res/layout/activity_main.xml +++ b/sampletracksapp/src/main/res/layout/activity_main.xml @@ -20,6 +20,12 @@ android:layout_height="wrap_content" android:text="Send report with exception" /> +