Skip to content

Commit 4b3500b

Browse files
committed
Bypass instrumentation time limits on spans for non-Embrace users
1 parent 39c295e commit 4b3500b

File tree

14 files changed

+223
-99
lines changed

14 files changed

+223
-99
lines changed

embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/OpenTelemetryModule.kt

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,6 @@ interface OpenTelemetryModule {
2424
*/
2525
val otelSdkConfig: OtelSdkConfig
2626

27-
/**
28-
* Setup configuration for redacting sensitive keys
29-
*/
30-
fun setupSensitiveKeysBehavior(sensitiveKeysBehavior: SensitiveKeysBehavior)
31-
3227
/**
3328
* Caches span instances that are in progress or completed in the current session
3429
*/
@@ -90,4 +85,12 @@ interface OpenTelemetryModule {
9085
* OpenTelemetry SDK compatible clock based on the internal Embrace clock instance
9186
*/
9287
val openTelemetryClock: io.opentelemetry.sdk.common.Clock
88+
89+
/**
90+
* Setup configuration configuration-dependent behavior
91+
*/
92+
fun applyConfiguration(
93+
sensitiveKeysBehavior: SensitiveKeysBehavior,
94+
bypassLimitsValidation: Boolean,
95+
)
9396
}

embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/OpenTelemetryModuleImpl.kt

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import io.embrace.android.embracesdk.internal.otel.impl.EmbOpenTelemetry
99
import io.embrace.android.embracesdk.internal.otel.impl.EmbTracerProvider
1010
import io.embrace.android.embracesdk.internal.otel.logs.LogSink
1111
import io.embrace.android.embracesdk.internal.otel.logs.LogSinkImpl
12+
import io.embrace.android.embracesdk.internal.otel.sdk.LimitsValidator
1213
import io.embrace.android.embracesdk.internal.otel.sdk.OtelSdkWrapper
1314
import io.embrace.android.embracesdk.internal.otel.spans.EmbraceSpanFactory
1415
import io.embrace.android.embracesdk.internal.otel.spans.EmbraceSpanFactoryImpl
@@ -78,17 +79,23 @@ internal class OpenTelemetryModuleImpl(
7879

7980
private var sensitiveKeysBehavior: SensitiveKeysBehavior? = null
8081

81-
override fun setupSensitiveKeysBehavior(sensitiveKeysBehavior: SensitiveKeysBehavior) {
82+
override fun applyConfiguration(sensitiveKeysBehavior: SensitiveKeysBehavior, bypassLimitsValidation: Boolean) {
8283
this.sensitiveKeysBehavior = sensitiveKeysBehavior
84+
this.bypassLimitsValidation = bypassLimitsValidation
8385
}
8486

8587
private var internalSpanStopCallback: ((spanId: String) -> Unit)? = null
8688

89+
private var bypassLimitsValidation: Boolean = false
90+
91+
private val limitsValidator: LimitsValidator = LimitsValidator(bypassLimitsValidation = ::bypassLimitsValidation)
92+
8793
private val embraceSpanFactory: EmbraceSpanFactory by singleton {
8894
EmbraceSpanFactoryImpl(
8995
tracer = sdkTracer,
9096
openTelemetryClock = openTelemetryClock,
9197
spanRepository = spanRepository,
98+
limitsValidator = limitsValidator,
9299
stopCallback = ::spanStopCallbackWrapper,
93100
redactionFunction = ::redactionFunction
94101
)
@@ -111,6 +118,7 @@ internal class OpenTelemetryModuleImpl(
111118
spanRepository = spanRepository,
112119
canStartNewSpan = currentSessionSpan::canStartNewSpan,
113120
initCallback = currentSessionSpan::initializeService,
121+
limitsValidator = limitsValidator
114122
) { embraceSpanFactory }
115123
}
116124

embrace-android-otel/src/main/kotlin/io/embrace/android/embracesdk/internal/otel/config/OtelLimitsConfigExt.kt

Lines changed: 0 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,6 @@
11
package io.embrace.android.embracesdk.internal.otel.config
22

33
import io.embrace.android.embracesdk.internal.config.instrumented.schema.OtelLimitsConfig
4-
import io.embrace.android.embracesdk.spans.EmbraceSpanEvent
5-
6-
fun OtelLimitsConfig.isNameValid(str: String, internal: Boolean): Boolean =
7-
str.isNotBlank() && ((internal && str.length <= getMaxInternalNameLength()) || str.length <= getMaxNameLength())
8-
9-
fun OtelLimitsConfig.isEventCountValid(events: List<EmbraceSpanEvent>, internal: Boolean): Boolean {
10-
val max = if (internal) {
11-
getMaxSystemEventCount()
12-
} else {
13-
getMaxCustomEventCount()
14-
}
15-
16-
return events.size <= max
17-
}
18-
19-
fun OtelLimitsConfig.isAttributeCountValid(attributes: Map<String, String>, internal: Boolean): Boolean {
20-
val max = if (internal) {
21-
getMaxSystemAttributeCount()
22-
} else {
23-
getMaxCustomAttributeCount()
24-
}
25-
26-
return attributes.size <= max
27-
}
28-
29-
fun OtelLimitsConfig.isAttributeValid(key: String, value: String, internal: Boolean) =
30-
((internal && key.length <= getMaxInternalAttributeKeyLength()) || key.length <= getMaxCustomAttributeKeyLength()) &&
31-
((internal && value.length <= getMaxInternalAttributeValueLength()) || value.length <= getMaxCustomAttributeValueLength())
324

335
fun OtelLimitsConfig.getMaxTotalAttributeCount() = getMaxSystemAttributeCount() + getMaxCustomAttributeCount()
346

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
package io.embrace.android.embracesdk.internal.otel.sdk
2+
3+
import io.embrace.android.embracesdk.internal.config.instrumented.OtelLimitsConfigImpl
4+
import io.embrace.android.embracesdk.internal.config.instrumented.schema.OtelLimitsConfig
5+
import io.embrace.android.embracesdk.spans.EmbraceSpanEvent
6+
7+
class LimitsValidator(
8+
val otelLimitsConfig: OtelLimitsConfig = OtelLimitsConfigImpl,
9+
private val bypassLimitsValidation: (() -> Boolean) = { false },
10+
) {
11+
fun isNameValid(str: String, internal: Boolean): Boolean {
12+
return if (internal) {
13+
str.isNotBlank() && str.length <= otelLimitsConfig.getMaxInternalNameLength()
14+
} else if (!bypassLimitsValidation()) {
15+
str.isNotBlank() && str.length <= otelLimitsConfig.getMaxNameLength()
16+
} else {
17+
true
18+
}
19+
}
20+
21+
fun isEventCountValid(events: List<EmbraceSpanEvent>, internal: Boolean): Boolean {
22+
return if (internal) {
23+
events.size <= otelLimitsConfig.getMaxSystemEventCount()
24+
} else if (!bypassLimitsValidation()) {
25+
events.size <= otelLimitsConfig.getMaxCustomEventCount()
26+
} else {
27+
true
28+
}
29+
}
30+
31+
fun isAttributeCountValid(attributes: Map<String, String>, internal: Boolean): Boolean {
32+
return if (internal) {
33+
attributes.size <= otelLimitsConfig.getMaxSystemAttributeCount()
34+
} else if (!bypassLimitsValidation()) {
35+
attributes.size <= otelLimitsConfig.getMaxCustomAttributeCount()
36+
} else {
37+
true
38+
}
39+
}
40+
41+
fun isAttributeValid(key: String, value: String, internal: Boolean): Boolean {
42+
with(otelLimitsConfig) {
43+
return if (internal) {
44+
key.length <= getMaxInternalAttributeKeyLength() && value.length <= getMaxInternalAttributeValueLength()
45+
} else if (!bypassLimitsValidation()) {
46+
key.length <= getMaxCustomAttributeKeyLength() && value.length <= getMaxCustomAttributeValueLength()
47+
} else {
48+
true
49+
}
50+
}
51+
}
52+
}

embrace-android-otel/src/main/kotlin/io/embrace/android/embracesdk/internal/otel/sdk/OtelExt.kt

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,8 @@
11
package io.embrace.android.embracesdk.internal.otel.sdk
22

3-
import io.embrace.android.embracesdk.internal.config.instrumented.InstrumentedConfigImpl
4-
import io.embrace.android.embracesdk.internal.config.instrumented.schema.OtelLimitsConfig
53
import io.embrace.android.embracesdk.internal.otel.attrs.EmbraceAttribute
64
import io.embrace.android.embracesdk.internal.otel.attrs.EmbraceAttributeKey
75
import io.embrace.android.embracesdk.internal.otel.attrs.asOtelAttributeKey
8-
import io.embrace.android.embracesdk.internal.otel.config.isAttributeValid
96
import io.embrace.android.embracesdk.internal.otel.payload.toEmbracePayload
107
import io.embrace.android.embracesdk.internal.otel.spans.EmbraceSpanData
118
import io.embrace.android.embracesdk.internal.otel.spans.EmbraceSpanData.Companion.fromEventData
@@ -27,10 +24,10 @@ import io.opentelemetry.sdk.trace.data.SpanData
2724
fun AttributesBuilder.fromMap(
2825
attributes: Map<String, String>,
2926
internal: Boolean,
30-
limits: OtelLimitsConfig = InstrumentedConfigImpl.otelLimits,
27+
limitsValidator: LimitsValidator
3128
): AttributesBuilder {
3229
attributes.filter {
33-
limits.isAttributeValid(it.key, it.value, internal) || it.key.isValidLongValueAttribute()
30+
limitsValidator.isAttributeValid(it.key, it.value, internal) || it.key.isValidLongValueAttribute()
3431
}.forEach {
3532
put(it.key, it.value)
3633
}

embrace-android-otel/src/main/kotlin/io/embrace/android/embracesdk/internal/otel/spans/EmbraceSpanFactoryImpl.kt

Lines changed: 16 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,14 @@ package io.embrace.android.embracesdk.internal.otel.spans
33
import io.embrace.android.embracesdk.internal.clock.millisToNanos
44
import io.embrace.android.embracesdk.internal.clock.nanosToMillis
55
import io.embrace.android.embracesdk.internal.clock.normalizeTimestampAsMillis
6-
import io.embrace.android.embracesdk.internal.config.instrumented.InstrumentedConfigImpl
7-
import io.embrace.android.embracesdk.internal.config.instrumented.schema.OtelLimitsConfig
86
import io.embrace.android.embracesdk.internal.otel.attrs.EmbraceAttribute
97
import io.embrace.android.embracesdk.internal.otel.attrs.asPair
10-
import io.embrace.android.embracesdk.internal.otel.config.isAttributeValid
11-
import io.embrace.android.embracesdk.internal.otel.config.isNameValid
128
import io.embrace.android.embracesdk.internal.otel.payload.toEmbracePayload
139
import io.embrace.android.embracesdk.internal.otel.schema.EmbType
1410
import io.embrace.android.embracesdk.internal.otel.schema.ErrorCodeAttribute
1511
import io.embrace.android.embracesdk.internal.otel.schema.ErrorCodeAttribute.Failure.fromErrorCode
1612
import io.embrace.android.embracesdk.internal.otel.schema.LinkType
13+
import io.embrace.android.embracesdk.internal.otel.sdk.LimitsValidator
1714
import io.embrace.android.embracesdk.internal.otel.sdk.fromMap
1815
import io.embrace.android.embracesdk.internal.otel.sdk.hasEmbraceAttribute
1916
import io.embrace.android.embracesdk.internal.otel.sdk.id.OtelIds
@@ -47,6 +44,7 @@ class EmbraceSpanFactoryImpl(
4744
private val tracer: Tracer,
4845
private val openTelemetryClock: Clock,
4946
private val spanRepository: SpanRepository,
47+
private val limitsValidator: LimitsValidator = LimitsValidator(),
5048
private val stopCallback: ((spanId: String) -> Unit)? = null,
5149
private var redactionFunction: ((key: String, value: String) -> String)? = null,
5250
) : EmbraceSpanFactory {
@@ -77,6 +75,7 @@ class EmbraceSpanFactoryImpl(
7775
otelSpanBuilderWrapper = otelSpanBuilderWrapper,
7876
openTelemetryClock = openTelemetryClock,
7977
spanRepository = spanRepository,
78+
limitsValidator = limitsValidator,
8079
stopCallback = stopCallback,
8180
redactionFunction = redactionFunction,
8281
autoTerminationMode = autoTerminationMode
@@ -87,9 +86,9 @@ private class EmbraceSpanImpl(
8786
private val otelSpanBuilderWrapper: OtelSpanBuilderWrapper,
8887
private val openTelemetryClock: Clock,
8988
private val spanRepository: SpanRepository,
89+
private val limitsValidator: LimitsValidator,
9090
private val stopCallback: ((spanId: String) -> Unit)? = null,
9191
private val redactionFunction: ((key: String, value: String) -> String)? = null,
92-
private val limits: OtelLimitsConfig = InstrumentedConfigImpl.otelLimits,
9392
override val autoTerminationMode: AutoTerminationMode = AutoTerminationMode.NONE,
9493
) : EmbraceSdkSpan {
9594

@@ -213,7 +212,7 @@ private class EmbraceSpanImpl(
213212
}
214213

215214
override fun addEvent(name: String, timestampMs: Long?, attributes: Map<String, String>?): Boolean =
216-
addObject(customEvents, customEventCount, limits.getMaxCustomEventCount()) {
215+
addObject(customEvents, customEventCount, limitsValidator.otelLimitsConfig.getMaxCustomEventCount()) {
217216
EmbraceSpanEvent.create(
218217
name = name,
219218
timestampMs = timestampMs?.normalizeTimestampAsMillis() ?: openTelemetryClock.now().nanosToMillis(),
@@ -222,7 +221,7 @@ private class EmbraceSpanImpl(
222221
}
223222

224223
override fun recordException(exception: Throwable, attributes: Map<String, String>?): Boolean =
225-
addObject(customEvents, customEventCount, limits.getMaxCustomEventCount()) {
224+
addObject(customEvents, customEventCount, limitsValidator.otelLimitsConfig.getMaxCustomEventCount()) {
226225
val eventAttributes = mutableMapOf<String, String>()
227226
if (attributes != null) {
228227
eventAttributes.putAll(attributes)
@@ -239,14 +238,14 @@ private class EmbraceSpanImpl(
239238
eventAttributes[ExceptionAttributes.EXCEPTION_STACKTRACE.key] = exception.truncatedStacktraceText()
240239

241240
EmbraceSpanEvent.create(
242-
name = limits.getExceptionEventName(),
241+
name = limitsValidator.otelLimitsConfig.getExceptionEventName(),
243242
timestampMs = openTelemetryClock.now().nanosToMillis(),
244243
attributes = eventAttributes
245244
)
246245
}
247246

248247
override fun addSystemEvent(name: String, timestampMs: Long?, attributes: Map<String, String>?): Boolean =
249-
addObject(systemEvents, systemEventCount, limits.getMaxSystemEventCount()) {
248+
addObject(systemEvents, systemEventCount, limitsValidator.otelLimitsConfig.getMaxSystemEventCount()) {
250249
EmbraceSpanEvent.create(
251250
name = name,
252251
timestampMs = timestampMs?.normalizeTimestampAsMillis() ?: openTelemetryClock.now().nanosToMillis(),
@@ -281,11 +280,11 @@ private class EmbraceSpanImpl(
281280
override fun getStartTimeMs(): Long? = spanStartTimeMs
282281

283282
override fun addAttribute(key: String, value: String): Boolean {
284-
if (customAttributes.size < limits.getMaxCustomAttributeCount() &&
285-
limits.isAttributeValid(key, value, otelSpanBuilderWrapper.internal)
283+
if (customAttributes.size < limitsValidator.otelLimitsConfig.getMaxCustomAttributeCount() &&
284+
limitsValidator.isAttributeValid(key, value, otelSpanBuilderWrapper.internal)
286285
) {
287286
synchronized(customAttributes) {
288-
if (customAttributes.size < limits.getMaxCustomAttributeCount() && isRecording) {
287+
if (customAttributes.size < limitsValidator.otelLimitsConfig.getMaxCustomAttributeCount() && isRecording) {
289288
customAttributes[key] = value
290289
spanRepository.notifySpanUpdate()
291290
return true
@@ -297,7 +296,7 @@ private class EmbraceSpanImpl(
297296
}
298297

299298
override fun updateName(newName: String): Boolean {
300-
if (limits.isNameValid(newName, otelSpanBuilderWrapper.internal)) {
299+
if (limitsValidator.isNameValid(newName, otelSpanBuilderWrapper.internal)) {
301300
synchronized(startedSpan) {
302301
if (!spanStarted() || isRecording) {
303302
updatedName = newName
@@ -312,12 +311,12 @@ private class EmbraceSpanImpl(
312311
}
313312

314313
override fun addSystemLink(linkedSpanContext: SpanContext, type: LinkType, attributes: Map<String, String>): Boolean =
315-
addObject(systemLinks, systemLinkCount, limits.getMaxSystemLinkCount()) {
314+
addObject(systemLinks, systemLinkCount, limitsValidator.otelLimitsConfig.getMaxSystemLinkCount()) {
316315
EmbraceLinkData(linkedSpanContext, mutableMapOf(type.asPair()).apply { putAll(attributes) })
317316
}
318317

319318
override fun addLink(linkedSpanContext: SpanContext, attributes: Map<String, String>?): Boolean =
320-
addObject(customLinks, customLinkCount, limits.getMaxCustomLinkCount()) {
319+
addObject(customLinks, customLinkCount, limitsValidator.otelLimitsConfig.getMaxCustomLinkCount()) {
321320
EmbraceLinkData(linkedSpanContext, attributes ?: emptyMap())
322321
}
323322

@@ -415,7 +414,7 @@ private class EmbraceSpanImpl(
415414

416415
(systemEvents + redactedCustomEvents).forEach { event ->
417416
val eventAttributes = if (event.attributes.isNotEmpty()) {
418-
Attributes.builder().fromMap(event.attributes, otelSpanBuilderWrapper.internal).build()
417+
Attributes.builder().fromMap(event.attributes, otelSpanBuilderWrapper.internal, limitsValidator).build()
419418
} else {
420419
Attributes.empty()
421420
}
@@ -434,7 +433,7 @@ private class EmbraceSpanImpl(
434433

435434
(systemLinks + redactedCustomLinks).forEach {
436435
val linkAttributes = if (it.attributes.isNotEmpty()) {
437-
Attributes.builder().fromMap(attributes = it.attributes, false).build()
436+
Attributes.builder().fromMap(attributes = it.attributes, false, limitsValidator).build()
438437
} else {
439438
Attributes.empty()
440439
}

embrace-android-otel/src/main/kotlin/io/embrace/android/embracesdk/internal/otel/spans/EmbraceSpanService.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package io.embrace.android.embracesdk.internal.otel.spans
22

33
import io.embrace.android.embracesdk.internal.otel.schema.EmbType
4+
import io.embrace.android.embracesdk.internal.otel.sdk.LimitsValidator
45
import io.embrace.android.embracesdk.internal.utils.Provider
56
import io.embrace.android.embracesdk.spans.AutoTerminationMode
67
import io.embrace.android.embracesdk.spans.EmbraceSpan
@@ -14,6 +15,7 @@ import io.embrace.android.embracesdk.spans.ErrorCode
1415
*/
1516
class EmbraceSpanService(
1617
private val spanRepository: SpanRepository,
18+
private val limitsValidator: LimitsValidator,
1719
private val canStartNewSpan: (parentSpan: EmbraceSpan?, internal: Boolean) -> Boolean,
1820
private val initCallback: (initTimeMs: Long) -> Unit,
1921
private val embraceSpanFactorySupplier: Provider<EmbraceSpanFactory>,
@@ -29,6 +31,7 @@ class EmbraceSpanService(
2931
if (!initialized()) {
3032
val realSpansService = SpanServiceImpl(
3133
spanRepository = spanRepository,
34+
limitsValidator = limitsValidator,
3235
embraceSpanFactory = embraceSpanFactorySupplier(),
3336
canStartNewSpan = canStartNewSpan,
3437
initCallback = initCallback

embrace-android-otel/src/main/kotlin/io/embrace/android/embracesdk/internal/otel/spans/SpanServiceImpl.kt

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,8 @@
11
package io.embrace.android.embracesdk.internal.otel.spans
22

33
import io.embrace.android.embracesdk.internal.clock.nanosToMillis
4-
import io.embrace.android.embracesdk.internal.config.instrumented.InstrumentedConfigImpl
5-
import io.embrace.android.embracesdk.internal.config.instrumented.schema.OtelLimitsConfig
6-
import io.embrace.android.embracesdk.internal.otel.config.isAttributeCountValid
7-
import io.embrace.android.embracesdk.internal.otel.config.isEventCountValid
8-
import io.embrace.android.embracesdk.internal.otel.config.isNameValid
94
import io.embrace.android.embracesdk.internal.otel.schema.EmbType
5+
import io.embrace.android.embracesdk.internal.otel.sdk.LimitsValidator
106
import io.embrace.android.embracesdk.internal.utils.EmbTrace
117
import io.embrace.android.embracesdk.spans.AutoTerminationMode
128
import io.embrace.android.embracesdk.spans.EmbraceSpan
@@ -20,9 +16,9 @@ import java.util.concurrent.atomic.AtomicBoolean
2016
internal class SpanServiceImpl(
2117
private val spanRepository: SpanRepository,
2218
private val embraceSpanFactory: EmbraceSpanFactory,
19+
private val limitsValidator: LimitsValidator,
2320
private val canStartNewSpan: (parentSpan: EmbraceSpan?, internal: Boolean) -> Boolean,
24-
private val initCallback: (initTimeMs: Long) -> Unit,
25-
private val limits: OtelLimitsConfig = InstrumentedConfigImpl.otelLimits,
21+
private val initCallback: (initTimeMs: Long) -> Unit
2622
) : SpanService {
2723
private val initialized = AtomicBoolean(false)
2824

@@ -44,7 +40,7 @@ internal class SpanServiceImpl(
4440
autoTerminationMode: AutoTerminationMode,
4541
): EmbraceSdkSpan? {
4642
EmbTrace.trace("span-create") {
47-
return if (limits.isNameValid(name, internal) && canStartNewSpan(parent, internal)) {
43+
return if (limitsValidator.isNameValid(name, internal) && canStartNewSpan(parent, internal)) {
4844
embraceSpanFactory.create(
4945
name = name,
5046
type = type,
@@ -62,7 +58,7 @@ internal class SpanServiceImpl(
6258
override fun createSpan(otelSpanBuilderWrapper: OtelSpanBuilderWrapper): EmbraceSdkSpan? {
6359
EmbTrace.trace("span-create") {
6460
return if (
65-
limits.isNameValid(otelSpanBuilderWrapper.initialSpanName, otelSpanBuilderWrapper.internal) &&
61+
limitsValidator.isNameValid(otelSpanBuilderWrapper.initialSpanName, otelSpanBuilderWrapper.internal) &&
6662
canStartNewSpan(
6763
otelSpanBuilderWrapper.getParentContext().getEmbraceSpan(),
6864
otelSpanBuilderWrapper.internal
@@ -166,7 +162,7 @@ internal class SpanServiceImpl(
166162
internal: Boolean,
167163
events: List<EmbraceSpanEvent>,
168164
attributes: Map<String, String>,
169-
): Boolean = limits.isNameValid(name, internal) &&
170-
limits.isEventCountValid(events, internal) &&
171-
limits.isAttributeCountValid(attributes, internal)
165+
): Boolean = limitsValidator.isNameValid(name, internal) &&
166+
limitsValidator.isEventCountValid(events, internal) &&
167+
limitsValidator.isAttributeCountValid(attributes, internal)
172168
}

0 commit comments

Comments
 (0)