Skip to content

Commit

Permalink
Merge pull request #202 from Automattic/add/js-exception-logging
Browse files Browse the repository at this point in the history
Add support to send JS exceptions to Sentry
  • Loading branch information
jhnstn committed Mar 8, 2024
2 parents 6879b56 + 9313f55 commit 7f6f941
Show file tree
Hide file tree
Showing 5 changed files with 109 additions and 3 deletions.
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -40,4 +39,9 @@ interface CrashLogging {
tags: Map<String, String> = emptyMap(),
message: String? = null
)

fun sendJavaScriptReport(
jsException: JsException,
callback: JsExceptionCallback
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.automattic.android.tracks.crashlogging

data class JsException(
val type: String,
val message: String,
var stackTrace: List<JsExceptionStackTraceElement>,
val context: Map<String, Any>,
val tags: Map<String, String>,
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)
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,22 @@ 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
import io.sentry.Breadcrumb
import io.sentry.Sentry
import io.sentry.SentryEvent
import io.sentry.SentryLevel
import io.sentry.SentryOptions
import io.sentry.android.fragment.FragmentLifecycleIntegration
import io.sentry.protocol.Mechanism
import io.sentry.protocol.Message
import io.sentry.protocol.SentryException
import io.sentry.protocol.SentryStackFrame
import io.sentry.protocol.SentryStackTrace
import io.sentry.protocol.User
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
Expand All @@ -24,7 +31,6 @@ internal class SentryCrashLogging constructor(
private val sentryWrapper: SentryErrorTrackerWrapper,
applicationScope: CoroutineScope
) : CrashLogging {

init {
sentryWrapper.initialize(application) { options ->

Expand Down Expand Up @@ -134,6 +140,46 @@ internal class SentryCrashLogging constructor(
sentryWrapper.captureEvent(event)
}

override fun sendJavaScriptReport(
jsException: JsException,
callback: JsExceptionCallback
) {
val frames = jsException.stackTrace.map {
SentryStackFrame().apply {
this.filename = it.fileName
this.function = it.function
this.lineno = it.lineNumber
this.colno = it.colNumber
this.isInApp = true
}
}.toMutableList()

val sentryException = SentryException().apply {
this.type = jsException.type
this.value = jsException.message
this.module = "javascript"
this.stacktrace = SentryStackTrace().apply { this.frames = frames }
this.mechanism = Mechanism().apply {
this.isHandled = jsException.isHandled
this.type = jsException.handledBy
}
}

val event = SentryEvent().apply {
this.message = Message().apply { this.message = message }
this.level = SentryLevel.FATAL
this.platform = "javascript"
this.appendTags(jsException.tags)
this.exceptions = mutableListOf(sentryException)
}

Sentry.configureScope { scope ->
scope.setContexts("react_native_context", jsException.context)
}
sentryWrapper.captureEvent(event)
callback.onReportSent(true)
}

private fun SentryEvent.appendTags(tags: Map<String, String>) {
for ((key, value) in tags) {
this.setTag(key, value)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
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
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
Expand All @@ -26,7 +30,6 @@ import java.io.IOException
import java.util.Locale

class MainActivity : AppCompatActivity() {

val transactionRepository: PerformanceTransactionRepository =
PerformanceMonitoringRepositoryProvider.createInstance()

Expand Down Expand Up @@ -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",
Expand Down
6 changes: 6 additions & 0 deletions sampletracksapp/src/main/res/layout/activity_main.xml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,12 @@
android:layout_height="wrap_content"
android:text="Send report with exception" />

<Button
android:id="@+id/sendReportWithJavaScriptException"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Send report with JS exception" />

<View
android:layout_width="match_parent"
android:layout_height="1dp"
Expand Down

0 comments on commit 7f6f941

Please sign in to comment.