mirror of https://github.com/M66B/FairEmail.git
316 lines
11 KiB
Kotlin
316 lines
11 KiB
Kotlin
package com.bugsnag.android
|
|
|
|
import com.bugsnag.android.internal.ImmutableConfig
|
|
import com.bugsnag.android.internal.InternalMetrics
|
|
import com.bugsnag.android.internal.InternalMetricsNoop
|
|
import com.bugsnag.android.internal.JsonHelper
|
|
import com.bugsnag.android.internal.TrimMetrics
|
|
import java.io.IOException
|
|
|
|
internal class EventInternal : FeatureFlagAware, JsonStream.Streamable, MetadataAware, UserAware {
|
|
|
|
@JvmOverloads
|
|
internal constructor(
|
|
originalError: Throwable? = null,
|
|
config: ImmutableConfig,
|
|
severityReason: SeverityReason,
|
|
data: Metadata = Metadata(),
|
|
featureFlags: FeatureFlags = FeatureFlags()
|
|
) : this(
|
|
config.apiKey,
|
|
config.logger,
|
|
mutableListOf(),
|
|
config.discardClasses.toSet(),
|
|
when (originalError) {
|
|
null -> mutableListOf()
|
|
else -> Error.createError(originalError, config.projectPackages, config.logger)
|
|
},
|
|
data.copy(),
|
|
featureFlags.copy(),
|
|
originalError,
|
|
config.projectPackages,
|
|
severityReason,
|
|
ThreadState(originalError, severityReason.unhandled, config).threads,
|
|
User(),
|
|
config.redactedKeys.toSet()
|
|
)
|
|
|
|
internal constructor(
|
|
apiKey: String,
|
|
logger: Logger,
|
|
breadcrumbs: MutableList<Breadcrumb> = mutableListOf(),
|
|
discardClasses: Set<String> = setOf(),
|
|
errors: MutableList<Error> = mutableListOf(),
|
|
metadata: Metadata = Metadata(),
|
|
featureFlags: FeatureFlags = FeatureFlags(),
|
|
originalError: Throwable? = null,
|
|
projectPackages: Collection<String> = setOf(),
|
|
severityReason: SeverityReason = SeverityReason.newInstance(SeverityReason.REASON_HANDLED_EXCEPTION),
|
|
threads: MutableList<Thread> = mutableListOf(),
|
|
user: User = User(),
|
|
redactionKeys: Set<String>? = null
|
|
) {
|
|
this.logger = logger
|
|
this.apiKey = apiKey
|
|
this.breadcrumbs = breadcrumbs
|
|
this.discardClasses = discardClasses
|
|
this.errors = errors
|
|
this.metadata = metadata
|
|
this.featureFlags = featureFlags
|
|
this.originalError = originalError
|
|
this.projectPackages = projectPackages
|
|
this.severityReason = severityReason
|
|
this.threads = threads
|
|
this.userImpl = user
|
|
|
|
redactionKeys?.let {
|
|
this.redactedKeys = it
|
|
}
|
|
}
|
|
|
|
val originalError: Throwable?
|
|
internal var severityReason: SeverityReason
|
|
|
|
val logger: Logger
|
|
val metadata: Metadata
|
|
val featureFlags: FeatureFlags
|
|
private val discardClasses: Set<String>
|
|
internal var projectPackages: Collection<String>
|
|
|
|
private val jsonStreamer: ObjectJsonStreamer = ObjectJsonStreamer().apply {
|
|
redactedKeys = redactedKeys.toSet()
|
|
}
|
|
|
|
@JvmField
|
|
internal var session: Session? = null
|
|
|
|
var severity: Severity
|
|
get() = severityReason.currentSeverity
|
|
set(value) {
|
|
severityReason.currentSeverity = value
|
|
}
|
|
|
|
var apiKey: String
|
|
lateinit var app: AppWithState
|
|
lateinit var device: DeviceWithState
|
|
var unhandled: Boolean
|
|
get() = severityReason.unhandled
|
|
set(value) {
|
|
severityReason.unhandled = value
|
|
}
|
|
|
|
var breadcrumbs: MutableList<Breadcrumb>
|
|
var errors: MutableList<Error>
|
|
var threads: MutableList<Thread>
|
|
var groupingHash: String? = null
|
|
var context: String? = null
|
|
|
|
var redactedKeys: Collection<String>
|
|
get() = jsonStreamer.redactedKeys
|
|
set(value) {
|
|
jsonStreamer.redactedKeys = value.toSet()
|
|
metadata.redactedKeys = value.toSet()
|
|
}
|
|
var internalMetrics: InternalMetrics = InternalMetricsNoop()
|
|
|
|
/**
|
|
* @return user information associated with this Event
|
|
*/
|
|
internal var userImpl: User
|
|
|
|
fun getUnhandledOverridden(): Boolean = severityReason.unhandledOverridden
|
|
|
|
fun getOriginalUnhandled(): Boolean = severityReason.originalUnhandled
|
|
|
|
protected fun shouldDiscardClass(): Boolean {
|
|
return when {
|
|
errors.isEmpty() -> true
|
|
else -> errors.any { discardClasses.contains(it.errorClass) }
|
|
}
|
|
}
|
|
|
|
protected fun isAnr(event: Event): Boolean {
|
|
val errors = event.errors
|
|
var errorClass: String? = null
|
|
if (errors.isNotEmpty()) {
|
|
val error = errors[0]
|
|
errorClass = error.errorClass
|
|
}
|
|
return "ANR" == errorClass
|
|
}
|
|
|
|
@Throws(IOException::class)
|
|
override fun toStream(parentWriter: JsonStream) {
|
|
val writer = JsonStream(parentWriter, jsonStreamer)
|
|
// Write error basics
|
|
writer.beginObject()
|
|
writer.name("context").value(context)
|
|
writer.name("metaData").value(metadata)
|
|
|
|
writer.name("severity").value(severity)
|
|
writer.name("severityReason").value(severityReason)
|
|
writer.name("unhandled").value(severityReason.unhandled)
|
|
|
|
// Write exception info
|
|
writer.name("exceptions")
|
|
writer.beginArray()
|
|
errors.forEach { writer.value(it) }
|
|
writer.endArray()
|
|
|
|
// Write project packages
|
|
writer.name("projectPackages")
|
|
writer.beginArray()
|
|
projectPackages.forEach { writer.value(it) }
|
|
writer.endArray()
|
|
|
|
// Write user info
|
|
writer.name("user").value(userImpl)
|
|
|
|
// Write diagnostics
|
|
writer.name("app").value(app)
|
|
writer.name("device").value(device)
|
|
writer.name("breadcrumbs").value(breadcrumbs)
|
|
writer.name("groupingHash").value(groupingHash)
|
|
val usage = internalMetrics.toJsonableMap()
|
|
if (usage.isNotEmpty()) {
|
|
writer.name("usage")
|
|
writer.beginObject()
|
|
usage.forEach { entry ->
|
|
writer.name(entry.key).value(entry.value)
|
|
}
|
|
writer.endObject()
|
|
}
|
|
|
|
writer.name("threads")
|
|
writer.beginArray()
|
|
threads.forEach { writer.value(it) }
|
|
writer.endArray()
|
|
|
|
writer.name("featureFlags").value(featureFlags)
|
|
|
|
if (session != null) {
|
|
val copy = Session.copySession(session)
|
|
writer.name("session").beginObject()
|
|
writer.name("id").value(copy.id)
|
|
writer.name("startedAt").value(copy.startedAt)
|
|
writer.name("events").beginObject()
|
|
writer.name("handled").value(copy.handledCount.toLong())
|
|
writer.name("unhandled").value(copy.unhandledCount.toLong())
|
|
writer.endObject()
|
|
writer.endObject()
|
|
}
|
|
|
|
writer.endObject()
|
|
}
|
|
|
|
internal fun getErrorTypesFromStackframes(): Set<ErrorType> {
|
|
val errorTypes = errors.mapNotNull(Error::getType).toSet()
|
|
val frameOverrideTypes = errors
|
|
.map { it.stacktrace }
|
|
.flatMap { it.mapNotNull(Stackframe::type) }
|
|
return errorTypes.plus(frameOverrideTypes)
|
|
}
|
|
|
|
internal fun normalizeStackframeErrorTypes() {
|
|
if (getErrorTypesFromStackframes().size == 1) {
|
|
errors.flatMap { it.stacktrace }.forEach {
|
|
it.type = null
|
|
}
|
|
}
|
|
}
|
|
|
|
internal fun updateSeverityReasonInternal(severityReason: SeverityReason) {
|
|
this.severityReason = severityReason
|
|
}
|
|
|
|
protected fun updateSeverityInternal(severity: Severity) {
|
|
severityReason = SeverityReason(
|
|
severityReason.severityReasonType,
|
|
severity,
|
|
severityReason.unhandled,
|
|
severityReason.unhandledOverridden,
|
|
severityReason.attributeValue,
|
|
severityReason.attributeKey
|
|
)
|
|
}
|
|
|
|
protected fun updateSeverityReason(@SeverityReason.SeverityReasonType reason: String) {
|
|
severityReason = SeverityReason(
|
|
reason,
|
|
severityReason.currentSeverity,
|
|
severityReason.unhandled,
|
|
severityReason.unhandledOverridden,
|
|
severityReason.attributeValue,
|
|
severityReason.attributeKey
|
|
)
|
|
}
|
|
|
|
fun getSeverityReasonType(): String = severityReason.severityReasonType
|
|
|
|
fun trimMetadataStringsTo(maxLength: Int): TrimMetrics {
|
|
var stringCount = 0
|
|
var charCount = 0
|
|
|
|
var stringAndCharCounts = metadata.trimMetadataStringsTo(maxLength)
|
|
stringCount += stringAndCharCounts.itemsTrimmed
|
|
charCount += stringAndCharCounts.dataTrimmed
|
|
for (breadcrumb in breadcrumbs) {
|
|
stringAndCharCounts = breadcrumb.impl.trimMetadataStringsTo(maxLength)
|
|
stringCount += stringAndCharCounts.itemsTrimmed
|
|
charCount += stringAndCharCounts.dataTrimmed
|
|
}
|
|
return TrimMetrics(stringCount, charCount)
|
|
}
|
|
|
|
fun trimBreadcrumbsBy(byteCount: Int): TrimMetrics {
|
|
var removedBreadcrumbCount = 0
|
|
var removedByteCount = 0
|
|
while (removedByteCount < byteCount && breadcrumbs.isNotEmpty()) {
|
|
val breadcrumb = breadcrumbs.removeAt(0)
|
|
removedByteCount += JsonHelper.serialize(breadcrumb).size
|
|
removedBreadcrumbCount++
|
|
}
|
|
when (removedBreadcrumbCount) {
|
|
1 -> breadcrumbs.add(Breadcrumb("Removed to reduce payload size", logger))
|
|
else -> breadcrumbs.add(
|
|
Breadcrumb(
|
|
"Removed, along with ${removedBreadcrumbCount - 1} older breadcrumbs, to reduce payload size",
|
|
logger
|
|
)
|
|
)
|
|
}
|
|
return TrimMetrics(removedBreadcrumbCount, removedByteCount)
|
|
}
|
|
|
|
override fun setUser(id: String?, email: String?, name: String?) {
|
|
userImpl = User(id, email, name)
|
|
}
|
|
|
|
override fun getUser() = userImpl
|
|
|
|
override fun addMetadata(section: String, value: Map<String, Any?>) =
|
|
metadata.addMetadata(section, value)
|
|
|
|
override fun addMetadata(section: String, key: String, value: Any?) =
|
|
metadata.addMetadata(section, key, value)
|
|
|
|
override fun clearMetadata(section: String) = metadata.clearMetadata(section)
|
|
|
|
override fun clearMetadata(section: String, key: String) = metadata.clearMetadata(section, key)
|
|
|
|
override fun getMetadata(section: String) = metadata.getMetadata(section)
|
|
|
|
override fun getMetadata(section: String, key: String) = metadata.getMetadata(section, key)
|
|
|
|
override fun addFeatureFlag(name: String) = featureFlags.addFeatureFlag(name)
|
|
|
|
override fun addFeatureFlag(name: String, variant: String?) = featureFlags.addFeatureFlag(name, variant)
|
|
|
|
override fun addFeatureFlags(featureFlags: MutableIterable<FeatureFlag>) =
|
|
this.featureFlags.addFeatureFlags(featureFlags)
|
|
|
|
override fun clearFeatureFlag(name: String) = featureFlags.clearFeatureFlag(name)
|
|
|
|
override fun clearFeatureFlags() = featureFlags.clearFeatureFlags()
|
|
}
|