Skip to content

Commit 9f85267

Browse files
authored
Merge pull request #2502 from DataDog/xgouchet/RUM-8366/forward_rum_session_id
RUM-8366 Forward RUM Session ID in trace headers
2 parents b05960c + acb0446 commit 9f85267

File tree

8 files changed

+119
-12
lines changed

8 files changed

+119
-12
lines changed

features/dd-sdk-android-trace/api/dd-sdk-android-trace.api

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4481,10 +4481,12 @@ public abstract class com/datadog/trace/core/propagation/PropagationTags {
44814481
public static fun factory (Lcom/datadog/trace/api/Config;)Lcom/datadog/trace/core/propagation/PropagationTags$Factory;
44824482
public abstract fun fillTagMap (Ljava/util/Map;)V
44834483
public abstract fun getOrigin ()Ljava/lang/CharSequence;
4484+
public abstract fun getRumSessionId ()Ljava/lang/String;
44844485
public abstract fun getSamplingPriority ()I
44854486
public abstract fun getTraceIdHighOrderBits ()J
44864487
public abstract fun getW3CTracestate ()Ljava/lang/String;
44874488
public abstract fun headerValue (Lcom/datadog/trace/core/propagation/PropagationTags$HeaderType;)Ljava/lang/String;
4489+
public abstract fun updateRumSessionId (Ljava/lang/String;)V
44884490
public abstract fun updateTraceIdHighOrderBits (J)V
44894491
public abstract fun updateTraceOrigin (Ljava/lang/CharSequence;)V
44904492
public abstract fun updateTraceSamplingPriority (II)V
@@ -4520,6 +4522,7 @@ public class com/datadog/trace/core/propagation/ptags/W3CPTagsCodec {
45204522
protected static final field DECISION_MAKER_TAG Lcom/datadog/trace/core/propagation/ptags/TagKey;
45214523
protected static final field PROPAGATION_ERROR_INCONSISTENT_TID Ljava/lang/String;
45224524
protected static final field PROPAGATION_ERROR_MALFORMED_TID Ljava/lang/String;
4525+
protected static final field RUM_SESSION_ID_TAG Lcom/datadog/trace/core/propagation/ptags/TagKey;
45234526
protected static final field TRACE_ID_TAG Lcom/datadog/trace/core/propagation/ptags/TagKey;
45244527
protected static final field UPSTREAM_SERVICES_DEPRECATED_TAG Lcom/datadog/trace/core/propagation/ptags/TagKey;
45254528
public fun <init> ()V

features/dd-sdk-android-trace/src/main/java/com/datadog/opentracing/propagation/DatadogHttpCodec.java

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,9 @@ class DatadogHttpCodec {
3131

3232
public static final String OT_BAGGAGE_PREFIX = "ot-baggage-";
3333
public static final String LEAST_SIGNIFICANT_TRACE_ID_KEY = "x-datadog-trace-id";
34-
public static final String MOST_SIGNIFICANT_TRACE_ID_KEY = "_dd.p.tid";
34+
public static final String MOST_SIGNIFICANT_TRACE_ID_TAG_KEY = "_dd.p.tid";
35+
public static final String RUM_SESSION_ID_TAG_KEY = "_dd.p.rsid";
36+
public static final String RUM_SESSION_ID_KEY = "session_id";
3537
public static final String DATADOG_TAGS_KEY = "x-datadog-tags";
3638
public static final String SPAN_ID_KEY = "x-datadog-parent-id";
3739
public static final String SAMPLING_PRIORITY_KEY = "x-datadog-sampling-priority";
@@ -58,6 +60,7 @@ public void inject(final DDSpanContext context, final TextMapInject carrier) {
5860
final BigInteger traceId = context.getTraceId();
5961
final String leastSignificantTraceId = bigIntegerUtils.leastSignificant64BitsAsDecimal(traceId);
6062
final String mostSignificantTraceId = bigIntegerUtils.mostSignificant64BitsAsHex(traceId);
63+
final String sessionId = (String) context.getTags().get(RUM_SESSION_ID_KEY);
6164
carrier.put(LEAST_SIGNIFICANT_TRACE_ID_KEY, leastSignificantTraceId);
6265
carrier.put(SPAN_ID_KEY, context.getSpanId().toString());
6366
final String origin = context.getOrigin();
@@ -69,7 +72,18 @@ public void inject(final DDSpanContext context, final TextMapInject carrier) {
6972
carrier.put(OT_BAGGAGE_PREFIX + entry.getKey(), HttpCodec.encode(entry.getValue()));
7073
}
7174
// adding the tags
72-
carrier.put(DATADOG_TAGS_KEY, MOST_SIGNIFICANT_TRACE_ID_KEY + "=" + mostSignificantTraceId);
75+
76+
StringBuilder tags = new StringBuilder();
77+
tags.append(MOST_SIGNIFICANT_TRACE_ID_TAG_KEY);
78+
tags.append('=');
79+
tags.append(mostSignificantTraceId);
80+
if(sessionId != null) {
81+
tags.append(',');
82+
tags.append(RUM_SESSION_ID_TAG_KEY);
83+
tags.append('=');
84+
tags.append(sessionId);
85+
}
86+
carrier.put(DATADOG_TAGS_KEY, tags.toString());
7387

7488
if (context.lockSamplingPriority()) {
7589
carrier.put(SAMPLING_PRIORITY_KEY, String.valueOf(context.getSamplingPriority()));
@@ -162,7 +176,7 @@ private String extractMostSignificant64BitsTraceId(final String tags) {
162176
final String[] tagArray = tags.split(",");
163177
for (String tag : tagArray) {
164178
final String[] tagKeyValue = tag.split("=");
165-
if (tagKeyValue.length >= 2 && MOST_SIGNIFICANT_TRACE_ID_KEY.equals(tagKeyValue[0])) {
179+
if (tagKeyValue.length >= 2 && MOST_SIGNIFICANT_TRACE_ID_TAG_KEY.equals(tagKeyValue[0])) {
166180
return tagKeyValue[1];
167181
}
168182
}

features/dd-sdk-android-trace/src/main/java/com/datadog/opentracing/propagation/W3CHttpCodec.java

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ class W3CHttpCodec {
4747
private static final String SAMPLING_PRIORITY_TRACESTATE_TAG_VALUE = "s";
4848
private static final String PARENT_SPAN_ID_TRACESTATE_TAG_VALUE = "p";
4949
private static final String DATADOG_VENDOR_TRACESTATE_PREFIX = "dd=";
50+
private static final String RUM_SESSION_ID_TAG_VALUE = "t.rsid";
5051

5152
private W3CHttpCodec() {
5253
// This class should not be created. This also makes code coverage checks happy.
@@ -61,13 +62,14 @@ public void inject(final DDSpanContext context, final TextMapInject carrier) {
6162
String spanId = context.getSpanId().toString(HEX_RADIX).toLowerCase(Locale.US);
6263
String samplingPriority = convertSamplingPriority(context.getSamplingPriority());
6364
String origin = context.getOrigin();
65+
String sessionId = (String) context.getTags().get(DatadogHttpCodec.RUM_SESSION_ID_KEY);
6466

6567
carrier.put(TRACEPARENT_KEY, String.format(TRACEPARENT_VALUE,
6668
StringsKt.padStart(traceId, TRACECONTEXT_TRACE_ID_LENGTH, '0'),
6769
StringsKt.padStart(spanId, TRACECONTEXT_PARENT_ID_LENGTH, '0'),
6870
samplingPriority));
6971
// TODO RUM-2121 3rd party vendor information will be erased
70-
carrier.put(TRACESTATE_KEY, createTraceStateHeader(samplingPriority, origin, spanId));
72+
carrier.put(TRACESTATE_KEY, createTraceStateHeader(samplingPriority, origin, spanId, sessionId));
7173

7274
} catch (final NumberFormatException e) {
7375
}
@@ -80,24 +82,32 @@ private String convertSamplingPriority(final int samplingPriority) {
8082
private String createTraceStateHeader(
8183
String samplingPriority,
8284
String origin,
83-
String parentSpanId
85+
String parentSpanId,
86+
String sessionId
8487
) {
8588
StringBuilder sb = new StringBuilder(DATADOG_VENDOR_TRACESTATE_PREFIX)
8689
.append(SAMPLING_PRIORITY_TRACESTATE_TAG_VALUE)
87-
.append(":")
90+
.append(':')
8891
.append(samplingPriority)
89-
.append(";")
92+
.append(';')
9093
.append(PARENT_SPAN_ID_TRACESTATE_TAG_VALUE)
91-
.append(":")
94+
.append(':')
9295
.append(parentSpanId);
9396

9497
if (origin != null) {
95-
sb.append(";")
98+
sb.append(';')
9699
.append(ORIGIN_TRACESTATE_TAG_VALUE)
97-
.append(":")
100+
.append(':')
98101
.append(origin.toLowerCase(Locale.US));
99102
}
100103

104+
if (sessionId != null) {
105+
sb.append(';')
106+
.append(RUM_SESSION_ID_TAG_VALUE)
107+
.append(':')
108+
.append(sessionId);
109+
}
110+
101111
return sb.toString();
102112
}
103113
}

features/dd-sdk-android-trace/src/main/java/com/datadog/trace/core/propagation/HttpCodec.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ public class HttpCodec {
4848
static final String CF_CONNECTING_IP_KEY = "cf-connecting-ip";
4949
static final String CF_CONNECTING_IP_V6_KEY = "cf-connecting-ipv6";
5050

51+
static final String RUM_SESSION_ID_KEY = "session_id";
52+
5153
public interface Injector {
5254
<C> void inject(
5355
final DDSpanContext context, final C carrier, final AgentPropagation.Setter<C> setter);
@@ -184,6 +186,12 @@ public CompoundInjector(final List<Injector> injectors) {
184186
public <C> void inject(
185187
final DDSpanContext context, final C carrier, final AgentPropagation.Setter<C> setter) {
186188
log.debug("Inject context {}", context);
189+
// Update session ide before injecting propagation tags
190+
final String sessionId = (String) context.getTags().get(RUM_SESSION_ID_KEY);
191+
if (sessionId != null) {
192+
context.getPropagationTags().updateRumSessionId(sessionId);
193+
}
194+
187195
for (final Injector injector : injectors) {
188196
injector.inject(context, carrier, setter);
189197
}

features/dd-sdk-android-trace/src/main/java/com/datadog/trace/core/propagation/PropagationTags.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,10 @@ public interface Factory {
6767

6868
public abstract void updateTraceIdHighOrderBits(long highOrderBits);
6969

70+
public abstract String getRumSessionId();
71+
72+
public abstract void updateRumSessionId(String sessionId);
73+
7074
/**
7175
* Gets the original <a href="https://www.w3.org/TR/trace-context/#tracestate-header">W3C
7276
* tracestate header</a> value.

features/dd-sdk-android-trace/src/main/java/com/datadog/trace/core/propagation/ptags/PTagsCodec.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ abstract class PTagsCodec {
1616

1717
protected static final TagKey DECISION_MAKER_TAG = TagKey.from("dm");
1818
protected static final TagKey TRACE_ID_TAG = TagKey.from("tid");
19+
protected static final TagKey RUM_SESSION_ID_TAG = TagKey.from("rsid");
1920
protected static final String PROPAGATION_ERROR_MALFORMED_TID = "malformed_tid ";
2021
protected static final String PROPAGATION_ERROR_INCONSISTENT_TID = "inconsistent_tid ";
2122
protected static final TagKey UPSTREAM_SERVICES_DEPRECATED_TAG = TagKey.from("upstream_services");
@@ -36,6 +37,9 @@ static String headerValue(PTagsCodec codec, PTags ptags) {
3637
if (ptags.getTraceIdHighOrderBitsHexTagValue() != null) {
3738
size = codec.appendTag(sb, TRACE_ID_TAG, ptags.getTraceIdHighOrderBitsHexTagValue(), size);
3839
}
40+
if (ptags.getRumSessionId() != null) {
41+
size = codec.appendTag(sb, RUM_SESSION_ID_TAG, ptags.getRumSessionIdTagValue(), size);
42+
}
3943
Iterator<TagElement> it = ptags.getTagPairs().iterator();
4044
while (it.hasNext() && !codec.isTooLarge(sb, size)) {
4145
TagElement tagKey = it.next();

features/dd-sdk-android-trace/src/main/java/com/datadog/trace/core/propagation/ptags/PTagsFactory.java

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,10 @@ static class PTags extends PropagationTags {
8484
private volatile String[] headerCache = null;
8585
/** The high-order 64 bits of the trace id. */
8686
private volatile long traceIdHighOrderBits;
87+
88+
private volatile String rumSessionId;
89+
private volatile TagValue rumSessionIdTagValue ;
90+
8791
/**
8892
* The zero-padded lower-case 16 character hexadecimal representation of the high-order 64 bits
8993
* of the trace id, wrapped into a {@link TagValue}, <code>null</code> if not set.
@@ -203,6 +207,7 @@ public long getTraceIdHighOrderBits() {
203207
return traceIdHighOrderBits;
204208
}
205209

210+
@Override
206211
public void updateTraceIdHighOrderBits(long highOrderBits) {
207212
if (traceIdHighOrderBits != highOrderBits) {
208213
traceIdHighOrderBits = highOrderBits;
@@ -214,6 +219,21 @@ public void updateTraceIdHighOrderBits(long highOrderBits) {
214219
}
215220
}
216221

222+
@Override
223+
public String getRumSessionId() {
224+
return rumSessionId;
225+
}
226+
227+
@Override
228+
public void updateRumSessionId(String sessionId) {
229+
if (!Objects.equals(rumSessionId, sessionId)) {
230+
rumSessionId = sessionId;
231+
rumSessionIdTagValue = TagValue.from(rumSessionId);
232+
clearCachedHeader(DATADOG);
233+
clearCachedHeader(W3C);
234+
}
235+
}
236+
217237
@Override
218238
@SuppressWarnings("StringEquality")
219239
public String headerValue(HeaderType headerType) {
@@ -296,6 +316,10 @@ TagValue getTraceIdHighOrderBitsHexTagValue() {
296316
return traceIdHighOrderBitsHexTagValue;
297317
}
298318

319+
TagValue getRumSessionIdTagValue() {
320+
return rumSessionIdTagValue;
321+
}
322+
299323
TagValue getDecisionMakerTagValue() {
300324
return decisionMakerTagValue;
301325
}

features/dd-sdk-android-trace/src/test/kotlin/com/datadog/opentracing/propagation/DatadogHttpCodecTest.kt

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import org.mockito.junit.jupiter.MockitoSettings
2929
import org.mockito.kotlin.whenever
3030
import org.mockito.quality.Strictness
3131
import java.math.BigInteger
32+
import java.util.UUID
3233

3334
@Extensions(
3435
ExtendWith(MockitoExtension::class),
@@ -54,6 +55,9 @@ internal class DatadogHttpCodecTest {
5455
@StringForgery(regex = "[0-9a-f]{16}")
5556
lateinit var fakeMostSignificant64BitsTraceId: String
5657

58+
@Forgery
59+
lateinit var fakeSessionId: UUID
60+
5761
private lateinit var fakeTaggedHeaders: Map<String, String>
5862

5963
private lateinit var fakeIdAsHexString: String
@@ -105,6 +109,38 @@ internal class DatadogHttpCodecTest {
105109
}
106110
}
107111

112+
@Test
113+
fun `M inject the required headers W inject {with sessionId}`() {
114+
// Given
115+
val headers = mutableMapOf<String, String>()
116+
fakeDDSpanContext.setTag("session_id", fakeSessionId.toString())
117+
118+
// When
119+
testedInjector.inject(fakeDDSpanContext) { key, value ->
120+
headers[key] = value
121+
}
122+
123+
// Then
124+
assertThat(headers[DatadogHttpCodec.ORIGIN_KEY]).isEqualTo(fakeDDSpanContext.origin)
125+
assertThat(
126+
headers[DatadogHttpCodec.LEAST_SIGNIFICANT_TRACE_ID_KEY]
127+
).isEqualTo(fakeLeastSignificant64BitsTraceId)
128+
assertThat(headers[DatadogHttpCodec.DATADOG_TAGS_KEY]).isEqualTo(expectedInjectedTags(fakeSessionId))
129+
assertThat(headers[DatadogHttpCodec.SAMPLING_PRIORITY_KEY])
130+
.let {
131+
if (fakeDDSpanContext.samplingPriority != PrioritySampling.UNSET) {
132+
it.isEqualTo(fakeDDSpanContext.samplingPriority.toString())
133+
} else {
134+
it.isNull()
135+
}
136+
}
137+
assertThat(headers[DatadogHttpCodec.SPAN_ID_KEY])
138+
.isEqualTo(fakeDDSpanContext.spanId.toString())
139+
fakeDDSpanContext.baggageItems.forEach { (key, value) ->
140+
assertThat(headers[DatadogHttpCodec.OT_BAGGAGE_PREFIX + key]).isEqualTo(HttpCodec.encode(value))
141+
}
142+
}
143+
108144
// endregion
109145

110146
@ParameterizedTest
@@ -166,7 +202,7 @@ internal class DatadogHttpCodecTest {
166202
// Given
167203
val headers = resolveExtractedHeadersFromSpanContext(fakeDDSpanContext, forge).apply {
168204
remove(DatadogHttpCodec.DATADOG_TAGS_KEY)
169-
set(DatadogHttpCodec.DATADOG_TAGS_KEY, DatadogHttpCodec.MOST_SIGNIFICANT_TRACE_ID_KEY + "=broken")
205+
set(DatadogHttpCodec.DATADOG_TAGS_KEY, DatadogHttpCodec.MOST_SIGNIFICANT_TRACE_ID_TAG_KEY + "=broken")
170206
}
171207

172208
// When
@@ -183,7 +219,7 @@ internal class DatadogHttpCodecTest {
183219
// Given
184220
val headers = resolveExtractedHeadersFromSpanContext(fakeDDSpanContext, forge).apply {
185221
remove(DatadogHttpCodec.DATADOG_TAGS_KEY)
186-
set(DatadogHttpCodec.DATADOG_TAGS_KEY, DatadogHttpCodec.MOST_SIGNIFICANT_TRACE_ID_KEY)
222+
set(DatadogHttpCodec.DATADOG_TAGS_KEY, DatadogHttpCodec.MOST_SIGNIFICANT_TRACE_ID_TAG_KEY)
187223
}
188224

189225
// When
@@ -283,6 +319,10 @@ internal class DatadogHttpCodecTest {
283319
return "_dd.p.tid=$fakeMostSignificant64BitsTraceId"
284320
}
285321

322+
private fun expectedInjectedTags(sessionId: UUID): String {
323+
return "_dd.p.tid=$fakeMostSignificant64BitsTraceId,_dd.p.rsid=$sessionId"
324+
}
325+
286326
private fun traceIdTagsAndNoise(traceId: String, forge: Forge): String {
287327
val noiseTags = forge.aMap(size = forge.anInt(min = 0, max = 10)) {
288328
anAlphabeticalString() to anAlphabeticalString()

0 commit comments

Comments
 (0)