Skip to content

Commit

Permalink
Add Otel SpanLink use case into the sample app
Browse files Browse the repository at this point in the history
  • Loading branch information
mariusc83 committed Apr 18, 2024
1 parent 94c4b4d commit 0a423fe
Show file tree
Hide file tree
Showing 9 changed files with 144 additions and 37 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ class OtelTraceConfigurationTest {
var spanId: String
val fullDuration = measureNanoTime {
val span = tracer.spanBuilder(fakeOperation).startSpan()
traceId = span.lessSignificantTraceIdAsHexString()
traceId = span.leastSignificantTraceIdAsHexString()
spanId = span.spanIdAsHexString()
Thread.sleep(OP_DURATION_MS)
span.end()
Expand Down Expand Up @@ -117,7 +117,7 @@ class OtelTraceConfigurationTest {
var spanId: String
val fullDuration = measureNanoTime {
val span = tracer.spanBuilder(fakeOperation).startSpan()
traceId = span.lessSignificantTraceIdAsHexString()
traceId = span.leastSignificantTraceIdAsHexString()
spanId = span.spanIdAsHexString()
Thread.sleep(OP_DURATION_MS)
span.end()
Expand Down Expand Up @@ -175,7 +175,7 @@ class OtelTraceConfigurationTest {
var spanId: String
val fullDuration = measureNanoTime {
val span = tracer.spanBuilder(fakeOperation).startSpan()
traceId = span.lessSignificantTraceIdAsHexString()
traceId = span.leastSignificantTraceIdAsHexString()
spanId = span.spanIdAsHexString()
Thread.sleep(OP_DURATION_MS)
span.end()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,12 +60,12 @@ internal class OtelTracerProviderTest {
fun `set up`(forge: Forge) {
val datadogContextStorageClass =
Class.forName("com.datadog.android.trace.opentelemetry.DatadogContextStorage")
val constructor = datadogContextStorageClass.constructors.first { it.parameterCount == 1 }
val constructorRef = datadogContextStorageClass.constructors.first { it.parameterCount == 1 }
ContextStorage.addWrapper {
if (it::class.java.isAssignableFrom(datadogContextStorageClass)) {
it
} else {
constructor.newInstance(it) as ContextStorage
constructorRef.newInstance(it) as ContextStorage
}
}
stubSdkCore = StubSDKCore(forge)
Expand All @@ -91,7 +91,7 @@ internal class OtelTracerProviderTest {
var spanId: String
val fullDuration = measureNanoTime {
val span = tracer.spanBuilder(fakeOperationName).startSpan()
traceId = span.lessSignificantTraceIdAsHexString()
traceId = span.leastSignificantTraceIdAsHexString()
spanId = span.spanIdAsHexString()
Thread.sleep(OP_DURATION_MS)
span.end()
Expand Down Expand Up @@ -137,7 +137,7 @@ internal class OtelTracerProviderTest {
var spanId: String
val fullDuration = measureNanoTime {
val span = tracer.spanBuilder(fakeOperationName).startSpan()
traceId = span.lessSignificantTraceIdAsHexString()
traceId = span.leastSignificantTraceIdAsHexString()
spanId = span.spanIdAsHexString()
Thread.sleep(OP_DURATION_MS)
span.end()
Expand Down Expand Up @@ -194,7 +194,7 @@ internal class OtelTracerProviderTest {
.setAttribute(fakeStringAttributeKey, fakeStringAttributeValue)
.setAttribute(fakeBooleanAttributeKey, fakeBooleanAttributeValue)
.startSpan()
traceId = span.lessSignificantTraceIdAsHexString()
traceId = span.leastSignificantTraceIdAsHexString()
spanId = span.spanIdAsHexString()
Thread.sleep(OP_DURATION_MS)
span.end()
Expand Down Expand Up @@ -254,7 +254,7 @@ internal class OtelTracerProviderTest {
.setAttribute(AttributeKey.stringKey(fakeStringAttributeKey), fakeStringAttributeValue)
.setAttribute(AttributeKey.booleanKey(fakeBooleanAttributeKey), fakeBooleanAttributeValue)
.startSpan()
traceId = span.lessSignificantTraceIdAsHexString()
traceId = span.leastSignificantTraceIdAsHexString()
spanId = span.spanIdAsHexString()
Thread.sleep(OP_DURATION_MS)
span.end()
Expand Down Expand Up @@ -307,7 +307,7 @@ internal class OtelTracerProviderTest {
// When
val fullParentSpanDuration = measureNanoTime {
val parenSpan = tracer.spanBuilder(fakeParentSpanName).startSpan()
parentSpanTraceId = parenSpan.lessSignificantTraceIdAsHexString()
parentSpanTraceId = parenSpan.leastSignificantTraceIdAsHexString()
parentSpanId = parenSpan.spanIdAsHexString()
fullChildDuration = measureNanoTime {
val span = tracer
Expand Down Expand Up @@ -383,7 +383,7 @@ internal class OtelTracerProviderTest {
span = tracer.spanBuilder(fakeOperationName)
.setSpanKind(spanKind)
.startSpan()
traceId = span.lessSignificantTraceIdAsHexString()
traceId = span.leastSignificantTraceIdAsHexString()
spanId = span.spanIdAsHexString()
Thread.sleep(OP_DURATION_MS)
span.end()
Expand Down Expand Up @@ -439,7 +439,7 @@ internal class OtelTracerProviderTest {
span = tracer.spanBuilder(fakeOperationName)
.setStartTimestamp(Instant.ofEpochMilli(fakeStartTimestamp))
.startSpan()
traceId = span.lessSignificantTraceIdAsHexString()
traceId = span.leastSignificantTraceIdAsHexString()
spanId = span.spanIdAsHexString()
Thread.sleep(OP_DURATION_MS)
span.end()
Expand Down Expand Up @@ -502,7 +502,7 @@ internal class OtelTracerProviderTest {
val span = tracer.spanBuilder(fakeOperationName)
.addLink(linkedSpan.spanContext, attributes)
.startSpan()
val traceId = span.lessSignificantTraceIdAsHexString()
val traceId = span.leastSignificantTraceIdAsHexString()
val spanId = span.spanIdAsHexString()
Thread.sleep(OP_DURATION_MS)
span.end()
Expand Down Expand Up @@ -549,7 +549,7 @@ internal class OtelTracerProviderTest {
// When
val parentSpan = tracer.spanBuilder(fakeParentSpanName).startSpan()
val scope = parentSpan.makeCurrent()
val parentSpanTraceId = parentSpan.lessSignificantTraceIdAsHexString()
val parentSpanTraceId = parentSpan.leastSignificantTraceIdAsHexString()
val parentSpanId = parentSpan.spanIdAsHexString()
val childSpan = tracer.spanBuilder(fakeSpanName).startSpan()
val childSpanId = childSpan.spanIdAsHexString()
Expand Down Expand Up @@ -606,7 +606,7 @@ internal class OtelTracerProviderTest {
// When
val parentSpan = tracer.spanBuilder(fakeParentSpanName).startSpan()
val scope = parentSpan.makeCurrent()
val parentSpanTraceId = parentSpan.lessSignificantTraceIdAsHexString()
val parentSpanTraceId = parentSpan.leastSignificantTraceIdAsHexString()
val parentSpanId = parentSpan.spanIdAsHexString()
var childSpanId = ""
Thread {
Expand Down Expand Up @@ -674,7 +674,7 @@ internal class OtelTracerProviderTest {
.spanBuilder(fakeOperationName)
.startSpan()
Thread.sleep(OP_DURATION_MS)
val traceId = span.lessSignificantTraceIdAsHexString()
val traceId = span.leastSignificantTraceIdAsHexString()
val spanId = span.spanIdAsHexString()
span.end()

Expand Down Expand Up @@ -714,7 +714,7 @@ internal class OtelTracerProviderTest {
.spanBuilder(fakeOperationName)
.startSpan()
Thread.sleep(OP_DURATION_MS)
val traceId = span.lessSignificantTraceIdAsHexString()
val traceId = span.leastSignificantTraceIdAsHexString()
val spanId = span.spanIdAsHexString()
span.end()

Expand Down Expand Up @@ -754,7 +754,7 @@ internal class OtelTracerProviderTest {
.spanBuilder(fakeOperationName)
.startSpan()
Thread.sleep(OP_DURATION_MS)
val traceId = span.lessSignificantTraceIdAsHexString()
val traceId = span.leastSignificantTraceIdAsHexString()
val spanId = span.spanIdAsHexString()
span.end()

Expand Down Expand Up @@ -848,7 +848,7 @@ internal class OtelTracerProviderTest {
var spanId: String
val fullDuration = measureNanoTime {
val span = tracer.spanBuilder(fakeOperationName).startSpan()
traceId = span.lessSignificantTraceIdAsHexString()
traceId = span.leastSignificantTraceIdAsHexString()
spanId = span.spanIdAsHexString()
Thread.sleep(AndroidTracerTest.OP_DURATION_MS)
span.end()
Expand Down Expand Up @@ -899,7 +899,7 @@ internal class OtelTracerProviderTest {
var spanId: String
val fullDuration = measureNanoTime {
val span = tracer.spanBuilder(fakeOperationName).startSpan()
traceId = span.lessSignificantTraceIdAsHexString()
traceId = span.leastSignificantTraceIdAsHexString()
spanId = span.spanIdAsHexString()
Thread.sleep(AndroidTracerTest.OP_DURATION_MS)
span.end()
Expand Down Expand Up @@ -945,7 +945,7 @@ internal class OtelTracerProviderTest {
var spanId: String
val fullDuration = measureNanoTime {
val span = tracer.spanBuilder(fakeOperation).startSpan()
traceId = span.lessSignificantTraceIdAsHexString()
traceId = span.leastSignificantTraceIdAsHexString()
spanId = span.spanIdAsHexString()
Thread.sleep(AndroidTracerTest.OP_DURATION_MS)
span.end()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import com.datadog.tools.unit.getFieldValue
import com.datadog.trace.bootstrap.instrumentation.api.AgentSpan
import io.opentelemetry.api.trace.Span

internal fun Span.lessSignificantTraceIdAsHexString(): String {
internal fun Span.leastSignificantTraceIdAsHexString(): String {
return spanContext.traceId.takeLast(16)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -283,8 +283,8 @@ internal class SpansPayloadAssert(actual: JsonObject) :
}

fun hasGenericLinkedAttribute(key: String, value: List<String>): SpanAssert {
value.forEachIndexed { index, it ->
hasGenericLinkedAttribute("$key.$index", it)
value.forEachIndexed { index, valueAtIndex ->
hasGenericLinkedAttribute("$key.$index", valueAtIndex)
}
return this
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,10 @@ import java.util.concurrent.TimeUnit
import java.util.concurrent.TimeoutException
import java.util.concurrent.atomic.AtomicInteger

internal class BlockingWriterWrapper(wrapped: Writer) : Writer {
internal class BlockingWriterWrapper(private val wrappedWriter: Writer) : Writer {
private val latches: LinkedList<CountDownLatch> = LinkedList()
private val currentTraceCount: AtomicInteger = AtomicInteger(0)

private val wrappedWriter = wrapped

override fun write(trace: MutableList<DDSpan>?) {
wrappedWriter.write(trace)
currentTraceCount.incrementAndGet()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ internal class OtelTracesFragment : Fragment(), View.OnClickListener {
val rootView = inflater.inflate(R.layout.fragment_otel_traces, container, false)
rootView.findViewById<Button>(R.id.start_async_operation).setOnClickListener(this)
rootView.findViewById<Button>(R.id.start_chained_contexts_test).setOnClickListener(this)
rootView.findViewById<Button>(R.id.start_linked_spans_test).setOnClickListener(this)
progressBarAsync = rootView.findViewById(R.id.spinner_async)
return rootView
}
Expand Down Expand Up @@ -74,6 +75,14 @@ internal class OtelTracesFragment : Fragment(), View.OnClickListener {
}
)
}
R.id.start_linked_spans_test -> {
progressBarAsync.visibility = View.VISIBLE
viewModel.startLinkedSpans(
onDone = {
progressBarAsync.visibility = View.INVISIBLE
}
)
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import androidx.lifecycle.ViewModel
import com.datadog.android.log.Logger
import com.datadog.android.sample.BuildConfig
import io.opentelemetry.api.GlobalOpenTelemetry
import io.opentelemetry.api.common.Attributes
import io.opentelemetry.api.trace.Span
import io.opentelemetry.api.trace.Tracer
import io.opentelemetry.context.Context
Expand All @@ -22,6 +23,7 @@ internal class OtelTracesViewModel : ViewModel() {

private var asyncOperationTask: AsyncTask<Unit, Unit, Unit>? = null
private var chainedContextsTask: AsyncTask<Unit, Unit, Unit>? = null
private var linkedSpansTask: AsyncTask<Unit, Unit, Unit>? = null

fun startAsyncOperation(
onProgress: (Int) -> Unit = {},
Expand All @@ -31,16 +33,22 @@ internal class OtelTracesViewModel : ViewModel() {
asyncOperationTask?.execute()
}

fun stopAsyncOperations() {
asyncOperationTask?.cancel(true)
chainedContextsTask?.cancel(true)
}

fun startChainedContexts(onDone: () -> Unit = {}) {
chainedContextsTask = ChainedContextsTask(onDone)
chainedContextsTask?.execute()
}

fun startLinkedSpans(onDone: () -> Unit = {}) {
linkedSpansTask = LinkedSpansTask(onDone)
linkedSpansTask?.execute()
}

fun stopAsyncOperations() {
asyncOperationTask?.cancel(true)
chainedContextsTask?.cancel(true)
linkedSpansTask?.cancel(true)
}

// region AsyncOperationTask

private class AsyncOperationTask(
Expand All @@ -62,7 +70,7 @@ internal class OtelTracesViewModel : ViewModel() {
.build()
.apply {
addTag(ATTR_FLAVOR, BuildConfig.FLAVOR)
addTag("build_type", BuildConfig.BUILD_TYPE)
addTag(BUILD_TYPE, BuildConfig.BUILD_TYPE)
}
}

Expand Down Expand Up @@ -110,6 +118,10 @@ internal class OtelTracesViewModel : ViewModel() {
}
}

// endregion

// region ChainedContextsTask

private class ChainedContextsTask(
val onDone: () -> Unit
) : AsyncTask<Unit, Unit, Unit>() {
Expand All @@ -122,7 +134,7 @@ internal class OtelTracesViewModel : ViewModel() {
private val context: Context =
Context.current().with(emailKey, email).with(usernameKey, username)
val startSpan: Span = tracer
.spanBuilder("submitForm")
.spanBuilder("submitForm with chained contexts")
.setParent(context)
.startSpan()
val scope: Scope = startSpan.makeCurrent()
Expand All @@ -134,7 +146,7 @@ internal class OtelTracesViewModel : ViewModel() {
.build()
.apply {
addTag(ATTR_FLAVOR, BuildConfig.FLAVOR)
addTag("build_type", BuildConfig.BUILD_TYPE)
addTag(BUILD_TYPE, BuildConfig.BUILD_TYPE)
}
}

Expand Down Expand Up @@ -170,7 +182,69 @@ internal class OtelTracesViewModel : ViewModel() {

// endregion

// region LinkedSpansTask

private class LinkedSpansTask(
val onDone: () -> Unit
) : AsyncTask<Unit, Unit, Unit>() {
private val email = "[email protected]"
private val username = "John Doe"
private val tracer: Tracer = GlobalOpenTelemetry.get()
.getTracer("spanLinks")
val startSpan: Span = tracer
.spanBuilder("submitForm with linked spans")
.startSpan()
val scope: Scope = startSpan.makeCurrent()

private val logger: Logger by lazy {
Logger.Builder()
.setName("chained-contexts-task")
.setLogcatLogsEnabled(true)
.build()
.apply {
addTag(ATTR_FLAVOR, BuildConfig.FLAVOR)
addTag(BUILD_TYPE, BuildConfig.BUILD_TYPE)
}
}

@Suppress("MagicNumber")
@Deprecated("Deprecated in Java")
override fun doInBackground(vararg params: Unit?) {
val processingFormSpan = tracer
.spanBuilder("processingForm")
.setParent(Context.current().with(startSpan))
.startSpan()
val formScope = processingFormSpan.makeCurrent()
val attributes = Attributes
.builder()
.put("email", email)
.put("username", username)
.build()
val processingSanitization = tracer
.spanBuilder("formSanitization")
.addLink(processingFormSpan.spanContext, attributes)
.startSpan()
logger.v("Sanitizing email")
logger.v("Sanitizing username")
Thread.sleep(2000)
processingSanitization.end()
Thread.sleep(5000)
formScope.close()
processingFormSpan.end()
}

@Deprecated("Deprecated in Java")
override fun onPostExecute(result: Unit?) {
scope.close()
startSpan.end()
onDone()
}
}

// endregion

companion object {
const val ATTR_FLAVOR = "flavor"
private const val BUILD_TYPE = "build_type"
private const val ATTR_FLAVOR = "flavor"
}
}
Loading

0 comments on commit 0a423fe

Please sign in to comment.