Skip to content

Commit

Permalink
Add CoroutineExceptionHandler and try/catch blocks over critical area… (
Browse files Browse the repository at this point in the history
#146)

* Add CoroutineExceptionHandler and try/catch blocks over critical areas. Also moved AndroidLogger setup to earlier time to handle early crashes.

* Move try/catch to timeline.add() instead of build.
  • Loading branch information
didiergarcia authored Mar 10, 2023
1 parent 19cc5e9 commit 75de6f1
Show file tree
Hide file tree
Showing 5 changed files with 38 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ public fun Analytics(
context: Context,
configs: Configuration.() -> Unit
): Analytics {
Analytics.setLogger(AndroidLogger())
require(writeKey.isNotBlank()) { "writeKey cannot be blank " }
val conf = Configuration(
writeKey = writeKey,
Expand All @@ -66,7 +67,6 @@ public fun Analytics(
private fun Analytics.startup() {
add(AndroidContextPlugin())
add(AndroidLifecyclePlugin())
Analytics.setLogger(AndroidLogger())
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,7 @@ import com.segment.analytics.kotlin.core.platform.plugins.ContextPlugin
import com.segment.analytics.kotlin.core.platform.plugins.SegmentDestination
import com.segment.analytics.kotlin.core.platform.plugins.StartupQueue
import com.segment.analytics.kotlin.core.platform.plugins.UserInfoPlugin
import com.segment.analytics.kotlin.core.platform.plugins.logger.ConsoleLogger
import com.segment.analytics.kotlin.core.platform.plugins.logger.Logger
import com.segment.analytics.kotlin.core.platform.plugins.logger.log
import com.segment.analytics.kotlin.core.platform.plugins.logger.*
import kotlinx.coroutines.*
import kotlinx.serialization.DeserializationStrategy
import kotlinx.serialization.SerializationStrategy
Expand Down Expand Up @@ -87,7 +85,12 @@ open class Analytics protected constructor(
constructor(configuration: Configuration) : this(configuration,
object : CoroutineConfiguration {
override val store = Store()
override val analyticsScope = CoroutineScope(SupervisorJob())
val exceptionHandler = CoroutineExceptionHandler { _, t ->
Analytics.segmentLog(
"Caught Exception in Analytics Scope: ${t}"
)
}
override val analyticsScope = CoroutineScope(SupervisorJob() + exceptionHandler)
override val analyticsDispatcher : CloseableCoroutineDispatcher =
Executors.newCachedThreadPool().asCoroutineDispatcher()
override val networkIODispatcher : CloseableCoroutineDispatcher =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,11 @@ internal class Mediator(internal var plugins: CopyOnWriteArrayList<Plugin> = Cop

fun applyClosure(closure: (Plugin) -> Unit) {
plugins.forEach {
closure(it)
try {
closure(it)
} catch (t: Throwable) {
Analytics.segmentLog("Caught Exception applying closure to plugin: $it: $t")
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package com.segment.analytics.kotlin.core.platform
import com.segment.analytics.kotlin.core.Analytics
import com.segment.analytics.kotlin.core.BaseEvent
import com.segment.analytics.kotlin.core.System
import com.segment.analytics.kotlin.core.platform.plugins.logger.segmentLog
import kotlinx.coroutines.launch
import java.util.concurrent.CopyOnWriteArrayList
import kotlin.reflect.KClass
Expand Down Expand Up @@ -60,7 +61,11 @@ internal class Timeline {

// Register a new plugin
fun add(plugin: Plugin) {
plugin.setup(analytics)
try {
plugin.setup(analytics)
} catch (t: Throwable) {
Analytics.segmentLog("Caught Exception while setting up plugin $plugin: $t")
}
plugins[plugin.type]?.add(plugin)
with(analytics) {
analyticsScope.launch(analyticsDispatcher) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import kotlinx.serialization.json.put
import org.junit.jupiter.api.Assertions
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.TestInstance
import org.junit.jupiter.api.assertDoesNotThrow
import java.util.*

@TestInstance(TestInstance.Lifecycle.PER_CLASS)
Expand Down Expand Up @@ -59,4 +60,22 @@ class PluginTests {
Assertions.assertEquals(trackEvent, result)
verify(exactly = 1) { plugin.execute(trackEvent) }
}

@Test
fun `throwing exception in setup() is handled in timeline`() {
val plugin = object : Plugin {
override val type: Plugin.Type = Plugin.Type.Before
override lateinit var analytics: Analytics
override fun setup(analytics: Analytics) {
super.setup(analytics)
throw Exception("Boom!")
}
}
val spy = spyk(plugin)
assertDoesNotThrow {
timeline.add(spy)
}
verify(exactly = 1) { spy.setup(mockAnalytics) }
Assertions.assertTrue(spy.analytics === mockAnalytics)
}
}

0 comments on commit 75de6f1

Please sign in to comment.