Skip to content

Commit 9ad9971

Browse files
committed
RUM-9021 use session consistent trace sampling
1 parent f2b505a commit 9ad9971

File tree

6 files changed

+151
-5
lines changed

6 files changed

+151
-5
lines changed

detekt_custom.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1032,6 +1032,7 @@ datadog:
10321032
- "kotlin.collections.MutableMap.filterValues(kotlin.Function1)"
10331033
- "kotlin.collections.MutableMap.forEach(kotlin.Function1)"
10341034
- "kotlin.collections.MutableMap.get(kotlin.String)"
1035+
- "kotlin.collections.MutableMap.get(kotlin.String?)"
10351036
- "kotlin.collections.MutableMap.getOrElse(java.lang.Class, kotlin.Function0)"
10361037
- "kotlin.collections.MutableMap.getOrPut(kotlin.String, kotlin.Function0)"
10371038
- "kotlin.collections.MutableMap.isEmpty()"
@@ -1278,6 +1279,7 @@ datadog:
12781279
- "kotlin.String.toIntOrNull()"
12791280
- "kotlin.String.toIntOrNull(kotlin.Int)"
12801281
- "kotlin.String.toLongOrNull()"
1282+
- "kotlin.String.toLongOrNull(kotlin.Int)"
12811283
- "kotlin.String.toMediaTypeOrNull()"
12821284
- "kotlin.String.toMethod()"
12831285
- "kotlin.String.trim()"

integrations/dd-sdk-android-okhttp/src/main/kotlin/com/datadog/android/okhttp/internal/utils/SpanContextExt.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import com.datadog.opentracing.DDSpanContext
1010
import io.opentracing.SpanContext
1111

1212
private const val TRACE_ID_REQUIRED_LENGTH = 32
13-
private const val HEX_RADIX = 16
13+
internal const val HEX_RADIX = 16
1414

1515
internal fun SpanContext.traceIdAsHexString(): String {
1616
return (this as? DDSpanContext)?.traceId?.toString(HEX_RADIX)?.padStart(TRACE_ID_REQUIRED_LENGTH, '0') ?: ""
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/*
2+
* Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0.
3+
* This product includes software developed at Datadog (https://www.datadoghq.com/).
4+
* Copyright 2016-Present Datadog, Inc.
5+
*/
6+
7+
package com.datadog.android.okhttp.internal.utils
8+
9+
import com.datadog.android.log.LogAttributes
10+
import com.datadog.opentracing.DDSpanContext
11+
import io.opentracing.Span
12+
13+
internal object SpanSamplingIdProvider {
14+
15+
fun provideId(span: Span): ULong {
16+
val context = span.context()
17+
val sessionId = (context as? DDSpanContext)?.tags?.get(LogAttributes.RUM_SESSION_ID) as? String
18+
19+
// for a UUID with value aaaaaaaa-bbbb-Mccc-Nddd-1234567890ab
20+
// we use as the input id the last part : 0x1234567890ab
21+
val sessionIdToken = sessionId?.split('-')?.lastOrNull()?.toLongOrNull(HEX_RADIX)?.toULong()
22+
23+
return if (sessionIdToken != null) {
24+
sessionIdToken
25+
} else {
26+
context.toTraceId().toBigIntegerOrNull()?.toLong()?.toULong() ?: 0u
27+
}
28+
}
29+
}

integrations/dd-sdk-android-okhttp/src/main/kotlin/com/datadog/android/okhttp/trace/DeterministicTraceSampler.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ package com.datadog.android.okhttp.trace
88

99
import androidx.annotation.FloatRange
1010
import com.datadog.android.core.sampling.DeterministicSampler
11+
import com.datadog.android.okhttp.internal.utils.SpanSamplingIdProvider
1112
import io.opentracing.Span
1213

1314
/**
@@ -19,7 +20,7 @@ import io.opentracing.Span
1920
open class DeterministicTraceSampler(
2021
sampleRateProvider: () -> Float
2122
) : DeterministicSampler<Span>(
22-
{ it.context().toTraceId().toBigIntegerOrNull()?.toLong()?.toULong() ?: 0u },
23+
SpanSamplingIdProvider::provideId,
2324
sampleRateProvider
2425
) {
2526

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
/*
2+
* Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0.
3+
* This product includes software developed at Datadog (https://www.datadoghq.com/).
4+
* Copyright 2016-Present Datadog, Inc.
5+
*/
6+
7+
package com.datadog.android.okhttp.internal.utils
8+
9+
import com.datadog.android.core.internal.utils.toHexString
10+
import com.datadog.android.log.LogAttributes
11+
import com.datadog.opentracing.DDSpanContext
12+
import com.datadog.tools.unit.forge.BaseConfigurator
13+
import fr.xgouchet.elmyr.Forge
14+
import fr.xgouchet.elmyr.annotation.LongForgery
15+
import fr.xgouchet.elmyr.annotation.StringForgery
16+
import fr.xgouchet.elmyr.junit5.ForgeConfiguration
17+
import fr.xgouchet.elmyr.junit5.ForgeExtension
18+
import io.opentracing.Span
19+
import org.assertj.core.api.Assertions.assertThat
20+
import org.junit.jupiter.api.BeforeEach
21+
import org.junit.jupiter.api.Test
22+
import org.junit.jupiter.api.extension.ExtendWith
23+
import org.junit.jupiter.api.extension.Extensions
24+
import org.mockito.Mock
25+
import org.mockito.junit.jupiter.MockitoExtension
26+
import org.mockito.junit.jupiter.MockitoSettings
27+
import org.mockito.kotlin.doReturn
28+
import org.mockito.kotlin.whenever
29+
import org.mockito.quality.Strictness
30+
31+
@Extensions(
32+
ExtendWith(MockitoExtension::class),
33+
ExtendWith(ForgeExtension::class)
34+
)
35+
@MockitoSettings(strictness = Strictness.LENIENT)
36+
@ForgeConfiguration(BaseConfigurator::class)
37+
internal class SpanSamplingIdProviderTest {
38+
39+
@Mock
40+
lateinit var mockSpan: Span
41+
42+
@Mock
43+
lateinit var mockSpanContext: DDSpanContext
44+
45+
lateinit var fakeTags: Map<String, Any>
46+
47+
@BeforeEach
48+
fun `set up`(forge: Forge) {
49+
fakeTags = forge.aMap { anAlphabeticalString() to aString() }
50+
whenever(mockSpan.context()) doReturn mockSpanContext
51+
whenever(mockSpanContext.tags) doReturn forge.aNullable { fakeTags }
52+
}
53+
54+
@Test
55+
fun `M return sessionId lsb as ULong W provideId() {has rum session}`(
56+
@LongForgery(0L, 0xFFFFFFFF) part0: Long,
57+
@LongForgery(0L, 0xFFFF) part1: Long,
58+
@LongForgery(0x4000L, 0x4FFF) part2: Long,
59+
@LongForgery(8000L, 0xDFFF) part3: Long,
60+
@LongForgery(0L, 0xFFFFFFFFFFFF) part4: Long
61+
) {
62+
// Given
63+
val expectedId = part4.toULong()
64+
val sessionId = arrayOf(part0, part1, part2, part3, part4).joinToString("-") { it.toHexString() }
65+
val fakeTagsWithSessionId = fakeTags + mapOf(LogAttributes.RUM_SESSION_ID to sessionId)
66+
whenever(mockSpanContext.tags) doReturn fakeTagsWithSessionId
67+
68+
// When
69+
val result = SpanSamplingIdProvider.provideId(mockSpan)
70+
71+
// Then
72+
assertThat(result).isEqualTo(expectedId)
73+
}
74+
75+
@Test
76+
fun `M return traceId as ULong W provideId() {no rum session}`(
77+
@LongForgery traceId: Long
78+
) {
79+
// Given
80+
val expectedId = traceId.toULong()
81+
whenever(mockSpanContext.toTraceId()) doReturn traceId.toString()
82+
83+
// When
84+
val result = SpanSamplingIdProvider.provideId(mockSpan)
85+
86+
// Then
87+
assertThat(result).isEqualTo(expectedId)
88+
}
89+
90+
@Test
91+
fun `M return 0u W provideId() {no rum session, invalid traceId}`(
92+
@StringForgery(regex = "([g-z][\\w\\d])+") fakeString: String
93+
) {
94+
// Given
95+
val expectedId: ULong = 0u
96+
whenever(mockSpanContext.toTraceId()) doReturn fakeString
97+
98+
// When
99+
val result = SpanSamplingIdProvider.provideId(mockSpan)
100+
101+
// Then
102+
assertThat(result).isEqualTo(expectedId)
103+
}
104+
105+
@Test
106+
fun `M return 0u W provideId() {no rum session, empty traceId}`() {
107+
// Given
108+
val expectedId: ULong = 0u
109+
whenever(mockSpanContext.toTraceId()) doReturn ""
110+
111+
// When
112+
val result = SpanSamplingIdProvider.provideId(mockSpan)
113+
114+
// Then
115+
assertThat(result).isEqualTo(expectedId)
116+
}
117+
}

sample/kotlin/src/main/kotlin/com/datadog/android/sample/SampleApplication.kt

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ import com.datadog.android.core.configuration.BackPressureStrategy
1818
import com.datadog.android.core.configuration.BatchSize
1919
import com.datadog.android.core.configuration.Configuration
2020
import com.datadog.android.core.configuration.UploadFrequency
21-
import com.datadog.android.core.sampling.RateBasedSampler
2221
import com.datadog.android.log.Logger
2322
import com.datadog.android.log.Logs
2423
import com.datadog.android.log.LogsConfiguration
@@ -83,12 +82,10 @@ class SampleApplication : Application() {
8382
private val okHttpClient = OkHttpClient.Builder()
8483
.addInterceptor(
8584
DatadogInterceptor.Builder(tracedHosts)
86-
.setTraceSampler(RateBasedSampler(100f))
8785
.build()
8886
)
8987
.addNetworkInterceptor(
9088
TracingInterceptor.Builder(tracedHosts)
91-
.setTraceSampler(RateBasedSampler(100f))
9289
.build()
9390
)
9491
.eventListenerFactory(DatadogEventListener.Factory())

0 commit comments

Comments
 (0)