Expedia Group Technology

Stories from the Expedia Group Technology teams

Follow publication

EXPEDIA GROUP TECHNOLOGY — SOFTWARE

How we got Zipkin Brave to accept UUIDs

7 min readAug 27, 2020
Photo by Bhumika Singh on Unsplash
the /my-trips trace consists of several spans, a /auth one, and a /trips one which itself has /tracking and /flights spans
The whole graph represents a trace and every box represents a span

Once upon a time…

/**
* Extra holds the information of the inbound context to be kept
* and use as replacement in propagation and reporting.
*/
internal data class OriginalIDs(
val traceIdAsUUID: String, // The incoming trace ID in UUID
val spanIdAsUUID: String, // The incoming span ID in UUID
val parentSpanIdAsUUID: String?, // The incoming parent span ID
// in UUID
val syntheticRootSpanId: Long, // The synthetic local root span
// ID for the trace
)
// Copied from Brave as we need to generate IDs in the same way https://github.com/openzipkin/brave/blob/ae2b26adda/brave/src/main/java/brave/Tracer.java#L645private fun nextId(): Long {
var nextId = Platform.get().randomLong()
while (nextId < 0L) {
nextId = Platform.get().randomLong()
}
return nextId
}
internal class Extractor<C, K>(
private val propagation: UUIDPropagation,
private val getter: Propagation.Getter<C, K>,
): TraceContext.Extractor<C> {
override fun extract(carrier: C): TraceContextOrSamplingFlags {
var samplingFlags = SamplingFlags.EMPTY
if (getter.get(carrier, propagation.debugKey) != null) {
samplingFlags = SamplingFlags.DEBUG
}
val traceIdAsUIDString = getter.get(carrier, propagation.traceIdKey) ?: return TraceContextOrSamplingFlags.create(samplingFlags)

val spanIdAsUIDString = getter.get(carrier, propagation.spanIdKey) ?: return TraceContextOrSamplingFlags.create(samplingFlags)
val parentIdAsUIDString = getter.get(carrier, propagation.parentSpanIdKey) ?: null var result = TraceContext.newBuilder() val syntheticRootSpanId = nextId() val extra = OriginalIDs(traceIdAsUIDString, spanIdAsUIDString, parentIdAsUIDString, syntheticRootSpanId) result = result
.sampled(true)
.debug(samplingFlags.debug())
// we will always replace the traceId
.traceId(nextId())
.traceIdHigh(nextId())
// if spanId == syntheticRootSpanId we replace the
// spanId from the original IDs
.spanId(syntheticRootSpanId)
.extra(listOf(extra))
if (parentIdAsUIDString != null) {
// if spanId == syntheticRootSpanId we replace the
// parent from the original IDs
result.parentId(nextId())
}

return TraceContextOrSamplingFlags.create(result.build())
}}
val spanIdAsUUID = new UUID(0L, context.spanId)
import zipkin2.Span;...public interface Reporter<S> {
/**
* Schedules the span to be sent onto the transport.
*
* @param span Span, should not be <code>null</code>.
*/
void report(S span);
}
...import brave.handler.MutableSpan as ZipkinMutableSpanobject SyntheticTagsHandler : FinishedSpanHandler() {
private const val SYNTHETIC_ID = "x-message-propagation-synthetic-id"
private const val TRACE_ID = "x-message-propagation-trace-id"
private const val SPAN_ID = "x-message-propagation-span-id"
private const val PARENT_SPAN_ID = "x-message-propagation-parent-id"
override fun handle(context: TraceContext, span: ZipkinMutableSpan): Boolean {
val originalIDs = context.findExtra(OriginalIDs::class.java) ?: return
span.tag(SYNTHETIC_ID, originalIDs.syntheticId.toString(16).padStart(16, ‘0’))
span.tag(TRACE_ID, originalIDs.traceId)
span.tag(SPAN_ID, originalIDs.spanId)
if (originalIDs.parentSpanId != null) {
span.tag(PARENT_SPAN_ID, originalIDs.parentSpanId)
}
return true
}
internal fun isSyntheticTag(key: String): Boolean {
return key == SYNTHETIC_ID
|| key == TRACE_ID
|| key == SPAN_ID
|| key == PARENT_SPAN_ID
}
private fun getTraceId(tags: Map<String, String>): String = tags[TRACE_ID] ?: throw NullPointerException("Missing $TRACE_ID synthetic tag") private fun getSyntheticId(tags: Map<String, String>): String = tags[SYNTHETIC_ID] ?: throw NullPointerException("Missing $SYNTHETIC_ID synthetic tag") private fun getSpanId(tags: Map<String, String>): String = tags[SPAN_ID] ?: throw NullPointerException("Missing $SPAN_ID synthetic tag") private fun getParentId(tags: Map<String, String>): String? = tags[PARENT_SPAN_ID]
}

Free

Distraction-free reading. No ads.

Organize your knowledge with lists and highlights.

Tell your story. Find your audience.

Membership

Read member-only stories

Support writers you read most

Earn money for your writing

Listen to audio narrations

Read offline with the Medium app

Expedia Group Technology
Expedia Group Technology
José Carlos Chávez
José Carlos Chávez

Written by José Carlos Chávez

Software Engineer @Traceableai, ex @ExpediaGroup. Wine lover and Llama ambassador

Responses (1)

Write a response