Skip to content

Commit

Permalink
Merge pull request #487 from embrace-io/add-custom-breadcrumb-data-so…
Browse files Browse the repository at this point in the history
…urce

Add custom breadcrumb data source
  • Loading branch information
fractalwrench committed Mar 7, 2024
2 parents 026492f + 4ff32a8 commit 4e21f52
Show file tree
Hide file tree
Showing 4 changed files with 121 additions and 20 deletions.
Original file line number Diff line number Diff line change
@@ -1,32 +1,48 @@
package io.embrace.android.embracesdk.capture.crumbs

import android.text.TextUtils
import io.embrace.android.embracesdk.arch.DataCaptureService
import io.embrace.android.embracesdk.arch.datasource.DataSourceImpl
import io.embrace.android.embracesdk.arch.destination.SessionSpanWriter
import io.embrace.android.embracesdk.arch.destination.SpanEventData
import io.embrace.android.embracesdk.arch.destination.SpanEventMapper
import io.embrace.android.embracesdk.arch.limits.UpToLimitStrategy
import io.embrace.android.embracesdk.config.ConfigService
import io.embrace.android.embracesdk.logging.InternalEmbraceLogger
import io.embrace.android.embracesdk.logging.InternalStaticEmbraceLogger
import io.embrace.android.embracesdk.internal.clock.millisToNanos
import io.embrace.android.embracesdk.payload.CustomBreadcrumb

/**
* Captures custom breadcrumbs.
*/
internal class CustomBreadcrumbDataSource(
private val configService: ConfigService,
private val store: BreadcrumbDataStore<CustomBreadcrumb> = BreadcrumbDataStore {
configService.breadcrumbBehavior.getCustomBreadcrumbLimit()
},
private val logger: InternalEmbraceLogger = InternalStaticEmbraceLogger.logger
) : DataCaptureService<List<CustomBreadcrumb>> by store {
configService: ConfigService,
writer: SessionSpanWriter
) : DataSourceImpl<SessionSpanWriter>(
destination = writer,
limitStrategy = UpToLimitStrategy(configService.breadcrumbBehavior::getCustomBreadcrumbLimit)
),
SpanEventMapper<CustomBreadcrumb> {

companion object {
internal const val TYPE_NAME = "system.breadcrumb"
internal const val EVENT_NAME = "custom-breadcrumb"
internal const val ATTR_KEY_MESSAGE = "message"
}

fun logCustom(message: String, timestamp: Long) {
if (TextUtils.isEmpty(message)) {
logger.logWarning("Breadcrumb message must not be blank")
return
}
try {
store.tryAddBreadcrumb(CustomBreadcrumb(message, timestamp))
} catch (ex: Exception) {
logger.logError("Failed to log custom breadcrumb with message $message", ex)
}
alterSessionSpan(
inputValidation = {
message.isNotEmpty()
},
captureAction = {
val crumb = CustomBreadcrumb(message, timestamp)
addEvent(crumb, ::toSpanEventData)
}
)
}

override fun toSpanEventData(obj: CustomBreadcrumb) = SpanEventData(
TYPE_NAME,
EVENT_NAME,
obj.timestamp.millisToNanos(),
mapOf("message" to (obj.message ?: ""))
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ internal class EmbraceBreadcrumbService(
private val logger: InternalEmbraceLogger = InternalStaticEmbraceLogger.logger
) : BreadcrumbService, ActivityLifecycleListener, MemoryCleanerListener {

private val customBreadcrumbDataSource = CustomBreadcrumbDataSource(configService)
private val customBreadcrumbDataSource = LegacyCustomBreadcrumbDataSource(configService)
private val webViewBreadcrumbDataSource = WebViewBreadcrumbDataSource(configService)
private val rnBreadcrumbDataSource = RnBreadcrumbDataSource(configService)
private val tapBreadcrumbDataSource = TapBreadcrumbDataSource(configService)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package io.embrace.android.embracesdk.capture.crumbs

import android.text.TextUtils
import io.embrace.android.embracesdk.arch.DataCaptureService
import io.embrace.android.embracesdk.config.ConfigService
import io.embrace.android.embracesdk.logging.InternalEmbraceLogger
import io.embrace.android.embracesdk.logging.InternalStaticEmbraceLogger
import io.embrace.android.embracesdk.payload.CustomBreadcrumb

/**
* Captures custom breadcrumbs.
*/
internal class LegacyCustomBreadcrumbDataSource(
private val configService: ConfigService,
private val store: BreadcrumbDataStore<CustomBreadcrumb> = BreadcrumbDataStore {
configService.breadcrumbBehavior.getCustomBreadcrumbLimit()
},
private val logger: InternalEmbraceLogger = InternalStaticEmbraceLogger.logger
) : DataCaptureService<List<CustomBreadcrumb>> by store {

fun logCustom(message: String, timestamp: Long) {
if (TextUtils.isEmpty(message)) {
logger.logWarning("Breadcrumb message must not be blank")
return
}
try {
store.tryAddBreadcrumb(CustomBreadcrumb(message, timestamp))
} catch (ex: Exception) {
logger.logError("Failed to log custom breadcrumb with message $message", ex)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package io.embrace.android.embracesdk.capture.crumbs

import io.embrace.android.embracesdk.fakes.FakeConfigService
import io.embrace.android.embracesdk.fakes.FakeCurrentSessionSpan
import io.embrace.android.embracesdk.internal.clock.millisToNanos
import org.junit.Assert.assertEquals
import org.junit.Before
import org.junit.Test

internal class CustomBreadcrumbDataSourceTest {

private lateinit var source: CustomBreadcrumbDataSource
private lateinit var writer: FakeCurrentSessionSpan

@Before
fun setUp() {
writer = FakeCurrentSessionSpan()
source = CustomBreadcrumbDataSource(
FakeConfigService(),
writer
)
}

@Test
fun `add invalid breadcrumb`() {
source.logCustom("", 0)
assertEquals(0, writer.addedEvents.size)
}

@Test
fun `add breadcrumb`() {
source.logCustom("Hello, world!", 15000000000)
with(writer.addedEvents.single()) {
assertEquals("custom-breadcrumb", spanName)
assertEquals(15000000000.millisToNanos(), spanStartTimeMs)
assertEquals(
mapOf(
"emb.type" to "system.breadcrumb",
"message" to "Hello, world!"
),
attributes
)
}
}

@Test
fun `limit not exceeded`() {
repeat(150) { k ->
source.logCustom("Crumb #$k", 15000000000)
}
assertEquals(100, writer.addedEvents.size)
}
}

0 comments on commit 4e21f52

Please sign in to comment.