Skip to content

Commit

Permalink
add internal error payload model
Browse files Browse the repository at this point in the history
  • Loading branch information
fractalwrench committed Mar 8, 2024
1 parent d08aac0 commit 11be2c2
Show file tree
Hide file tree
Showing 23 changed files with 233 additions and 66 deletions.
10 changes: 6 additions & 4 deletions embrace-android-sdk/config/detekt/baseline.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@
<SmellBaseline>
<ManuallySuppressedIssues></ManuallySuppressedIssues>
<CurrentIssues>
<ID>DataClassContainsFunctions:ExceptionError.kt$ExceptionError$fun addException(ex: Throwable?, appState: String?, clock: Clock)</ID>
<ID>DataClassContainsFunctions:ExceptionError.kt$ExceptionError$private fun getExceptionInfo(ex: Throwable?): List&lt;ExceptionInfo&gt;</ID>
<ID>DataClassShouldBeImmutable:ExceptionError.kt$ExceptionError$@Json(name = "c") var occurrences = 0</ID>
<ID>DataClassShouldBeImmutable:ExceptionError.kt$ExceptionError$@Json(name = "rep") var exceptionErrors = mutableListOf&lt;ExceptionErrorInfo&gt;()</ID>
<ID>ArgumentListWrapping:LegacyExceptionInfoTest.kt$LegacyExceptionInfoTest$( "io.embrace.android.embracesdk.LegacyExceptionInfoTest.testOfThrowable(LegacyExceptionInfoTest.kt:45)", info.lines.first())</ID>
<ID>ArgumentListWrapping:LegacyExceptionInfoTest.kt$LegacyExceptionInfoTest$("io.embrace.android.embracesdk.LegacyExceptionInfoTest.testOfThrowable(LegacyExceptionInfoTest.kt:45)", info.lines.first())</ID>
<ID>DataClassContainsFunctions:LegacyExceptionError.kt$LegacyExceptionError$fun addException(ex: Throwable?, appState: String?, clock: Clock)</ID>
<ID>DataClassContainsFunctions:LegacyExceptionError.kt$LegacyExceptionError$private fun getExceptionInfo(ex: Throwable?): List&lt;LegacyExceptionInfo&gt;</ID>
<ID>DataClassShouldBeImmutable:LegacyExceptionError.kt$LegacyExceptionError$@Json(name = "c") var occurrences = 0</ID>
<ID>DataClassShouldBeImmutable:LegacyExceptionError.kt$LegacyExceptionError$@Json(name = "rep") var exceptionErrors = mutableListOf&lt;LegacyExceptionErrorInfo&gt;()</ID>
</CurrentIssues>
</SmellBaseline>
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
package io.embrace.android.embracesdk.assertions

import io.embrace.android.embracesdk.payload.ExceptionError
import io.embrace.android.embracesdk.payload.LegacyExceptionError
import io.embrace.android.embracesdk.IntegrationTestRule
import org.junit.Assert.assertTrue

/**
* Return true if at least one exception matching the expected time, exception type, and error message is found in the internal errors
*/
internal fun assertInternalErrorLogged(
exceptionError: ExceptionError?,
exceptionError: LegacyExceptionError?,
exceptionClassName: String,
errorMessage: String,
errorTimeMs: Long = IntegrationTestRule.DEFAULT_SDK_START_TIME_MS
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package io.embrace.android.embracesdk.internal.payload

import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass

/**
*
*
* @param timestamp The timestamp in milliseconds of when an error happened. Previous name: s.e.rep.ts
* @param appState The app state at the time of the error (foreground/background). Previous name: s.e.rep.s
* @param exceptions A list of exceptions. Previous name: s.e.rep.ex
*/
@JsonClass(generateAdapter = true)
internal data class ExceptionErrorInfo(

/* The timestamp in milliseconds of when an error happened. Previous name: s.e.rep.ts */
@Json(name = "timestamp")
val timestamp: Long? = null,

/* The app state at the time of the error (foreground/background). Previous name: s.e.rep.s */
@Json(name = "app_state")
val appState: AppState? = null,

/* A list of exceptions. Previous name: s.e.rep.ex */
@Json(name = "exceptions")
val exceptions: List<ExceptionInfo>? = null

) {

/**
* The app state at the time of the error (foreground/background). Previous name: s.e.rep.s
*
* Values: ACTIVE,BACKGROUND
*/
internal enum class AppState(val value: String) {
@Json(name = "active")
ACTIVE("active"),

@Json(name = "background")
BACKGROUND("background")
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package io.embrace.android.embracesdk.internal.payload

import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass

/**
* Describes a Java exception.
*
* @param name The name of the class causing an error. Previous name: s.e.rep.ex.n
* @param message The error message, if any. Previous name: s.e.rep.ex.m
* @param stacktrace String representation of each line in the stack trace. Previous name: s.e.rep.ex.tt
*/
@JsonClass(generateAdapter = true)
internal data class ExceptionInfo(

/* The name of the class causing an error. Previous name: s.e.rep.ex.n */
@Json(name = "name")
val name: String? = null,

/* The error message, if any. Previous name: s.e.rep.ex.m */
@Json(name = "message")
val message: String? = null,

/* String representation of each line in the stack trace. Previous name: s.e.rep.ex.tt */
@Json(name = "stacktrace")
val stacktrace: List<String>? = null

)
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package io.embrace.android.embracesdk.internal.payload

import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass

/**
* Describes an Exception Error with a count of occurrences and a list of exceptions (causes).
*
* @param count The number of internal error that occurred within Embrace. Previous name: s.e.c
* @param errors A list of causes of the internal error. Previous name: s.e.rep
*/
@JsonClass(generateAdapter = true)
internal data class InternalError(

/* The number of internal error that occurred within Embrace. Previous name: s.e.c */
@Json(name = "count")
val count: Int? = null,

/* A list of causes of the internal error. Previous name: s.e.rep */
@Json(name = "errors")
val errors: List<ExceptionErrorInfo>? = null

)
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ import com.squareup.moshi.JsonClass

@JsonClass(generateAdapter = true)
internal data class SessionPayload(

@Json(name = "internal_error")
val internalError: InternalError? = null,

/**
* A map of symbols that are associated with the session.
* We use this to associate the symbolication files that have been uploaded with UUIDs with the stacktrace module names,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import io.embrace.android.embracesdk.config.ConfigService
import io.embrace.android.embracesdk.internal.clock.Clock
import io.embrace.android.embracesdk.logging.InternalStaticEmbraceLogger.Companion.logDebug
import io.embrace.android.embracesdk.logging.InternalStaticEmbraceLogger.Companion.logDeveloper
import io.embrace.android.embracesdk.payload.ExceptionError
import io.embrace.android.embracesdk.payload.LegacyExceptionError
import io.embrace.android.embracesdk.session.lifecycle.ProcessStateService
import java.net.BindException
import java.net.ConnectException
Expand All @@ -26,9 +26,9 @@ internal class EmbraceInternalErrorService(
private val logStrictMode: Boolean
) : InternalErrorService {
private var configService: ConfigService? = null
private var err: ExceptionError? = null
private var err: LegacyExceptionError? = null

override val currentExceptionError: ExceptionError?
override val currentExceptionError: LegacyExceptionError?
get() = err

// ignore network-related exceptions since they are expected
Expand Down Expand Up @@ -104,7 +104,7 @@ internal class EmbraceInternalErrorService(
return
}
if (err == null) {
err = ExceptionError(logStrictMode)
err = LegacyExceptionError(logStrictMode)
}

// if the config service has not been set yet, capture the exception
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
package io.embrace.android.embracesdk.logging

import io.embrace.android.embracesdk.config.ConfigService
import io.embrace.android.embracesdk.payload.ExceptionError
import io.embrace.android.embracesdk.payload.LegacyExceptionError

internal interface InternalErrorService {
fun setConfigService(configService: ConfigService?)
fun handleInternalError(throwable: Throwable)
fun resetExceptionErrorObject()
val currentExceptionError: ExceptionError?
val currentExceptionError: LegacyExceptionError?
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ internal data class Crash(
val crashId: String,

@Json(name = "ex")
val exceptions: List<ExceptionInfo>? = null,
val exceptions: List<LegacyExceptionInfo>? = null,

@Json(name = "rep_js")
val jsExceptions: List<String>? = null,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,19 @@ package io.embrace.android.embracesdk.payload
import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
import io.embrace.android.embracesdk.internal.clock.Clock
import io.embrace.android.embracesdk.internal.payload.InternalError

/**
* Describes an Exception Error with a count of occurrences and a list of exceptions (causes).
*/
@JsonClass(generateAdapter = true)
internal data class ExceptionError(@Transient private val logStrictMode: Boolean = false) {
internal data class LegacyExceptionError(@Transient private val logStrictMode: Boolean = false) {

@Json(name = "c")
var occurrences = 0

@Json(name = "rep")
var exceptionErrors = mutableListOf<ExceptionErrorInfo>()
var exceptionErrors = mutableListOf<LegacyExceptionErrorInfo>()

/**
* Add a new exception error info if exceptionError's size is below 20.
Expand All @@ -31,7 +32,7 @@ internal data class ExceptionError(@Transient private val logStrictMode: Boolean
}
if (exceptionErrors.size < exceptionsLimits) {
exceptionErrors.add(
ExceptionErrorInfo(
LegacyExceptionErrorInfo(
clock.now(),
appState,
getExceptionInfo(ex)
Expand All @@ -40,16 +41,20 @@ internal data class ExceptionError(@Transient private val logStrictMode: Boolean
}
}

private fun getExceptionInfo(ex: Throwable?): List<ExceptionInfo> {
val result = mutableListOf<ExceptionInfo>()
private fun getExceptionInfo(ex: Throwable?): List<LegacyExceptionInfo> {
val result = mutableListOf<LegacyExceptionInfo>()
var throwable: Throwable? = ex
while (throwable != null && throwable != throwable.cause) {
val exceptionInfo = ExceptionInfo.ofThrowable(throwable)
val exceptionInfo = LegacyExceptionInfo.ofThrowable(throwable)
result.add(0, exceptionInfo)
throwable = throwable.cause
}
return result
}

fun toNewPayload(): InternalError {
return InternalError(occurrences, exceptionErrors.map(LegacyExceptionErrorInfo::toNewPayload))
}
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@ package io.embrace.android.embracesdk.payload

import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
import io.embrace.android.embracesdk.internal.payload.ExceptionErrorInfo

/**
* Describes a particular Exception error. Where an exception error has a cause, there will be an
* {@link ExceptionErrorInfo} for each nested cause.
*/
@JsonClass(generateAdapter = true)
internal data class ExceptionErrorInfo(
internal data class LegacyExceptionErrorInfo(

/**
* Timestamp when exception error happened.
Expand All @@ -23,6 +24,18 @@ internal data class ExceptionErrorInfo(
/**
* A list of exceptions.
*/
@Json(name = "ex") val exceptions: List<ExceptionInfo>? = null
@Json(name = "ex") val exceptions: List<LegacyExceptionInfo>? = null

)
) {
fun toNewPayload(): ExceptionErrorInfo {
val mappedState = when (state) {
"background" -> ExceptionErrorInfo.AppState.BACKGROUND

Check warning on line 32 in embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/payload/LegacyExceptionErrorInfo.kt

View check run for this annotation

Codecov / codecov/patch

embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/payload/LegacyExceptionErrorInfo.kt#L32

Added line #L32 was not covered by tests
else -> ExceptionErrorInfo.AppState.ACTIVE
}
return ExceptionErrorInfo(
timestamp,
mappedState,
exceptions?.map(LegacyExceptionInfo::toNewPayload)
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ import com.squareup.moshi.JsonClass

/**
* Describes a particular Java exception. Where an exception has a cause, there will be an
* [ExceptionInfo] for each nested cause.
* [LegacyExceptionInfo] for each nested cause.
*/
@JsonClass(generateAdapter = true)
internal class ExceptionInfo internal constructor(
internal class LegacyExceptionInfo internal constructor(

/**
* The name of the class throwing the exception.
Expand All @@ -35,6 +35,10 @@ internal class ExceptionInfo internal constructor(
@Json(name = "length")
val originalLength: Int? = lines.size.takeIf { it > STACK_FRAME_LIMIT }

fun toNewPayload(): io.embrace.android.embracesdk.internal.payload.ExceptionInfo {
return io.embrace.android.embracesdk.internal.payload.ExceptionInfo(name, message, lines)
}

companion object {

/**
Expand All @@ -43,26 +47,26 @@ internal class ExceptionInfo internal constructor(
private const val STACK_FRAME_LIMIT = 200

/**
* Creates a [ExceptionInfo] from a [Throwable], using the classname as the name,
* Creates a [LegacyExceptionInfo] from a [Throwable], using the classname as the name,
* the exception message as the message, and each stacktrace element as each line.
*
* @param throwable the exception
* @return the stacktrace instance
*/
@JvmStatic
fun ofThrowable(throwable: Throwable): ExceptionInfo {
fun ofThrowable(throwable: Throwable): LegacyExceptionInfo {
val name = throwable.javaClass.name
val message = throwable.message ?: ""
val lines = throwable.stackTrace.map(StackTraceElement::toString)
return ExceptionInfo(name, message, lines)
return LegacyExceptionInfo(name, message, lines)
}
}

override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false

other as ExceptionInfo
other as LegacyExceptionInfo

Check warning on line 69 in embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/payload/LegacyExceptionInfo.kt

View check run for this annotation

Codecov / codecov/patch

embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/payload/LegacyExceptionInfo.kt#L69

Added line #L69 was not covered by tests

if (name != other.name) return false
if (message != other.message) return false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ internal data class Session @JvmOverloads internal constructor(
val errorLogsAttemptedToSend: Int? = null,

@Json(name = "e")
val exceptionError: ExceptionError? = null,
val exceptionError: LegacyExceptionError? = null,

@Json(name = "ri")
val crashReportId: String? = null,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import io.embrace.android.embracesdk.internal.serialization.EmbraceSerializer
import io.embrace.android.embracesdk.internal.utils.Uuid
import io.embrace.android.embracesdk.logging.InternalStaticEmbraceLogger
import io.embrace.android.embracesdk.payload.Crash
import io.embrace.android.embracesdk.payload.ExceptionInfo
import io.embrace.android.embracesdk.payload.JsException
import io.embrace.android.embracesdk.payload.LegacyExceptionInfo
import io.embrace.android.embracesdk.payload.ThreadInfo

internal object CrashFactory {
Expand Down Expand Up @@ -39,14 +39,14 @@ internal object CrashFactory {

/**
* @param ex the throwable to parse
* @return a list of [ExceptionInfo] elements of the throwable.
* @return a list of [LegacyExceptionInfo] elements of the throwable.
*/
@JvmStatic
private fun exceptionInfo(ex: Throwable?): List<ExceptionInfo> {
val result = mutableListOf<ExceptionInfo>()
private fun exceptionInfo(ex: Throwable?): List<LegacyExceptionInfo> {
val result = mutableListOf<LegacyExceptionInfo>()
var throwable: Throwable? = ex
while (throwable != null && throwable != throwable.cause) {
val exceptionInfo = ExceptionInfo.ofThrowable(throwable)
val exceptionInfo = LegacyExceptionInfo.ofThrowable(throwable)
result.add(0, exceptionInfo)
throwable = throwable.cause
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ import io.embrace.android.embracesdk.fakes.fakeAutoDataCaptureBehavior
import io.embrace.android.embracesdk.gating.EmbraceGatingService
import io.embrace.android.embracesdk.internal.crash.CrashFileMarker
import io.embrace.android.embracesdk.payload.Crash
import io.embrace.android.embracesdk.payload.ExceptionInfo
import io.embrace.android.embracesdk.payload.JsException
import io.embrace.android.embracesdk.payload.LegacyExceptionInfo
import io.embrace.android.embracesdk.payload.ThreadInfo
import io.embrace.android.embracesdk.payload.extensions.CrashFactory
import io.embrace.android.embracesdk.session.properties.SessionPropertiesService
Expand Down Expand Up @@ -183,7 +183,7 @@ internal class EmbraceCrashServiceTest {
val crash = Crash(
"123",
listOf(
ExceptionInfo(
LegacyExceptionInfo(
"java.lang.RuntimeException",
"ExceptionMessage",
listOf("stacktrace.line")
Expand Down
Loading

0 comments on commit 11be2c2

Please sign in to comment.