Skip to content

Commit

Permalink
Merge branch 'main' into add-auto-term-enum
Browse files Browse the repository at this point in the history
  • Loading branch information
fractalwrench committed Dec 6, 2024
2 parents 370b045 + 4631a27 commit d6b5529
Show file tree
Hide file tree
Showing 26 changed files with 211 additions and 130 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@ package io.embrace.android.embracesdk.internal.config.behavior

import io.embrace.android.embracesdk.internal.config.UnimplementedConfig
import io.embrace.android.embracesdk.internal.config.remote.RemoteConfig
import kotlin.math.max
import kotlin.math.min

/**
* Provides whether the SDK should enable certain 'behavior' modes, such as 'integration mode'
Expand All @@ -19,11 +17,6 @@ class SdkModeBehaviorImpl(
* The default percentage of devices for which the SDK is enabled.
*/
private const val DEFAULT_THRESHOLD = 100

/**
* The default percentage offset of devices for which the SDK is enabled.
*/
private const val DEFAULT_OFFSET = 0
}

override val local: UnimplementedConfig = null
Expand All @@ -33,17 +26,7 @@ class SdkModeBehaviorImpl(
*/
private fun getThreshold(): Int = remote?.threshold ?: DEFAULT_THRESHOLD

/**
* The % at which to start enabling devices.
*/
private fun getOffset(): Int = remote?.offset ?: DEFAULT_OFFSET

override fun isSdkDisabled(): Boolean {
val result = thresholdCheck.getNormalizedDeviceId()
// Check if this is lower than the threshold, to determine whether
// we should enable/disable the SDK.
val lowerBound = max(0, getOffset())
val upperBound = min(getOffset() + getThreshold(), 100)
return (lowerBound == upperBound || result < lowerBound || result > upperBound)
return !thresholdCheck.isBehaviorEnabled(getThreshold().toFloat())
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package io.embrace.android.embracesdk.internal.config.instrumented

import io.embrace.android.embracesdk.internal.config.instrumented.schema.OtelLimitsConfig

internal fun OtelLimitsConfig.isNameValid(str: String, internal: Boolean): Boolean =
str.isNotBlank() && ((internal && str.length <= getMaxInternalNameLength()) || str.length <= getMaxNameLength())

internal fun OtelLimitsConfig.isAttributeValid(key: String, value: String, internal: Boolean) =
((internal && key.length <= getMaxInternalAttributeKeyLength()) || key.length <= getMaxCustomAttributeKeyLength()) &&
((internal && value.length <= getMaxInternalAttributeValueLength()) || value.length <= getMaxCustomAttributeValueLength())
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package io.embrace.android.embracesdk.internal.opentelemetry

import io.embrace.android.embracesdk.internal.Systrace
import io.embrace.android.embracesdk.internal.spans.EmbraceSpanLimits.MAX_TOTAL_ATTRIBUTE_COUNT
import io.embrace.android.embracesdk.internal.spans.EmbraceSpanLimits.MAX_TOTAL_EVENT_COUNT
import io.embrace.android.embracesdk.internal.config.instrumented.InstrumentedConfigImpl
import io.embrace.android.embracesdk.internal.config.instrumented.schema.OtelLimitsConfig
import io.opentelemetry.api.logs.Logger
import io.opentelemetry.api.trace.Tracer
import io.opentelemetry.sdk.OpenTelemetrySdk
Expand All @@ -19,6 +19,7 @@ import io.opentelemetry.sdk.trace.SpanLimits
internal class OpenTelemetrySdk(
openTelemetryClock: Clock,
configuration: OpenTelemetryConfiguration,
limits: OtelLimitsConfig = InstrumentedConfigImpl.otelLimits,
) {
init {
// Enforce the use of default ThreadLocal ContextStorage of the OTel Java to bypass SPI looking that violates Android strict mode
Expand All @@ -35,8 +36,8 @@ internal class OpenTelemetrySdk(
SpanLimits
.getDefault()
.toBuilder()
.setMaxNumberOfEvents(MAX_TOTAL_EVENT_COUNT)
.setMaxNumberOfAttributes(MAX_TOTAL_ATTRIBUTE_COUNT)
.setMaxNumberOfEvents(limits.getMaxTotalEventCount())
.setMaxNumberOfAttributes(limits.getMaxTotalAttributeCount())
.build()
)
.setClock(openTelemetryClock)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@ package io.embrace.android.embracesdk.internal.spans
import io.embrace.android.embracesdk.internal.arch.schema.EmbraceAttributeKey
import io.embrace.android.embracesdk.internal.arch.schema.FixedAttribute
import io.embrace.android.embracesdk.internal.arch.schema.toSessionPropertyAttributeName
import io.embrace.android.embracesdk.internal.config.instrumented.InstrumentedConfigImpl
import io.embrace.android.embracesdk.internal.config.instrumented.isAttributeValid
import io.embrace.android.embracesdk.internal.config.instrumented.schema.OtelLimitsConfig
import io.embrace.android.embracesdk.internal.payload.Attribute
import io.embrace.android.embracesdk.internal.payload.SpanEvent
import io.embrace.android.embracesdk.internal.spans.EmbraceSpanLimits.isAttributeValid
import io.embrace.android.embracesdk.internal.utils.isBlankish
import io.embrace.android.embracesdk.spans.EmbraceSpanEvent
import io.opentelemetry.api.common.AttributeKey
Expand Down Expand Up @@ -39,8 +41,14 @@ internal fun LogRecordBuilder.setAttribute(
/**
* Populate an [AttributesBuilder] with String key-value pairs from a [Map]
*/
fun AttributesBuilder.fromMap(attributes: Map<String, String>, internal: Boolean): AttributesBuilder {
attributes.filter { isAttributeValid(it.key, it.value, internal) || it.key.isValidLongValueAttribute() }.forEach {
fun AttributesBuilder.fromMap(
attributes: Map<String, String>,
internal: Boolean,
limits: OtelLimitsConfig = InstrumentedConfigImpl.otelLimits,
): AttributesBuilder {
attributes.filter {
limits.isAttributeValid(it.key, it.value, internal) || it.key.isValidLongValueAttribute()
}.forEach {
put(it.key, it.value)
}
return this
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,13 @@ import io.embrace.android.embracesdk.internal.clock.nanosToMillis
import io.embrace.android.embracesdk.internal.clock.normalizeTimestampAsMillis
import io.embrace.android.embracesdk.internal.config.behavior.REDACTED_LABEL
import io.embrace.android.embracesdk.internal.config.behavior.SensitiveKeysBehavior
import io.embrace.android.embracesdk.internal.config.instrumented.InstrumentedConfigImpl
import io.embrace.android.embracesdk.internal.config.instrumented.isAttributeValid
import io.embrace.android.embracesdk.internal.config.instrumented.isNameValid
import io.embrace.android.embracesdk.internal.config.instrumented.schema.OtelLimitsConfig
import io.embrace.android.embracesdk.internal.payload.Attribute
import io.embrace.android.embracesdk.internal.payload.Span
import io.embrace.android.embracesdk.internal.payload.toNewPayload
import io.embrace.android.embracesdk.internal.spans.EmbraceSpanLimits.EXCEPTION_EVENT_NAME
import io.embrace.android.embracesdk.internal.spans.EmbraceSpanLimits.MAX_CUSTOM_ATTRIBUTE_COUNT
import io.embrace.android.embracesdk.internal.spans.EmbraceSpanLimits.MAX_CUSTOM_EVENT_COUNT
import io.embrace.android.embracesdk.internal.spans.EmbraceSpanLimits.MAX_TOTAL_EVENT_COUNT
import io.embrace.android.embracesdk.internal.spans.EmbraceSpanLimits.isAttributeValid
import io.embrace.android.embracesdk.internal.spans.EmbraceSpanLimits.isNameValid
import io.embrace.android.embracesdk.internal.utils.truncatedStacktraceText
import io.embrace.android.embracesdk.spans.AutoTerminationMode
import io.embrace.android.embracesdk.spans.EmbraceSpan
Expand All @@ -43,6 +41,7 @@ internal class EmbraceSpanImpl(
private val openTelemetryClock: Clock,
private val spanRepository: SpanRepository,
private val sensitiveKeysBehavior: SensitiveKeysBehavior?,
private val limits: OtelLimitsConfig = InstrumentedConfigImpl.otelLimits,
) : PersistableEmbraceSpan {

private val startedSpan: AtomicReference<io.opentelemetry.api.trace.Span?> = AtomicReference(null)
Expand Down Expand Up @@ -166,7 +165,7 @@ internal class EmbraceSpanImpl(
}

override fun addEvent(name: String, timestampMs: Long?, attributes: Map<String, String>?): Boolean =
recordEvent(customEvents, customEventCount, MAX_CUSTOM_EVENT_COUNT) {
recordEvent(customEvents, customEventCount, limits.getMaxCustomEventCount()) {
EmbraceSpanEvent.create(
name = name,
timestampMs = timestampMs?.normalizeTimestampAsMillis() ?: openTelemetryClock.now().nanosToMillis(),
Expand All @@ -175,7 +174,7 @@ internal class EmbraceSpanImpl(
}

override fun recordException(exception: Throwable, attributes: Map<String, String>?): Boolean =
recordEvent(customEvents, customEventCount, MAX_CUSTOM_EVENT_COUNT) {
recordEvent(customEvents, customEventCount, limits.getMaxCustomEventCount()) {
val eventAttributes = mutableMapOf<String, String>()
if (attributes != null) {
eventAttributes.putAll(attributes)
Expand All @@ -192,14 +191,14 @@ internal class EmbraceSpanImpl(
eventAttributes[ExceptionAttributes.EXCEPTION_STACKTRACE.key] = exception.truncatedStacktraceText()

EmbraceSpanEvent.create(
name = EXCEPTION_EVENT_NAME,
name = limits.getExceptionEventName(),
timestampMs = openTelemetryClock.now().nanosToMillis(),
attributes = eventAttributes
)
}

override fun addSystemEvent(name: String, timestampMs: Long?, attributes: Map<String, String>?): Boolean =
recordEvent(systemEvents, systemEventCount, MAX_TOTAL_EVENT_COUNT) {
recordEvent(systemEvents, systemEventCount, limits.getMaxTotalEventCount()) {
EmbraceSpanEvent.create(
name = name,
timestampMs = timestampMs?.normalizeTimestampAsMillis() ?: openTelemetryClock.now().nanosToMillis(),
Expand Down Expand Up @@ -232,9 +231,9 @@ internal class EmbraceSpanImpl(
}

override fun addAttribute(key: String, value: String): Boolean {
if (customAttributes.size < MAX_CUSTOM_ATTRIBUTE_COUNT && isAttributeValid(key, value, spanBuilder.internal)) {
if (customAttributes.size < limits.getMaxCustomAttributeCount() && limits.isAttributeValid(key, value, spanBuilder.internal)) {
synchronized(customAttributes) {
if (customAttributes.size < MAX_CUSTOM_ATTRIBUTE_COUNT && isRecording) {
if (customAttributes.size < limits.getMaxCustomAttributeCount() && isRecording) {
customAttributes[key] = value
spanRepository.notifySpanUpdate()
return true
Expand All @@ -246,7 +245,7 @@ internal class EmbraceSpanImpl(
}

override fun updateName(newName: String): Boolean {
if (newName.isNameValid(spanBuilder.internal)) {
if (limits.isNameValid(newName, spanBuilder.internal)) {
synchronized(startedSpan) {
if (!spanStarted() || isRecording) {
updatedName = newName
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ package io.embrace.android.embracesdk.internal.spans

import io.embrace.android.embracesdk.internal.arch.schema.TelemetryType
import io.embrace.android.embracesdk.internal.clock.nanosToMillis
import io.embrace.android.embracesdk.internal.spans.EmbraceSpanLimits.isNameValid
import io.embrace.android.embracesdk.internal.config.instrumented.InstrumentedConfigImpl
import io.embrace.android.embracesdk.internal.config.instrumented.isNameValid
import io.embrace.android.embracesdk.internal.config.instrumented.schema.OtelLimitsConfig
import io.embrace.android.embracesdk.spans.AutoTerminationMode
import io.embrace.android.embracesdk.spans.EmbraceSpan
import io.embrace.android.embracesdk.spans.EmbraceSpanEvent
Expand All @@ -16,6 +18,7 @@ internal class SpanServiceImpl(
private val spanRepository: SpanRepository,
private val embraceSpanFactory: EmbraceSpanFactory,
private val currentSessionSpan: CurrentSessionSpan,
private val limits: OtelLimitsConfig = InstrumentedConfigImpl.otelLimits,
) : SpanService {
private val initialized = AtomicBoolean(false)

Expand Down Expand Up @@ -153,9 +156,9 @@ internal class SpanServiceImpl(
events: List<EmbraceSpanEvent>? = null,
attributes: Map<String, String>? = null,
): Boolean {
return (name.isNameValid(internal)) &&
((events == null) || (events.size <= EmbraceSpanLimits.MAX_CUSTOM_EVENT_COUNT)) &&
((attributes == null) || (attributes.size <= EmbraceSpanLimits.MAX_CUSTOM_ATTRIBUTE_COUNT))
return (limits.isNameValid(name, internal)) &&
((events == null) || (events.size <= limits.getMaxCustomEventCount())) &&
((attributes == null) || (attributes.size <= limits.getMaxCustomAttributeCount()))
}

companion object {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package io.embrace.android.embracesdk.internal.config.behavior

import io.embrace.android.embracesdk.fakes.createSdkModeBehavior
import io.embrace.android.embracesdk.internal.config.remote.RemoteConfig
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Test
Expand All @@ -12,7 +13,7 @@ internal class SdkModeBehaviorImplTest {
private val enabled = BehaviorThresholdCheck { "07D85B44E4E245F4A30E559BFC000000" }

// ~50% enabled
private val halfEnabled = BehaviorThresholdCheck { "07D85B44E4E245F4A30E559BFC888888" }
private val halfEnabled = BehaviorThresholdCheck { "07D85B44E4E245F4A30E559BFC800000" }

// 0% enabled
private val disabled = BehaviorThresholdCheck { "07D85B44E4E245F4A30E559BFCFFFFFF" }
Expand All @@ -30,8 +31,17 @@ internal class SdkModeBehaviorImplTest {

@Test
fun testSdkEnabled() {
// SDK disabled
// Device disabled
assertEquals(100.0f, disabled.getNormalizedDeviceId())
var behavior = createSdkModeBehavior(
thresholdCheck = disabled,
remoteCfg = RemoteConfig(threshold = 99)
)
assertTrue(behavior.isSdkDisabled())

// SDK disabled
assertEquals(0.0f, enabled.getNormalizedDeviceId())
behavior = createSdkModeBehavior(
thresholdCheck = enabled,
remoteCfg = RemoteConfig(threshold = 0)
)
Expand All @@ -44,17 +54,18 @@ internal class SdkModeBehaviorImplTest {
)
assertFalse(behavior.isSdkDisabled())

// SDK 30% enabled with default offset
// SDK 30% enabled
assertEquals(50.000008f, halfEnabled.getNormalizedDeviceId())
behavior = createSdkModeBehavior(
thresholdCheck = halfEnabled,
remoteCfg = RemoteConfig(threshold = 30)
)
assertTrue(behavior.isSdkDisabled())

// SDK 30% enabled with non-default offset
// SDK 51% enabled
behavior = createSdkModeBehavior(
thresholdCheck = halfEnabled,
remoteCfg = RemoteConfig(threshold = 30, offset = 25)
remoteCfg = RemoteConfig(threshold = 51)
)
assertFalse(behavior.isSdkDisabled())
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import io.embrace.android.embracesdk.fakes.FakeClock
import io.embrace.android.embracesdk.fakes.FakePersistableEmbraceSpan
import io.embrace.android.embracesdk.fakes.injection.FakeInitModule
import io.embrace.android.embracesdk.internal.arch.schema.ErrorCodeAttribute
import io.embrace.android.embracesdk.internal.config.instrumented.InstrumentedConfigImpl
import io.embrace.android.embracesdk.internal.payload.Span
import io.embrace.android.embracesdk.internal.spans.EmbraceSpanLimits.EXCEPTION_EVENT_NAME
import io.embrace.android.embracesdk.internal.spans.hasFixedAttribute
import io.opentelemetry.api.common.AttributeKey
import io.opentelemetry.api.common.Attributes
Expand Down Expand Up @@ -143,14 +143,15 @@ internal class EmbSpanTest {

with(checkNotNull(fakeEmbraceSpan.events)) {
assertEquals(2, size)
val expectedName = InstrumentedConfigImpl.otelLimits.getExceptionEventName()
with(first()) {
assertEquals(EXCEPTION_EVENT_NAME, name)
assertEquals(expectedName, name)
assertEquals(firstExceptionTime, timestampNanos)
assertEquals(0, attributes.size)
}

with(last()) {
assertEquals(EXCEPTION_EVENT_NAME, name)
assertEquals(expectedName, name)
assertEquals(secondExceptionTime, timestampNanos)
assertEquals(1, attributes.size)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,9 @@ import io.embrace.android.embracesdk.internal.arch.schema.SchemaType
import io.embrace.android.embracesdk.internal.arch.schema.TelemetryType
import io.embrace.android.embracesdk.internal.clock.nanosToMillis
import io.embrace.android.embracesdk.internal.config.behavior.SensitiveKeysBehavior
import io.embrace.android.embracesdk.internal.config.instrumented.InstrumentedConfigImpl
import io.embrace.android.embracesdk.internal.opentelemetry.embraceSpanBuilder
import io.embrace.android.embracesdk.internal.payload.toNewPayload
import io.embrace.android.embracesdk.internal.spans.EmbraceSpanLimits.MAX_TOTAL_ATTRIBUTE_COUNT
import io.embrace.android.embracesdk.internal.spans.EmbraceSpanLimits.MAX_TOTAL_EVENT_COUNT
import io.embrace.android.embracesdk.internal.telemetry.TelemetryService
import io.embrace.android.embracesdk.spans.AutoTerminationMode
import io.embrace.android.embracesdk.spans.EmbraceSpan
Expand Down Expand Up @@ -496,15 +495,16 @@ internal class CurrentSessionSpanImplTests {

@Test
fun `validate maximum events on session span`() {
repeat(MAX_TOTAL_EVENT_COUNT + 1) {
val limit = InstrumentedConfigImpl.otelLimits.getMaxTotalEventCount()
repeat(limit + 1) {
currentSessionSpan.addEvent(SchemaType.Breadcrumb("test-event"), 1000L + it)
}

val span = currentSessionSpan.endSession(true).single()
assertEquals("emb-session", span.name)

// verify event was added to the span
assertEquals(MAX_TOTAL_EVENT_COUNT, span.toNewPayload().events?.size)
assertEquals(limit, span.toNewPayload().events?.size)
}

@Test
Expand All @@ -522,15 +522,16 @@ internal class CurrentSessionSpanImplTests {

@Test
fun `validate maximum attributes on session span`() {
repeat(MAX_TOTAL_ATTRIBUTE_COUNT + 1) {
val limit = InstrumentedConfigImpl.otelLimits.getMaxTotalAttributeCount()
repeat(limit + 1) {
currentSessionSpan.addSystemAttribute(SpanAttributeData("attribute-$it", "value"))
}

val span = currentSessionSpan.endSession(true).single()
assertEquals("emb-session", span.name)

// verify event was added to the span
assertEquals(MAX_TOTAL_ATTRIBUTE_COUNT, span.toNewPayload().attributes?.size)
assertEquals(limit, span.toNewPayload().attributes?.size)
}

private fun CurrentSessionSpan.assertNoSessionSpan() {
Expand Down
Loading

0 comments on commit d6b5529

Please sign in to comment.