Skip to content

Commit

Permalink
Merge pull request #840 from embrace-io/internal-err-service-refactor
Browse files Browse the repository at this point in the history
Convert internal error service to implement data capture service
  • Loading branch information
fractalwrench committed May 14, 2024
2 parents 82666fd + 963c0fe commit f87a761
Show file tree
Hide file tree
Showing 19 changed files with 49 additions and 174 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -359,11 +359,11 @@ public void testAddSpanExporter() {

private void assertError(@NonNull String functionName) {
assertInternalErrorLogged(
IntegrationTestRuleExtensionsKt.internalErrorService().getCurrentExceptionError(),
IntegrationTestRuleExtensionsKt.internalErrorService().getCapturedData(),
IllegalArgumentException.class.getCanonicalName(),
functionName + NULL_PARAMETER_ERROR_MESSAGE_TEMPLATE,
IntegrationTestRule.DEFAULT_SDK_START_TIME_MS
);
IntegrationTestRuleExtensionsKt.internalErrorService().resetExceptionErrorObject();
IntegrationTestRuleExtensionsKt.internalErrorService().getCapturedData();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ internal class SessionPayloadSourceImpl(

override fun getSessionPayload(endType: SessionSnapshotType): SessionPayload {
val sharedLibSymbolMapping = captureDataSafely(logger) { nativeThreadSamplerService?.getNativeSymbols() }
val internalErrors = captureDataSafely(logger) { internalErrorService.currentExceptionError?.toNewPayload() }
val internalErrors = captureDataSafely(logger) { internalErrorService.getCapturedData()?.toNewPayload() }
val snapshots = retrieveSpanSnapshotData()

// Ensure the span retrieving is last as that potentially ends the session span, which effectively ends the session
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,6 @@ internal class SessionModuleImpl(
essentialServiceModule.userService,
ndkService,
essentialServiceModule.sessionProperties,
sdkObservabilityModule.internalErrorService,
essentialServiceModule.networkConnectivityService
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,6 @@ import io.embrace.android.embracesdk.config.ConfigService
import io.embrace.android.embracesdk.internal.clock.Clock
import io.embrace.android.embracesdk.payload.LegacyExceptionError
import io.embrace.android.embracesdk.session.lifecycle.ProcessStateService
import java.net.BindException
import java.net.ConnectException
import java.net.HttpRetryException
import java.net.NoRouteToHostException
import java.net.PortUnreachableException
import java.net.ProtocolException
import java.net.SocketException
import java.net.SocketTimeoutException
import java.net.UnknownHostException
import java.net.UnknownServiceException

/**
* Intercepts Embrace SDK's exceptions errors and forwards them to the Embrace API.
Expand All @@ -22,101 +12,36 @@ internal class EmbraceInternalErrorService(
private val processStateService: ProcessStateService,
private val clock: Clock
) : InternalErrorService {
private var configService: ConfigService? = null
private var err: LegacyExceptionError? = null

override val currentExceptionError: LegacyExceptionError?
get() = err
private var err: LegacyExceptionError = LegacyExceptionError()

// ignore network-related exceptions since they are expected
private val ignoredExceptionClasses by lazy {
setOf<Class<*>>(
BindException::class.java,
ConnectException::class.java,
HttpRetryException::class.java,
NoRouteToHostException::class.java,
PortUnreachableException::class.java,
ProtocolException::class.java,
SocketException::class.java,
SocketTimeoutException::class.java,
UnknownHostException::class.java,
UnknownServiceException::class.java
)
}

private val ignoredExceptionStrings by lazy {
ignoredExceptionClasses.map { it.name }
}

override fun setConfigService(configService: ConfigService?) {
this.configService = configService
}

private fun ignoreThrowableCause(
throwable: Throwable?,
capturedThrowable: HashSet<Throwable>
): Boolean {
return if (throwable != null) {
if (ignoredExceptionClasses.contains(throwable.javaClass)) {
true
} else {
/* if Hashset#add returns true means that the throwable was properly added,
if it returns false, the object already exists in the set so we return false
because we are in presence of a cycle in the Throwable cause */
val addResult = capturedThrowable.add(throwable)
addResult && ignoreThrowableCause(throwable.cause, capturedThrowable)
}
} else {
false
}
}

@Synchronized
override fun handleInternalError(throwable: Throwable) {
if (ignoredExceptionClasses.contains(throwable.javaClass)) {
return
} else {
val capturedThrowable = HashSet<Throwable>()
if (ignoreThrowableCause(throwable.cause, capturedThrowable)) {
return
}
}

// If the exception has been wrapped in another exception, the ignored exception name will
// show up as the start of the message, delimited by a semicolon.
val message = throwable.message

if (message != null && ignoredExceptionStrings.contains(
message.split(":".toRegex()).dropLastWhile { it.isEmpty() }
.toTypedArray()[0]
)
) {
return
}
if (err == null) {
err = LegacyExceptionError()
}

// if the config service has not been set yet, capture the exception
if (configService == null || configService?.dataCaptureEventBehavior?.isInternalExceptionCaptureEnabled() == true) {
err?.addException(
err.addException(
throwable,
getApplicationState(),
clock
)
}
}

override var configService: ConfigService? = null

override fun getCapturedData(): LegacyExceptionError? = when {
err.occurrences > 0 -> err
else -> null
}

override fun cleanCollections() {
err = LegacyExceptionError()
}

private fun getApplicationState(): String = when {
processStateService.isInBackground -> APPLICATION_STATE_BACKGROUND
else -> APPLICATION_STATE_FOREGROUND
}

@Synchronized
override fun resetExceptionErrorObject() {
err = null
}

companion object {

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
package io.embrace.android.embracesdk.logging

import io.embrace.android.embracesdk.arch.DataCaptureService
import io.embrace.android.embracesdk.config.ConfigService
import io.embrace.android.embracesdk.payload.LegacyExceptionError

/**
* Reports an internal error to Embrace. An internal error is defined as an exception that was
* caught within Embrace code & logged to [EmbLogger].
*/
internal interface InternalErrorService {
fun setConfigService(configService: ConfigService?)
internal interface InternalErrorService : DataCaptureService<LegacyExceptionError?> {
var configService: ConfigService?
fun handleInternalError(throwable: Throwable)
fun resetExceptionErrorObject()
val currentExceptionError: LegacyExceptionError?
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package io.embrace.android.embracesdk.session

import io.embrace.android.embracesdk.logging.EmbLogger
import io.embrace.android.embracesdk.logging.InternalErrorService
import io.embrace.android.embracesdk.logging.InternalErrorType
import io.embrace.android.embracesdk.utils.stream
import java.util.concurrent.CopyOnWriteArrayList
Expand All @@ -14,9 +13,7 @@ internal class EmbraceMemoryCleanerService(private val logger: EmbLogger) : Memo

val listeners = CopyOnWriteArrayList<MemoryCleanerListener>()

override fun cleanServicesCollections(
internalErrorService: InternalErrorService
) {
override fun cleanServicesCollections() {
stream(listeners) { listener: MemoryCleanerListener ->
try {
listener.cleanCollections()
Expand All @@ -25,7 +22,6 @@ internal class EmbraceMemoryCleanerService(private val logger: EmbLogger) : Memo
logger.trackInternalError(InternalErrorType.MEMORY_CLEAN_LISTENER_FAIL, ex)
}
}
internalErrorService.resetExceptionErrorObject()
}

override fun addListener(listener: MemoryCleanerListener) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
package io.embrace.android.embracesdk.session

import io.embrace.android.embracesdk.logging.InternalErrorService

internal interface MemoryCleanerService {

/**
Expand All @@ -14,7 +12,5 @@ internal interface MemoryCleanerService {
/**
* Flush collections from each service which has collections in memory.
*/
fun cleanServicesCollections(
internalErrorService: InternalErrorService
)
fun cleanServicesCollections()
}
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ internal class V1PayloadMessageCollator(
infoLogsAttemptedToSend = captureDataSafely(logger, logMessageService::getInfoLogsAttemptedToSend),
warnLogsAttemptedToSend = captureDataSafely(logger, logMessageService::getWarnLogsAttemptedToSend),
errorLogsAttemptedToSend = captureDataSafely(logger, logMessageService::getErrorLogsAttemptedToSend),
exceptionError = captureDataSafely(logger, internalErrorService::currentExceptionError),
exceptionError = captureDataSafely(logger, internalErrorService::getCapturedData),
lastHeartbeatTime = endTime,
endType = lifeEventType,
unhandledExceptions = captureDataSafely(logger, logMessageService::getUnhandledExceptionsSent),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package io.embrace.android.embracesdk.session.orchestrator

import io.embrace.android.embracesdk.capture.connectivity.NetworkConnectivityService
import io.embrace.android.embracesdk.capture.user.UserService
import io.embrace.android.embracesdk.logging.InternalErrorService
import io.embrace.android.embracesdk.ndk.NdkService
import io.embrace.android.embracesdk.session.MemoryCleanerService
import io.embrace.android.embracesdk.session.properties.EmbraceSessionProperties
Expand All @@ -20,7 +19,6 @@ internal class OrchestratorBoundaryDelegate(
private val userService: UserService,
private val ndkService: NdkService?,
private val sessionProperties: EmbraceSessionProperties,
private val internalErrorService: InternalErrorService,
private val networkConnectivityService: NetworkConnectivityService
) {

Expand All @@ -29,7 +27,7 @@ internal class OrchestratorBoundaryDelegate(
* resetting collections in services etc.
*/
fun prepareForNewSession(clearUserInfo: Boolean = false) {
memoryCleanerService.cleanServicesCollections(internalErrorService)
memoryCleanerService.cleanServicesCollections()
sessionProperties.clearTemporary()

if (clearUserInfo) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ internal class SessionPayloadSourceImplTest {
@Before
fun setUp() {
val errorService = FakeInternalErrorService().apply {
currentExceptionError = LegacyExceptionError().apply {
data = LegacyExceptionError().apply {
addException(RuntimeException(), "test", FakeClock())
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,20 @@ import io.embrace.android.embracesdk.payload.LegacyExceptionError

internal class FakeInternalErrorService : InternalErrorService {

var lastConfigService: ConfigService? = null
override var configService: ConfigService? = null
var throwables: MutableList<Throwable> = mutableListOf()
var resetCallCount: Int = 0

override fun setConfigService(configService: ConfigService?) {
this.lastConfigService = configService
}
var data: LegacyExceptionError? = null

override fun handleInternalError(throwable: Throwable) {
throwables.add(throwable)
}

override fun resetExceptionErrorObject() {
resetCallCount++
override fun getCapturedData(): LegacyExceptionError? {
return data
}

override var currentExceptionError: LegacyExceptionError? = null
override fun cleanCollections() {
resetCallCount++
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package io.embrace.android.embracesdk.fakes

import io.embrace.android.embracesdk.logging.InternalErrorService
import io.embrace.android.embracesdk.session.MemoryCleanerListener
import io.embrace.android.embracesdk.session.MemoryCleanerService

Expand All @@ -13,9 +12,7 @@ internal class FakeMemoryCleanerService : MemoryCleanerService {
listeners.add(listener)
}

override fun cleanServicesCollections(
internalErrorService: InternalErrorService
) {
override fun cleanServicesCollections() {
callCount++
}
}
Loading

0 comments on commit f87a761

Please sign in to comment.