diff --git a/ci/pipelines/default-pipeline.yml b/ci/pipelines/default-pipeline.yml index e4dfb0c481..d1fd6bf142 100644 --- a/ci/pipelines/default-pipeline.yml +++ b/ci/pipelines/default-pipeline.yml @@ -526,6 +526,23 @@ publish:release-internal: - dd-sdk-android-internal/verification-metadata.xml # region Publish features/* +publish:release-trace-api: + tags: [ "arch:amd64" ] + only: + - tags + - develop + image: $CI_IMAGE_DOCKER + stage: publish + timeout: 30m + script: + - !reference [.snippets, set-publishing-credentials] + - ./gradlew :features:dd-sdk-android-trace-api:publishToSonatype closeSonatypeStagingRepository --stacktrace --no-daemon + artifacts: + when: on_success + expire_in: 7 days + paths: + - features/dd-sdk-android-trace-api/verification-metadata.xml + publish:release-trace-internal: tags: [ "arch:amd64" ] diff --git a/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/DatadogContextProvider.kt b/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/DatadogContextProvider.kt index 28dffb0471..f60a0b5bd8 100644 --- a/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/DatadogContextProvider.kt +++ b/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/DatadogContextProvider.kt @@ -17,6 +17,7 @@ internal class DatadogContextProvider( private val coreFeature: CoreFeature, private val featureContextProvider: FeatureContextProvider ) : ContextProvider { + @Suppress("LongMethod") override fun getContext(withFeatureContexts: Set): DatadogContext { // IMPORTANT All properties should be immutable and be frozen at the state // of the context construction moment @@ -57,12 +58,13 @@ internal class DatadogContextProvider( architecture = architecture, numberOfDisplays = numberOfDisplays, localeInfo = with(coreFeature.androidInfoProvider) { - LocaleInfo( - locales = locales, - currentLocale = currentLocale, - timeZone = timeZone - ) - }) + LocaleInfo( + locales = locales, + currentLocale = currentLocale, + timeZone = timeZone + ) + } + ) }, userInfo = coreFeature.userInfoProvider.getUserInfo(), accountInfo = coreFeature.accountInfoProvider.getAccountInfo(), diff --git a/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/NoOpContextProvider.kt b/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/NoOpContextProvider.kt index 29866e69f3..baa2fad01e 100644 --- a/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/NoOpContextProvider.kt +++ b/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/NoOpContextProvider.kt @@ -56,10 +56,10 @@ internal class NoOpContextProvider : ContextProvider { architecture = "", numberOfDisplays = null, localeInfo = LocaleInfo( - locales = emptyList(), - currentLocale = "", - timeZone = "" - ) + locales = emptyList(), + currentLocale = "", + timeZone = "" + ) ), userInfo = UserInfo(null, null, null, null, emptyMap()), accountInfo = null, diff --git a/detekt_custom.yml b/detekt_custom.yml index d430947db3..6be32bc6b2 100644 --- a/detekt_custom.yml +++ b/detekt_custom.yml @@ -321,7 +321,6 @@ datadog: - "okhttp3.Request.Builder.url(kotlin.String):java.lang.NullPointerException,java.lang.IllegalArgumentException" - "okhttp3.Request.Builder.post(okhttp3.RequestBody):java.lang.NullPointerException,java.lang.IllegalArgumentException" - "okhttp3.Request.Builder.method(kotlin.String, okhttp3.RequestBody?):java.lang.NullPointerException,java.lang.IllegalArgumentException" - - "okhttp3.Request.Builder.tag(java.lang.Class, io.opentracing.Span?):java.lang.NullPointerException" - "okhttp3.Request.Builder.tag(java.lang.Class, com.datadog.android.okhttp.TraceContext?):java.lang.ClassCastException" - "okhttp3.Request.Builder.url(kotlin.String):java.lang.NullPointerException,java.lang.IllegalArgumentException" - "okhttp3.Interceptor.Chain.proceed(okhttp3.Request):java.io.IOException" @@ -339,9 +338,6 @@ datadog: - "org.json.JSONArray.get(kotlin.Int):org.json.JSONException" - "org.json.JSONObject.get(kotlin.String):org.json.JSONException" # endregion - # region OpenTracing - - "io.opentracing.Scope.close():java.io.IOException" - # endregion # region Gson - "com.google.gson.JsonParser.parseString(kotlin.String?):com.google.gson.JsonParseException" # endregion @@ -759,8 +755,11 @@ datadog: - "java.util.concurrent.atomic.AtomicLong.constructor(kotlin.Long)" - "java.util.concurrent.atomic.AtomicLong.get()" - "java.util.concurrent.atomic.AtomicLong.set(kotlin.Long)" + - "java.util.concurrent.atomic.AtomicReference.compareAndSet(com.datadog.trace.core.CoreTracer?, com.datadog.trace.core.CoreTracer?)" - "java.util.concurrent.atomic.AtomicReference.compareAndSet(com.datadog.android.api.SdkCore?, com.datadog.android.api.SdkCore?)" - - "java.util.concurrent.atomic.AtomicReference.compareAndSet(io.opentracing.Tracer?, io.opentracing.Tracer?)" + - "java.util.concurrent.atomic.AtomicReference.compareAndSet(com.datadog.trace.bootstrap.instrumentation.api.AgentTracer.TracerAPI?, com.datadog.trace.bootstrap.instrumentation.api.AgentTracer.TracerAPI?)" + - "java.util.concurrent.atomic.AtomicReference.compareAndSet(com.datadog.android.trace.api.tracer.DatadogTracer?, com.datadog.android.trace.api.tracer.DatadogTracer?)" + - "java.util.concurrent.atomic.AtomicReference.compareAndSet(UNKNOWN, UNKNOWN)" - "java.util.concurrent.atomic.AtomicReference.constructor()" - "java.util.concurrent.atomic.AtomicReference.constructor(android.app.Application.ActivityLifecycleCallbacks?)" - "java.util.concurrent.atomic.AtomicReference.constructor(com.datadog.android.api.SdkCore?)" @@ -773,9 +772,12 @@ datadog: - "java.util.concurrent.atomic.AtomicReference.set(com.datadog.android.api.SdkCore?)" - "java.util.concurrent.atomic.AtomicReference.set(com.datadog.android.api.feature.FeatureEventReceiver?)" - "java.util.concurrent.atomic.AtomicReference.set(com.datadog.android.rum.internal.domain.RumContext?)" - - "java.util.concurrent.atomic.AtomicReference.set(io.opentracing.Tracer?)" + - "java.util.concurrent.atomic.AtomicReference.set(com.datadog.android.trace.api.tracer.DatadogTracer?)" + - "java.util.concurrent.atomic.AtomicReference.set(com.datadog.trace.bootstrap.instrumentation.api.AgentTracer.TracerAPI?)" + - "java.util.concurrent.atomic.AtomicReference.set(com.datadog.trace.core.CoreTracer?)" - "java.util.concurrent.atomic.AtomicReference.set(kotlin.Nothing?)" - "java.util.concurrent.atomic.AtomicReference.set(kotlin.String?)" + - "java.util.concurrent.atomic.AtomicReference.set(UNKNOWN)" - "java.util.concurrent.locks.ReadWriteLock.readLock()" - "java.util.concurrent.locks.ReadWriteLock.writeLock()" - "java.util.concurrent.locks.ReentrantReadWriteLock.constructor()" @@ -853,6 +855,7 @@ datadog: - "java.util.Locale.getDefault()" - "java.util.Locale.toLanguageTag()" - "java.util.Properties.constructor()" + - "java.util.Properties.contains(kotlin.Any?)" - "java.util.Properties.setProperty(kotlin.String?, kotlin.String?)" - "java.util.TimeZone.getDefault()" - "java.util.UUID.constructor(kotlin.Long, kotlin.Long)" @@ -996,6 +999,7 @@ datadog: - "kotlin.collections.Map.forEach(kotlin.Function1)" - "kotlin.collections.Map.forEach(kotlin.Function1)" # one of our usage is with <*, *> which doesn't get captured - "kotlin.collections.Map.get(kotlin.String)" + - "kotlin.collections.Map.get(kotlin.String?)" - "kotlin.collections.Map.isEmpty()" - "kotlin.collections.Map.isNotEmpty()" - "kotlin.collections.Map.isNullOrEmpty()" @@ -1086,6 +1090,7 @@ datadog: - "kotlin.collections.MutableMap.isEmpty()" - "kotlin.collections.MutableMap.isNotEmpty()" - "kotlin.collections.MutableMap.iterator()" + - "kotlin.collections.MutableMap.orEmpty()" - "kotlin.collections.MutableMap.map(kotlin.Function1)" - "kotlin.collections.MutableMap.mapValues(kotlin.Function1)" - "kotlin.collections.MutableMap.put(kotlin.Any?, kotlin.Any?)" @@ -1365,7 +1370,6 @@ datadog: # region Kotlin Coroutines - "kotlinx.coroutines.CoroutineScope.async(kotlin.coroutines.CoroutineContext, kotlinx.coroutines.CoroutineStart, kotlin.coroutines.SuspendFunction1)" - "kotlinx.coroutines.CoroutineScope.launch(kotlin.coroutines.CoroutineContext, kotlinx.coroutines.CoroutineStart, kotlin.coroutines.SuspendFunction1)" - - "kotlinx.coroutines.CoroutineScope.withinCoroutineSpan(kotlin.String, io.opentracing.Span?, kotlin.coroutines.CoroutineContext, kotlin.coroutines.SuspendFunction1)" - "kotlinx.coroutines.flow.FlowCollector.emit(kotlin.Any?)" - "kotlinx.coroutines.flow.FlowCollector(kotlin.coroutines.SuspendFunction1)" - "kotlinx.coroutines.flow.flow(kotlin.coroutines.SuspendFunction1)" @@ -1420,7 +1424,6 @@ datadog: - "okhttp3.Request.Builder.addHeader(kotlin.String, kotlin.String)" - "okhttp3.Request.Builder.header(kotlin.String, kotlin.String)" - "okhttp3.Request.Builder.removeHeader(kotlin.String)" - - "okhttp3.Request.Builder.tag(java.lang.Class, io.opentracing.Span?)" - "okhttp3.Request.body()" - "okhttp3.Request.header(kotlin.String)" - "okhttp3.Request.headers()" @@ -1443,36 +1446,13 @@ datadog: - "org.json.JSONObject.keys()" - "org.json.JSONObject.toJsonObject()" # endregion - # region OpenTracing - - "io.opentracing.Span.context()" - - "io.opentracing.Span.finish()" - - "io.opentracing.Span.log(kotlin.collections.MutableMap?)" - - "io.opentracing.Span.setError(kotlin.Throwable)" - - "io.opentracing.Span.setTag(kotlin.String?, kotlin.Number?)" - - "io.opentracing.Span.setTag(kotlin.String?, kotlin.String?)" - - "io.opentracing.Span.setTag(io.opentracing.tag.Tag?, com.datadog.android.internal.concurrent.CompletableFuture?)" - - "io.opentracing.Span.setTag(io.opentracing.tag.Tag?, kotlin.String?)" - - "io.opentracing.SpanContext.toSpanId()" - - "io.opentracing.SpanContext.toTraceId()" - - "io.opentracing.Tracer.SpanBuilder.asChildOf(io.opentracing.Span?)" - - "io.opentracing.Tracer.SpanBuilder.asChildOf(io.opentracing.SpanContext?)" - - "io.opentracing.Tracer.SpanBuilder.start()" - - "io.opentracing.Tracer.activateSpan(io.opentracing.Span?)" - - "io.opentracing.Tracer.activeSpan()" - - "io.opentracing.Tracer.buildSpan(kotlin.String?)" - - "io.opentracing.Tracer.extract(io.opentracing.propagation.Format?, io.opentracing.propagation.TextMapExtract?)" - - "io.opentracing.Tracer.inject(io.opentracing.SpanContext?, io.opentracing.propagation.Format?, io.opentracing.propagation.TextMapInject?)" - - "io.opentracing.propagation.TextMapExtractAdapter.constructor(kotlin.collections.MutableMap?)" - - "io.opentracing.propagation.TextMapInject(kotlin.Function2)" - - "io.opentracing.util.GlobalTracer.get()" - - "io.opentracing.util.GlobalTracer.isRegistered()" - # endregion # region Opentelemetry - "io.opentelemetry.api.trace.Span.getInvalid()" - "io.opentelemetry.api.trace.TracerBuilder.build()" - "io.opentelemetry.api.trace.SpanBuilder.setAttribute(kotlin.String?, kotlin.String?)" - "io.opentelemetry.api.trace.TracerBuilder.setInstrumentationVersion(kotlin.String?)" - "io.opentelemetry.api.trace.TracerProvider.noop()" + - "io.opentelemetry.context.propagation.ContextPropagators.noop()" - "io.opentelemetry.context.Context.get(io.opentelemetry.context.ContextKey?)" - "io.opentelemetry.context.Context.makeCurrent()" - "io.opentelemetry.context.Context.root()" diff --git a/features/dd-sdk-android-logs/api/apiSurface b/features/dd-sdk-android-logs/api/apiSurface index 06581caf54..145f63da6d 100644 --- a/features/dd-sdk-android-logs/api/apiSurface +++ b/features/dd-sdk-android-logs/api/apiSurface @@ -35,17 +35,17 @@ data class com.datadog.android.log.LogsConfiguration fun setEventMapper(com.datadog.android.event.EventMapper): Builder fun build(): LogsConfiguration data class com.datadog.android.log.model.LogEvent - constructor(Device, Os, Status, kotlin.String, kotlin.String, kotlin.String, Logger, Usr? = null, Account? = null, Network? = null, Error? = null, kotlin.String? = null, kotlin.String, kotlin.collections.MutableMap = mutableMapOf()) + constructor(LogEventDevice, Os, Status, kotlin.String, kotlin.String, kotlin.String, Logger, Dd, Usr? = null, Account? = null, Network? = null, Error? = null, kotlin.String? = null, kotlin.String, kotlin.collections.MutableMap = mutableMapOf()) fun toJson(): com.google.gson.JsonElement companion object fun fromJson(kotlin.String): LogEvent fun fromJsonObject(com.google.gson.JsonObject): LogEvent - data class Device + data class LogEventDevice constructor(Type? = null, kotlin.String? = null, kotlin.String? = null, kotlin.String? = null, kotlin.String? = null, kotlin.String? = null, kotlin.collections.List? = null, kotlin.String? = null, kotlin.Number? = null, kotlin.Boolean? = null, kotlin.Number? = null) fun toJson(): com.google.gson.JsonElement companion object - fun fromJson(kotlin.String): Device - fun fromJsonObject(com.google.gson.JsonObject): Device + fun fromJson(kotlin.String): LogEventDevice + fun fromJsonObject(com.google.gson.JsonObject): LogEventDevice data class Os constructor(kotlin.String, kotlin.String, kotlin.String? = null, kotlin.String) fun toJson(): com.google.gson.JsonElement @@ -58,6 +58,12 @@ data class com.datadog.android.log.model.LogEvent companion object fun fromJson(kotlin.String): Logger fun fromJsonObject(com.google.gson.JsonObject): Logger + data class Dd + constructor(DdDevice) + fun toJson(): com.google.gson.JsonElement + companion object + fun fromJson(kotlin.String): Dd + fun fromJsonObject(com.google.gson.JsonObject): Dd data class Usr constructor(kotlin.String? = null, kotlin.String? = null, kotlin.String? = null, kotlin.collections.MutableMap = mutableMapOf()) fun toJson(): com.google.gson.JsonElement @@ -82,6 +88,12 @@ data class com.datadog.android.log.model.LogEvent companion object fun fromJson(kotlin.String): Error fun fromJsonObject(com.google.gson.JsonObject): Error + data class DdDevice + constructor(kotlin.String) + fun toJson(): com.google.gson.JsonElement + companion object + fun fromJson(kotlin.String): DdDevice + fun fromJsonObject(com.google.gson.JsonObject): DdDevice data class Client constructor(SimCarrier? = null, kotlin.String? = null, kotlin.String? = null, kotlin.String? = null, kotlin.String) fun toJson(): com.google.gson.JsonElement diff --git a/features/dd-sdk-android-logs/api/dd-sdk-android-logs.api b/features/dd-sdk-android-logs/api/dd-sdk-android-logs.api index 4dd58fdc08..8cefa9ef22 100644 --- a/features/dd-sdk-android-logs/api/dd-sdk-android-logs.api +++ b/features/dd-sdk-android-logs/api/dd-sdk-android-logs.api @@ -86,24 +86,25 @@ public final class com/datadog/android/log/LogsConfiguration$Builder { public final class com/datadog/android/log/model/LogEvent { public static final field Companion Lcom/datadog/android/log/model/LogEvent$Companion; - public fun (Lcom/datadog/android/log/model/LogEvent$Device;Lcom/datadog/android/log/model/LogEvent$Os;Lcom/datadog/android/log/model/LogEvent$Status;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lcom/datadog/android/log/model/LogEvent$Logger;Lcom/datadog/android/log/model/LogEvent$Usr;Lcom/datadog/android/log/model/LogEvent$Account;Lcom/datadog/android/log/model/LogEvent$Network;Lcom/datadog/android/log/model/LogEvent$Error;Ljava/lang/String;Ljava/lang/String;Ljava/util/Map;)V - public synthetic fun (Lcom/datadog/android/log/model/LogEvent$Device;Lcom/datadog/android/log/model/LogEvent$Os;Lcom/datadog/android/log/model/LogEvent$Status;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lcom/datadog/android/log/model/LogEvent$Logger;Lcom/datadog/android/log/model/LogEvent$Usr;Lcom/datadog/android/log/model/LogEvent$Account;Lcom/datadog/android/log/model/LogEvent$Network;Lcom/datadog/android/log/model/LogEvent$Error;Ljava/lang/String;Ljava/lang/String;Ljava/util/Map;ILkotlin/jvm/internal/DefaultConstructorMarker;)V - public final fun component1 ()Lcom/datadog/android/log/model/LogEvent$Device; - public final fun component10 ()Lcom/datadog/android/log/model/LogEvent$Network; - public final fun component11 ()Lcom/datadog/android/log/model/LogEvent$Error; - public final fun component12 ()Ljava/lang/String; + public fun (Lcom/datadog/android/log/model/LogEvent$LogEventDevice;Lcom/datadog/android/log/model/LogEvent$Os;Lcom/datadog/android/log/model/LogEvent$Status;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lcom/datadog/android/log/model/LogEvent$Logger;Lcom/datadog/android/log/model/LogEvent$Dd;Lcom/datadog/android/log/model/LogEvent$Usr;Lcom/datadog/android/log/model/LogEvent$Account;Lcom/datadog/android/log/model/LogEvent$Network;Lcom/datadog/android/log/model/LogEvent$Error;Ljava/lang/String;Ljava/lang/String;Ljava/util/Map;)V + public synthetic fun (Lcom/datadog/android/log/model/LogEvent$LogEventDevice;Lcom/datadog/android/log/model/LogEvent$Os;Lcom/datadog/android/log/model/LogEvent$Status;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lcom/datadog/android/log/model/LogEvent$Logger;Lcom/datadog/android/log/model/LogEvent$Dd;Lcom/datadog/android/log/model/LogEvent$Usr;Lcom/datadog/android/log/model/LogEvent$Account;Lcom/datadog/android/log/model/LogEvent$Network;Lcom/datadog/android/log/model/LogEvent$Error;Ljava/lang/String;Ljava/lang/String;Ljava/util/Map;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun component1 ()Lcom/datadog/android/log/model/LogEvent$LogEventDevice; + public final fun component10 ()Lcom/datadog/android/log/model/LogEvent$Account; + public final fun component11 ()Lcom/datadog/android/log/model/LogEvent$Network; + public final fun component12 ()Lcom/datadog/android/log/model/LogEvent$Error; public final fun component13 ()Ljava/lang/String; - public final fun component14 ()Ljava/util/Map; + public final fun component14 ()Ljava/lang/String; + public final fun component15 ()Ljava/util/Map; public final fun component2 ()Lcom/datadog/android/log/model/LogEvent$Os; public final fun component3 ()Lcom/datadog/android/log/model/LogEvent$Status; public final fun component4 ()Ljava/lang/String; public final fun component5 ()Ljava/lang/String; public final fun component6 ()Ljava/lang/String; public final fun component7 ()Lcom/datadog/android/log/model/LogEvent$Logger; - public final fun component8 ()Lcom/datadog/android/log/model/LogEvent$Usr; - public final fun component9 ()Lcom/datadog/android/log/model/LogEvent$Account; - public final fun copy (Lcom/datadog/android/log/model/LogEvent$Device;Lcom/datadog/android/log/model/LogEvent$Os;Lcom/datadog/android/log/model/LogEvent$Status;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lcom/datadog/android/log/model/LogEvent$Logger;Lcom/datadog/android/log/model/LogEvent$Usr;Lcom/datadog/android/log/model/LogEvent$Account;Lcom/datadog/android/log/model/LogEvent$Network;Lcom/datadog/android/log/model/LogEvent$Error;Ljava/lang/String;Ljava/lang/String;Ljava/util/Map;)Lcom/datadog/android/log/model/LogEvent; - public static synthetic fun copy$default (Lcom/datadog/android/log/model/LogEvent;Lcom/datadog/android/log/model/LogEvent$Device;Lcom/datadog/android/log/model/LogEvent$Os;Lcom/datadog/android/log/model/LogEvent$Status;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lcom/datadog/android/log/model/LogEvent$Logger;Lcom/datadog/android/log/model/LogEvent$Usr;Lcom/datadog/android/log/model/LogEvent$Account;Lcom/datadog/android/log/model/LogEvent$Network;Lcom/datadog/android/log/model/LogEvent$Error;Ljava/lang/String;Ljava/lang/String;Ljava/util/Map;ILjava/lang/Object;)Lcom/datadog/android/log/model/LogEvent; + public final fun component8 ()Lcom/datadog/android/log/model/LogEvent$Dd; + public final fun component9 ()Lcom/datadog/android/log/model/LogEvent$Usr; + public final fun copy (Lcom/datadog/android/log/model/LogEvent$LogEventDevice;Lcom/datadog/android/log/model/LogEvent$Os;Lcom/datadog/android/log/model/LogEvent$Status;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lcom/datadog/android/log/model/LogEvent$Logger;Lcom/datadog/android/log/model/LogEvent$Dd;Lcom/datadog/android/log/model/LogEvent$Usr;Lcom/datadog/android/log/model/LogEvent$Account;Lcom/datadog/android/log/model/LogEvent$Network;Lcom/datadog/android/log/model/LogEvent$Error;Ljava/lang/String;Ljava/lang/String;Ljava/util/Map;)Lcom/datadog/android/log/model/LogEvent; + public static synthetic fun copy$default (Lcom/datadog/android/log/model/LogEvent;Lcom/datadog/android/log/model/LogEvent$LogEventDevice;Lcom/datadog/android/log/model/LogEvent$Os;Lcom/datadog/android/log/model/LogEvent$Status;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lcom/datadog/android/log/model/LogEvent$Logger;Lcom/datadog/android/log/model/LogEvent$Dd;Lcom/datadog/android/log/model/LogEvent$Usr;Lcom/datadog/android/log/model/LogEvent$Account;Lcom/datadog/android/log/model/LogEvent$Network;Lcom/datadog/android/log/model/LogEvent$Error;Ljava/lang/String;Ljava/lang/String;Ljava/util/Map;ILjava/lang/Object;)Lcom/datadog/android/log/model/LogEvent; public fun equals (Ljava/lang/Object;)Z public static final fun fromJson (Ljava/lang/String;)Lcom/datadog/android/log/model/LogEvent; public static final fun fromJsonObject (Lcom/google/gson/JsonObject;)Lcom/datadog/android/log/model/LogEvent; @@ -111,8 +112,9 @@ public final class com/datadog/android/log/model/LogEvent { public final fun getAdditionalProperties ()Ljava/util/Map; public final fun getBuildId ()Ljava/lang/String; public final fun getDate ()Ljava/lang/String; + public final fun getDd ()Lcom/datadog/android/log/model/LogEvent$Dd; public final fun getDdtags ()Ljava/lang/String; - public final fun getDevice ()Lcom/datadog/android/log/model/LogEvent$Device; + public final fun getDevice ()Lcom/datadog/android/log/model/LogEvent$LogEventDevice; public final fun getError ()Lcom/datadog/android/log/model/LogEvent$Error; public final fun getLogger ()Lcom/datadog/android/log/model/LogEvent$Logger; public final fun getMessage ()Ljava/lang/String; @@ -189,46 +191,44 @@ public final class com/datadog/android/log/model/LogEvent$Companion { public final fun fromJsonObject (Lcom/google/gson/JsonObject;)Lcom/datadog/android/log/model/LogEvent; } -public final class com/datadog/android/log/model/LogEvent$Device { - public static final field Companion Lcom/datadog/android/log/model/LogEvent$Device$Companion; - public fun ()V - public fun (Lcom/datadog/android/log/model/LogEvent$Type;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/util/List;Ljava/lang/String;Ljava/lang/Number;Ljava/lang/Boolean;Ljava/lang/Number;)V - public synthetic fun (Lcom/datadog/android/log/model/LogEvent$Type;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/util/List;Ljava/lang/String;Ljava/lang/Number;Ljava/lang/Boolean;Ljava/lang/Number;ILkotlin/jvm/internal/DefaultConstructorMarker;)V - public final fun component1 ()Lcom/datadog/android/log/model/LogEvent$Type; - public final fun component10 ()Ljava/lang/Boolean; - public final fun component11 ()Ljava/lang/Number; - public final fun component2 ()Ljava/lang/String; - public final fun component3 ()Ljava/lang/String; - public final fun component4 ()Ljava/lang/String; - public final fun component5 ()Ljava/lang/String; - public final fun component6 ()Ljava/lang/String; - public final fun component7 ()Ljava/util/List; - public final fun component8 ()Ljava/lang/String; - public final fun component9 ()Ljava/lang/Number; - public final fun copy (Lcom/datadog/android/log/model/LogEvent$Type;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/util/List;Ljava/lang/String;Ljava/lang/Number;Ljava/lang/Boolean;Ljava/lang/Number;)Lcom/datadog/android/log/model/LogEvent$Device; - public static synthetic fun copy$default (Lcom/datadog/android/log/model/LogEvent$Device;Lcom/datadog/android/log/model/LogEvent$Type;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/util/List;Ljava/lang/String;Ljava/lang/Number;Ljava/lang/Boolean;Ljava/lang/Number;ILjava/lang/Object;)Lcom/datadog/android/log/model/LogEvent$Device; +public final class com/datadog/android/log/model/LogEvent$Dd { + public static final field Companion Lcom/datadog/android/log/model/LogEvent$Dd$Companion; + public fun (Lcom/datadog/android/log/model/LogEvent$DdDevice;)V + public final fun component1 ()Lcom/datadog/android/log/model/LogEvent$DdDevice; + public final fun copy (Lcom/datadog/android/log/model/LogEvent$DdDevice;)Lcom/datadog/android/log/model/LogEvent$Dd; + public static synthetic fun copy$default (Lcom/datadog/android/log/model/LogEvent$Dd;Lcom/datadog/android/log/model/LogEvent$DdDevice;ILjava/lang/Object;)Lcom/datadog/android/log/model/LogEvent$Dd; public fun equals (Ljava/lang/Object;)Z - public static final fun fromJson (Ljava/lang/String;)Lcom/datadog/android/log/model/LogEvent$Device; - public static final fun fromJsonObject (Lcom/google/gson/JsonObject;)Lcom/datadog/android/log/model/LogEvent$Device; + public static final fun fromJson (Ljava/lang/String;)Lcom/datadog/android/log/model/LogEvent$Dd; + public static final fun fromJsonObject (Lcom/google/gson/JsonObject;)Lcom/datadog/android/log/model/LogEvent$Dd; + public final fun getDevice ()Lcom/datadog/android/log/model/LogEvent$DdDevice; + public fun hashCode ()I + public final fun toJson ()Lcom/google/gson/JsonElement; + public fun toString ()Ljava/lang/String; +} + +public final class com/datadog/android/log/model/LogEvent$Dd$Companion { + public final fun fromJson (Ljava/lang/String;)Lcom/datadog/android/log/model/LogEvent$Dd; + public final fun fromJsonObject (Lcom/google/gson/JsonObject;)Lcom/datadog/android/log/model/LogEvent$Dd; +} + +public final class com/datadog/android/log/model/LogEvent$DdDevice { + public static final field Companion Lcom/datadog/android/log/model/LogEvent$DdDevice$Companion; + public fun (Ljava/lang/String;)V + public final fun component1 ()Ljava/lang/String; + public final fun copy (Ljava/lang/String;)Lcom/datadog/android/log/model/LogEvent$DdDevice; + public static synthetic fun copy$default (Lcom/datadog/android/log/model/LogEvent$DdDevice;Ljava/lang/String;ILjava/lang/Object;)Lcom/datadog/android/log/model/LogEvent$DdDevice; + public fun equals (Ljava/lang/Object;)Z + public static final fun fromJson (Ljava/lang/String;)Lcom/datadog/android/log/model/LogEvent$DdDevice; + public static final fun fromJsonObject (Lcom/google/gson/JsonObject;)Lcom/datadog/android/log/model/LogEvent$DdDevice; public final fun getArchitecture ()Ljava/lang/String; - public final fun getBatteryLevel ()Ljava/lang/Number; - public final fun getBrand ()Ljava/lang/String; - public final fun getBrightnessLevel ()Ljava/lang/Number; - public final fun getLocale ()Ljava/lang/String; - public final fun getLocales ()Ljava/util/List; - public final fun getModel ()Ljava/lang/String; - public final fun getName ()Ljava/lang/String; - public final fun getPowerSavingMode ()Ljava/lang/Boolean; - public final fun getTimeZone ()Ljava/lang/String; - public final fun getType ()Lcom/datadog/android/log/model/LogEvent$Type; public fun hashCode ()I public final fun toJson ()Lcom/google/gson/JsonElement; public fun toString ()Ljava/lang/String; } -public final class com/datadog/android/log/model/LogEvent$Device$Companion { - public final fun fromJson (Ljava/lang/String;)Lcom/datadog/android/log/model/LogEvent$Device; - public final fun fromJsonObject (Lcom/google/gson/JsonObject;)Lcom/datadog/android/log/model/LogEvent$Device; +public final class com/datadog/android/log/model/LogEvent$DdDevice$Companion { + public final fun fromJson (Ljava/lang/String;)Lcom/datadog/android/log/model/LogEvent$DdDevice; + public final fun fromJsonObject (Lcom/google/gson/JsonObject;)Lcom/datadog/android/log/model/LogEvent$DdDevice; } public final class com/datadog/android/log/model/LogEvent$Error { @@ -268,6 +268,48 @@ public final class com/datadog/android/log/model/LogEvent$Error$Companion { public final fun fromJsonObject (Lcom/google/gson/JsonObject;)Lcom/datadog/android/log/model/LogEvent$Error; } +public final class com/datadog/android/log/model/LogEvent$LogEventDevice { + public static final field Companion Lcom/datadog/android/log/model/LogEvent$LogEventDevice$Companion; + public fun ()V + public fun (Lcom/datadog/android/log/model/LogEvent$Type;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/util/List;Ljava/lang/String;Ljava/lang/Number;Ljava/lang/Boolean;Ljava/lang/Number;)V + public synthetic fun (Lcom/datadog/android/log/model/LogEvent$Type;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/util/List;Ljava/lang/String;Ljava/lang/Number;Ljava/lang/Boolean;Ljava/lang/Number;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun component1 ()Lcom/datadog/android/log/model/LogEvent$Type; + public final fun component10 ()Ljava/lang/Boolean; + public final fun component11 ()Ljava/lang/Number; + public final fun component2 ()Ljava/lang/String; + public final fun component3 ()Ljava/lang/String; + public final fun component4 ()Ljava/lang/String; + public final fun component5 ()Ljava/lang/String; + public final fun component6 ()Ljava/lang/String; + public final fun component7 ()Ljava/util/List; + public final fun component8 ()Ljava/lang/String; + public final fun component9 ()Ljava/lang/Number; + public final fun copy (Lcom/datadog/android/log/model/LogEvent$Type;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/util/List;Ljava/lang/String;Ljava/lang/Number;Ljava/lang/Boolean;Ljava/lang/Number;)Lcom/datadog/android/log/model/LogEvent$LogEventDevice; + public static synthetic fun copy$default (Lcom/datadog/android/log/model/LogEvent$LogEventDevice;Lcom/datadog/android/log/model/LogEvent$Type;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/util/List;Ljava/lang/String;Ljava/lang/Number;Ljava/lang/Boolean;Ljava/lang/Number;ILjava/lang/Object;)Lcom/datadog/android/log/model/LogEvent$LogEventDevice; + public fun equals (Ljava/lang/Object;)Z + public static final fun fromJson (Ljava/lang/String;)Lcom/datadog/android/log/model/LogEvent$LogEventDevice; + public static final fun fromJsonObject (Lcom/google/gson/JsonObject;)Lcom/datadog/android/log/model/LogEvent$LogEventDevice; + public final fun getArchitecture ()Ljava/lang/String; + public final fun getBatteryLevel ()Ljava/lang/Number; + public final fun getBrand ()Ljava/lang/String; + public final fun getBrightnessLevel ()Ljava/lang/Number; + public final fun getLocale ()Ljava/lang/String; + public final fun getLocales ()Ljava/util/List; + public final fun getModel ()Ljava/lang/String; + public final fun getName ()Ljava/lang/String; + public final fun getPowerSavingMode ()Ljava/lang/Boolean; + public final fun getTimeZone ()Ljava/lang/String; + public final fun getType ()Lcom/datadog/android/log/model/LogEvent$Type; + public fun hashCode ()I + public final fun toJson ()Lcom/google/gson/JsonElement; + public fun toString ()Ljava/lang/String; +} + +public final class com/datadog/android/log/model/LogEvent$LogEventDevice$Companion { + public final fun fromJson (Ljava/lang/String;)Lcom/datadog/android/log/model/LogEvent$LogEventDevice; + public final fun fromJsonObject (Lcom/google/gson/JsonObject;)Lcom/datadog/android/log/model/LogEvent$LogEventDevice; +} + public final class com/datadog/android/log/model/LogEvent$Logger { public static final field Companion Lcom/datadog/android/log/model/LogEvent$Logger$Companion; public fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V diff --git a/features/dd-sdk-android-logs/src/main/json/log/log-schema.json b/features/dd-sdk-android-logs/src/main/json/log/log-schema.json index 454ae26ebf..ea81b460d9 100644 --- a/features/dd-sdk-android-logs/src/main/json/log/log-schema.json +++ b/features/dd-sdk-android-logs/src/main/json/log/log-schema.json @@ -66,6 +66,31 @@ ], "readOnly": true }, + "_dd": { + "type": "object", + "description": "Datadog internal information", + "properties": { + "device": { + "type": "object", + "description": "Information about the device that produced this log.", + "properties": { + "architecture": { + "type": "string", + "description": "The CPU architecture of the device", + "readOnly": true + } + }, + "required": [ + "architecture" + ], + "readOnly": true + } + }, + "required": [ + "device" + ], + "readOnly": true + }, "usr": { "type": "object", "description": "User properties", @@ -239,6 +264,7 @@ "date", "service", "logger", + "_dd", "ddtags", "device", "os" diff --git a/features/dd-sdk-android-logs/src/main/kotlin/com/datadog/android/log/internal/domain/DatadogLogGenerator.kt b/features/dd-sdk-android-logs/src/main/kotlin/com/datadog/android/log/internal/domain/DatadogLogGenerator.kt index 050ba65ad3..a4a60590fb 100644 --- a/features/dd-sdk-android-logs/src/main/kotlin/com/datadog/android/log/internal/domain/DatadogLogGenerator.kt +++ b/features/dd-sdk-android-logs/src/main/kotlin/com/datadog/android/log/internal/domain/DatadogLogGenerator.kt @@ -194,6 +194,11 @@ internal class DatadogLogGenerator( buildId = datadogContext.appBuildId, error = error, logger = loggerInfo, + dd = LogEvent.Dd( + device = LogEvent.DdDevice( + architecture = deviceInfo.architecture + ) + ), usr = usr, account = account, network = network, @@ -210,7 +215,7 @@ internal class DatadogLogGenerator( versionMajor = deviceInfo.osMajorVersion ) - private fun resolveDeviceInfo(deviceInfo: DeviceInfo) = LogEvent.Device( + private fun resolveDeviceInfo(deviceInfo: DeviceInfo) = LogEvent.LogEventDevice( type = resolveDeviceType(deviceInfo.deviceType), name = deviceInfo.deviceName, model = deviceInfo.deviceModel, diff --git a/features/dd-sdk-android-logs/src/test/kotlin/com/datadog/android/utils/forge/LogEventForgeryFactory.kt b/features/dd-sdk-android-logs/src/test/kotlin/com/datadog/android/utils/forge/LogEventForgeryFactory.kt index dd0019fc6d..54ad0ebeee 100644 --- a/features/dd-sdk-android-logs/src/test/kotlin/com/datadog/android/utils/forge/LogEventForgeryFactory.kt +++ b/features/dd-sdk-android-logs/src/test/kotlin/com/datadog/android/utils/forge/LogEventForgeryFactory.kt @@ -87,13 +87,18 @@ internal class LogEventForgeryFactory : ForgeryFactory { version = forge.aStringMatching("[0-9]\\.[0-9]\\.[0-9]"), threadName = forge.aNullable { forge.anAlphabeticalString() } ), - device = LogEvent.Device( + device = LogEvent.LogEventDevice( type = resolveDeviceType(deviceInfo.deviceType), name = deviceInfo.deviceName, model = deviceInfo.deviceModel, brand = deviceInfo.deviceBrand, architecture = deviceInfo.architecture ), + dd = LogEvent.Dd( + device = LogEvent.DdDevice( + architecture = deviceInfo.architecture + ) + ), os = LogEvent.Os( name = deviceInfo.osName, version = deviceInfo.osVersion, diff --git a/features/dd-sdk-android-rum/build.gradle.kts b/features/dd-sdk-android-rum/build.gradle.kts index 3d38c3adec..1cfd2dc7d0 100644 --- a/features/dd-sdk-android-rum/build.gradle.kts +++ b/features/dd-sdk-android-rum/build.gradle.kts @@ -77,13 +77,15 @@ dependencies { ) } } - testImplementation(testFixtures(project(":dd-sdk-android-core"))) - testImplementation(testFixtures(project(":dd-sdk-android-internal"))) + testImplementation(libs.bundles.jUnit5) testImplementation(libs.bundles.testTools) testImplementation(libs.okHttp) testImplementation(libs.okHttpMock) - testImplementation(libs.bundles.openTracing) + testImplementation(project(":features:dd-sdk-android-trace")) + testImplementation(testFixtures(project(":dd-sdk-android-core"))) + testImplementation(testFixtures(project(":dd-sdk-android-internal"))) + testImplementation(testFixtures(project(":features:dd-sdk-android-trace"))) unmock(libs.robolectric) } diff --git a/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/telemetry/internal/TelemetryEventHandler.kt b/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/telemetry/internal/TelemetryEventHandler.kt index 0c3573462b..5f2ca44675 100644 --- a/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/telemetry/internal/TelemetryEventHandler.kt +++ b/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/telemetry/internal/TelemetryEventHandler.kt @@ -436,20 +436,21 @@ internal class TelemetryEventHandler( } private fun isGlobalTracerRegistered(): Boolean { - // We don't reference io.opentracing from RUM directly, so using reflection for this. - // Would be nice to add the test with the flavor which is has no io.opentracing and test + // We don't reference com.datadog.android.trace from RUM directly, so using reflection for this. + // Would be nice to add the test with the flavor which is has no com.datadog.android.trace and test // for obfuscation enabled case. return try { - val globalTracerClass = Class.forName("io.opentracing.util.GlobalTracer") + val globalDatadogTracer = + Class.forName("com.datadog.android.trace.GlobalDatadogTracer") return try { - globalTracerClass.getMethod("isRegistered") - .invoke(null) as Boolean + val holderInstance = globalDatadogTracer.getDeclaredField("INSTANCE").get(null) + globalDatadogTracer.getDeclaredMethod("getOrNull").invoke(holderInstance) != null } catch (@Suppress("TooGenericExceptionCaught") t: Throwable) { sdkCore.internalLogger.log( InternalLogger.Level.ERROR, InternalLogger.Target.TELEMETRY, { - "GlobalTracer class exists in the runtime classpath, " + + "GlobalDatadogTracer class exists in the runtime classpath, " + "but there is an error invoking isRegistered method" }, t diff --git a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/scope/RumViewScopeTest.kt b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/scope/RumViewScopeTest.kt index 831b2793a0..f2b918c250 100644 --- a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/scope/RumViewScopeTest.kt +++ b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/scope/RumViewScopeTest.kt @@ -88,6 +88,7 @@ import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Assumptions.assumeFalse import org.junit.jupiter.api.Assumptions.assumeTrue import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Disabled import org.junit.jupiter.api.Test import org.junit.jupiter.api.assertDoesNotThrow import org.junit.jupiter.api.extension.ExtendWith @@ -3552,6 +3553,7 @@ internal class RumViewScopeTest { // region Error @Test + @Disabled // RUM-11016 disabling because it's flaky fun `M send event W handleEvent(AddError) on active view`( @StringForgery message: String, @Forgery source: RumErrorSource, @@ -3680,7 +3682,6 @@ internal class RumViewScopeTest { hasActionId(fakeActionId) hasStartReason(fakeParentContext.sessionStartReason) hasReplay(fakeHasReplay) - hasSyntheticsSession() hasSessionType(fakeRumSessionType?.toError() ?: ErrorEvent.ErrorEventSessionType.SYNTHETICS) hasSyntheticsTest(fakeTestId, fakeResultId) hasDeviceInfo( diff --git a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/telemetry/internal/TelemetryEventHandlerTest.kt b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/telemetry/internal/TelemetryEventHandlerTest.kt index e8f93e41fb..fdb12c4e1b 100644 --- a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/telemetry/internal/TelemetryEventHandlerTest.kt +++ b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/telemetry/internal/TelemetryEventHandlerTest.kt @@ -45,16 +45,15 @@ import com.datadog.android.telemetry.model.TelemetryConfigurationEvent import com.datadog.android.telemetry.model.TelemetryDebugEvent import com.datadog.android.telemetry.model.TelemetryErrorEvent import com.datadog.android.telemetry.model.TelemetryUsageEvent +import com.datadog.android.trace.GlobalDatadogTracer +import com.datadog.android.trace.api.tracer.DatadogTracer import com.datadog.tools.unit.forge.aThrowable -import com.datadog.tools.unit.setStaticValue import fr.xgouchet.elmyr.Forge import fr.xgouchet.elmyr.annotation.FloatForgery import fr.xgouchet.elmyr.annotation.Forgery import fr.xgouchet.elmyr.annotation.StringForgery import fr.xgouchet.elmyr.junit5.ForgeConfiguration import fr.xgouchet.elmyr.junit5.ForgeExtension -import io.opentracing.Tracer -import io.opentracing.util.GlobalTracer import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.within import org.junit.jupiter.api.AfterEach @@ -224,7 +223,7 @@ internal class TelemetryEventHandlerTest { @AfterEach fun `tear down`() { - GlobalTracer::class.java.setStaticValue("isRegistered", false) + GlobalDatadogTracer.clear() } // region Debug Event @@ -615,7 +614,7 @@ internal class TelemetryEventHandlerTest { if (useTracer) { whenever(mockSdkCore.getFeature(Feature.TRACING_FEATURE_NAME)) doReturn mock() if (tracerApi == TelemetryEventHandler.TracerApi.OpenTracing) { - GlobalTracer.registerIfAbsent(mock()) + GlobalDatadogTracer.registerIfAbsent(mock()) } else if (tracerApi == TelemetryEventHandler.TracerApi.OpenTelemetry) { fakeDatadogContext = fakeDatadogContext.copy( featuresContext = fakeDatadogContext.featuresContext.toMutableMap().apply { diff --git a/features/dd-sdk-android-trace-api/.gitignore b/features/dd-sdk-android-trace-api/.gitignore new file mode 100644 index 0000000000..e5614b9871 --- /dev/null +++ b/features/dd-sdk-android-trace-api/.gitignore @@ -0,0 +1,18 @@ +# Built application files +*.apk +*.ap_ +*.aab + +# Files for the ART/Dalvik VM +*.dex + +# Java class files +*.class + +# Generated files +bin/ +gen/ +out/ + +# Gradle files +build/ diff --git a/features/dd-sdk-android-trace-api/api/apiSurface b/features/dd-sdk-android-trace-api/api/apiSurface new file mode 100644 index 0000000000..0bb08ff565 --- /dev/null +++ b/features/dd-sdk-android-trace-api/api/apiSurface @@ -0,0 +1,131 @@ +object com.datadog.android.trace.api.DatadogTracingConstants + const val DEFAULT_ASYNC_PROPAGATING: Boolean + object Tags + const val KEY_HTTP_URL: String + const val KEY_SPAN_KIND: String + const val KEY_HTTP_METHOD: String + const val KEY_HTTP_STATUS: String + const val KEY_ERROR_MSG: String + const val KEY_ERROR_TYPE: String + const val KEY_ERROR_STACK: String + const val VALUE_SPAN_KIND_CLIENT: String + const val VALUE_SPAN_KIND_SERVER: String + const val VALUE_SPAN_KIND_PRODUCER: String + const val VALUE_SPAN_KIND_CONSUMER: String + const val KEY_ANALYTICS_SAMPLE_RATE: String + const val RESOURCE_NAME: String + const val ERROR_MSG: String + const val ERROR_TYPE: String + object PrioritySampling + const val UNSET: Int + const val SAMPLER_DROP: Int + const val SAMPLER_KEEP: Int + const val USER_DROP: Int + const val USER_KEEP: Int + object TracerConfig + const val SPAN_TAGS: String + const val TRACE_RATE_LIMIT: String + const val PARTIAL_FLUSH_MIN_SPANS: String + const val TRACE_SAMPLE_RATE: String + const val PROPAGATION_STYLE_EXTRACT: String + const val PROPAGATION_STYLE_INJECT: String + const val SERVICE_NAME: String + const val SDK_V2_COMPATIBILITY_FLAG: String + const val URL_AS_RESOURCE_NAME: String + const val TAGS: String + object LogAttributes + const val ERROR_KIND: String + const val ERROR_OBJECT: String + const val EVENT: String + const val MESSAGE: String + const val STACK: String + const val STATUS: String + object ErrorPriorities + const val UNSET: Byte + const val HTTP_SERVER_DECORATOR: Byte + const val DEFAULT: Byte +interface com.datadog.android.trace.api.propagation.DatadogPropagation + fun inject(com.datadog.android.trace.api.span.DatadogSpanContext, C, (C) -> Unit) + fun extract(C, (C) -> Unit): com.datadog.android.trace.api.span.DatadogSpanContext? +class com.datadog.android.trace.api.propagation.NoOpDatadogPropagation : DatadogPropagation + override fun inject(com.datadog.android.trace.api.span.DatadogSpanContext, C, (C) -> Unit) + override fun extract(C, (C) -> Unit): com.datadog.android.trace.api.span.DatadogSpanContext? +interface com.datadog.android.trace.api.scope.DatadogScope : java.io.Closeable + override fun close() +interface com.datadog.android.trace.api.span.DatadogSpan + var isError: Boolean? + val isRootSpan: Boolean + val samplingPriority: Int? + val traceId: com.datadog.android.trace.api.trace.DatadogTraceId + val parentSpanId: Long? + var resourceName: String? + var serviceName: String + var operationName: String + val durationNano: Long + val startTimeNanos: Long + val localRootSpan: DatadogSpan? + fun context(): DatadogSpanContext + fun finish() + fun finish(Long) + fun drop() + fun setErrorMessage(String?) + fun addThrowable(Throwable) + fun addThrowable(Throwable, Byte) + fun setTag(String?, String?) + fun setTag(String?, Boolean) + fun setTag(String?, Number?) + fun setTag(String?, Any?) + fun getTag(String?): Any? + fun setMetric(String, Int) + fun logThrowable(Throwable) + fun logErrorMessage(String) + fun logMessage(String) + fun logAttributes(Map) +interface com.datadog.android.trace.api.span.DatadogSpanBuilder + fun start(): DatadogSpan + fun withOrigin(String?): DatadogSpanBuilder + fun withTag(String, Double?): DatadogSpanBuilder + fun withTag(String, Long?): DatadogSpanBuilder + fun withTag(String, Any?): DatadogSpanBuilder + fun withResourceName(String?): DatadogSpanBuilder + fun withParentContext(DatadogSpanContext?): DatadogSpanBuilder + fun withParentSpan(DatadogSpan?): DatadogSpanBuilder + fun withStartTimestamp(Long): DatadogSpanBuilder + fun ignoreActiveSpan(): DatadogSpanBuilder + fun withLink(DatadogSpanLink): DatadogSpanBuilder +interface com.datadog.android.trace.api.span.DatadogSpanContext + val traceId: com.datadog.android.trace.api.trace.DatadogTraceId + val spanId: Long + val samplingPriority: Int + val tags: Map + fun setSamplingPriority(Int): Boolean + fun setMetric(CharSequence?, Double) +interface com.datadog.android.trace.api.span.DatadogSpanLink + val spanId: Long + val sampled: Boolean + val traceId: com.datadog.android.trace.api.trace.DatadogTraceId + val traceStrace: String + val attributes: Map? +interface com.datadog.android.trace.api.span.DatadogSpanWriter +interface com.datadog.android.trace.api.trace.DatadogTraceId + fun toHexStringPadded(Int): String + fun toHexString(): String + fun toLong(): Long + companion object +interface com.datadog.android.trace.api.tracer.DatadogTracer + fun activeSpan(): com.datadog.android.trace.api.span.DatadogSpan? + fun propagate(): com.datadog.android.trace.api.propagation.DatadogPropagation + fun activateSpan(com.datadog.android.trace.api.span.DatadogSpan): com.datadog.android.trace.api.scope.DatadogScope? + fun activateSpan(com.datadog.android.trace.api.span.DatadogSpan, Boolean): com.datadog.android.trace.api.scope.DatadogScope? + fun buildSpan(CharSequence): com.datadog.android.trace.api.span.DatadogSpanBuilder + fun buildSpan(String, CharSequence): com.datadog.android.trace.api.span.DatadogSpanBuilder + fun addScopeListener(com.datadog.android.trace.api.scope.DatadogScopeListener) +interface com.datadog.android.trace.api.tracer.DatadogTracerBuilder + fun build(): DatadogTracer + fun withTracingHeadersTypes(Set): DatadogTracerBuilder + fun withServiceName(String): DatadogTracerBuilder + fun withSampleRate(Double): DatadogTracerBuilder + fun withPartialFlushMinSpans(Int): DatadogTracerBuilder + fun withTag(String, String): DatadogTracerBuilder + fun setBundleWithRumEnabled(Boolean): DatadogTracerBuilder + fun setTraceRateLimit(Int): DatadogTracerBuilder diff --git a/features/dd-sdk-android-trace-api/api/dd-sdk-android-trace-api.api b/features/dd-sdk-android-trace-api/api/dd-sdk-android-trace-api.api new file mode 100644 index 0000000000..3d4112861a --- /dev/null +++ b/features/dd-sdk-android-trace-api/api/dd-sdk-android-trace-api.api @@ -0,0 +1,211 @@ +public final class com/datadog/android/trace/api/DatadogTracingConstants { + public static final field DEFAULT_ASYNC_PROPAGATING Z + public static final field INSTANCE Lcom/datadog/android/trace/api/DatadogTracingConstants; +} + +public final class com/datadog/android/trace/api/DatadogTracingConstants$ErrorPriorities { + public static final field DEFAULT B + public static final field HTTP_SERVER_DECORATOR B + public static final field INSTANCE Lcom/datadog/android/trace/api/DatadogTracingConstants$ErrorPriorities; + public static final field UNSET B +} + +public final class com/datadog/android/trace/api/DatadogTracingConstants$LogAttributes { + public static final field ERROR_KIND Ljava/lang/String; + public static final field ERROR_OBJECT Ljava/lang/String; + public static final field EVENT Ljava/lang/String; + public static final field INSTANCE Lcom/datadog/android/trace/api/DatadogTracingConstants$LogAttributes; + public static final field MESSAGE Ljava/lang/String; + public static final field STACK Ljava/lang/String; + public static final field STATUS Ljava/lang/String; +} + +public final class com/datadog/android/trace/api/DatadogTracingConstants$PrioritySampling { + public static final field INSTANCE Lcom/datadog/android/trace/api/DatadogTracingConstants$PrioritySampling; + public static final field SAMPLER_DROP I + public static final field SAMPLER_KEEP I + public static final field UNSET I + public static final field USER_DROP I + public static final field USER_KEEP I +} + +public final class com/datadog/android/trace/api/DatadogTracingConstants$Tags { + public static final field ERROR_MSG Ljava/lang/String; + public static final field ERROR_TYPE Ljava/lang/String; + public static final field INSTANCE Lcom/datadog/android/trace/api/DatadogTracingConstants$Tags; + public static final field KEY_ANALYTICS_SAMPLE_RATE Ljava/lang/String; + public static final field KEY_ERROR_MSG Ljava/lang/String; + public static final field KEY_ERROR_STACK Ljava/lang/String; + public static final field KEY_ERROR_TYPE Ljava/lang/String; + public static final field KEY_HTTP_METHOD Ljava/lang/String; + public static final field KEY_HTTP_STATUS Ljava/lang/String; + public static final field KEY_HTTP_URL Ljava/lang/String; + public static final field KEY_SPAN_KIND Ljava/lang/String; + public static final field RESOURCE_NAME Ljava/lang/String; + public static final field VALUE_SPAN_KIND_CLIENT Ljava/lang/String; + public static final field VALUE_SPAN_KIND_CONSUMER Ljava/lang/String; + public static final field VALUE_SPAN_KIND_PRODUCER Ljava/lang/String; + public static final field VALUE_SPAN_KIND_SERVER Ljava/lang/String; +} + +public final class com/datadog/android/trace/api/DatadogTracingConstants$TracerConfig { + public static final field INSTANCE Lcom/datadog/android/trace/api/DatadogTracingConstants$TracerConfig; + public static final field PARTIAL_FLUSH_MIN_SPANS Ljava/lang/String; + public static final field PROPAGATION_STYLE_EXTRACT Ljava/lang/String; + public static final field PROPAGATION_STYLE_INJECT Ljava/lang/String; + public static final field SDK_V2_COMPATIBILITY_FLAG Ljava/lang/String; + public static final field SERVICE_NAME Ljava/lang/String; + public static final field SPAN_TAGS Ljava/lang/String; + public static final field TAGS Ljava/lang/String; + public static final field TRACE_RATE_LIMIT Ljava/lang/String; + public static final field TRACE_SAMPLE_RATE Ljava/lang/String; + public static final field URL_AS_RESOURCE_NAME Ljava/lang/String; +} + +public abstract interface class com/datadog/android/trace/api/propagation/DatadogPropagation { + public abstract fun extract (Ljava/lang/Object;Lkotlin/jvm/functions/Function2;)Lcom/datadog/android/trace/api/span/DatadogSpanContext; + public abstract fun inject (Lcom/datadog/android/trace/api/span/DatadogSpanContext;Ljava/lang/Object;Lkotlin/jvm/functions/Function3;)V +} + +public final class com/datadog/android/trace/api/propagation/NoOpDatadogPropagation : com/datadog/android/trace/api/propagation/DatadogPropagation { + public fun ()V + public fun extract (Ljava/lang/Object;Lkotlin/jvm/functions/Function2;)Lcom/datadog/android/trace/api/span/DatadogSpanContext; + public fun inject (Lcom/datadog/android/trace/api/span/DatadogSpanContext;Ljava/lang/Object;Lkotlin/jvm/functions/Function3;)V +} + +public abstract interface class com/datadog/android/trace/api/scope/DatadogScope : java/io/Closeable { + public abstract fun close ()V +} + +public abstract interface class com/datadog/android/trace/api/scope/DatadogScopeListener { + public abstract fun afterScopeActivated ()V + public abstract fun afterScopeClosed ()V +} + +public abstract interface class com/datadog/android/trace/api/span/DatadogSpan { + public abstract fun addThrowable (Ljava/lang/Throwable;)V + public abstract fun addThrowable (Ljava/lang/Throwable;B)V + public abstract fun context ()Lcom/datadog/android/trace/api/span/DatadogSpanContext; + public abstract fun drop ()V + public abstract fun finish ()V + public abstract fun finish (J)V + public abstract fun getDurationNano ()J + public abstract fun getLocalRootSpan ()Lcom/datadog/android/trace/api/span/DatadogSpan; + public abstract fun getOperationName ()Ljava/lang/String; + public abstract fun getParentSpanId ()Ljava/lang/Long; + public abstract fun getResourceName ()Ljava/lang/String; + public abstract fun getSamplingPriority ()Ljava/lang/Integer; + public abstract fun getServiceName ()Ljava/lang/String; + public abstract fun getStartTimeNanos ()J + public abstract fun getTag (Ljava/lang/String;)Ljava/lang/Object; + public abstract fun getTraceId ()Lcom/datadog/android/trace/api/trace/DatadogTraceId; + public abstract fun isError ()Ljava/lang/Boolean; + public abstract fun isRootSpan ()Z + public abstract fun logAttributes (Ljava/util/Map;)V + public abstract fun logErrorMessage (Ljava/lang/String;)V + public abstract fun logMessage (Ljava/lang/String;)V + public abstract fun logThrowable (Ljava/lang/Throwable;)V + public abstract fun setError (Ljava/lang/Boolean;)V + public abstract fun setErrorMessage (Ljava/lang/String;)V + public abstract fun setMetric (Ljava/lang/String;I)V + public abstract fun setOperationName (Ljava/lang/String;)V + public abstract fun setResourceName (Ljava/lang/String;)V + public abstract fun setServiceName (Ljava/lang/String;)V + public abstract fun setTag (Ljava/lang/String;Ljava/lang/Number;)V + public abstract fun setTag (Ljava/lang/String;Ljava/lang/Object;)V + public abstract fun setTag (Ljava/lang/String;Ljava/lang/String;)V + public abstract fun setTag (Ljava/lang/String;Z)V +} + +public abstract interface class com/datadog/android/trace/api/span/DatadogSpanBuilder { + public abstract fun ignoreActiveSpan ()Lcom/datadog/android/trace/api/span/DatadogSpanBuilder; + public abstract fun start ()Lcom/datadog/android/trace/api/span/DatadogSpan; + public abstract fun withLink (Lcom/datadog/android/trace/api/span/DatadogSpanLink;)Lcom/datadog/android/trace/api/span/DatadogSpanBuilder; + public abstract fun withOrigin (Ljava/lang/String;)Lcom/datadog/android/trace/api/span/DatadogSpanBuilder; + public abstract fun withParentContext (Lcom/datadog/android/trace/api/span/DatadogSpanContext;)Lcom/datadog/android/trace/api/span/DatadogSpanBuilder; + public abstract fun withParentSpan (Lcom/datadog/android/trace/api/span/DatadogSpan;)Lcom/datadog/android/trace/api/span/DatadogSpanBuilder; + public abstract fun withResourceName (Ljava/lang/String;)Lcom/datadog/android/trace/api/span/DatadogSpanBuilder; + public abstract fun withStartTimestamp (J)Lcom/datadog/android/trace/api/span/DatadogSpanBuilder; + public abstract fun withTag (Ljava/lang/String;Ljava/lang/Double;)Lcom/datadog/android/trace/api/span/DatadogSpanBuilder; + public abstract fun withTag (Ljava/lang/String;Ljava/lang/Long;)Lcom/datadog/android/trace/api/span/DatadogSpanBuilder; + public abstract fun withTag (Ljava/lang/String;Ljava/lang/Object;)Lcom/datadog/android/trace/api/span/DatadogSpanBuilder; +} + +public abstract interface class com/datadog/android/trace/api/span/DatadogSpanContext { + public abstract fun getSamplingPriority ()I + public abstract fun getSpanId ()J + public abstract fun getTags ()Ljava/util/Map; + public abstract fun getTraceId ()Lcom/datadog/android/trace/api/trace/DatadogTraceId; + public abstract fun setMetric (Ljava/lang/CharSequence;D)V + public abstract fun setSamplingPriority (I)Z +} + +public abstract interface class com/datadog/android/trace/api/span/DatadogSpanLink { + public abstract fun getAttributes ()Ljava/util/Map; + public abstract fun getSampled ()Z + public abstract fun getSpanId ()J + public abstract fun getTraceId ()Lcom/datadog/android/trace/api/trace/DatadogTraceId; + public abstract fun getTraceStrace ()Ljava/lang/String; +} + +public abstract interface class com/datadog/android/trace/api/span/DatadogSpanWriter { +} + +public abstract interface class com/datadog/android/trace/api/trace/DatadogTraceId { + public static final field Companion Lcom/datadog/android/trace/api/trace/DatadogTraceId$Companion; + public abstract fun toHexString ()Ljava/lang/String; + public abstract fun toHexStringPadded (I)Ljava/lang/String; + public abstract fun toLong ()J +} + +public final class com/datadog/android/trace/api/trace/DatadogTraceId$Companion { +} + +public abstract interface class com/datadog/android/trace/api/tracer/DatadogTracer { + public abstract fun activateSpan (Lcom/datadog/android/trace/api/span/DatadogSpan;)Lcom/datadog/android/trace/api/scope/DatadogScope; + public abstract fun activateSpan (Lcom/datadog/android/trace/api/span/DatadogSpan;Z)Lcom/datadog/android/trace/api/scope/DatadogScope; + public abstract fun activeSpan ()Lcom/datadog/android/trace/api/span/DatadogSpan; + public abstract fun addScopeListener (Lcom/datadog/android/trace/api/scope/DatadogScopeListener;)V + public abstract fun buildSpan (Ljava/lang/CharSequence;)Lcom/datadog/android/trace/api/span/DatadogSpanBuilder; + public abstract fun buildSpan (Ljava/lang/String;Ljava/lang/CharSequence;)Lcom/datadog/android/trace/api/span/DatadogSpanBuilder; + public abstract fun propagate ()Lcom/datadog/android/trace/api/propagation/DatadogPropagation; +} + +public final class com/datadog/android/trace/api/tracer/DatadogTracer$DefaultImpls { + public static fun propagate (Lcom/datadog/android/trace/api/tracer/DatadogTracer;)Lcom/datadog/android/trace/api/propagation/DatadogPropagation; +} + +public abstract interface class com/datadog/android/trace/api/tracer/DatadogTracerBuilder { + public abstract fun build ()Lcom/datadog/android/trace/api/tracer/DatadogTracer; + public abstract fun setBundleWithRumEnabled (Z)Lcom/datadog/android/trace/api/tracer/DatadogTracerBuilder; + public abstract fun setTraceRateLimit (I)Lcom/datadog/android/trace/api/tracer/DatadogTracerBuilder; + public abstract fun withPartialFlushMinSpans (I)Lcom/datadog/android/trace/api/tracer/DatadogTracerBuilder; + public abstract fun withSampleRate (D)Lcom/datadog/android/trace/api/tracer/DatadogTracerBuilder; + public abstract fun withServiceName (Ljava/lang/String;)Lcom/datadog/android/trace/api/tracer/DatadogTracerBuilder; + public abstract fun withTag (Ljava/lang/String;Ljava/lang/String;)Lcom/datadog/android/trace/api/tracer/DatadogTracerBuilder; + public abstract fun withTracingHeadersTypes (Ljava/util/Set;)Lcom/datadog/android/trace/api/tracer/DatadogTracerBuilder; +} + +public final class com/datadog/android/trace/api/tracer/NoOpDatadogTracer : com/datadog/android/trace/api/tracer/DatadogTracer { + public fun ()V + public fun activateSpan (Lcom/datadog/android/trace/api/span/DatadogSpan;)Lcom/datadog/android/trace/api/scope/DatadogScope; + public fun activateSpan (Lcom/datadog/android/trace/api/span/DatadogSpan;Z)Lcom/datadog/android/trace/api/scope/DatadogScope; + public fun activeSpan ()Lcom/datadog/android/trace/api/span/DatadogSpan; + public fun addScopeListener (Lcom/datadog/android/trace/api/scope/DatadogScopeListener;)V + public fun buildSpan (Ljava/lang/CharSequence;)Lcom/datadog/android/trace/api/span/DatadogSpanBuilder; + public fun buildSpan (Ljava/lang/String;Ljava/lang/CharSequence;)Lcom/datadog/android/trace/api/span/DatadogSpanBuilder; + public fun propagate ()Lcom/datadog/android/trace/api/propagation/DatadogPropagation; +} + +public final class com/datadog/android/trace/api/tracer/NoOpDatadogTracerBuilder : com/datadog/android/trace/api/tracer/DatadogTracerBuilder { + public fun ()V + public fun build ()Lcom/datadog/android/trace/api/tracer/DatadogTracer; + public fun setBundleWithRumEnabled (Z)Lcom/datadog/android/trace/api/tracer/DatadogTracerBuilder; + public fun setTraceRateLimit (I)Lcom/datadog/android/trace/api/tracer/DatadogTracerBuilder; + public fun withPartialFlushMinSpans (I)Lcom/datadog/android/trace/api/tracer/DatadogTracerBuilder; + public fun withSampleRate (D)Lcom/datadog/android/trace/api/tracer/DatadogTracerBuilder; + public fun withServiceName (Ljava/lang/String;)Lcom/datadog/android/trace/api/tracer/DatadogTracerBuilder; + public fun withTag (Ljava/lang/String;Ljava/lang/String;)Lcom/datadog/android/trace/api/tracer/DatadogTracerBuilder; + public fun withTracingHeadersTypes (Ljava/util/Set;)Lcom/datadog/android/trace/api/tracer/DatadogTracerBuilder; +} + diff --git a/features/dd-sdk-android-trace-api/build.gradle.kts b/features/dd-sdk-android-trace-api/build.gradle.kts new file mode 100644 index 0000000000..d1d9a4f546 --- /dev/null +++ b/features/dd-sdk-android-trace-api/build.gradle.kts @@ -0,0 +1,69 @@ +/* + * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. + * This product includes software developed at Datadog (https://www.datadoghq.com/). + * Copyright 2016-Present Datadog, Inc. + */ +@file:Suppress("StringLiteralDuplication") + +import com.datadog.gradle.config.androidLibraryConfig +import com.datadog.gradle.config.dependencyUpdateConfig +import com.datadog.gradle.config.detektCustomConfig +import com.datadog.gradle.config.javadocConfig +import com.datadog.gradle.config.junitConfig +import com.datadog.gradle.config.kotlinConfig +import com.datadog.gradle.config.publishingConfig +import org.jetbrains.kotlin.gradle.dsl.JvmTarget + +plugins { + // Build + id("com.android.library") + kotlin("android") + id("com.google.devtools.ksp") + + // Publish + `maven-publish` + signing + id("org.jetbrains.dokka-javadoc") + + // Analysis tools + id("com.github.ben-manes.versions") + + // Tests + id("de.mobilej.unmock") + id("org.jetbrains.kotlinx.kover") + + // Internal Generation + id("com.datadoghq.dependency-license") + id("apiSurface") + id("transitiveDependencies") + id("binary-compatibility-validator") +} + +android { + namespace = "com.datadog.android.trace.api" +} + +dependencies { + api(project(":dd-sdk-android-core")) + implementation(project(":dd-sdk-android-internal")) + implementation(libs.gson) + implementation(libs.androidXAnnotation) + implementation(libs.bundles.traceCore) + + // Generate NoOp implementations + ksp(project(":tools:noopfactory")) +} + +unMock { + keepStartingWith("org.json") +} + +kotlinConfig(jvmBytecodeTarget = JvmTarget.JVM_11) +androidLibraryConfig() +junitConfig() +javadocConfig() +dependencyUpdateConfig() +publishingConfig( + "Tracing engine API specification used for internal module communication." +) +detektCustomConfig() diff --git a/features/dd-sdk-android-trace-api/src/main/kotlin/com/datadog/android/trace/api/DatadogTracingConstants.kt b/features/dd-sdk-android-trace-api/src/main/kotlin/com/datadog/android/trace/api/DatadogTracingConstants.kt new file mode 100644 index 0000000000..dd09a1bcc7 --- /dev/null +++ b/features/dd-sdk-android-trace-api/src/main/kotlin/com/datadog/android/trace/api/DatadogTracingConstants.kt @@ -0,0 +1,229 @@ +/* + * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. + * This product includes software developed at Datadog (https://www.datadoghq.com/). + * Copyright 2016-Present Datadog, Inc. + */ +package com.datadog.android.trace.api + +/** + * Contains constants related to Datadog tracing. This object groups constants into several + * nested objects for better organization, such as [Tags], [PrioritySampling], + * [TracerConfig], [LogAttributes], and [ErrorPriorities]. + */ +object DatadogTracingConstants { + /** + * Represents the default value for enabling or disabling asynchronous context propagation + * in Datadog's tracing implementation. + */ + const val DEFAULT_ASYNC_PROPAGATING: Boolean = true + + /** + * Contains constants used for tagging spans in the Datadog tracer. + */ + object Tags { + /** The URL of the HTTP request. */ + const val KEY_HTTP_URL: String = "http.url" + + /** + * Represents the key used for tagging the kind of a span in the Datadog tracer. + * It is used to indicate the role or type of a span, such as client, server, producer, or consumer. + */ + const val KEY_SPAN_KIND: String = "span.kind" + + /** Indicates the desired action to be performed for a given resource. */ + const val KEY_HTTP_METHOD: String = "http.method" + + /** The HTTP response status code. */ + const val KEY_HTTP_STATUS: String = "http.status_code" + + /** String representing the error message. */ + const val KEY_ERROR_MSG: String = "error.msg" + + /** String representing the type of the error. */ + const val KEY_ERROR_TYPE: String = "error.type" + + /** Human readable version of the stack. */ + const val KEY_ERROR_STACK: String = "error.stack" + + /** + * Represents the value used for tagging a span as a "client" in the Datadog tracer with [KEY_SPAN_KIND] key. + */ + const val VALUE_SPAN_KIND_CLIENT: String = "client" + + /** + * Represents the value used for tagging a span as a "server" in the Datadog tracer with [KEY_SPAN_KIND] key. + */ + const val VALUE_SPAN_KIND_SERVER: String = "server" + + /** + * Represents the value used for tagging a span as a "producer" in the Datadog tracer. + */ + const val VALUE_SPAN_KIND_PRODUCER: String = "producer" + + /** + * Represents the value used for tagging a span as a "consumer" in the Datadog tracer. + */ + const val VALUE_SPAN_KIND_CONSUMER: String = "consumer" + + /** + * Represents the key used for tagging the analytics sample rate in the Datadog tracer. + */ + const val KEY_ANALYTICS_SAMPLE_RATE: String = "_dd1.sr.eausr" + + /** + * Represents the resource name tag used for tracing spans. + */ + const val RESOURCE_NAME: String = "resource.name" + + /** + * String representing the error message. + */ + const val ERROR_MSG: String = "error.message" + + /** + * String representing the type of the error. + */ + const val ERROR_TYPE: String = "error.type" + } + + /** + * [PrioritySampling] class defines the priority sampling decisions used + * for tracing purposes. These constants represent both system-driven + * and user-driven sampling decisions to manage trace propagation. + */ + object PrioritySampling { + /** + * Implementation detail of the client. will not be sent to the agent or propagated. + * + * + * Internal value used when the priority sampling flag has not been set on the span context. + */ + const val UNSET: Int = Int.MIN_VALUE + + /** The sampler has decided to drop the trace. */ + const val SAMPLER_DROP: Int = 0 + + /** The sampler has decided to keep the trace. */ + const val SAMPLER_KEEP: Int = 1 + + /** The user has decided to drop the trace. */ + const val USER_DROP: Int = -1 + + /** The user has decided to keep the trace. */ + const val USER_KEEP: Int = 2 + } + + /** + * Configuration constants for the [com.datadog.android.trace.api.tracer.DatadogTracer] implementation. + * These constants define the keys used to configure various tracer settings using [java.util.Properties]. + */ + object TracerConfig { + /** The configuration key used to set span-level tags in the tracer. */ + const val SPAN_TAGS: String = "trace.span.tags" + + /** Configuration key for setting the trace rate limit in the tracer. */ + const val TRACE_RATE_LIMIT: String = "trace.rate.limit" + + /** + * A configuration key used to set the minimum number of spans + * required to trigger a partial flush of trace data. + */ + const val PARTIAL_FLUSH_MIN_SPANS: String = "trace.partial.flush.min.spans" + + /** The configuration key used to set the trace sample rate for the tracer.*/ + const val TRACE_SAMPLE_RATE: String = "trace.sample.rate" + + /**Constant used to specify the propagation style for extracting context headers during distributed tracing. */ + const val PROPAGATION_STYLE_EXTRACT: String = "propagation.style.extract" + + /** Defines the property key for configuring the propagation style during the injection phase. */ + const val PROPAGATION_STYLE_INJECT: String = "propagation.style.inject" + + /** + * A constant key used to retrieve or set the service name configuration + * in the tracer's properties or context. + */ + const val SERVICE_NAME: String = "service.name" + + /** + * A constant flag used to toggle compatibility with SDK version 2 for the tracer. + * + * When this flag is enabled (set to "true"), the SDK adjusts its behavior of a sampler factory, + * making it same sampler as in SDK v2.*.*. + * + * This flag only being used by OTel tracer implementation and disabled by default. + */ + const val SDK_V2_COMPATIBILITY_FLAG: String = "v2.compatibility.enabled" + + /** + * A constant representing the configuration key for enabling or disabling the rule + * that uses URLs as resource names in tracing. + */ + const val URL_AS_RESOURCE_NAME: String = "trace.URLAsResourceNameRule.enabled" + + /** + * A constant representing the property key used for setting custom global tracer tags. + */ + const val TAGS: String = "tags" + } + + /** + * Defines a set of constant log attributes used for tracing and error reporting within the Datadog tracing library. + */ + object LogAttributes { + /** + * The type or "kind" of an error (only for event="error" logs). E.g., "Exception", "OSError" + */ + const val ERROR_KIND: String = "error.kind" + + /** + * The actual Throwable/Exception/Error object instance itself. + * E.g., a [UnsupportedOperationException] instance. + */ + const val ERROR_OBJECT: String = "error.object" + + /** + * A stable identifier for some notable moment in the lifetime of a Span. For instance, a mutex + * lock acquisition or release or the sorts of lifetime events in a browser page load described + * in the Performance.timing specification. E.g., from Zipkin, "cs", "sr", "ss", or "cr". Or, + * more generally, "initialized" or "timed out". For errors, "error" + */ + const val EVENT: String = "event" + + /** + * A concise, human-readable, one-line message explaining the event. E.g., "Could not connect + * to backend", "Cache invalidation succeeded" + */ + const val MESSAGE: String = "message" + + /** + * A stack trace in platform-conventional format; may or may not pertain to an error. + */ + const val STACK: String = "stack" + + /** + * Represents the key for a log attribute used to specify the status of a log entry. + */ + const val STATUS: String = "status" + } + + /** + * Defines constants representing the priorities of error recording in the Datadog tracing library. + */ + object ErrorPriorities { + /** + * Represents the unset value for error prioritization within the Datadog tracing system. + */ + const val UNSET: Byte = Byte.MIN_VALUE + + /** + * Represents the HTTP server decorator value. + */ + const val HTTP_SERVER_DECORATOR: Byte = -1 + + /** + * Represents the default value used in contexts where a specific value is not provided. + */ + const val DEFAULT: Byte = 0 + } +} diff --git a/features/dd-sdk-android-trace-api/src/main/kotlin/com/datadog/android/trace/api/propagation/DatadogPropagation.kt b/features/dd-sdk-android-trace-api/src/main/kotlin/com/datadog/android/trace/api/propagation/DatadogPropagation.kt new file mode 100644 index 0000000000..9bb3c369f9 --- /dev/null +++ b/features/dd-sdk-android-trace-api/src/main/kotlin/com/datadog/android/trace/api/propagation/DatadogPropagation.kt @@ -0,0 +1,50 @@ +/* + * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. + * This product includes software developed at Datadog (https://www.datadoghq.com/). + * Copyright 2016-Present Datadog, Inc. + */ +package com.datadog.android.trace.api.propagation + +import com.datadog.android.trace.api.span.DatadogSpanContext + +/** + * Provides an interface for injecting and extracting span context to/from specified carriers. + * Used for propagating context to http headers + */ +interface DatadogPropagation { + + /** + * Injects the span context into a specified carrier using the provided setter function. + * This method facilitates the propagation of trace information across process boundaries + * or systems by embedding relevant span context data into the carrier. + * + * @param C The type of the carrier containing the span context information. + * @param context The [DatadogSpanContext] containing the trace and span information to inject. + * @param carrier The carrier object where the trace information will be injected. + * @param setter A function used to set key-value pairs into the carrier. It takes the carrier, + * a key, and a value as parameters. + */ + fun inject( + context: DatadogSpanContext, + carrier: C, + setter: (carrier: C, key: String, value: String) -> Unit + ) + + /** + * Extracts a [DatadogSpanContext] from the provided carrier using the specified getter function. + * This method is used to propagate Datadog span context information across systems by retrieving + * trace and span information from the carrier. + * + * @param C The type of the carrier containing the span context information. + * @param carrier The carrier object from which the span context will be extracted. + * @param getter A function that retrieves key-value pairs from the carrier, using a classifier function + * to match desired key-value pairs. + * The classifier function takes two strings (key and value) and returns a boolean + * indicating whether the key-value pair belongs to the span context. + * @return The extracted [DatadogSpanContext] if one could be retrieved, or null otherwise. + */ + fun extract( + carrier: C, + getter: (carrier: C, classifier: (String, String) -> Boolean) -> Unit + ): DatadogSpanContext? +} diff --git a/features/dd-sdk-android-trace-api/src/main/kotlin/com/datadog/android/trace/api/propagation/NoOpDatadogPropagation.kt b/features/dd-sdk-android-trace-api/src/main/kotlin/com/datadog/android/trace/api/propagation/NoOpDatadogPropagation.kt new file mode 100644 index 0000000000..a90bd24e52 --- /dev/null +++ b/features/dd-sdk-android-trace-api/src/main/kotlin/com/datadog/android/trace/api/propagation/NoOpDatadogPropagation.kt @@ -0,0 +1,28 @@ +/* + * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. + * This product includes software developed at Datadog (https://www.datadoghq.com/). + * Copyright 2016-Present Datadog, Inc. + */ +package com.datadog.android.trace.api.propagation + +import com.datadog.android.trace.api.span.DatadogSpanContext + +/** + * A no-operation implementation of the [DatadogPropagation] interface. + * + * This implementation is intended as a placeholder making possible to create other NoOp.* classes. + */ +// TODO RUM-10573 - replace with @NoOpImplementation when method-level generics will be supported in noopfactory +class NoOpDatadogPropagation : DatadogPropagation { + + override fun inject( + context: DatadogSpanContext, + carrier: C, + setter: (carrier: C, key: String, value: String) -> Unit + ) = Unit // Do nothing + + override fun extract( + carrier: C, + getter: (carrier: C, classifier: (String, String) -> Boolean) -> Unit + ): DatadogSpanContext? = null +} diff --git a/features/dd-sdk-android-trace-api/src/main/kotlin/com/datadog/android/trace/api/scope/DatadogScope.kt b/features/dd-sdk-android-trace-api/src/main/kotlin/com/datadog/android/trace/api/scope/DatadogScope.kt new file mode 100644 index 0000000000..9257ce70e4 --- /dev/null +++ b/features/dd-sdk-android-trace-api/src/main/kotlin/com/datadog/android/trace/api/scope/DatadogScope.kt @@ -0,0 +1,22 @@ +/* + * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. + * This product includes software developed at Datadog (https://www.datadoghq.com/). + * Copyright 2016-Present Datadog, Inc. + */ +package com.datadog.android.trace.api.scope + +import com.datadog.android.trace.api.span.DatadogSpan +import com.datadog.tools.annotation.NoOpImplementation +import java.io.Closeable + +/** + * A DatadogScope formalizes the activation and deactivation of a [DatadogSpan]. + */ +@NoOpImplementation +interface DatadogScope : Closeable { + + /** + * Mark the end of the active period for the current context. + */ + override fun close() +} diff --git a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/legacy/trace/context/ScopeListener.java b/features/dd-sdk-android-trace-api/src/main/kotlin/com/datadog/android/trace/api/scope/DatadogScopeListener.java similarity index 78% rename from features/dd-sdk-android-trace-internal/src/main/java/com/datadog/legacy/trace/context/ScopeListener.java rename to features/dd-sdk-android-trace-api/src/main/kotlin/com/datadog/android/trace/api/scope/DatadogScopeListener.java index b62b061d34..ce4a3028db 100644 --- a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/legacy/trace/context/ScopeListener.java +++ b/features/dd-sdk-android-trace-api/src/main/kotlin/com/datadog/android/trace/api/scope/DatadogScopeListener.java @@ -4,13 +4,12 @@ * Copyright 2016-Present Datadog, Inc. */ -package com.datadog.legacy.trace.context; +package com.datadog.android.trace.api.scope; /** Hooks for scope activation */ -public interface ScopeListener { +public interface DatadogScopeListener { /** - * Called just after a scope becomes the active scope - * + * Called just after a scope becomes the active scope. *

May be called multiple times. When a scope is initially created, or after a child scope is * deactivated. */ diff --git a/features/dd-sdk-android-trace-api/src/main/kotlin/com/datadog/android/trace/api/span/DatadogSpan.kt b/features/dd-sdk-android-trace-api/src/main/kotlin/com/datadog/android/trace/api/span/DatadogSpan.kt new file mode 100644 index 0000000000..a37874d192 --- /dev/null +++ b/features/dd-sdk-android-trace-api/src/main/kotlin/com/datadog/android/trace/api/span/DatadogSpan.kt @@ -0,0 +1,204 @@ +/* + * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. + * This product includes software developed at Datadog (https://www.datadoghq.com/). + * Copyright 2016-Present Datadog, Inc. + */ +package com.datadog.android.trace.api.span + +import com.datadog.android.trace.api.trace.DatadogTraceId +import com.datadog.tools.annotation.NoOpImplementation + +/** + * Represents an interface for a Datadog span, which encapsulates information about a single span within a trace. + * This includes span metadata, error handling, timing, and tag/metric management. + */ +@NoOpImplementation +@SuppressWarnings("TooManyFunctions") +interface DatadogSpan { + /** + * Indicates whether the current span is marked as an error. + */ + var isError: Boolean? + + /** + * Indicates whether the current span is the root span in a trace. + */ + val isRootSpan: Boolean + + /** + * Represents the sampling priority of the span, which determines if the span should be + * included or excluded from trace collection and analysis. + */ + val samplingPriority: Int? + + /** + * Represents the unique identifier for a trace in the Datadog tracing system. + */ + val traceId: DatadogTraceId + + /** + * Represents the parent span's unique identifier in a trace. + */ + val parentSpanId: Long? + + /** + * Represents the name of the resource associated with the span. + */ + var resourceName: String? + + /** + * Defines the name of the service associated with this span. + */ + var serviceName: String + + /** + * The name of the operation represented by the span. + */ + var operationName: String + + /** + * Represents the duration of a span in nanoseconds. + */ + val durationNano: Long + + /** + * Represents the start time of the span in nanoseconds. + */ + val startTimeNanos: Long + + /** + * Refers to the local root span within a trace hierarchy. + */ + val localRootSpan: DatadogSpan? + + /** + * Retrieves the context associated with this Datadog span. + * + * @return The [DatadogSpanContext] containing trace and span-specific information such as identifiers, sampling priority, and tags. + */ + fun context(): DatadogSpanContext + + /** + * Marks the end of this span and captures its duration. + * This method should be called once the operation represented by the span + * is completed to ensure proper timing and resource tracking. + */ + fun finish() + + /** + * Marks the end of this span and sets its finish time. + * This method should be used when the operation represented by the span is completed, + * and the exact finish timestamp in microseconds needs to be explicitly provided. + * + * @param finishMicros The finish time of the span, provided in microseconds since the epoch. + */ + fun finish(finishMicros: Long) + + /** + * Marks the current span for removal, indicating it should no longer be processed or considered active. + * This method can be used to discard spans that are no longer relevant or should not be reported. + */ + fun drop() + + /** + * Sets the error message for the current span. This message provides a description + * of the error that occurred during the span's operation. + * + * @param message The error message to be associated with the span. + */ + fun setErrorMessage(message: String?) + + /** + * Associates a throwable with the current span, marking it as an error + * and capturing the provided throwable for additional context. + * + * @param throwable The throwable to associate with the current span. + */ + fun addThrowable(throwable: Throwable) + + /** + * Associates a throwable with the current span, marking it as an error + * and capturing the provided throwable for additional context with a specified error priority. + * + * @param throwable The throwable to associate with the current span. + * @param errorPriority The priority level of the error, represented as a byte. + */ + fun addThrowable(throwable: Throwable, errorPriority: Byte) + + /** + * Associates a tag with the specified value for the current span. + * + * @param tag The name of the tag to associate with the span. + * @param value The value to associate with the specified tag. + */ + fun setTag(tag: String?, value: String?) + + /** + * Associates a tag with a boolean value for the current span. + * + * @param tag The name of the tag to associate with the span. + * @param value The boolean value to associate with the specified tag. + */ + fun setTag(tag: String?, value: Boolean) + + /** + * Associates a tag with a numerical value for the current span. + * + * @param tag The name of the tag to associate with the span. + * @param value The numerical value to associate with the specified tag. + */ + fun setTag(tag: String?, value: Number?) + + /** + * Associates a tag with a specified value for the current span. + * + * @param tag The name of the tag to associate with the span. + * @param value The value to associate with the specified tag. + */ + fun setTag(tag: String?, value: Any?) + + /** + * Retrieves the value associated with the specified tag for the current span. + * + * @param tag The name of the tag whose value is to be retrieved. + * @return The value associated with the specified tag. + */ + fun getTag(tag: String?): Any? + + /** + * Sets a metric for the current span with the specified key and value. + * + * @param key The name of the metric to be associated with the span. + * @param value The value of the metric to be set for the specified key. + */ + fun setMetric(key: String, value: Int) + + /** + * Logs a throwable and associates it with the current [DatadogSpan]. + * + * @param throwable The throwable containing error details to be logged with the span. + */ + fun logThrowable(throwable: Throwable) + + /** + * Logs an error message and associates it with the current [DatadogSpan]. + * + * @param message The error message to log. + */ + fun logErrorMessage(message: String) + + /** + * Logs a message associated with the current [DatadogSpan]. + * + * @param message The log message to be associated with the span. + */ + fun logMessage(message: String) + + /** + * Logs a set of attributes and associates them with the current [DatadogSpan]. + * + * @param attributes A map containing key-value pairs of attributes to be logged. + * These attributes provide additional context or metadata for the span. + */ + fun logAttributes(attributes: Map) +} diff --git a/features/dd-sdk-android-trace-api/src/main/kotlin/com/datadog/android/trace/api/span/DatadogSpanBuilder.kt b/features/dd-sdk-android-trace-api/src/main/kotlin/com/datadog/android/trace/api/span/DatadogSpanBuilder.kt new file mode 100644 index 0000000000..1458ebb19f --- /dev/null +++ b/features/dd-sdk-android-trace-api/src/main/kotlin/com/datadog/android/trace/api/span/DatadogSpanBuilder.kt @@ -0,0 +1,120 @@ +/* + * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. + * This product includes software developed at Datadog (https://www.datadoghq.com/). + * Copyright 2016-Present Datadog, Inc. + */ +package com.datadog.android.trace.api.span + +import com.datadog.tools.annotation.NoOpImplementation + +/** + * Builder interface for creating instances of [DatadogSpan]. + * Provides methods to configure various attributes and context of a Datadog span + * before initializing it. + */ +@NoOpImplementation +@SuppressWarnings("TooManyFunctions") +interface DatadogSpanBuilder { + + /** + * Builds and starts a new span with the current configuration of the builder. + * + * @return The newly created instance of [DatadogSpan]. + */ + fun start(): DatadogSpan + + /** + * Specifies the origin of the span. + * The origin provides metadata about the context or source where the span was created. + * + * @param origin The origin string to associate with the span. Can be nullable. + * @return The current instance of [DatadogSpanBuilder] to allow method chaining. + */ + fun withOrigin(origin: String?): DatadogSpanBuilder + + /** + * Adds a tag to the span with the specified key and a nullable double value. + * Tags are used to add contextual information to spans, which can later be + * used for filtering, grouping, or analyzing spans in trace data. + * + * @param key The key identifying the tag. Cannot be null. + * @param value The value associated with the tag. Can be null. + * @return The current instance of [DatadogSpanBuilder] to allow method chaining. + */ + fun withTag(key: String, value: Double?): DatadogSpanBuilder + + /** + * Adds a tag to the span with the specified key and a nullable long value. + * Tags are used to add contextual information to spans, which can later be + * used for filtering, grouping, or analyzing spans in trace data. + * + * @param key The key identifying the tag. Must not be null. + * @param value The nullable long value associated with the tag. + * @return The current instance of [DatadogSpanBuilder] to allow method chaining. + */ + fun withTag(key: String, value: Long?): DatadogSpanBuilder + + /** + * Adds a tag to the span with the specified key and a nullable value of any type. + * Tags are used to add contextual information to spans, which can later be + * used for filtering, grouping, or analyzing spans in trace data. + * + * @param key The key identifying the tag. Cannot be null. + * @param value The nullable value associated with the tag. + * @return The current instance of [DatadogSpanBuilder] to allow method chaining. + */ + fun withTag(key: String, value: Any?): DatadogSpanBuilder + + /** + * Specifies the resource name of the span.The resource name is used to describe + * the operation or endpoint associated with the span. + * + * @param resourceName The resource name to associate with the span. + * @return The current instance of [DatadogSpanBuilder] to allow method chaining. + */ + fun withResourceName(resourceName: String?): DatadogSpanBuilder + + /** + * Sets the parent context for the span being built. Allowing to link related operations together. + * + * @param parentContext The parent context to associate with the span. Can be null. + * @return The current instance of [DatadogSpanBuilder] to enable method chaining. + */ + fun withParentContext(parentContext: DatadogSpanContext?): DatadogSpanBuilder + + /** + * Sets the parent span for the span being built. The default implementation uses parentSpan only for + * context retrieval. + * + * @param parentSpan The parent span to associate with the span being built. Can be null. + * @return The current instance of [DatadogSpanBuilder] to enable method chaining. + */ + fun withParentSpan(parentSpan: DatadogSpan?): DatadogSpanBuilder + + /** + * Sets the start timestamp for the span being built, specified in microseconds. + * This value determines the starting point of the span in the overall trace timeline. + * + * @param micros The start timestamp in microseconds. + * @return The current instance of [DatadogSpanBuilder] to allow method chaining. + */ + fun withStartTimestamp(micros: Long): DatadogSpanBuilder + + /** + * Prevents builder from using current active span as a parent context. + * + * @return The current instance of [DatadogSpanBuilder] to allow method chaining. + */ + fun ignoreActiveSpan(): DatadogSpanBuilder + + /** + * Adds a link to another span in the context of distributed tracing. + * This allows associating the current span with an external span, + * enabling relationship tracking across traces. + * + * @param link The link to be associated with the span. Represents a relationship + * to another span in a different or the same trace. + * @return The current instance of [DatadogSpanBuilder] to allow method chaining. + */ + fun withLink(link: DatadogSpanLink): DatadogSpanBuilder +} diff --git a/features/dd-sdk-android-trace-api/src/main/kotlin/com/datadog/android/trace/api/span/DatadogSpanContext.kt b/features/dd-sdk-android-trace-api/src/main/kotlin/com/datadog/android/trace/api/span/DatadogSpanContext.kt new file mode 100644 index 0000000000..681db1f5c1 --- /dev/null +++ b/features/dd-sdk-android-trace-api/src/main/kotlin/com/datadog/android/trace/api/span/DatadogSpanContext.kt @@ -0,0 +1,51 @@ +/* + * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. + * This product includes software developed at Datadog (https://www.datadoghq.com/). + * Copyright 2016-Present Datadog, Inc. + */ +package com.datadog.android.trace.api.span + +import com.datadog.android.trace.api.trace.DatadogTraceId +import com.datadog.tools.annotation.NoOpImplementation + +/** + * [DatadogSpanContext] represents span state that must propagate to descendant spans and across process boundaries. + */ +@NoOpImplementation +interface DatadogSpanContext { + /** + * Represents the unique identifier for a Datadog trace. + */ + val traceId: DatadogTraceId + + /** + * Represents the unique identifier for a Datadog span. + */ + val spanId: Long + + /** + * Represents the sampling priority value for a span. + */ + val samplingPriority: Int + + /** + * Represents a collection of tags associated with the span. + */ + val tags: Map + + /** + * Sets the sampling priority for the span. + * + * @param samplingPriority The sampling priority value to be set for the span. + * @return True if the sampling priority was successfully set, false otherwise. + */ + fun setSamplingPriority(samplingPriority: Int): Boolean + + /** + * Sets a numerical metric associated with the span. + * + * @param key The name of the metric to be set. + * @param value The numerical value of the metric to be associated with the specified key. + */ + fun setMetric(key: CharSequence?, value: Double) +} diff --git a/features/dd-sdk-android-trace-api/src/main/kotlin/com/datadog/android/trace/api/span/DatadogSpanLink.kt b/features/dd-sdk-android-trace-api/src/main/kotlin/com/datadog/android/trace/api/span/DatadogSpanLink.kt new file mode 100644 index 0000000000..9bffcae3cb --- /dev/null +++ b/features/dd-sdk-android-trace-api/src/main/kotlin/com/datadog/android/trace/api/span/DatadogSpanLink.kt @@ -0,0 +1,41 @@ +/* + * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. + * This product includes software developed at Datadog (https://www.datadoghq.com/). + * Copyright 2016-Present Datadog, Inc. + */ +package com.datadog.android.trace.api.span + +import com.datadog.android.trace.api.trace.DatadogTraceId +import com.datadog.tools.annotation.NoOpImplementation + +/** + * Represents a link to a Datadog span, which contains metadata and identifiers for associating a span + * with its trace and related details. + */ +@NoOpImplementation +interface DatadogSpanLink { + /** + * The unique identifier for a Datadog span. + */ + val spanId: Long + + /** + * Indicates whether the Datadog span has been sampled. + */ + val sampled: Boolean + + /** + * The unique identifier for a Datadog trace. + */ + val traceId: DatadogTraceId + + /** + * Represents a string representation of a specific trace related to the Datadog span. + */ + val traceStrace: String + + /** + * A map containing key-value pairs of additional attributes associated with a Datadog span. + */ + val attributes: Map? +} diff --git a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/opentracing/jfr/DDScopeEvent.java b/features/dd-sdk-android-trace-api/src/main/kotlin/com/datadog/android/trace/api/span/DatadogSpanWriter.kt similarity index 53% rename from features/dd-sdk-android-trace-internal/src/main/java/com/datadog/opentracing/jfr/DDScopeEvent.java rename to features/dd-sdk-android-trace-api/src/main/kotlin/com/datadog/android/trace/api/span/DatadogSpanWriter.kt index 202330b4e0..5e04084700 100644 --- a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/opentracing/jfr/DDScopeEvent.java +++ b/features/dd-sdk-android-trace-api/src/main/kotlin/com/datadog/android/trace/api/span/DatadogSpanWriter.kt @@ -3,13 +3,10 @@ * This product includes software developed at Datadog (https://www.datadoghq.com/). * Copyright 2016-Present Datadog, Inc. */ +package com.datadog.android.trace.api.span -package com.datadog.opentracing.jfr; - -/** Scope event */ -public interface DDScopeEvent { - - void start(); - - void finish(); -} +/** + * A writer is responsible for sending collected spans. + * This wrapper is required to not expose CoreTracer wrapper to dependent modules + * */ +interface DatadogSpanWriter diff --git a/features/dd-sdk-android-trace-api/src/main/kotlin/com/datadog/android/trace/api/trace/DatadogTraceId.kt b/features/dd-sdk-android-trace-api/src/main/kotlin/com/datadog/android/trace/api/trace/DatadogTraceId.kt new file mode 100644 index 0000000000..67b480c3de --- /dev/null +++ b/features/dd-sdk-android-trace-api/src/main/kotlin/com/datadog/android/trace/api/trace/DatadogTraceId.kt @@ -0,0 +1,39 @@ +/* + * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. + * This product includes software developed at Datadog (https://www.datadoghq.com/). + * Copyright 2016-Present Datadog, Inc. + */ +package com.datadog.android.trace.api.trace + +import com.datadog.tools.annotation.NoOpImplementation + +/** + * Represents a Datadog trace ID, which is a unique identifier for a specific trace. + */ +@NoOpImplementation +interface DatadogTraceId { + /** + * Returns the lower-case zero-padded hexadecimal String representation of the trace ID. + * The size will be rounded up to 16 or 32 characters. + * + * @param size The size in characters of the zero-padded String (rounded up to 16 or 32) + * @return A lower-case zero-padded hexadecimal string representation of this trace ID + */ + fun toHexStringPadded(size: Int): String + + /** + * Converts the current trace ID into its hexadecimal string representation. + * + * @return the hexadecimal string representation of the trace ID. + */ + fun toHexString(): String + + /** + * Converts the Datadog trace ID to its numeric representation as a `Long`. + * + * @return the numeric value of the trace ID as a `Long`. + */ + fun toLong(): Long + + companion object +} diff --git a/features/dd-sdk-android-trace-api/src/main/kotlin/com/datadog/android/trace/api/tracer/DatadogTracer.kt b/features/dd-sdk-android-trace-api/src/main/kotlin/com/datadog/android/trace/api/tracer/DatadogTracer.kt new file mode 100644 index 0000000000..31bfb3ee29 --- /dev/null +++ b/features/dd-sdk-android-trace-api/src/main/kotlin/com/datadog/android/trace/api/tracer/DatadogTracer.kt @@ -0,0 +1,80 @@ +/* + * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. + * This product includes software developed at Datadog (https://www.datadoghq.com/). + * Copyright 2016-Present Datadog, Inc. + */ +package com.datadog.android.trace.api.tracer + +import com.datadog.android.trace.api.propagation.DatadogPropagation +import com.datadog.android.trace.api.propagation.NoOpDatadogPropagation +import com.datadog.android.trace.api.scope.DatadogScope +import com.datadog.android.trace.api.scope.DatadogScopeListener +import com.datadog.android.trace.api.span.DatadogSpan +import com.datadog.android.trace.api.span.DatadogSpanBuilder +import com.datadog.tools.annotation.NoOpImplementation + +/** + * [DatadogTracer] is a simple, thin interface for span creation and propagation across arbitrary transports. + */ +@NoOpImplementation(publicNoOpImplementation = true) +interface DatadogTracer { + /** + * Retrieves the currently active span in the context of the tracer. + * + * @return the active [DatadogSpan]. + */ + fun activeSpan(): DatadogSpan? + + /** + * Provides an implementation of the [DatadogPropagation] interface to be used for span propagation. + * + * @return An instance of [DatadogPropagation] + */ + fun propagate(): DatadogPropagation = NoOpDatadogPropagation() + + /** + * Activates the provided span within the current context of the tracer. + * Once activated, the span becomes the currently active span, and any operations + * requiring an active span will use this one until it is explicitly deactivated. + * + * @param span The span to be activated. Represents the logical unit of work being traced. + * @return An instance of [DatadogScope] representing the activated scope. + */ + fun activateSpan(span: DatadogSpan): DatadogScope? + + /** + * Activates the provided span within the current context of the tracer. + * If `asyncPropagating` is set to true, the span is propagated asynchronously. + * Once activated, the span becomes the currently active span until it is explicitly deactivated. + * + * @param span The span to be activated. Represents the logical unit of work being traced. + * @param asyncPropagating If true, this context will propagate across async boundaries. + * @return An instance of [DatadogScope] representing the activated scope. + */ + fun activateSpan(span: DatadogSpan, asyncPropagating: Boolean): DatadogScope? + + /** + * Creates a new span builder instance with the specified span name. + * + * @param spanName The name of the span to be built. Represents the operation being performed. + * @return An instance of [DatadogSpanBuilder] to allow further configuration of the span. + */ + fun buildSpan(spanName: CharSequence): DatadogSpanBuilder + + /** + * Creates a new span builder instance with the specified instrumentation and span names. + * + * @param instrumentationName The name of the instrumentation associated with the span. + * @param spanName The name of the span to be built. Represents the operation being performed. + * @return An instance of [DatadogSpanBuilder] to allow further configuration of the span. + */ + fun buildSpan(instrumentationName: String, spanName: CharSequence): DatadogSpanBuilder + + /** + * Adds a listener to be notified when a scope is activated or closed. + * + * @param scopeListener The listener to be added. It defines the actions + * to be executed after a scope is activated or closed. + */ + fun addScopeListener(scopeListener: DatadogScopeListener) +} diff --git a/features/dd-sdk-android-trace-api/src/main/kotlin/com/datadog/android/trace/api/tracer/DatadogTracerBuilder.kt b/features/dd-sdk-android-trace-api/src/main/kotlin/com/datadog/android/trace/api/tracer/DatadogTracerBuilder.kt new file mode 100644 index 0000000000..9d87becf9a --- /dev/null +++ b/features/dd-sdk-android-trace-api/src/main/kotlin/com/datadog/android/trace/api/tracer/DatadogTracerBuilder.kt @@ -0,0 +1,86 @@ +/* + * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. + * This product includes software developed at Datadog (https://www.datadoghq.com/). + * Copyright 2016-Present Datadog, Inc. + */ +package com.datadog.android.trace.api.tracer + +import androidx.annotation.FloatRange +import androidx.annotation.IntRange +import com.datadog.android.trace.TracingHeaderType +import com.datadog.tools.annotation.NoOpImplementation + +/** + * Builder interface for creating and configuring a [DatadogTracer] instance. + */ +@NoOpImplementation(publicNoOpImplementation = true) +@SuppressWarnings("TooManyFunctions") +interface DatadogTracerBuilder { + /** + * Constructs and returns an instance of [DatadogTracer] based on the current builder configuration. + * + * @return An instance of [DatadogTracer] configured with the options provided in the builder. + */ + fun build(): DatadogTracer + + /** + * Configures the builder with the specified set of tracing header types. Tracing headers + * define the standards used for propagating trace information across different components. + * + * @param tracingHeadersTypes A set of [TracingHeaderType] values representing the tracing + * headers to be used. + * @return The updated instance of [DatadogTracerBuilder] to allow method chaining. + */ + fun withTracingHeadersTypes(tracingHeadersTypes: Set): DatadogTracerBuilder + + /** + * Sets the service name for the tracer. The service name is a key identifier + * associated with the traces generated by the tracer, typically representing + * the application or microservice being traced. + * + * @param serviceName The name of the service to be associated with the tracer. + * @return The updated instance of [DatadogTracerBuilder] to allow method chaining. + */ + fun withServiceName(serviceName: String): DatadogTracerBuilder + + /** + * Sets the sample rate of spans. + * @param sampleRate the sample rate as a percentage between 0 and 100 (default is 100%) + */ + fun withSampleRate(@FloatRange(from = 0.0, to = 100.0) sampleRate: Double): DatadogTracerBuilder + + /** + * Configures the builder to enable partial flushes when the number of spans in a specific trace + * reaches the given threshold. + * + * @param partialFlushMinSpans The minimum number of spans required to trigger a partial flush. + * @return The updated instance of [DatadogTracerBuilder] to allow method chaining. + */ + fun withPartialFlushMinSpans(partialFlushMinSpans: Int): DatadogTracerBuilder + + /** + * Adds a global tag which will be appended to all spans created with the built tracer. + * @param key the tag key + * @param value the tag value + */ + fun withTag(key: String, value: String): DatadogTracerBuilder + + /** + * Enables the trace bundling with the current active RUM View. If this feature is enabled all + * the spans from this moment on will be bundled with the current view information and you + * will be able to see all the traces sent during a specific view in the RUM Explorer. + * @param enabled true by default + */ + fun setBundleWithRumEnabled(enabled: Boolean): DatadogTracerBuilder + + /** + * Sets the trace rate limit. This is the maximum number of traces per second that will be + * accepted. Please note that this property is used in conjunction with the sample rate. If no sample rate + * is provided this property and its related logic will be ignored. + * + * @param traceRateLimit the trace rate limit as a value between 1 and Int.MAX_VALUE (default is Int.MAX_VALUE) + */ + fun setTraceRateLimit( + @IntRange(from = 1, to = Int.MAX_VALUE.toLong()) traceRateLimit: Int + ): DatadogTracerBuilder +} diff --git a/features/dd-sdk-android-trace-api/transitiveDependencies b/features/dd-sdk-android-trace-api/transitiveDependencies new file mode 100644 index 0000000000..998404520a --- /dev/null +++ b/features/dd-sdk-android-trace-api/transitiveDependencies @@ -0,0 +1,11 @@ +Dependencies List + +androidx.annotation:annotation-jvm:1.9.1 : 59 Kb +com.google.code.gson:gson:2.10.1 : 276 Kb +com.google.re2j:re2j:1.7 : 111 Kb +org.jctools:jctools-core:3.3.0 : 328 Kb +org.jetbrains.kotlin:kotlin-stdlib:2.0.21 : 1706 Kb +org.jetbrains:annotations:13.0 : 17 Kb + +Total transitive dependencies size : 2 Mb + diff --git a/features/dd-sdk-android-trace-internal/api/dd-sdk-android-trace-internal.api b/features/dd-sdk-android-trace-internal/api/dd-sdk-android-trace-internal.api index 5b3dcf2d2c..e25c877f5c 100644 --- a/features/dd-sdk-android-trace-internal/api/dd-sdk-android-trace-internal.api +++ b/features/dd-sdk-android-trace-internal/api/dd-sdk-android-trace-internal.api @@ -28,789 +28,6 @@ public final class com/datadog/exec/DaemonThreadFactory : java/util/concurrent/T public fun newThread (Ljava/lang/Runnable;)Ljava/lang/Thread; } -public class com/datadog/legacy/trace/api/Config { - public static final field AGENT_HOST Ljava/lang/String; - public static final field AGENT_PORT_LEGACY Ljava/lang/String; - public static final field AGENT_UNIX_DOMAIN_SOCKET Ljava/lang/String; - public static final field API_KEY Ljava/lang/String; - public static final field API_KEY_FILE Ljava/lang/String; - public static final field CONFIGURATION_FILE Ljava/lang/String; - public static final field DB_CLIENT_HOST_SPLIT_BY_INSTANCE Ljava/lang/String; - public static final field DD_AGENT_WRITER_TYPE Ljava/lang/String; - public static final field DEFAULT_AGENT_HOST Ljava/lang/String; - public static final field DEFAULT_AGENT_UNIX_DOMAIN_SOCKET Ljava/lang/String; - public static final field DEFAULT_ANALYTICS_SAMPLE_RATE F - public static final field DEFAULT_INTEGRATIONS_ENABLED Z - public static final field DEFAULT_JMX_FETCH_STATSD_PORT I - public static final field DEFAULT_LOGS_INJECTION_ENABLED Z - public static final field DEFAULT_METRICS_ENABLED Z - public static final field DEFAULT_PROFILING_ENABLED Z - public static final field DEFAULT_PROFILING_EXCEPTION_HISTOGRAM_MAX_COLLECTION_SIZE I - public static final field DEFAULT_PROFILING_EXCEPTION_HISTOGRAM_TOP_ITEMS I - public static final field DEFAULT_PROFILING_EXCEPTION_SAMPLE_LIMIT I - public static final field DEFAULT_PROFILING_PROXY_PORT I - public static final field DEFAULT_PROFILING_START_DELAY I - public static final field DEFAULT_PROFILING_START_FORCE_FIRST Z - public static final field DEFAULT_PROFILING_UPLOAD_COMPRESSION Ljava/lang/String; - public static final field DEFAULT_PROFILING_UPLOAD_PERIOD I - public static final field DEFAULT_PROFILING_UPLOAD_TIMEOUT I - public static final field DEFAULT_SERVICE_NAME Ljava/lang/String; - public static final field DEFAULT_SITE Ljava/lang/String; - public static final field DEFAULT_TRACE_AGENT_PORT I - public static final field DEFAULT_TRACE_ANALYTICS_ENABLED Z - public static final field DEFAULT_TRACE_RATE_LIMIT D - public static final field GLOBAL_TAGS Ljava/lang/String; - public static final field HEADER_TAGS Ljava/lang/String; - public static final field HEALTH_METRICS_ENABLED Ljava/lang/String; - public static final field HEALTH_METRICS_STATSD_HOST Ljava/lang/String; - public static final field HEALTH_METRICS_STATSD_PORT Ljava/lang/String; - public static final field HOST_TAG Ljava/lang/String; - public static final field HTTP_CLIENT_ERROR_STATUSES Ljava/lang/String; - public static final field HTTP_CLIENT_HOST_SPLIT_BY_DOMAIN Ljava/lang/String; - public static final field HTTP_CLIENT_TAG_QUERY_STRING Ljava/lang/String; - public static final field HTTP_SERVER_ERROR_STATUSES Ljava/lang/String; - public static final field HTTP_SERVER_TAG_QUERY_STRING Ljava/lang/String; - public static final field INTEGRATIONS_ENABLED Ljava/lang/String; - public static final field JMX_FETCH_CHECK_PERIOD Ljava/lang/String; - public static final field JMX_FETCH_CONFIG Ljava/lang/String; - public static final field JMX_FETCH_CONFIG_DIR Ljava/lang/String; - public static final field JMX_FETCH_ENABLED Ljava/lang/String; - public static final field JMX_FETCH_METRICS_CONFIGS Ljava/lang/String; - public static final field JMX_FETCH_REFRESH_BEANS_PERIOD Ljava/lang/String; - public static final field JMX_FETCH_STATSD_HOST Ljava/lang/String; - public static final field JMX_FETCH_STATSD_PORT Ljava/lang/String; - public static final field JMX_TAGS Ljava/lang/String; - public static final field LANGUAGE_TAG_KEY Ljava/lang/String; - public static final field LANGUAGE_TAG_VALUE Ljava/lang/String; - public static final field LOGGING_WRITER_TYPE Ljava/lang/String; - public static final field LOGS_INJECTION_ENABLED Ljava/lang/String; - public static final field PARTIAL_FLUSH_MIN_SPANS Ljava/lang/String; - public static final field PRIORITY_SAMPLING Ljava/lang/String; - public static final field PROFILING_API_KEY_FILE_OLD Ljava/lang/String; - public static final field PROFILING_API_KEY_FILE_VERY_OLD Ljava/lang/String; - public static final field PROFILING_API_KEY_OLD Ljava/lang/String; - public static final field PROFILING_API_KEY_VERY_OLD Ljava/lang/String; - public static final field PROFILING_ENABLED Ljava/lang/String; - public static final field PROFILING_EXCEPTION_HISTOGRAM_MAX_COLLECTION_SIZE Ljava/lang/String; - public static final field PROFILING_EXCEPTION_HISTOGRAM_TOP_ITEMS Ljava/lang/String; - public static final field PROFILING_EXCEPTION_SAMPLE_LIMIT Ljava/lang/String; - public static final field PROFILING_PROXY_HOST Ljava/lang/String; - public static final field PROFILING_PROXY_PASSWORD Ljava/lang/String; - public static final field PROFILING_PROXY_PORT Ljava/lang/String; - public static final field PROFILING_PROXY_USERNAME Ljava/lang/String; - public static final field PROFILING_START_DELAY Ljava/lang/String; - public static final field PROFILING_START_FORCE_FIRST Ljava/lang/String; - public static final field PROFILING_TAGS Ljava/lang/String; - public static final field PROFILING_TEMPLATE_OVERRIDE_FILE Ljava/lang/String; - public static final field PROFILING_UPLOAD_COMPRESSION Ljava/lang/String; - public static final field PROFILING_UPLOAD_PERIOD Ljava/lang/String; - public static final field PROFILING_UPLOAD_TIMEOUT Ljava/lang/String; - public static final field PROFILING_URL Ljava/lang/String; - public static final field PROFILING_URL_TEMPLATE Ljava/lang/String; - public static final field PROPAGATION_STYLE_EXTRACT Ljava/lang/String; - public static final field PROPAGATION_STYLE_INJECT Ljava/lang/String; - public static final field RUNTIME_CONTEXT_FIELD_INJECTION Ljava/lang/String; - public static final field RUNTIME_ID_TAG Ljava/lang/String; - public static final field SCOPE_DEPTH_LIMIT Ljava/lang/String; - public static final field SERVICE Ljava/lang/String; - public static final field SERVICE_MAPPING Ljava/lang/String; - public static final field SERVICE_NAME Ljava/lang/String; - public static final field SERVICE_TAG Ljava/lang/String; - public static final field SITE Ljava/lang/String; - public static final field SPAN_TAGS Ljava/lang/String; - public static final field SPLIT_BY_TAGS Ljava/lang/String; - public static final field TAGS Ljava/lang/String; - public static final field TRACE_AGENT_PORT Ljava/lang/String; - public static final field TRACE_ANALYTICS_ENABLED Ljava/lang/String; - public static final field TRACE_ANNOTATIONS Ljava/lang/String; - public static final field TRACE_CLASSES_EXCLUDE Ljava/lang/String; - public static final field TRACE_ENABLED Ljava/lang/String; - public static final field TRACE_EXECUTORS Ljava/lang/String; - public static final field TRACE_EXECUTORS_ALL Ljava/lang/String; - public static final field TRACE_METHODS Ljava/lang/String; - public static final field TRACE_RATE_LIMIT Ljava/lang/String; - public static final field TRACE_REPORT_HOSTNAME Ljava/lang/String; - public static final field TRACE_RESOLVER_ENABLED Ljava/lang/String; - public static final field TRACE_SAMPLE_RATE Ljava/lang/String; - public static final field TRACE_SAMPLING_OPERATION_RULES Ljava/lang/String; - public static final field TRACE_SAMPLING_SERVICE_RULES Ljava/lang/String; - public static final field WRITER_TYPE Ljava/lang/String; - public static fun get ()Lcom/datadog/legacy/trace/api/Config; - public static fun get (Ljava/util/Properties;)Lcom/datadog/legacy/trace/api/Config; - public fun getAgentHost ()Ljava/lang/String; - public fun getAgentPort ()I - public fun getAgentUnixDomainSocket ()Ljava/lang/String; - public static fun getBooleanSettingFromEnvironment (Ljava/lang/String;Ljava/lang/Boolean;)Ljava/lang/Boolean; - public fun getExcludedClasses ()Ljava/util/List; - public fun getFinalProfilingUrl ()Ljava/lang/String; - public static fun getFloatSettingFromEnvironment (Ljava/lang/String;Ljava/lang/Float;)Ljava/lang/Float; - public fun getHeaderTags ()Ljava/util/Map; - public fun getHealthMetricsStatsdHost ()Ljava/lang/String; - public fun getHealthMetricsStatsdPort ()Ljava/lang/Integer; - public fun getHttpClientErrorStatuses ()Ljava/util/Set; - public fun getHttpServerErrorStatuses ()Ljava/util/Set; - public fun getInstrumentationAnalyticsSampleRate ([Ljava/lang/String;)F - public fun getJmxFetchCheckPeriod ()Ljava/lang/Integer; - public fun getJmxFetchConfigDir ()Ljava/lang/String; - public fun getJmxFetchConfigs ()Ljava/util/List; - public fun getJmxFetchMetricsConfigs ()Ljava/util/List; - public fun getJmxFetchRefreshBeansPeriod ()Ljava/lang/Integer; - public fun getJmxFetchStatsdHost ()Ljava/lang/String; - public fun getJmxFetchStatsdPort ()Ljava/lang/Integer; - public fun getLocalRootSpanTags ()Ljava/util/Map; - public fun getMergedJmxTags ()Ljava/util/Map; - public fun getMergedProfilingTags ()Ljava/util/Map; - public fun getMergedSpanTags ()Ljava/util/Map; - public fun getPartialFlushMinSpans ()Ljava/lang/Integer; - public fun getProfilingExceptionHistogramMaxCollectionSize ()I - public fun getProfilingExceptionHistogramTopItems ()I - public fun getProfilingExceptionSampleLimit ()I - public fun getProfilingProxyHost ()Ljava/lang/String; - public fun getProfilingProxyPassword ()Ljava/lang/String; - public fun getProfilingProxyPort ()I - public fun getProfilingProxyUsername ()Ljava/lang/String; - public fun getProfilingStartDelay ()I - public fun getProfilingTemplateOverrideFile ()Ljava/lang/String; - public fun getProfilingUploadCompression ()Ljava/lang/String; - public fun getProfilingUploadPeriod ()I - public fun getProfilingUploadTimeout ()I - public fun getPropagationStylesToExtract ()Ljava/util/Set; - public fun getPropagationStylesToInject ()Ljava/util/Set; - public fun getRuntimeId ()Ljava/lang/String; - public fun getScopeDepthLimit ()Ljava/lang/Integer; - public fun getServiceMapping ()Ljava/util/Map; - public fun getServiceName ()Ljava/lang/String; - public static fun getSettingFromEnvironment (Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String; - public fun getSite ()Ljava/lang/String; - public fun getSplitByTags ()Ljava/util/Set; - public fun getTraceAnnotations ()Ljava/lang/String; - public fun getTraceExecutors ()Ljava/util/List; - public fun getTraceMethods ()Ljava/lang/String; - public fun getTraceRateLimit ()Ljava/lang/Double; - public fun getTraceSampleRate ()Ljava/lang/Double; - public fun getTraceSamplingOperationRules ()Ljava/util/Map; - public fun getTraceSamplingServiceRules ()Ljava/util/Map; - public fun getWriterType ()Ljava/lang/String; - public fun isDbClientSplitByInstance ()Z - public fun isHealthMetricsEnabled ()Z - public fun isHttpClientSplitByDomain ()Z - public fun isHttpClientTagQueryString ()Z - public fun isHttpServerTagQueryString ()Z - public fun isIntegrationEnabled (Ljava/util/SortedSet;Z)Z - public fun isIntegrationsEnabled ()Z - public fun isJmxFetchEnabled ()Z - public fun isJmxFetchIntegrationEnabled (Ljava/util/SortedSet;Z)Z - public fun isLogsInjectionEnabled ()Z - public fun isPrioritySamplingEnabled ()Z - public fun isProfilingEnabled ()Z - public fun isProfilingStartForceFirst ()Z - public fun isReportHostName ()Z - public fun isRuleEnabled (Ljava/lang/String;)Z - public fun isRuntimeContextFieldInjection ()Z - public fun isTraceAnalyticsEnabled ()Z - public fun isTraceAnalyticsIntegrationEnabled (Ljava/util/SortedSet;Z)Z - public fun isTraceEnabled ()Z - public fun isTraceExecutorsAll ()Z - public fun isTraceResolverEnabled ()Z - public static fun jmxFetchIntegrationEnabled (Ljava/util/SortedSet;Z)Z - public fun toString ()Ljava/lang/String; - public static fun traceAnalyticsIntegrationEnabled (Ljava/util/SortedSet;Z)Z -} - -public final class com/datadog/legacy/trace/api/Config$PropagationStyle : java/lang/Enum { - public static final field B3 Lcom/datadog/legacy/trace/api/Config$PropagationStyle; - public static final field B3MULTI Lcom/datadog/legacy/trace/api/Config$PropagationStyle; - public static final field DATADOG Lcom/datadog/legacy/trace/api/Config$PropagationStyle; - public static final field HAYSTACK Lcom/datadog/legacy/trace/api/Config$PropagationStyle; - public static final field TRACECONTEXT Lcom/datadog/legacy/trace/api/Config$PropagationStyle; - public static fun valueOf (Ljava/lang/String;)Lcom/datadog/legacy/trace/api/Config$PropagationStyle; - public static fun values ()[Lcom/datadog/legacy/trace/api/Config$PropagationStyle; -} - -public class com/datadog/legacy/trace/api/DDSpanTypes { - public static final field CACHE Ljava/lang/String; - public static final field CASSANDRA Ljava/lang/String; - public static final field COUCHBASE Ljava/lang/String; - public static final field ELASTICSEARCH Ljava/lang/String; - public static final field HIBERNATE Ljava/lang/String; - public static final field HTTP_CLIENT Ljava/lang/String; - public static final field HTTP_SERVER Ljava/lang/String; - public static final field MEMCACHED Ljava/lang/String; - public static final field MESSAGE_CLIENT Ljava/lang/String; - public static final field MESSAGE_CONSUMER Ljava/lang/String; - public static final field MESSAGE_PRODUCER Ljava/lang/String; - public static final field MONGO Ljava/lang/String; - public static final field REDIS Ljava/lang/String; - public static final field RPC Ljava/lang/String; - public static final field SQL Ljava/lang/String; - public static final field WEB_SERVLET Ljava/lang/String; - public fun ()V -} - -public class com/datadog/legacy/trace/api/DDTags { - public static final field ANALYTICS_SAMPLE_RATE Ljava/lang/String; - public static final field DB_STATEMENT Ljava/lang/String; - public static final field ERROR_MSG Ljava/lang/String; - public static final field ERROR_STACK Ljava/lang/String; - public static final field ERROR_TYPE Ljava/lang/String; - public static final field EVENT_SAMPLE_RATE Ljava/lang/String; - public static final field HTTP_FRAGMENT Ljava/lang/String; - public static final field HTTP_QUERY Ljava/lang/String; - public static final field MANUAL_DROP Ljava/lang/String; - public static final field MANUAL_KEEP Ljava/lang/String; - public static final field RESOURCE_NAME Ljava/lang/String; - public static final field SERVICE_NAME Ljava/lang/String; - public static final field SPAN_TYPE Ljava/lang/String; - public static final field THREAD_ID Ljava/lang/String; - public static final field THREAD_NAME Ljava/lang/String; - public static final field USER_NAME Ljava/lang/String; - public fun ()V -} - -public class com/datadog/legacy/trace/api/DDTraceApiInfo { - public static final field VERSION Ljava/lang/String; - public fun ()V - public static fun main ([Ljava/lang/String;)V -} - -public abstract interface annotation class com/datadog/legacy/trace/api/Trace : java/lang/annotation/Annotation { - public abstract fun operationName ()Ljava/lang/String; - public abstract fun resourceName ()Ljava/lang/String; -} - -public abstract interface class com/datadog/legacy/trace/api/Tracer { - public abstract fun addScopeListener (Lcom/datadog/legacy/trace/context/ScopeListener;)V - public abstract fun addTraceInterceptor (Lcom/datadog/legacy/trace/api/interceptor/TraceInterceptor;)Z - public abstract fun getSpanId ()Ljava/lang/String; - public abstract fun getTraceId ()Ljava/lang/String; -} - -public abstract interface class com/datadog/legacy/trace/api/interceptor/MutableSpan { - public abstract fun drop ()V - public abstract fun getDurationNano ()J - public abstract fun getLocalRootSpan ()Lcom/datadog/legacy/trace/api/interceptor/MutableSpan; - public abstract fun getOperationName ()Ljava/lang/String; - public abstract fun getResourceName ()Ljava/lang/String; - public abstract fun getRootSpan ()Lcom/datadog/legacy/trace/api/interceptor/MutableSpan; - public abstract fun getSamplingPriority ()Ljava/lang/Integer; - public abstract fun getServiceName ()Ljava/lang/String; - public abstract fun getSpanType ()Ljava/lang/String; - public abstract fun getStartTime ()J - public abstract fun getTags ()Ljava/util/Map; - public abstract fun isError ()Ljava/lang/Boolean; - public abstract fun setError (Z)Lcom/datadog/legacy/trace/api/interceptor/MutableSpan; - public abstract fun setOperationName (Ljava/lang/String;)Lcom/datadog/legacy/trace/api/interceptor/MutableSpan; - public abstract fun setResourceName (Ljava/lang/String;)Lcom/datadog/legacy/trace/api/interceptor/MutableSpan; - public abstract fun setSamplingPriority (I)Lcom/datadog/legacy/trace/api/interceptor/MutableSpan; - public abstract fun setServiceName (Ljava/lang/String;)Lcom/datadog/legacy/trace/api/interceptor/MutableSpan; - public abstract fun setSpanType (Ljava/lang/String;)Lcom/datadog/legacy/trace/api/interceptor/MutableSpan; - public abstract fun setTag (Ljava/lang/String;Ljava/lang/Number;)Lcom/datadog/legacy/trace/api/interceptor/MutableSpan; - public abstract fun setTag (Ljava/lang/String;Ljava/lang/String;)Lcom/datadog/legacy/trace/api/interceptor/MutableSpan; - public abstract fun setTag (Ljava/lang/String;Z)Lcom/datadog/legacy/trace/api/interceptor/MutableSpan; -} - -public abstract interface class com/datadog/legacy/trace/api/interceptor/TraceInterceptor { - public abstract fun onTraceComplete (Ljava/util/Collection;)Ljava/util/Collection; - public abstract fun priority ()I -} - -public class com/datadog/legacy/trace/api/sampling/PrioritySampling { - public static final field SAMPLER_DROP I - public static final field SAMPLER_KEEP I - public static final field UNSET I - public static final field USER_DROP I - public static final field USER_KEEP I -} - -public abstract class com/datadog/legacy/trace/common/sampling/AbstractSampler : com/datadog/legacy/trace/common/sampling/Sampler { - protected field skipTagsPatterns Ljava/util/Map; - public fun ()V - public fun addSkipTagPattern (Ljava/lang/String;Ljava/util/regex/Pattern;)V - protected abstract fun doSample (Lcom/datadog/opentracing/DDSpan;)Z - public fun sample (Lcom/datadog/opentracing/DDSpan;)Z -} - -public class com/datadog/legacy/trace/common/sampling/AllSampler : com/datadog/legacy/trace/common/sampling/AbstractSampler { - public fun ()V - public fun doSample (Lcom/datadog/opentracing/DDSpan;)Z - public fun toString ()Ljava/lang/String; -} - -public class com/datadog/legacy/trace/common/sampling/DeterministicSampler : com/datadog/legacy/trace/common/sampling/RateSampler { - public fun (D)V - public fun getSampleRate ()D - public fun sample (Lcom/datadog/opentracing/DDSpan;)Z -} - -public abstract interface class com/datadog/legacy/trace/common/sampling/PrioritySampler { - public abstract fun setSamplingPriority (Lcom/datadog/opentracing/DDSpan;)V -} - -public class com/datadog/legacy/trace/common/sampling/PrioritySampling { - public static final field SAMPLER_DROP I - public static final field SAMPLER_KEEP I - public static final field UNSET I - public static final field USER_DROP I - public static final field USER_KEEP I -} - -public class com/datadog/legacy/trace/common/sampling/RateByServiceSampler : com/datadog/legacy/trace/common/sampling/PrioritySampler, com/datadog/legacy/trace/common/sampling/Sampler { - public static final field SAMPLING_AGENT_RATE Ljava/lang/String; - public fun ()V - public fun (Ljava/lang/Double;)V - public fun sample (Lcom/datadog/opentracing/DDSpan;)Z - public fun setSamplingPriority (Lcom/datadog/opentracing/DDSpan;)V -} - -public abstract interface class com/datadog/legacy/trace/common/sampling/RateSampler : com/datadog/legacy/trace/common/sampling/Sampler { - public abstract fun getSampleRate ()D -} - -public abstract interface class com/datadog/legacy/trace/common/sampling/Sampler { - public abstract fun sample (Lcom/datadog/opentracing/DDSpan;)Z -} - -public final class com/datadog/legacy/trace/common/sampling/Sampler$Builder { - public static fun forConfig (Lcom/datadog/legacy/trace/api/Config;)Lcom/datadog/legacy/trace/common/sampling/Sampler; - public static fun forConfig (Ljava/util/Properties;)Lcom/datadog/legacy/trace/common/sampling/Sampler; -} - -public abstract class com/datadog/legacy/trace/common/sampling/SamplingRule { - public fun (Lcom/datadog/legacy/trace/common/sampling/RateSampler;)V - public fun getSampler ()Lcom/datadog/legacy/trace/common/sampling/RateSampler; - public abstract fun matches (Lcom/datadog/opentracing/DDSpan;)Z - public fun sample (Lcom/datadog/opentracing/DDSpan;)Z -} - -public class com/datadog/legacy/trace/common/sampling/SamplingRule$AlwaysMatchesSamplingRule : com/datadog/legacy/trace/common/sampling/SamplingRule { - public fun (Lcom/datadog/legacy/trace/common/sampling/RateSampler;)V - public fun matches (Lcom/datadog/opentracing/DDSpan;)Z -} - -public class com/datadog/legacy/trace/common/sampling/SamplingRule$OperationSamplingRule : com/datadog/legacy/trace/common/sampling/SamplingRule$PatternMatchSamplingRule { - public fun (Ljava/lang/String;Lcom/datadog/legacy/trace/common/sampling/RateSampler;)V - protected fun getRelevantString (Lcom/datadog/opentracing/DDSpan;)Ljava/lang/String; -} - -public abstract class com/datadog/legacy/trace/common/sampling/SamplingRule$PatternMatchSamplingRule : com/datadog/legacy/trace/common/sampling/SamplingRule { - public fun (Ljava/lang/String;Lcom/datadog/legacy/trace/common/sampling/RateSampler;)V - protected abstract fun getRelevantString (Lcom/datadog/opentracing/DDSpan;)Ljava/lang/String; - public fun matches (Lcom/datadog/opentracing/DDSpan;)Z -} - -public class com/datadog/legacy/trace/common/sampling/SamplingRule$ServiceSamplingRule : com/datadog/legacy/trace/common/sampling/SamplingRule$PatternMatchSamplingRule { - public fun (Ljava/lang/String;Lcom/datadog/legacy/trace/common/sampling/RateSampler;)V - protected fun getRelevantString (Lcom/datadog/opentracing/DDSpan;)Ljava/lang/String; -} - -public class com/datadog/legacy/trace/common/util/Clock { - public fun ()V - public static fun currentMicroTime ()J - public static fun currentNanoTicks ()J - public static fun currentNanoTime ()J -} - -public class com/datadog/legacy/trace/common/writer/LoggingWriter : com/datadog/legacy/trace/common/writer/Writer { - public fun ()V - public fun close ()V - public fun incrementTraceCount ()V - public fun start ()V - public fun toString ()Ljava/lang/String; - public fun write (Ljava/util/List;)V -} - -public abstract interface class com/datadog/legacy/trace/common/writer/Writer : java/io/Closeable { - public abstract fun close ()V - public abstract fun incrementTraceCount ()V - public abstract fun start ()V - public abstract fun write (Ljava/util/List;)V -} - -public abstract interface class com/datadog/legacy/trace/context/ScopeListener { - public abstract fun afterScopeActivated ()V - public abstract fun afterScopeClosed ()V -} - -public abstract interface class com/datadog/legacy/trace/context/TraceScope : java/io/Closeable { - public abstract fun capture ()Lcom/datadog/legacy/trace/context/TraceScope$Continuation; - public abstract fun close ()V - public abstract fun isAsyncPropagating ()Z - public abstract fun setAsyncPropagation (Z)V -} - -public abstract interface class com/datadog/legacy/trace/context/TraceScope$Continuation { - public abstract fun activate ()Lcom/datadog/legacy/trace/context/TraceScope; - public abstract fun close ()V - public abstract fun close (Z)V -} - -public class com/datadog/opentracing/DDSpan : com/datadog/legacy/trace/api/interceptor/MutableSpan, io/opentracing/Span { - public final fun context ()Lcom/datadog/opentracing/DDSpanContext; - public synthetic fun context ()Lio/opentracing/SpanContext; - public final fun drop ()V - public final fun finish ()V - public final fun finish (J)V - public final fun getBaggageItem (Ljava/lang/String;)Ljava/lang/String; - public fun getDurationNano ()J - public fun getError ()I - public fun getLocalRootSpan ()Lcom/datadog/legacy/trace/api/interceptor/MutableSpan; - public fun getMeta ()Ljava/util/Map; - public fun getMetrics ()Ljava/util/Map; - public fun getOperationName ()Ljava/lang/String; - public fun getParentId ()Ljava/math/BigInteger; - public fun getResourceName ()Ljava/lang/String; - public fun getRootSpan ()Lcom/datadog/legacy/trace/api/interceptor/MutableSpan; - public fun getSamplingPriority ()Ljava/lang/Integer; - public fun getServiceName ()Ljava/lang/String; - public fun getSpanId ()Ljava/math/BigInteger; - public fun getSpanType ()Ljava/lang/String; - public fun getStartTime ()J - public fun getTags ()Ljava/util/Map; - public fun getTraceId ()Ljava/math/BigInteger; - public fun getType ()Ljava/lang/String; - public fun isError ()Ljava/lang/Boolean; - public fun isFinished ()Z - public final fun isRootSpan ()Z - public final fun log (JLjava/lang/String;)Lcom/datadog/opentracing/DDSpan; - public synthetic fun log (JLjava/lang/String;)Lio/opentracing/Span; - public final fun log (JLjava/util/Map;)Lcom/datadog/opentracing/DDSpan; - public synthetic fun log (JLjava/util/Map;)Lio/opentracing/Span; - public final fun log (Ljava/lang/String;)Lcom/datadog/opentracing/DDSpan; - public synthetic fun log (Ljava/lang/String;)Lio/opentracing/Span; - public final fun log (Ljava/util/Map;)Lcom/datadog/opentracing/DDSpan; - public synthetic fun log (Ljava/util/Map;)Lio/opentracing/Span; - public final fun setBaggageItem (Ljava/lang/String;Ljava/lang/String;)Lcom/datadog/opentracing/DDSpan; - public synthetic fun setBaggageItem (Ljava/lang/String;Ljava/lang/String;)Lio/opentracing/Span; - public synthetic fun setError (Z)Lcom/datadog/legacy/trace/api/interceptor/MutableSpan; - public fun setError (Z)Lcom/datadog/opentracing/DDSpan; - public fun setErrorMeta (Ljava/lang/Throwable;)V - public synthetic fun setOperationName (Ljava/lang/String;)Lcom/datadog/legacy/trace/api/interceptor/MutableSpan; - public final fun setOperationName (Ljava/lang/String;)Lcom/datadog/opentracing/DDSpan; - public synthetic fun setOperationName (Ljava/lang/String;)Lio/opentracing/Span; - public synthetic fun setResourceName (Ljava/lang/String;)Lcom/datadog/legacy/trace/api/interceptor/MutableSpan; - public final fun setResourceName (Ljava/lang/String;)Lcom/datadog/opentracing/DDSpan; - public synthetic fun setSamplingPriority (I)Lcom/datadog/legacy/trace/api/interceptor/MutableSpan; - public final fun setSamplingPriority (I)Lcom/datadog/opentracing/DDSpan; - public synthetic fun setServiceName (Ljava/lang/String;)Lcom/datadog/legacy/trace/api/interceptor/MutableSpan; - public final fun setServiceName (Ljava/lang/String;)Lcom/datadog/opentracing/DDSpan; - public synthetic fun setSpanType (Ljava/lang/String;)Lcom/datadog/legacy/trace/api/interceptor/MutableSpan; - public final fun setSpanType (Ljava/lang/String;)Lcom/datadog/opentracing/DDSpan; - public fun setTag (Lio/opentracing/tag/Tag;Ljava/lang/Object;)Lio/opentracing/Span; - public synthetic fun setTag (Ljava/lang/String;Ljava/lang/Number;)Lcom/datadog/legacy/trace/api/interceptor/MutableSpan; - public final fun setTag (Ljava/lang/String;Ljava/lang/Number;)Lcom/datadog/opentracing/DDSpan; - public synthetic fun setTag (Ljava/lang/String;Ljava/lang/Number;)Lio/opentracing/Span; - public synthetic fun setTag (Ljava/lang/String;Ljava/lang/String;)Lcom/datadog/legacy/trace/api/interceptor/MutableSpan; - public final fun setTag (Ljava/lang/String;Ljava/lang/String;)Lcom/datadog/opentracing/DDSpan; - public synthetic fun setTag (Ljava/lang/String;Ljava/lang/String;)Lio/opentracing/Span; - public synthetic fun setTag (Ljava/lang/String;Z)Lcom/datadog/legacy/trace/api/interceptor/MutableSpan; - public final fun setTag (Ljava/lang/String;Z)Lcom/datadog/opentracing/DDSpan; - public synthetic fun setTag (Ljava/lang/String;Z)Lio/opentracing/Span; - public fun toString ()Ljava/lang/String; -} - -public class com/datadog/opentracing/DDSpanContext : io/opentracing/SpanContext { - public static final field ORIGIN_KEY Ljava/lang/String; - public static final field PRIORITY_SAMPLING_KEY Ljava/lang/String; - public static final field SAMPLE_RATE_KEY Ljava/lang/String; - public fun (Ljava/math/BigInteger;Ljava/math/BigInteger;Ljava/math/BigInteger;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ILjava/lang/String;Ljava/util/Map;ZLjava/lang/String;Ljava/util/Map;Lcom/datadog/opentracing/PendingTrace;Lcom/datadog/opentracing/DDTracer;Ljava/util/Map;Lcom/datadog/android/api/InternalLogger;)V - public fun baggageItems ()Ljava/lang/Iterable; - public fun getBaggageItem (Ljava/lang/String;)Ljava/lang/String; - public fun getBaggageItems ()Ljava/util/Map; - public fun getErrorFlag ()Z - public fun getMetrics ()Ljava/util/Map; - public fun getOperationName ()Ljava/lang/String; - public fun getOrigin ()Ljava/lang/String; - public fun getParentId ()Ljava/math/BigInteger; - public fun getResourceName ()Ljava/lang/String; - public fun getSamplingPriority ()I - public fun getServiceName ()Ljava/lang/String; - public fun getSpanId ()Ljava/math/BigInteger; - public fun getSpanType ()Ljava/lang/String; - public fun getTags ()Ljava/util/Map; - public fun getTrace ()Lcom/datadog/opentracing/PendingTrace; - public fun getTraceId ()Ljava/math/BigInteger; - public fun getTracer ()Lcom/datadog/opentracing/DDTracer; - public fun hasResourceName ()Z - public fun isResourceNameSet ()Z - public fun lockSamplingPriority ()Z - public fun setBaggageItem (Ljava/lang/String;Ljava/lang/String;)V - public fun setErrorFlag (Z)V - public fun setMetric (Ljava/lang/String;Ljava/lang/Number;)V - public fun setOperationName (Ljava/lang/String;)V - public fun setResourceName (Ljava/lang/String;)V - public fun setSamplingPriority (I)Z - public fun setServiceName (Ljava/lang/String;)V - public fun setSpanType (Ljava/lang/String;)V - public fun setTag (Ljava/lang/String;Ljava/lang/Object;)V - public fun toSpanId ()Ljava/lang/String; - public fun toString ()Ljava/lang/String; - public fun toTraceId ()Ljava/lang/String; -} - -public class com/datadog/opentracing/DDTraceOTInfo { - public static final field JAVA_VERSION Ljava/lang/String; - public static final field JAVA_VM_NAME Ljava/lang/String; - public static final field JAVA_VM_VENDOR Ljava/lang/String; - public static final field VERSION Ljava/lang/String; - public fun ()V - public static fun main ([Ljava/lang/String;)V -} - -public class com/datadog/opentracing/DDTracer : com/datadog/legacy/trace/api/Tracer, io/opentracing/Tracer, java/io/Closeable { - public static final field TRACE_ID_128_BITS_MAX Ljava/math/BigInteger; - public static final field TRACE_ID_64_BITS_MAX Ljava/math/BigInteger; - public static final field TRACE_ID_MIN Ljava/math/BigInteger; - protected fun (Lcom/datadog/legacy/trace/api/Config;Lcom/datadog/legacy/trace/common/writer/Writer;Ljava/util/Random;)V - public fun activateSpan (Lio/opentracing/Span;)Lio/opentracing/Scope; - public fun activeSpan ()Lio/opentracing/Span; - public fun addDecorator (Lcom/datadog/opentracing/decorators/AbstractDecorator;)V - public fun addScopeContext (Lcom/datadog/opentracing/scopemanager/ScopeContext;)V - public fun addScopeListener (Lcom/datadog/legacy/trace/context/ScopeListener;)V - public fun addTraceInterceptor (Lcom/datadog/legacy/trace/api/interceptor/TraceInterceptor;)Z - public fun buildSpan (Ljava/lang/String;)Lio/opentracing/Tracer$SpanBuilder; - public fun close ()V - public fun extract (Lio/opentracing/propagation/Format;Ljava/lang/Object;)Lio/opentracing/SpanContext; - public fun finalize ()V - public fun getPartialFlushMinSpans ()I - public fun getSpanContextDecorators (Ljava/lang/String;)Ljava/util/List; - public fun getSpanId ()Ljava/lang/String; - public fun getTraceId ()Ljava/lang/String; - public fun inject (Lio/opentracing/SpanContext;Lio/opentracing/propagation/Format;Ljava/lang/Object;)V - public fun registerClassLoader (Ljava/lang/ClassLoader;)V - public fun scopeManager ()Lio/opentracing/ScopeManager; - public fun toString ()Ljava/lang/String; -} - -public class com/datadog/opentracing/DDTracer$DDSpanBuilder : io/opentracing/Tracer$SpanBuilder { - public fun (Lcom/datadog/opentracing/DDTracer;Ljava/lang/String;Lio/opentracing/ScopeManager;)V - public fun addReference (Ljava/lang/String;Lio/opentracing/SpanContext;)Lcom/datadog/opentracing/DDTracer$DDSpanBuilder; - public synthetic fun addReference (Ljava/lang/String;Lio/opentracing/SpanContext;)Lio/opentracing/Tracer$SpanBuilder; - public fun asChildOf (Lio/opentracing/Span;)Lcom/datadog/opentracing/DDTracer$DDSpanBuilder; - public synthetic fun asChildOf (Lio/opentracing/Span;)Lio/opentracing/Tracer$SpanBuilder; - public fun asChildOf (Lio/opentracing/SpanContext;)Lcom/datadog/opentracing/DDTracer$DDSpanBuilder; - public synthetic fun asChildOf (Lio/opentracing/SpanContext;)Lio/opentracing/Tracer$SpanBuilder; - public fun baggageItems ()Ljava/lang/Iterable; - public fun ignoreActiveSpan ()Lio/opentracing/Tracer$SpanBuilder; - public fun start ()Lio/opentracing/Span; - public fun startActive (Z)Lio/opentracing/Scope; - public fun startManual ()Lio/opentracing/Span; - public fun withErrorFlag ()Lcom/datadog/opentracing/DDTracer$DDSpanBuilder; - public fun withInternalLogger (Lcom/datadog/android/api/InternalLogger;)Lcom/datadog/opentracing/DDTracer$DDSpanBuilder; - public fun withLogHandler (Lcom/datadog/opentracing/LogHandler;)Lcom/datadog/opentracing/DDTracer$DDSpanBuilder; - public fun withOrigin (Ljava/lang/String;)Lcom/datadog/opentracing/DDTracer$DDSpanBuilder; - public fun withResourceName (Ljava/lang/String;)Lcom/datadog/opentracing/DDTracer$DDSpanBuilder; - public fun withServiceName (Ljava/lang/String;)Lcom/datadog/opentracing/DDTracer$DDSpanBuilder; - public fun withSpanType (Ljava/lang/String;)Lcom/datadog/opentracing/DDTracer$DDSpanBuilder; - public fun withStartTimestamp (J)Lcom/datadog/opentracing/DDTracer$DDSpanBuilder; - public synthetic fun withStartTimestamp (J)Lio/opentracing/Tracer$SpanBuilder; - public fun withTag (Lio/opentracing/tag/Tag;Ljava/lang/Object;)Lio/opentracing/Tracer$SpanBuilder; - public fun withTag (Ljava/lang/String;Ljava/lang/Number;)Lcom/datadog/opentracing/DDTracer$DDSpanBuilder; - public synthetic fun withTag (Ljava/lang/String;Ljava/lang/Number;)Lio/opentracing/Tracer$SpanBuilder; - public fun withTag (Ljava/lang/String;Ljava/lang/String;)Lcom/datadog/opentracing/DDTracer$DDSpanBuilder; - public synthetic fun withTag (Ljava/lang/String;Ljava/lang/String;)Lio/opentracing/Tracer$SpanBuilder; - public fun withTag (Ljava/lang/String;Z)Lcom/datadog/opentracing/DDTracer$DDSpanBuilder; - public synthetic fun withTag (Ljava/lang/String;Z)Lio/opentracing/Tracer$SpanBuilder; -} - -public class com/datadog/opentracing/DefaultLogHandler : com/datadog/opentracing/LogHandler { - public fun ()V - public fun log (JLjava/lang/String;Lcom/datadog/opentracing/DDSpan;)V - public fun log (JLjava/util/Map;Lcom/datadog/opentracing/DDSpan;)V - public fun log (Ljava/lang/String;Lcom/datadog/opentracing/DDSpan;)V - public fun log (Ljava/util/Map;Lcom/datadog/opentracing/DDSpan;)V -} - -public abstract interface class com/datadog/opentracing/LogHandler { - public abstract fun log (JLjava/lang/String;Lcom/datadog/opentracing/DDSpan;)V - public abstract fun log (JLjava/util/Map;Lcom/datadog/opentracing/DDSpan;)V - public abstract fun log (Ljava/lang/String;Lcom/datadog/opentracing/DDSpan;)V - public abstract fun log (Ljava/util/Map;Lcom/datadog/opentracing/DDSpan;)V -} - -public class com/datadog/opentracing/PendingTrace : java/util/LinkedList { - public fun addFirst (Lcom/datadog/opentracing/DDSpan;)V - public synthetic fun addFirst (Ljava/lang/Object;)V - public fun addSpan (Lcom/datadog/opentracing/DDSpan;)V - public fun cancelContinuation (Lcom/datadog/opentracing/scopemanager/ContinuableScope$Continuation;)V - public fun clean ()Z - public fun dropSpan (Lcom/datadog/opentracing/DDSpan;)V - public fun getCurrentTimeNano ()J - public fun getRootSpan ()Lcom/datadog/opentracing/DDSpan; - public fun registerContinuation (Lcom/datadog/opentracing/scopemanager/ContinuableScope$Continuation;)V - public fun registerSpan (Lcom/datadog/opentracing/DDSpan;)V - public fun size ()I -} - -public class com/datadog/opentracing/StringCachingBigInteger : java/math/BigInteger { - public fun (IILjava/util/Random;)V - public fun (ILjava/util/Random;)V - public fun (I[B)V - public fun (Ljava/lang/String;)V - public fun (Ljava/lang/String;I)V - public fun ([B)V - public fun equals (Ljava/lang/Object;)Z - public fun hashCode ()I - public fun toString ()Ljava/lang/String; -} - -public abstract class com/datadog/opentracing/decorators/AbstractDecorator { - public fun ()V - public fun getMatchingTag ()Ljava/lang/String; - public fun getMatchingValue ()Ljava/lang/Object; - public fun getReplacementTag ()Ljava/lang/String; - public fun getReplacementValue ()Ljava/lang/String; - public fun setMatchingTag (Ljava/lang/String;)V - public fun setMatchingValue (Ljava/lang/Object;)V - public fun setReplacementTag (Ljava/lang/String;)V - public fun setReplacementValue (Ljava/lang/String;)V - public fun shouldSetTag (Lcom/datadog/opentracing/DDSpanContext;Ljava/lang/String;Ljava/lang/Object;)Z -} - -public class com/datadog/opentracing/decorators/DDDecoratorsFactory { - public fun ()V - public static fun createBuiltinDecorators ()Ljava/util/List; -} - -public class com/datadog/opentracing/decorators/ForceManualDropDecorator : com/datadog/opentracing/decorators/AbstractDecorator { - public fun ()V - public fun shouldSetTag (Lcom/datadog/opentracing/DDSpanContext;Ljava/lang/String;Ljava/lang/Object;)Z -} - -public class com/datadog/opentracing/decorators/ForceManualKeepDecorator : com/datadog/opentracing/decorators/AbstractDecorator { - public fun ()V - public fun shouldSetTag (Lcom/datadog/opentracing/DDSpanContext;Ljava/lang/String;Ljava/lang/Object;)Z -} - -public class com/datadog/opentracing/decorators/PeerServiceDecorator : com/datadog/opentracing/decorators/AbstractDecorator { - public fun ()V - public fun shouldSetTag (Lcom/datadog/opentracing/DDSpanContext;Ljava/lang/String;Ljava/lang/Object;)Z -} - -public class com/datadog/opentracing/decorators/ServiceNameDecorator : com/datadog/opentracing/decorators/AbstractDecorator { - public fun ()V - public fun (Ljava/lang/String;Z)V - public fun shouldSetTag (Lcom/datadog/opentracing/DDSpanContext;Ljava/lang/String;Ljava/lang/Object;)Z -} - -public final class com/datadog/opentracing/jfr/DDNoopScopeEvent : com/datadog/opentracing/jfr/DDScopeEvent { - public static final field INSTANCE Lcom/datadog/opentracing/jfr/DDNoopScopeEvent; - public fun ()V - public fun finish ()V - public fun start ()V -} - -public final class com/datadog/opentracing/jfr/DDNoopScopeEventFactory : com/datadog/opentracing/jfr/DDScopeEventFactory { - public fun ()V - public fun create (Lcom/datadog/opentracing/DDSpanContext;)Lcom/datadog/opentracing/jfr/DDScopeEvent; -} - -public abstract interface class com/datadog/opentracing/jfr/DDScopeEvent { - public abstract fun finish ()V - public abstract fun start ()V -} - -public abstract interface class com/datadog/opentracing/jfr/DDScopeEventFactory { - public abstract fun create (Lcom/datadog/opentracing/DDSpanContext;)Lcom/datadog/opentracing/jfr/DDScopeEvent; -} - -public class com/datadog/opentracing/propagation/ExtractedContext : com/datadog/opentracing/propagation/TagContext { - public fun (Ljava/math/BigInteger;Ljava/math/BigInteger;ILjava/lang/String;Ljava/util/Map;Ljava/util/Map;)V - public fun baggageItems ()Ljava/lang/Iterable; - public fun getBaggage ()Ljava/util/Map; - public fun getSamplingPriority ()I - public fun getSamplingPriorityLocked ()Z - public fun getSpanId ()Ljava/math/BigInteger; - public fun getTraceId ()Ljava/math/BigInteger; - public fun lockSamplingPriority ()V -} - -public class com/datadog/opentracing/propagation/HaystackHttpCodec { -} - -public class com/datadog/opentracing/propagation/HaystackHttpCodec$Extractor : com/datadog/opentracing/propagation/HttpCodec$Extractor { - public fun (Ljava/util/Map;)V - public fun extract (Lio/opentracing/propagation/TextMapExtract;)Lio/opentracing/SpanContext; -} - -public class com/datadog/opentracing/propagation/HaystackHttpCodec$Injector : com/datadog/opentracing/propagation/HttpCodec$Injector { - public fun ()V - public fun inject (Lcom/datadog/opentracing/DDSpanContext;Lio/opentracing/propagation/TextMapInject;)V -} - -public class com/datadog/opentracing/propagation/HttpCodec { - public fun ()V - public static fun createExtractor (Lcom/datadog/legacy/trace/api/Config;Ljava/util/Map;)Lcom/datadog/opentracing/propagation/HttpCodec$Extractor; - public static fun createInjector (Lcom/datadog/legacy/trace/api/Config;)Lcom/datadog/opentracing/propagation/HttpCodec$Injector; -} - -public class com/datadog/opentracing/propagation/HttpCodec$CompoundExtractor : com/datadog/opentracing/propagation/HttpCodec$Extractor { - public fun (Ljava/util/List;)V - public fun extract (Lio/opentracing/propagation/TextMapExtract;)Lio/opentracing/SpanContext; -} - -public class com/datadog/opentracing/propagation/HttpCodec$CompoundInjector : com/datadog/opentracing/propagation/HttpCodec$Injector { - public fun (Ljava/util/List;)V - public fun inject (Lcom/datadog/opentracing/DDSpanContext;Lio/opentracing/propagation/TextMapInject;)V -} - -public abstract interface class com/datadog/opentracing/propagation/HttpCodec$Extractor { - public abstract fun extract (Lio/opentracing/propagation/TextMapExtract;)Lio/opentracing/SpanContext; -} - -public abstract interface class com/datadog/opentracing/propagation/HttpCodec$Injector { - public abstract fun inject (Lcom/datadog/opentracing/DDSpanContext;Lio/opentracing/propagation/TextMapInject;)V -} - -public class com/datadog/opentracing/propagation/TagContext : io/opentracing/SpanContext { - public fun (Ljava/lang/String;Ljava/util/Map;)V - public fun baggageItems ()Ljava/lang/Iterable; - public fun getOrigin ()Ljava/lang/String; - public fun getTags ()Ljava/util/Map; - public fun toSpanId ()Ljava/lang/String; - public fun toTraceId ()Ljava/lang/String; -} - -public class com/datadog/opentracing/scopemanager/ContextualScopeManager : io/opentracing/ScopeManager { - public fun (ILcom/datadog/opentracing/jfr/DDScopeEventFactory;)V - public fun activate (Lio/opentracing/Span;)Lio/opentracing/Scope; - public fun activate (Lio/opentracing/Span;Z)Lio/opentracing/Scope; - public fun active ()Lio/opentracing/Scope; - public fun activeSpan ()Lio/opentracing/Span; - public fun addScopeContext (Lcom/datadog/opentracing/scopemanager/ScopeContext;)V - public fun addScopeListener (Lcom/datadog/legacy/trace/context/ScopeListener;)V -} - -public class com/datadog/opentracing/scopemanager/ContinuableScope : com/datadog/legacy/trace/context/TraceScope, com/datadog/opentracing/scopemanager/DDScope { - public synthetic fun capture ()Lcom/datadog/legacy/trace/context/TraceScope$Continuation; - public fun capture ()Lcom/datadog/opentracing/scopemanager/ContinuableScope$Continuation; - public fun close ()V - public fun depth ()I - public fun isAsyncPropagating ()Z - public fun setAsyncPropagation (Z)V - public fun span ()Lcom/datadog/opentracing/DDSpan; - public synthetic fun span ()Lio/opentracing/Span; - public fun toString ()Ljava/lang/String; -} - -public class com/datadog/opentracing/scopemanager/ContinuableScope$Continuation : com/datadog/legacy/trace/context/TraceScope$Continuation, java/io/Closeable { - public field ref Ljava/lang/ref/WeakReference; - public synthetic fun activate ()Lcom/datadog/legacy/trace/context/TraceScope; - public fun activate ()Lcom/datadog/opentracing/scopemanager/ContinuableScope; - public fun close ()V - public fun close (Z)V -} - -public abstract interface class com/datadog/opentracing/scopemanager/ScopeContext : io/opentracing/ScopeManager { - public abstract fun inContext ()Z -} - -public class com/datadog/opentracing/scopemanager/SimpleScope : com/datadog/opentracing/scopemanager/DDScope { - public fun (Lcom/datadog/opentracing/scopemanager/ContextualScopeManager;Lio/opentracing/Span;Z)V - public fun close ()V - public fun depth ()I - public fun span ()Lio/opentracing/Span; -} - public class com/datadog/trace/api/Config { public static final field PREFIX Ljava/lang/String; public static fun get ()Lcom/datadog/trace/api/Config; @@ -834,8 +51,6 @@ public class com/datadog/trace/api/Config { public fun getPrimaryTag ()Ljava/lang/String; public fun getPrioritySamplingForce ()Ljava/lang/String; public fun getProcessId ()Ljava/lang/Long; - public fun getPropagationStylesToExtract ()Ljava/util/Set; - public fun getPropagationStylesToInject ()Ljava/util/Set; public fun getRequestHeaderTags ()Ljava/util/Map; public fun getResponseHeaderTags ()Ljava/util/Map; public fun getRootContextServiceName ()Ljava/lang/String; @@ -892,6 +107,7 @@ public class com/datadog/trace/api/Config { public fun isTracePropagationExtractFirst ()Z public fun isTracePropagationStyleB3PaddingEnabled ()Z public fun isTraceStrictWritesEnabled ()Z + public fun isV2CompatibilityEnabled ()Z public static fun jmxFetchIntegrationEnabled (Ljava/util/SortedSet;Z)Z public fun toString ()Ljava/lang/String; public static fun traceAnalyticsIntegrationEnabled (Ljava/util/SortedSet;Z)Z @@ -976,6 +192,7 @@ public final class com/datadog/trace/api/DDSpanId { public static fun from (Ljava/lang/String;)J public static fun fromHex (Ljava/lang/String;)J public static fun fromHex (Ljava/lang/String;IIZ)J + public static fun fromHexOrDefault (Ljava/lang/String;J)J public static fun toHexString (J)Ljava/lang/String; public static fun toHexStringPadded (J)Ljava/lang/String; public static fun toString (J)Ljava/lang/String; @@ -1071,6 +288,7 @@ public abstract class com/datadog/trace/api/DDTraceId { public static fun from (J)Lcom/datadog/trace/api/DDTraceId; public static fun from (Ljava/lang/String;)Lcom/datadog/trace/api/DDTraceId; public static fun fromHex (Ljava/lang/String;)Lcom/datadog/trace/api/DDTraceId; + public static fun fromHexOrDefault (Ljava/lang/String;Lcom/datadog/trace/api/DDTraceId;)Lcom/datadog/trace/api/DDTraceId; public abstract fun toHexString ()Ljava/lang/String; public abstract fun toHexStringPadded (I)Ljava/lang/String; public abstract fun toHighOrderLong ()J @@ -1137,10 +355,6 @@ public abstract interface class com/datadog/trace/api/EndpointTracker { public abstract fun endpointWritten (Lcom/datadog/trace/bootstrap/instrumentation/api/AgentSpan;)V } -public class com/datadog/trace/api/EventTracker { - public static final field NO_EVENT_TRACKER Lcom/datadog/trace/api/EventTracker; -} - public final class com/datadog/trace/api/Functions { public static final field UTF8_ENCODE Lcom/datadog/android/trace/internal/compat/function/Function; } @@ -1152,14 +366,6 @@ public final class com/datadog/trace/api/Functions$LowerCase : com/datadog/andro public fun apply (Ljava/lang/String;)Ljava/lang/String; } -public class com/datadog/trace/api/GlobalTracer { - public fun ()V - public static fun forceRegister (Lcom/datadog/trace/api/Tracer;)V - public static fun get ()Lcom/datadog/trace/api/Tracer; - public static fun getEventTracker ()Lcom/datadog/trace/api/EventTracker; - public static fun registerIfAbsent (Lcom/datadog/trace/api/Tracer;)V -} - public abstract class com/datadog/trace/api/IdGenerationStrategy { protected final field traceId128BitGenerationEnabled Z public static fun fromName (Ljava/lang/String;)Lcom/datadog/trace/api/IdGenerationStrategy; @@ -1260,17 +466,6 @@ public final class com/datadog/trace/api/Platform$GC : java/lang/Enum { public static fun values ()[Lcom/datadog/trace/api/Platform$GC; } -public final class com/datadog/trace/api/PropagationStyle : java/lang/Enum { - public static final field B3 Lcom/datadog/trace/api/PropagationStyle; - public static final field DATADOG Lcom/datadog/trace/api/PropagationStyle; - public static final field HAYSTACK Lcom/datadog/trace/api/PropagationStyle; - public static final field XRAY Lcom/datadog/trace/api/PropagationStyle; - public fun getNewStyles ()Ljava/util/List; - public static fun valueOf (Ljava/lang/String;)Lcom/datadog/trace/api/PropagationStyle; - public static fun valueOfConfigName (Ljava/lang/String;)Lcom/datadog/trace/api/PropagationStyle; - public static fun values ()[Lcom/datadog/trace/api/PropagationStyle; -} - public abstract class com/datadog/trace/api/ResolverCacheConfig : java/lang/Enum { public static final field LARGE Lcom/datadog/trace/api/ResolverCacheConfig; public static final field LEGACY Lcom/datadog/trace/api/ResolverCacheConfig; @@ -1334,11 +529,6 @@ public final class com/datadog/trace/api/TracePropagationStyle : java/lang/Enum public static fun values ()[Lcom/datadog/trace/api/TracePropagationStyle; } -public abstract interface class com/datadog/trace/api/Tracer { - public abstract fun getSpanId ()Ljava/lang/String; - public abstract fun getTraceId ()Ljava/lang/String; -} - public class com/datadog/trace/api/WellKnownTags { public fun (Ljava/lang/CharSequence;Ljava/lang/CharSequence;Ljava/lang/CharSequence;Ljava/lang/CharSequence;Ljava/lang/CharSequence;Ljava/lang/CharSequence;)V public fun getEnv ()Lcom/datadog/trace/bootstrap/instrumentation/api/UTF8BytesString; @@ -1678,6 +868,7 @@ public final class com/datadog/trace/api/config/TracerConfig { public static final field SCOPE_INHERIT_ASYNC_PROPAGATION Ljava/lang/String; public static final field SCOPE_ITERATION_KEEP_ALIVE Ljava/lang/String; public static final field SCOPE_STRICT_MODE Ljava/lang/String; + public static final field SDK_V2_COMPATIBILITY_FLAG Ljava/lang/String; public static final field SECURE_RANDOM Ljava/lang/String; public static final field SERVICE_MAPPING Ljava/lang/String; public static final field SPAN_SAMPLING_RULES Ljava/lang/String; @@ -1801,17 +992,18 @@ public final class com/datadog/trace/api/gateway/RequestContextSlot : java/lang/ } public abstract interface class com/datadog/trace/api/interceptor/MutableSpan { + public abstract fun drop ()V public abstract fun getDurationNano ()J public abstract fun getLocalRootSpan ()Lcom/datadog/trace/api/interceptor/MutableSpan; public abstract fun getOperationName ()Ljava/lang/CharSequence; public abstract fun getResourceName ()Ljava/lang/CharSequence; public abstract fun getRootSpan ()Lcom/datadog/trace/api/interceptor/MutableSpan; - public abstract fun getSamplingPriority ()Ljava/lang/Integer; public abstract fun getServiceName ()Ljava/lang/String; public abstract fun getSpanType ()Ljava/lang/String; public abstract fun getStartTime ()J public fun getTag (Ljava/lang/String;)Ljava/lang/Object; public abstract fun getTags ()Ljava/util/Map; + public abstract fun getTraceSamplingPriority ()Ljava/lang/Integer; public abstract fun isError ()Z public abstract fun setError (Z)Lcom/datadog/trace/api/interceptor/MutableSpan; public abstract fun setMetric (Ljava/lang/CharSequence;D)Lcom/datadog/trace/api/interceptor/MutableSpan; @@ -1827,10 +1019,6 @@ public abstract interface class com/datadog/trace/api/interceptor/MutableSpan { public abstract fun setTag (Ljava/lang/String;Z)Lcom/datadog/trace/api/interceptor/MutableSpan; } -public abstract interface class com/datadog/trace/api/internal/InternalTracer { - public abstract fun flush ()V -} - public abstract interface class com/datadog/trace/api/internal/TraceSegment { public abstract fun effectivelyBlocked ()V public abstract fun setDataCurrent (Ljava/lang/String;Ljava/lang/Object;)V @@ -2380,7 +1568,7 @@ public abstract interface class com/datadog/trace/bootstrap/instrumentation/api/ public abstract fun getSpan ()Lcom/datadog/trace/bootstrap/instrumentation/api/AgentSpan; } -public abstract interface class com/datadog/trace/bootstrap/instrumentation/api/AgentScopeManager : com/datadog/trace/bootstrap/instrumentation/api/ScopeStateAware { +public abstract interface class com/datadog/trace/bootstrap/instrumentation/api/AgentScopeManager { public abstract fun activate (Lcom/datadog/trace/bootstrap/instrumentation/api/AgentSpan;Lcom/datadog/trace/bootstrap/instrumentation/api/ScopeSource;)Lcom/datadog/trace/bootstrap/instrumentation/api/AgentScope; public abstract fun activate (Lcom/datadog/trace/bootstrap/instrumentation/api/AgentSpan;Lcom/datadog/trace/bootstrap/instrumentation/api/ScopeSource;Z)Lcom/datadog/trace/bootstrap/instrumentation/api/AgentScope; public abstract fun activateNext (Lcom/datadog/trace/bootstrap/instrumentation/api/AgentSpan;)Lcom/datadog/trace/bootstrap/instrumentation/api/AgentScope; @@ -2453,10 +1641,10 @@ public abstract interface class com/datadog/trace/bootstrap/instrumentation/api/ public abstract interface class com/datadog/trace/bootstrap/instrumentation/api/AgentSpan$Context { public abstract fun baggageItems ()Ljava/lang/Iterable; public abstract fun getPathwayContext ()Lcom/datadog/trace/bootstrap/instrumentation/api/PathwayContext; - public abstract fun getSamplingPriority ()I public abstract fun getSpanId ()J public abstract fun getTrace ()Lcom/datadog/trace/bootstrap/instrumentation/api/AgentTrace; public abstract fun getTraceId ()Lcom/datadog/trace/api/DDTraceId; + public abstract fun getTraceSamplingPriority ()I public fun mergePathwayContext (Lcom/datadog/trace/bootstrap/instrumentation/api/PathwayContext;)V } @@ -2523,6 +1711,7 @@ public final class com/datadog/trace/bootstrap/instrumentation/api/AgentTracer$N public fun addThrowable (Ljava/lang/Throwable;B)Lcom/datadog/trace/bootstrap/instrumentation/api/AgentSpan; public fun beginEndToEnd ()V public fun context ()Lcom/datadog/trace/bootstrap/instrumentation/api/AgentSpan$Context; + public fun drop ()V public fun eligibleForDropping ()Z public fun finish ()V public fun finish (J)V @@ -2542,7 +1731,6 @@ public final class com/datadog/trace/bootstrap/instrumentation/api/AgentTracer$N public fun getResourceNamePriority ()B public synthetic fun getRootSpan ()Lcom/datadog/trace/api/interceptor/MutableSpan; public fun getRootSpan ()Lcom/datadog/trace/bootstrap/instrumentation/api/AgentSpan; - public fun getSamplingPriority ()Ljava/lang/Integer; public fun getServiceName ()Ljava/lang/String; public fun getSpanId ()J public synthetic fun getSpanName ()Ljava/lang/CharSequence; @@ -2552,6 +1740,7 @@ public final class com/datadog/trace/bootstrap/instrumentation/api/AgentTracer$N public fun getTag (Ljava/lang/String;)Ljava/lang/Object; public fun getTags ()Ljava/util/Map; public fun getTraceId ()Lcom/datadog/trace/api/DDTraceId; + public fun getTraceSamplingPriority ()Ljava/lang/Integer; public fun hasResourceName ()Z public fun isError ()Z public fun isSameTrace (Lcom/datadog/trace/bootstrap/instrumentation/api/AgentSpan;)Z @@ -2615,11 +1804,11 @@ public final class com/datadog/trace/bootstrap/instrumentation/api/AgentTracer$N public fun getForwarded ()Ljava/lang/String; public fun getForwardedFor ()Ljava/lang/String; public fun getPathwayContext ()Lcom/datadog/trace/bootstrap/instrumentation/api/PathwayContext; - public fun getSamplingPriority ()I public fun getSpanId ()J public fun getTerminatedContextLinks ()Ljava/util/List; public fun getTrace ()Lcom/datadog/trace/bootstrap/instrumentation/api/AgentTrace; public fun getTraceId ()Lcom/datadog/trace/api/DDTraceId; + public fun getTraceSamplingPriority ()I public fun getTrueClientIp ()Ljava/lang/String; public fun getUserAgent ()Ljava/lang/String; public fun getXClientIp ()Ljava/lang/String; @@ -2667,6 +1856,7 @@ public abstract interface class com/datadog/trace/bootstrap/instrumentation/api/ public abstract fun start ()Lcom/datadog/trace/bootstrap/instrumentation/api/AgentSpan; public abstract fun withErrorFlag ()Lcom/datadog/trace/bootstrap/instrumentation/api/AgentTracer$SpanBuilder; public abstract fun withLink (Lcom/datadog/trace/bootstrap/instrumentation/api/AgentSpanLink;)Lcom/datadog/trace/bootstrap/instrumentation/api/AgentTracer$SpanBuilder; + public abstract fun withOrigin (Ljava/lang/String;)Lcom/datadog/trace/bootstrap/instrumentation/api/AgentTracer$SpanBuilder; public abstract fun withRequestContextData (Lcom/datadog/trace/api/gateway/RequestContextSlot;Ljava/lang/Object;)Lcom/datadog/trace/bootstrap/instrumentation/api/AgentTracer$SpanBuilder; public abstract fun withResourceName (Ljava/lang/String;)Lcom/datadog/trace/bootstrap/instrumentation/api/AgentTracer$SpanBuilder; public abstract fun withServiceName (Ljava/lang/String;)Lcom/datadog/trace/bootstrap/instrumentation/api/AgentTracer$SpanBuilder; @@ -2678,27 +1868,18 @@ public abstract interface class com/datadog/trace/bootstrap/instrumentation/api/ public abstract fun withTag (Ljava/lang/String;Z)Lcom/datadog/trace/bootstrap/instrumentation/api/AgentTracer$SpanBuilder; } -public abstract interface class com/datadog/trace/bootstrap/instrumentation/api/AgentTracer$TracerAPI : com/datadog/trace/api/EndpointCheckpointer, com/datadog/trace/api/Tracer, com/datadog/trace/api/internal/InternalTracer, com/datadog/trace/bootstrap/instrumentation/api/ScopeStateAware { - public abstract fun activateNext (Lcom/datadog/trace/bootstrap/instrumentation/api/AgentSpan;)Lcom/datadog/trace/bootstrap/instrumentation/api/AgentScope; +public abstract interface class com/datadog/trace/bootstrap/instrumentation/api/AgentTracer$TracerAPI : com/datadog/trace/api/EndpointCheckpointer { public abstract fun activateSpan (Lcom/datadog/trace/bootstrap/instrumentation/api/AgentSpan;Lcom/datadog/trace/bootstrap/instrumentation/api/ScopeSource;)Lcom/datadog/trace/bootstrap/instrumentation/api/AgentScope; public abstract fun activateSpan (Lcom/datadog/trace/bootstrap/instrumentation/api/AgentSpan;Lcom/datadog/trace/bootstrap/instrumentation/api/ScopeSource;Z)Lcom/datadog/trace/bootstrap/instrumentation/api/AgentScope; - public abstract fun activeScope ()Lcom/datadog/trace/bootstrap/instrumentation/api/AgentScope; public abstract fun activeSpan ()Lcom/datadog/trace/bootstrap/instrumentation/api/AgentSpan; public abstract fun addScopeListener (Lcom/datadog/trace/api/scopemanager/ScopeListener;)V public fun buildSpan (Ljava/lang/CharSequence;)Lcom/datadog/trace/bootstrap/instrumentation/api/AgentTracer$SpanBuilder; public abstract fun buildSpan (Ljava/lang/String;Ljava/lang/CharSequence;)Lcom/datadog/trace/bootstrap/instrumentation/api/AgentTracer$SpanBuilder; - public abstract fun captureSpan (Lcom/datadog/trace/bootstrap/instrumentation/api/AgentSpan;)Lcom/datadog/trace/bootstrap/instrumentation/api/AgentScope$Continuation; public abstract fun captureTraceConfig ()Lcom/datadog/trace/api/TraceConfig; public abstract fun close ()V - public abstract fun closePrevious (Z)V - public abstract fun getProfilingContext ()Lcom/datadog/trace/bootstrap/instrumentation/api/ProfilingContextIntegration; public abstract fun getSpanId (Lcom/datadog/trace/bootstrap/instrumentation/api/AgentSpan;)Ljava/lang/String; - public abstract fun getTimer ()Lcom/datadog/trace/api/profiling/Timer; public abstract fun getTraceId (Lcom/datadog/trace/bootstrap/instrumentation/api/AgentSpan;)Ljava/lang/String; - public abstract fun noopSpan ()Lcom/datadog/trace/bootstrap/instrumentation/api/AgentSpan; public abstract fun propagate ()Lcom/datadog/trace/bootstrap/instrumentation/api/AgentPropagation; - public abstract fun registerCheckpointer (Lcom/datadog/trace/api/EndpointCheckpointer;)V - public abstract fun registerTimer (Lcom/datadog/trace/api/profiling/Timer;)V public abstract fun startSpan (Ljava/lang/String;Ljava/lang/CharSequence;)Lcom/datadog/trace/bootstrap/instrumentation/api/AgentSpan; public abstract fun startSpan (Ljava/lang/String;Ljava/lang/CharSequence;J)Lcom/datadog/trace/bootstrap/instrumentation/api/AgentSpan; public abstract fun startSpan (Ljava/lang/String;Ljava/lang/CharSequence;Lcom/datadog/trace/bootstrap/instrumentation/api/AgentSpan$Context;)Lcom/datadog/trace/bootstrap/instrumentation/api/AgentSpan; @@ -2887,15 +2068,6 @@ public final class com/datadog/trace/bootstrap/instrumentation/api/ScopeSource : public static fun values ()[Lcom/datadog/trace/bootstrap/instrumentation/api/ScopeSource; } -public abstract interface class com/datadog/trace/bootstrap/instrumentation/api/ScopeState { - public abstract fun activate ()V - public abstract fun fetchFromActive ()V -} - -public abstract interface class com/datadog/trace/bootstrap/instrumentation/api/ScopeStateAware { - public abstract fun newScopeState ()Lcom/datadog/trace/bootstrap/instrumentation/api/ScopeState; -} - public class com/datadog/trace/bootstrap/instrumentation/api/SpanLink : com/datadog/trace/bootstrap/instrumentation/api/AgentSpanLink { protected fun (Lcom/datadog/trace/api/DDTraceId;JBLjava/lang/String;Lcom/datadog/trace/bootstrap/instrumentation/api/AgentSpanLink$Attributes;)V public fun attributes ()Lcom/datadog/trace/bootstrap/instrumentation/api/AgentSpanLink$Attributes; @@ -2963,13 +2135,13 @@ public class com/datadog/trace/bootstrap/instrumentation/api/TagContext : com/da public fun getPropagationStyle ()Lcom/datadog/trace/api/TracePropagationStyle; public final fun getRequestContextDataAppSec ()Ljava/lang/Object; public final fun getRequestContextDataIast ()Ljava/lang/Object; - public final fun getSamplingPriority ()I public fun getSpanId ()J public final fun getTags ()Ljava/util/Map; public fun getTerminatedContextLinks ()Ljava/util/List; public final fun getTrace ()Lcom/datadog/trace/bootstrap/instrumentation/api/AgentTrace; public fun getTraceConfig ()Lcom/datadog/trace/api/TraceConfig; public fun getTraceId ()Lcom/datadog/trace/api/DDTraceId; + public final fun getTraceSamplingPriority ()I public fun getTrueClientIp ()Ljava/lang/String; public fun getUserAgent ()Ljava/lang/String; public fun getXClientIp ()Ljava/lang/String; @@ -3206,6 +2378,8 @@ public class com/datadog/trace/common/sampling/PrioritySampling { public class com/datadog/trace/common/sampling/RateByServiceTraceSampler : com/datadog/trace/common/sampling/PrioritySampler, com/datadog/trace/common/sampling/Sampler, com/datadog/trace/common/writer/RemoteResponseListener { public static final field SAMPLING_AGENT_RATE Ljava/lang/String; public fun ()V + public fun (Ljava/lang/Double;)V + public fun getSampleRate ()D public fun onResponse (Ljava/lang/String;Ljava/util/Map;)V public fun sample (Lcom/datadog/trace/core/CoreSpan;)Z public fun setSamplingPriority (Lcom/datadog/trace/core/CoreSpan;)V @@ -3418,7 +2592,6 @@ public abstract interface class com/datadog/trace/core/CoreSpan { public abstract fun isTopLevel ()Z public abstract fun processTagsAndBaggage (Lcom/datadog/trace/core/MetadataConsumer;)V public abstract fun removeTag (Ljava/lang/String;)Lcom/datadog/trace/core/CoreSpan; - public abstract fun samplingPriority ()I public abstract fun setErrorMessage (Ljava/lang/String;)Lcom/datadog/trace/core/CoreSpan; public abstract fun setFlag (Ljava/lang/CharSequence;Z)Lcom/datadog/trace/core/CoreSpan; public abstract fun setMeasured (Z)Lcom/datadog/trace/core/CoreSpan; @@ -3440,37 +2613,27 @@ public abstract interface class com/datadog/trace/core/CoreSpan { } public class com/datadog/trace/core/CoreTracer : com/datadog/trace/bootstrap/instrumentation/api/AgentTracer$TracerAPI { - public fun activateNext (Lcom/datadog/trace/bootstrap/instrumentation/api/AgentSpan;)Lcom/datadog/trace/bootstrap/instrumentation/api/AgentScope; public fun activateSpan (Lcom/datadog/trace/bootstrap/instrumentation/api/AgentSpan;)Lcom/datadog/trace/bootstrap/instrumentation/api/AgentScope; public fun activateSpan (Lcom/datadog/trace/bootstrap/instrumentation/api/AgentSpan;Lcom/datadog/trace/bootstrap/instrumentation/api/ScopeSource;)Lcom/datadog/trace/bootstrap/instrumentation/api/AgentScope; public fun activateSpan (Lcom/datadog/trace/bootstrap/instrumentation/api/AgentSpan;Lcom/datadog/trace/bootstrap/instrumentation/api/ScopeSource;Z)Lcom/datadog/trace/bootstrap/instrumentation/api/AgentScope; - public fun activeScope ()Lcom/datadog/trace/bootstrap/instrumentation/api/AgentScope; public fun activeSpan ()Lcom/datadog/trace/bootstrap/instrumentation/api/AgentSpan; public fun addScopeListener (Lcom/datadog/trace/api/scopemanager/ScopeListener;)V public synthetic fun buildSpan (Ljava/lang/String;Ljava/lang/CharSequence;)Lcom/datadog/trace/bootstrap/instrumentation/api/AgentTracer$SpanBuilder; public fun buildSpan (Ljava/lang/String;Ljava/lang/CharSequence;)Lcom/datadog/trace/core/CoreTracer$CoreSpanBuilder; - public fun captureSpan (Lcom/datadog/trace/bootstrap/instrumentation/api/AgentSpan;)Lcom/datadog/trace/bootstrap/instrumentation/api/AgentScope$Continuation; public synthetic fun captureTraceConfig ()Lcom/datadog/trace/api/TraceConfig; public fun captureTraceConfig ()Lcom/datadog/trace/core/CoreTracer$ConfigSnapshot; public fun close ()V - public fun closePrevious (Z)V protected fun finalize ()V public fun flush ()V public fun getPartialFlushMinSpans ()I - public fun getProfilingContext ()Lcom/datadog/trace/bootstrap/instrumentation/api/ProfilingContextIntegration; public fun getSpanId ()Ljava/lang/String; public fun getSpanId (Lcom/datadog/trace/bootstrap/instrumentation/api/AgentSpan;)Ljava/lang/String; public fun getTagInterceptor ()Lcom/datadog/trace/core/taginterceptor/TagInterceptor; - public fun getTimer ()Lcom/datadog/trace/api/profiling/Timer; public fun getTraceId ()Ljava/lang/String; public fun getTraceId (Lcom/datadog/trace/bootstrap/instrumentation/api/AgentSpan;)Ljava/lang/String; - public fun newScopeState ()Lcom/datadog/trace/bootstrap/instrumentation/api/ScopeState; - public fun noopSpan ()Lcom/datadog/trace/bootstrap/instrumentation/api/AgentSpan; public fun onRootSpanFinished (Lcom/datadog/trace/bootstrap/instrumentation/api/AgentSpan;Lcom/datadog/trace/api/EndpointTracker;)V public fun onRootSpanStarted (Lcom/datadog/trace/bootstrap/instrumentation/api/AgentSpan;)Lcom/datadog/trace/api/EndpointTracker; public fun propagate ()Lcom/datadog/trace/bootstrap/instrumentation/api/AgentPropagation; - public fun registerCheckpointer (Lcom/datadog/trace/api/EndpointCheckpointer;)V - public fun registerTimer (Lcom/datadog/trace/api/profiling/Timer;)V public fun startSpan (Ljava/lang/String;Ljava/lang/CharSequence;)Lcom/datadog/trace/bootstrap/instrumentation/api/AgentSpan; public fun startSpan (Ljava/lang/String;Ljava/lang/CharSequence;J)Lcom/datadog/trace/bootstrap/instrumentation/api/AgentSpan; public fun startSpan (Ljava/lang/String;Ljava/lang/CharSequence;Lcom/datadog/trace/bootstrap/instrumentation/api/AgentSpan$Context;)Lcom/datadog/trace/bootstrap/instrumentation/api/AgentSpan; @@ -3491,6 +2654,7 @@ public class com/datadog/trace/core/CoreTracer$CoreSpanBuilder : com/datadog/tra public synthetic fun withErrorFlag ()Lcom/datadog/trace/bootstrap/instrumentation/api/AgentTracer$SpanBuilder; public fun withErrorFlag ()Lcom/datadog/trace/core/CoreTracer$CoreSpanBuilder; public fun withLink (Lcom/datadog/trace/bootstrap/instrumentation/api/AgentSpanLink;)Lcom/datadog/trace/bootstrap/instrumentation/api/AgentTracer$SpanBuilder; + public fun withOrigin (Ljava/lang/String;)Lcom/datadog/trace/bootstrap/instrumentation/api/AgentTracer$SpanBuilder; public fun withRequestContextData (Lcom/datadog/trace/api/gateway/RequestContextSlot;Ljava/lang/Object;)Lcom/datadog/trace/bootstrap/instrumentation/api/AgentTracer$SpanBuilder; public synthetic fun withResourceName (Ljava/lang/String;)Lcom/datadog/trace/bootstrap/instrumentation/api/AgentTracer$SpanBuilder; public fun withResourceName (Ljava/lang/String;)Lcom/datadog/trace/core/CoreTracer$CoreSpanBuilder; @@ -3546,6 +2710,7 @@ public class com/datadog/trace/core/DDSpan : com/datadog/trace/api/profiling/Tra public fun beginEndToEnd ()V public synthetic fun context ()Lcom/datadog/trace/bootstrap/instrumentation/api/AgentSpan$Context; public final fun context ()Lcom/datadog/trace/core/DDSpanContext; + public fun drop ()V public fun eligibleForDropping ()Z public fun finish ()V public fun finish (J)V @@ -3573,10 +2738,10 @@ public class com/datadog/trace/core/DDSpan : com/datadog/trace/api/profiling/Tra public fun getResourceNamePriority ()B public synthetic fun getRootSpan ()Lcom/datadog/trace/api/interceptor/MutableSpan; public fun getRootSpan ()Lcom/datadog/trace/bootstrap/instrumentation/api/AgentSpan; - public fun getSamplingPriority ()Ljava/lang/Integer; public fun getServiceName ()Ljava/lang/String; public fun getSpanId ()J public fun getSpanName ()Ljava/lang/CharSequence; + public fun getSpanSamplingPriority ()I public fun getSpanType ()Ljava/lang/String; public fun getStartTime ()J public fun getStartTimeNano ()J @@ -3585,6 +2750,7 @@ public class com/datadog/trace/core/DDSpan : com/datadog/trace/api/profiling/Tra public fun getTag (Ljava/lang/String;)Ljava/lang/Object; public fun getTags ()Ljava/util/Map; public fun getTraceId ()Lcom/datadog/trace/api/DDTraceId; + public fun getTraceSamplingPriority ()Ljava/lang/Integer; public fun getType ()Ljava/lang/CharSequence; public fun getWrapper ()Ljava/lang/Object; public fun hasResourceName ()Z @@ -3602,7 +2768,6 @@ public class com/datadog/trace/core/DDSpan : com/datadog/trace/api/profiling/Tra public final fun publish ()V public synthetic fun removeTag (Ljava/lang/String;)Lcom/datadog/trace/core/CoreSpan; public fun removeTag (Ljava/lang/String;)Lcom/datadog/trace/core/DDSpan; - public fun samplingPriority ()I public synthetic fun setBaggageItem (Ljava/lang/String;Ljava/lang/String;)Lcom/datadog/trace/bootstrap/instrumentation/api/AgentSpan; public final fun setBaggageItem (Ljava/lang/String;Ljava/lang/String;)Lcom/datadog/trace/core/DDSpan; public fun setEndpointTracker (Lcom/datadog/trace/api/EndpointTracker;)V @@ -3722,14 +2887,15 @@ public class com/datadog/trace/core/DDSpanContext : com/datadog/trace/api/gatewa public fun getResourceName ()Ljava/lang/CharSequence; public fun getResourceNamePriority ()B public fun getRootSpanId ()J - public fun getSamplingPriority ()I public fun getServiceName ()Ljava/lang/String; public fun getSpanId ()J + public fun getSpanSamplingPriority ()I public fun getSpanType ()Ljava/lang/CharSequence; public fun getTags ()Ljava/util/Map; public synthetic fun getTrace ()Lcom/datadog/trace/bootstrap/instrumentation/api/AgentTrace; public fun getTrace ()Lcom/datadog/trace/core/PendingTrace; public fun getTraceId ()Lcom/datadog/trace/api/DDTraceId; + public fun getTraceSamplingPriority ()I public fun getTraceSegment ()Lcom/datadog/trace/api/internal/TraceSegment; public fun getTracer ()Lcom/datadog/trace/core/CoreTracer; public fun hasResourceName ()Z @@ -4072,7 +3238,6 @@ public final class com/datadog/trace/core/scopemanager/ContinuableScopeManager : public fun addScopeListener (Lcom/datadog/trace/api/scopemanager/ScopeListener;)V public fun captureSpan (Lcom/datadog/trace/bootstrap/instrumentation/api/AgentSpan;)Lcom/datadog/trace/bootstrap/instrumentation/api/AgentScope$Continuation; public fun closePrevious (Z)V - public fun newScopeState ()Lcom/datadog/trace/bootstrap/instrumentation/api/ScopeState; } public class com/datadog/trace/core/taginterceptor/RuleFlags { diff --git a/features/dd-sdk-android-trace-internal/build.gradle.kts b/features/dd-sdk-android-trace-internal/build.gradle.kts index 002f711528..b8e29bfb94 100644 --- a/features/dd-sdk-android-trace-internal/build.gradle.kts +++ b/features/dd-sdk-android-trace-internal/build.gradle.kts @@ -53,9 +53,6 @@ dependencies { implementation(libs.androidXAnnotation) implementation(libs.bundles.traceCore) - // OpenTracing - api(libs.bundles.openTracing) - testImplementation(project(":tools:unit")) { attributes { attribute( diff --git a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/legacy/trace/api/Config.java b/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/legacy/trace/api/Config.java deleted file mode 100644 index 9400d07f8f..0000000000 --- a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/legacy/trace/api/Config.java +++ /dev/null @@ -1,1651 +0,0 @@ -/* - * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. - * This product includes software developed at Datadog (https://www.datadoghq.com/). - * Copyright 2016-Present Datadog, Inc. - */ - -package com.datadog.legacy.trace.api; - -import java.io.BufferedReader; -import java.io.File; -import java.io.FileNotFoundException; -import java.io.FileReader; -import java.io.IOException; -import java.io.InputStreamReader; -import java.lang.reflect.Method; -import java.net.InetAddress; -import java.net.UnknownHostException; -import java.util.*; -import java.util.regex.Pattern; - -/** - * Config reads values with the following priority: 1) system properties, 2) environment variables, - * 3) optional configuration file. It also includes default values to ensure a valid config. - * - *

- * - *

System properties are {@link Config#PREFIX}'ed. Environment variables are the same as the - * system property, but uppercased with '.' -> '_'. - */ -public class Config { - /** - * Config keys below - */ - private static final String PREFIX = "dd."; - - public static final String PROFILING_URL_TEMPLATE = "https://intake.profile.%s/v1/input"; - - private static final Pattern ENV_REPLACEMENT = Pattern.compile("[^a-zA-Z0-9_]"); - - public static final String CONFIGURATION_FILE = "trace.config"; - public static final String API_KEY = "api-key"; - public static final String API_KEY_FILE = "api-key-file"; - public static final String SITE = "site"; - public static final String SERVICE_NAME = "service.name"; - public static final String TRACE_ENABLED = "trace.enabled"; - public static final String INTEGRATIONS_ENABLED = "integrations.enabled"; - public static final String WRITER_TYPE = "writer.type"; - public static final String AGENT_HOST = "agent.host"; - public static final String TRACE_AGENT_PORT = "trace.agent.port"; - public static final String AGENT_PORT_LEGACY = "agent.port"; - public static final String AGENT_UNIX_DOMAIN_SOCKET = "trace.agent.unix.domain.socket"; - public static final String PRIORITY_SAMPLING = "priority.sampling"; - public static final String TRACE_RESOLVER_ENABLED = "trace.resolver.enabled"; - public static final String SERVICE_MAPPING = "service.mapping"; - - private static final String ENV = "env"; - private static final String VERSION = "version"; - public static final String TAGS = "tags"; - @Deprecated // Use dd.tags instead - public static final String GLOBAL_TAGS = "trace.global.tags"; - public static final String SPAN_TAGS = "trace.span.tags"; - public static final String JMX_TAGS = "trace.jmx.tags"; - public static final String TRACE_ANALYTICS_ENABLED = "trace.analytics.enabled"; - public static final String TRACE_ANNOTATIONS = "trace.annotations"; - public static final String TRACE_EXECUTORS_ALL = "trace.executors.all"; - public static final String TRACE_EXECUTORS = "trace.executors"; - public static final String TRACE_METHODS = "trace.methods"; - public static final String TRACE_CLASSES_EXCLUDE = "trace.classes.exclude"; - public static final String TRACE_SAMPLING_SERVICE_RULES = "trace.sampling.service.rules"; - public static final String TRACE_SAMPLING_OPERATION_RULES = "trace.sampling.operation.rules"; - public static final String TRACE_SAMPLE_RATE = "trace.sample.rate"; - public static final String TRACE_RATE_LIMIT = "trace.rate.limit"; - public static final String TRACE_REPORT_HOSTNAME = "trace.report-hostname"; - public static final String HEADER_TAGS = "trace.header.tags"; - public static final String HTTP_SERVER_ERROR_STATUSES = "http.server.error.statuses"; - public static final String HTTP_CLIENT_ERROR_STATUSES = "http.client.error.statuses"; - public static final String HTTP_SERVER_TAG_QUERY_STRING = "http.server.tag.query-string"; - public static final String HTTP_CLIENT_TAG_QUERY_STRING = "http.client.tag.query-string"; - public static final String HTTP_CLIENT_HOST_SPLIT_BY_DOMAIN = "trace.http.client.split-by-domain"; - public static final String DB_CLIENT_HOST_SPLIT_BY_INSTANCE = "trace.db.client.split-by-instance"; - public static final String SPLIT_BY_TAGS = "trace.split-by-tags"; - public static final String SCOPE_DEPTH_LIMIT = "trace.scope.depth.limit"; - public static final String PARTIAL_FLUSH_MIN_SPANS = "trace.partial.flush.min.spans"; - public static final String RUNTIME_CONTEXT_FIELD_INJECTION = - "trace.runtime.context.field.injection"; - public static final String PROPAGATION_STYLE_EXTRACT = "propagation.style.extract"; - public static final String PROPAGATION_STYLE_INJECT = "propagation.style.inject"; - - public static final String JMX_FETCH_ENABLED = "jmxfetch.enabled"; - public static final String JMX_FETCH_CONFIG_DIR = "jmxfetch.config.dir"; - public static final String JMX_FETCH_CONFIG = "jmxfetch.config"; - @Deprecated - public static final String JMX_FETCH_METRICS_CONFIGS = "jmxfetch.metrics-configs"; - public static final String JMX_FETCH_CHECK_PERIOD = "jmxfetch.check-period"; - public static final String JMX_FETCH_REFRESH_BEANS_PERIOD = "jmxfetch.refresh-beans-period"; - public static final String JMX_FETCH_STATSD_HOST = "jmxfetch.statsd.host"; - public static final String JMX_FETCH_STATSD_PORT = "jmxfetch.statsd.port"; - - public static final String HEALTH_METRICS_ENABLED = "trace.health.metrics.enabled"; - public static final String HEALTH_METRICS_STATSD_HOST = "trace.health.metrics.statsd.host"; - public static final String HEALTH_METRICS_STATSD_PORT = "trace.health.metrics.statsd.port"; - - public static final String LOGS_INJECTION_ENABLED = "logs.injection"; - - public static final String PROFILING_ENABLED = "profiling.enabled"; - @Deprecated // Use dd.site instead - public static final String PROFILING_URL = "profiling.url"; - @Deprecated // Use dd.api-key instead - public static final String PROFILING_API_KEY_OLD = "profiling.api-key"; - @Deprecated // Use dd.api-key-file instead - public static final String PROFILING_API_KEY_FILE_OLD = "profiling.api-key-file"; - @Deprecated // Use dd.api-key instead - public static final String PROFILING_API_KEY_VERY_OLD = "profiling.apikey"; - @Deprecated // Use dd.api-key-file instead - public static final String PROFILING_API_KEY_FILE_VERY_OLD = "profiling.apikey.file"; - public static final String PROFILING_TAGS = "profiling.tags"; - public static final String PROFILING_START_DELAY = "profiling.start-delay"; - // DANGEROUS! May lead on sigsegv on JVMs before 14 - // Not intended for production use - public static final String PROFILING_START_FORCE_FIRST = - "profiling.experimental.start-force-first"; - public static final String PROFILING_UPLOAD_PERIOD = "profiling.upload.period"; - public static final String PROFILING_TEMPLATE_OVERRIDE_FILE = - "profiling.jfr-template-override-file"; - public static final String PROFILING_UPLOAD_TIMEOUT = "profiling.upload.timeout"; - public static final String PROFILING_UPLOAD_COMPRESSION = "profiling.upload.compression"; - public static final String PROFILING_PROXY_HOST = "profiling.proxy.host"; - public static final String PROFILING_PROXY_PORT = "profiling.proxy.port"; - public static final String PROFILING_PROXY_USERNAME = "profiling.proxy.username"; - public static final String PROFILING_PROXY_PASSWORD = "profiling.proxy.password"; - public static final String PROFILING_EXCEPTION_SAMPLE_LIMIT = "profiling.exception.sample.limit"; - public static final String PROFILING_EXCEPTION_HISTOGRAM_TOP_ITEMS = - "profiling.exception.histogram.top-items"; - public static final String PROFILING_EXCEPTION_HISTOGRAM_MAX_COLLECTION_SIZE = - "profiling.exception.histogram.max-collection-size"; - - public static final String RUNTIME_ID_TAG = "runtime-id"; - public static final String SERVICE = "service"; - public static final String SERVICE_TAG = SERVICE; - public static final String HOST_TAG = "host"; - public static final String LANGUAGE_TAG_KEY = "language"; - public static final String LANGUAGE_TAG_VALUE = "jvm"; - - public static final String DEFAULT_SITE = "datadoghq.com"; - public static final String DEFAULT_SERVICE_NAME = "unnamed-java-app"; - - private static final boolean DEFAULT_TRACE_ENABLED = true; - public static final boolean DEFAULT_INTEGRATIONS_ENABLED = true; - public static final String DD_AGENT_WRITER_TYPE = "DDAgentWriter"; - public static final String LOGGING_WRITER_TYPE = "LoggingWriter"; - private static final String DEFAULT_AGENT_WRITER_TYPE = DD_AGENT_WRITER_TYPE; - - public static final String DEFAULT_AGENT_HOST = "localhost"; - public static final int DEFAULT_TRACE_AGENT_PORT = 8126; - public static final String DEFAULT_AGENT_UNIX_DOMAIN_SOCKET = null; - - private static final boolean DEFAULT_RUNTIME_CONTEXT_FIELD_INJECTION = true; - - private static final boolean DEFAULT_PRIORITY_SAMPLING_ENABLED = true; - private static final boolean DEFAULT_TRACE_RESOLVER_ENABLED = true; - private static final Set DEFAULT_HTTP_SERVER_ERROR_STATUSES = - parseIntegerRangeSet("500-599", "default"); - private static final Set DEFAULT_HTTP_CLIENT_ERROR_STATUSES = - parseIntegerRangeSet("400-499", "default"); - private static final boolean DEFAULT_HTTP_SERVER_TAG_QUERY_STRING = false; - private static final boolean DEFAULT_HTTP_CLIENT_TAG_QUERY_STRING = false; - private static final boolean DEFAULT_HTTP_CLIENT_SPLIT_BY_DOMAIN = false; - private static final boolean DEFAULT_DB_CLIENT_HOST_SPLIT_BY_INSTANCE = false; - private static final String DEFAULT_SPLIT_BY_TAGS = ""; - private static final int DEFAULT_SCOPE_DEPTH_LIMIT = 100; - private static final int DEFAULT_PARTIAL_FLUSH_MIN_SPANS = 1000; - private static final String DEFAULT_PROPAGATION_STYLE_EXTRACT = PropagationStyle.DATADOG.name(); - private static final String DEFAULT_PROPAGATION_STYLE_INJECT = PropagationStyle.DATADOG.name(); - private static final boolean DEFAULT_JMX_FETCH_ENABLED = true; - - public static final int DEFAULT_JMX_FETCH_STATSD_PORT = 8125; - - public static final boolean DEFAULT_METRICS_ENABLED = false; - // No default constants for metrics statsd support -- falls back to jmxfetch values - - public static final boolean DEFAULT_LOGS_INJECTION_ENABLED = false; - - public static final boolean DEFAULT_PROFILING_ENABLED = false; - public static final int DEFAULT_PROFILING_START_DELAY = 10; - public static final boolean DEFAULT_PROFILING_START_FORCE_FIRST = false; - public static final int DEFAULT_PROFILING_UPLOAD_PERIOD = 60; // 1 min - public static final int DEFAULT_PROFILING_UPLOAD_TIMEOUT = 30; // seconds - public static final String DEFAULT_PROFILING_UPLOAD_COMPRESSION = "on"; - public static final int DEFAULT_PROFILING_PROXY_PORT = 8080; - public static final int DEFAULT_PROFILING_EXCEPTION_SAMPLE_LIMIT = 10_000; - public static final int DEFAULT_PROFILING_EXCEPTION_HISTOGRAM_TOP_ITEMS = 50; - public static final int DEFAULT_PROFILING_EXCEPTION_HISTOGRAM_MAX_COLLECTION_SIZE = 10000; - - private static final String SPLIT_BY_SPACE_OR_COMMA_REGEX = "[,\\s]+"; - - private static final boolean DEFAULT_TRACE_REPORT_HOSTNAME = false; - private static final String DEFAULT_TRACE_ANNOTATIONS = null; - private static final boolean DEFAULT_TRACE_EXECUTORS_ALL = false; - private static final String DEFAULT_TRACE_EXECUTORS = ""; - private static final String DEFAULT_TRACE_METHODS = null; - public static final boolean DEFAULT_TRACE_ANALYTICS_ENABLED = false; - public static final float DEFAULT_ANALYTICS_SAMPLE_RATE = 1.0f; - public static final double DEFAULT_TRACE_RATE_LIMIT = 100; - - public enum PropagationStyle { - DATADOG, - B3, - B3MULTI, - TRACECONTEXT, - HAYSTACK - } - - /** - * A tag intended for internal use only, hence not added to the public api DDTags class. - */ - private static final String INTERNAL_HOST_NAME = "_dd.hostname"; - - /** - * this is a random UUID that gets generated on JVM start up and is attached to every root span - * and every JMX metric that is sent out. - */ - private final String runtimeId; - - /** - * Note: this has effect only on profiling site. Traces are sent to Datadog agent and are not - * affected by this setting. - */ - private final String site; - - private final String serviceName; - private final boolean traceEnabled; - private final boolean integrationsEnabled; - private final String writerType; - private final String agentHost; - private final int agentPort; - private final String agentUnixDomainSocket; - private final boolean prioritySamplingEnabled; - private final boolean traceResolverEnabled; - private final Map serviceMapping; - private final Map tags; - private final Map spanTags; - private final Map jmxTags; - private final List excludedClasses; - private final Map headerTags; - private final Set httpServerErrorStatuses; - private final Set httpClientErrorStatuses; - private final boolean httpServerTagQueryString; - private final boolean httpClientTagQueryString; - private final boolean httpClientSplitByDomain; - private final boolean dbClientSplitByInstance; - private final Set splitByTags; - private final Integer scopeDepthLimit; - private final Integer partialFlushMinSpans; - private final boolean runtimeContextFieldInjection; - private final Set propagationStylesToExtract; - private final Set propagationStylesToInject; - - private final boolean jmxFetchEnabled; - private final String jmxFetchConfigDir; - private final List jmxFetchConfigs; - @Deprecated - private final List jmxFetchMetricsConfigs; - private final Integer jmxFetchCheckPeriod; - private final Integer jmxFetchRefreshBeansPeriod; - private final String jmxFetchStatsdHost; - private final Integer jmxFetchStatsdPort; - - // These values are default-ed to those of jmx fetch values as needed - private final boolean healthMetricsEnabled; - private final String healthMetricsStatsdHost; - private final Integer healthMetricsStatsdPort; - - private final boolean logsInjectionEnabled; - private final boolean reportHostName; - - private final String traceAnnotations; - - private final String traceMethods; - - private final boolean traceExecutorsAll; - private final List traceExecutors; - - private final boolean traceAnalyticsEnabled; - - private final Map traceSamplingServiceRules; - private final Map traceSamplingOperationRules; - private final Double traceSampleRate; - private final Double traceRateLimit; - - private final boolean profilingEnabled; - @Deprecated - private final String profilingUrl; - private final Map profilingTags; - private final int profilingStartDelay; - private final boolean profilingStartForceFirst; - private final int profilingUploadPeriod; - private final String profilingTemplateOverrideFile; - private final int profilingUploadTimeout; - private final String profilingUploadCompression; - private final String profilingProxyHost; - private final int profilingProxyPort; - private final String profilingProxyUsername; - private final String profilingProxyPassword; - private final int profilingExceptionSampleLimit; - private final int profilingExceptionHistogramTopItems; - private final int profilingExceptionHistogramMaxCollectionSize; - - // Values from an optionally provided properties file - private static Properties propertiesFromConfigFile; - - // Read order: System Properties -> Env Variables, [-> properties file], [-> default value] - // Visible for testing - Config() { - propertiesFromConfigFile = loadConfigurationFile(); - - runtimeId = UUID.randomUUID().toString(); - - site = getSettingFromEnvironment(SITE, DEFAULT_SITE); - serviceName = - getSettingFromEnvironment( - SERVICE, getSettingFromEnvironment(SERVICE_NAME, DEFAULT_SERVICE_NAME)); - - traceEnabled = getBooleanSettingFromEnvironment(TRACE_ENABLED, DEFAULT_TRACE_ENABLED); - integrationsEnabled = - getBooleanSettingFromEnvironment(INTEGRATIONS_ENABLED, DEFAULT_INTEGRATIONS_ENABLED); - writerType = getSettingFromEnvironment(WRITER_TYPE, DEFAULT_AGENT_WRITER_TYPE); - agentHost = getSettingFromEnvironment(AGENT_HOST, DEFAULT_AGENT_HOST); - agentPort = - getIntegerSettingFromEnvironment( - TRACE_AGENT_PORT, - getIntegerSettingFromEnvironment(AGENT_PORT_LEGACY, DEFAULT_TRACE_AGENT_PORT)); - agentUnixDomainSocket = - getSettingFromEnvironment(AGENT_UNIX_DOMAIN_SOCKET, DEFAULT_AGENT_UNIX_DOMAIN_SOCKET); - prioritySamplingEnabled = - getBooleanSettingFromEnvironment(PRIORITY_SAMPLING, DEFAULT_PRIORITY_SAMPLING_ENABLED); - traceResolverEnabled = - getBooleanSettingFromEnvironment(TRACE_RESOLVER_ENABLED, DEFAULT_TRACE_RESOLVER_ENABLED); - serviceMapping = getMapSettingFromEnvironment(SERVICE_MAPPING, null); - - { - final Map tags = - new HashMap<>(getMapSettingFromEnvironment(GLOBAL_TAGS, null)); - tags.putAll(getMapSettingFromEnvironment(TAGS, null)); - this.tags = getMapWithPropertiesDefinedByEnvironment(tags, ENV, VERSION); - } - - spanTags = getMapSettingFromEnvironment(SPAN_TAGS, null); - jmxTags = getMapSettingFromEnvironment(JMX_TAGS, null); - - excludedClasses = getListSettingFromEnvironment(TRACE_CLASSES_EXCLUDE, null); - headerTags = getMapSettingFromEnvironment(HEADER_TAGS, null); - - httpServerErrorStatuses = - getIntegerRangeSettingFromEnvironment( - HTTP_SERVER_ERROR_STATUSES, DEFAULT_HTTP_SERVER_ERROR_STATUSES); - - httpClientErrorStatuses = - getIntegerRangeSettingFromEnvironment( - HTTP_CLIENT_ERROR_STATUSES, DEFAULT_HTTP_CLIENT_ERROR_STATUSES); - - httpServerTagQueryString = - getBooleanSettingFromEnvironment( - HTTP_SERVER_TAG_QUERY_STRING, DEFAULT_HTTP_SERVER_TAG_QUERY_STRING); - - httpClientTagQueryString = - getBooleanSettingFromEnvironment( - HTTP_CLIENT_TAG_QUERY_STRING, DEFAULT_HTTP_CLIENT_TAG_QUERY_STRING); - - httpClientSplitByDomain = - getBooleanSettingFromEnvironment( - HTTP_CLIENT_HOST_SPLIT_BY_DOMAIN, DEFAULT_HTTP_CLIENT_SPLIT_BY_DOMAIN); - - dbClientSplitByInstance = - getBooleanSettingFromEnvironment( - DB_CLIENT_HOST_SPLIT_BY_INSTANCE, DEFAULT_DB_CLIENT_HOST_SPLIT_BY_INSTANCE); - - splitByTags = - Collections.unmodifiableSet( - new LinkedHashSet<>( - getListSettingFromEnvironment(SPLIT_BY_TAGS, DEFAULT_SPLIT_BY_TAGS))); - - scopeDepthLimit = - getIntegerSettingFromEnvironment(SCOPE_DEPTH_LIMIT, DEFAULT_SCOPE_DEPTH_LIMIT); - - partialFlushMinSpans = - getIntegerSettingFromEnvironment(PARTIAL_FLUSH_MIN_SPANS, DEFAULT_PARTIAL_FLUSH_MIN_SPANS); - - runtimeContextFieldInjection = - getBooleanSettingFromEnvironment( - RUNTIME_CONTEXT_FIELD_INJECTION, DEFAULT_RUNTIME_CONTEXT_FIELD_INJECTION); - - propagationStylesToExtract = - getPropagationStyleSetSettingFromEnvironmentOrDefault( - PROPAGATION_STYLE_EXTRACT, DEFAULT_PROPAGATION_STYLE_EXTRACT); - propagationStylesToInject = - getPropagationStyleSetSettingFromEnvironmentOrDefault( - PROPAGATION_STYLE_INJECT, DEFAULT_PROPAGATION_STYLE_INJECT); - - jmxFetchEnabled = - getBooleanSettingFromEnvironment(JMX_FETCH_ENABLED, DEFAULT_JMX_FETCH_ENABLED); - jmxFetchConfigDir = getSettingFromEnvironment(JMX_FETCH_CONFIG_DIR, null); - jmxFetchConfigs = getListSettingFromEnvironment(JMX_FETCH_CONFIG, null); - jmxFetchMetricsConfigs = getListSettingFromEnvironment(JMX_FETCH_METRICS_CONFIGS, null); - jmxFetchCheckPeriod = getIntegerSettingFromEnvironment(JMX_FETCH_CHECK_PERIOD, null); - jmxFetchRefreshBeansPeriod = - getIntegerSettingFromEnvironment(JMX_FETCH_REFRESH_BEANS_PERIOD, null); - jmxFetchStatsdHost = getSettingFromEnvironment(JMX_FETCH_STATSD_HOST, null); - jmxFetchStatsdPort = - getIntegerSettingFromEnvironment(JMX_FETCH_STATSD_PORT, DEFAULT_JMX_FETCH_STATSD_PORT); - - // Writer.Builder createMonitor will use the values of the JMX fetch & agent to fill-in defaults - healthMetricsEnabled = - getBooleanSettingFromEnvironment(HEALTH_METRICS_ENABLED, DEFAULT_METRICS_ENABLED); - healthMetricsStatsdHost = getSettingFromEnvironment(HEALTH_METRICS_STATSD_HOST, null); - healthMetricsStatsdPort = getIntegerSettingFromEnvironment(HEALTH_METRICS_STATSD_PORT, null); - - logsInjectionEnabled = - getBooleanSettingFromEnvironment(LOGS_INJECTION_ENABLED, DEFAULT_LOGS_INJECTION_ENABLED); - reportHostName = - getBooleanSettingFromEnvironment(TRACE_REPORT_HOSTNAME, DEFAULT_TRACE_REPORT_HOSTNAME); - - traceAnnotations = getSettingFromEnvironment(TRACE_ANNOTATIONS, DEFAULT_TRACE_ANNOTATIONS); - - traceMethods = getSettingFromEnvironment(TRACE_METHODS, DEFAULT_TRACE_METHODS); - - traceExecutorsAll = - getBooleanSettingFromEnvironment(TRACE_EXECUTORS_ALL, DEFAULT_TRACE_EXECUTORS_ALL); - - traceExecutors = getListSettingFromEnvironment(TRACE_EXECUTORS, DEFAULT_TRACE_EXECUTORS); - - traceAnalyticsEnabled = - getBooleanSettingFromEnvironment(TRACE_ANALYTICS_ENABLED, DEFAULT_TRACE_ANALYTICS_ENABLED); - - traceSamplingServiceRules = getMapSettingFromEnvironment(TRACE_SAMPLING_SERVICE_RULES, null); - traceSamplingOperationRules = - getMapSettingFromEnvironment(TRACE_SAMPLING_OPERATION_RULES, null); - traceSampleRate = getDoubleSettingFromEnvironment(TRACE_SAMPLE_RATE, null); - traceRateLimit = getDoubleSettingFromEnvironment(TRACE_RATE_LIMIT, DEFAULT_TRACE_RATE_LIMIT); - - profilingEnabled = - getBooleanSettingFromEnvironment(PROFILING_ENABLED, DEFAULT_PROFILING_ENABLED); - profilingUrl = getSettingFromEnvironment(PROFILING_URL, null); - - profilingTags = getMapSettingFromEnvironment(PROFILING_TAGS, null); - profilingStartDelay = - getIntegerSettingFromEnvironment(PROFILING_START_DELAY, DEFAULT_PROFILING_START_DELAY); - profilingStartForceFirst = - getBooleanSettingFromEnvironment( - PROFILING_START_FORCE_FIRST, DEFAULT_PROFILING_START_FORCE_FIRST); - profilingUploadPeriod = - getIntegerSettingFromEnvironment(PROFILING_UPLOAD_PERIOD, DEFAULT_PROFILING_UPLOAD_PERIOD); - profilingTemplateOverrideFile = - getSettingFromEnvironment(PROFILING_TEMPLATE_OVERRIDE_FILE, null); - profilingUploadTimeout = - getIntegerSettingFromEnvironment( - PROFILING_UPLOAD_TIMEOUT, DEFAULT_PROFILING_UPLOAD_TIMEOUT); - profilingUploadCompression = - getSettingFromEnvironment( - PROFILING_UPLOAD_COMPRESSION, DEFAULT_PROFILING_UPLOAD_COMPRESSION); - profilingProxyHost = getSettingFromEnvironment(PROFILING_PROXY_HOST, null); - profilingProxyPort = - getIntegerSettingFromEnvironment(PROFILING_PROXY_PORT, DEFAULT_PROFILING_PROXY_PORT); - profilingProxyUsername = getSettingFromEnvironment(PROFILING_PROXY_USERNAME, null); - profilingProxyPassword = getSettingFromEnvironment(PROFILING_PROXY_PASSWORD, null); - - profilingExceptionSampleLimit = - getIntegerSettingFromEnvironment( - PROFILING_EXCEPTION_SAMPLE_LIMIT, DEFAULT_PROFILING_EXCEPTION_SAMPLE_LIMIT); - profilingExceptionHistogramTopItems = - getIntegerSettingFromEnvironment( - PROFILING_EXCEPTION_HISTOGRAM_TOP_ITEMS, - DEFAULT_PROFILING_EXCEPTION_HISTOGRAM_TOP_ITEMS); - profilingExceptionHistogramMaxCollectionSize = - getIntegerSettingFromEnvironment( - PROFILING_EXCEPTION_HISTOGRAM_MAX_COLLECTION_SIZE, - DEFAULT_PROFILING_EXCEPTION_HISTOGRAM_MAX_COLLECTION_SIZE); - } - - // Read order: Properties -> Parent - private Config(final Properties properties, final Config parent) { - runtimeId = parent.runtimeId; - - site = properties.getProperty(SITE, parent.site); - serviceName = - properties.getProperty(SERVICE, properties.getProperty(SERVICE_NAME, parent.serviceName)); - - traceEnabled = getPropertyBooleanValue(properties, TRACE_ENABLED, parent.traceEnabled); - integrationsEnabled = - getPropertyBooleanValue(properties, INTEGRATIONS_ENABLED, parent.integrationsEnabled); - writerType = properties.getProperty(WRITER_TYPE, parent.writerType); - agentHost = properties.getProperty(AGENT_HOST, parent.agentHost); - agentPort = - getPropertyIntegerValue( - properties, - TRACE_AGENT_PORT, - getPropertyIntegerValue(properties, AGENT_PORT_LEGACY, parent.agentPort)); - agentUnixDomainSocket = - properties.getProperty(AGENT_UNIX_DOMAIN_SOCKET, parent.agentUnixDomainSocket); - prioritySamplingEnabled = - getPropertyBooleanValue(properties, PRIORITY_SAMPLING, parent.prioritySamplingEnabled); - traceResolverEnabled = - getPropertyBooleanValue(properties, TRACE_RESOLVER_ENABLED, parent.traceResolverEnabled); - serviceMapping = getPropertyMapValue(properties, SERVICE_MAPPING, parent.serviceMapping); - - { - final Map preTags = - new HashMap<>( - getPropertyMapValue(properties, GLOBAL_TAGS, Collections.emptyMap())); - preTags.putAll(getPropertyMapValue(properties, TAGS, parent.tags)); - this.tags = overwriteKeysFromProperties(preTags, properties, ENV, VERSION); - } - spanTags = getPropertyMapValue(properties, SPAN_TAGS, parent.spanTags); - jmxTags = getPropertyMapValue(properties, JMX_TAGS, parent.jmxTags); - excludedClasses = - getPropertyListValue(properties, TRACE_CLASSES_EXCLUDE, parent.excludedClasses); - headerTags = getPropertyMapValue(properties, HEADER_TAGS, parent.headerTags); - - httpServerErrorStatuses = - getPropertyIntegerRangeValue( - properties, HTTP_SERVER_ERROR_STATUSES, parent.httpServerErrorStatuses); - - httpClientErrorStatuses = - getPropertyIntegerRangeValue( - properties, HTTP_CLIENT_ERROR_STATUSES, parent.httpClientErrorStatuses); - - httpServerTagQueryString = - getPropertyBooleanValue( - properties, HTTP_SERVER_TAG_QUERY_STRING, parent.httpServerTagQueryString); - - httpClientTagQueryString = - getPropertyBooleanValue( - properties, HTTP_CLIENT_TAG_QUERY_STRING, parent.httpClientTagQueryString); - - httpClientSplitByDomain = - getPropertyBooleanValue( - properties, HTTP_CLIENT_HOST_SPLIT_BY_DOMAIN, parent.httpClientSplitByDomain); - - dbClientSplitByInstance = - getPropertyBooleanValue( - properties, DB_CLIENT_HOST_SPLIT_BY_INSTANCE, parent.dbClientSplitByInstance); - - splitByTags = - Collections.unmodifiableSet( - new LinkedHashSet<>( - getPropertyListValue( - properties, SPLIT_BY_TAGS, new ArrayList<>(parent.splitByTags)))); - - scopeDepthLimit = - getPropertyIntegerValue(properties, SCOPE_DEPTH_LIMIT, parent.scopeDepthLimit); - - partialFlushMinSpans = - getPropertyIntegerValue(properties, PARTIAL_FLUSH_MIN_SPANS, parent.partialFlushMinSpans); - - runtimeContextFieldInjection = - getPropertyBooleanValue( - properties, RUNTIME_CONTEXT_FIELD_INJECTION, parent.runtimeContextFieldInjection); - - final Set parsedPropagationStylesToExtract = - getPropagationStyleSetFromPropertyValue(properties, PROPAGATION_STYLE_EXTRACT); - propagationStylesToExtract = - parsedPropagationStylesToExtract == null - ? parent.propagationStylesToExtract - : parsedPropagationStylesToExtract; - final Set parsedPropagationStylesToInject = - getPropagationStyleSetFromPropertyValue(properties, PROPAGATION_STYLE_INJECT); - propagationStylesToInject = - parsedPropagationStylesToInject == null - ? parent.propagationStylesToInject - : parsedPropagationStylesToInject; - - jmxFetchEnabled = - getPropertyBooleanValue(properties, JMX_FETCH_ENABLED, parent.jmxFetchEnabled); - jmxFetchConfigDir = properties.getProperty(JMX_FETCH_CONFIG_DIR, parent.jmxFetchConfigDir); - jmxFetchConfigs = getPropertyListValue(properties, JMX_FETCH_CONFIG, parent.jmxFetchConfigs); - jmxFetchMetricsConfigs = - getPropertyListValue(properties, JMX_FETCH_METRICS_CONFIGS, parent.jmxFetchMetricsConfigs); - jmxFetchCheckPeriod = - getPropertyIntegerValue(properties, JMX_FETCH_CHECK_PERIOD, parent.jmxFetchCheckPeriod); - jmxFetchRefreshBeansPeriod = - getPropertyIntegerValue( - properties, JMX_FETCH_REFRESH_BEANS_PERIOD, parent.jmxFetchRefreshBeansPeriod); - jmxFetchStatsdHost = properties.getProperty(JMX_FETCH_STATSD_HOST, parent.jmxFetchStatsdHost); - jmxFetchStatsdPort = - getPropertyIntegerValue(properties, JMX_FETCH_STATSD_PORT, parent.jmxFetchStatsdPort); - - healthMetricsEnabled = - getPropertyBooleanValue(properties, HEALTH_METRICS_ENABLED, DEFAULT_METRICS_ENABLED); - healthMetricsStatsdHost = - properties.getProperty(HEALTH_METRICS_STATSD_HOST, parent.healthMetricsStatsdHost); - healthMetricsStatsdPort = - getPropertyIntegerValue( - properties, HEALTH_METRICS_STATSD_PORT, parent.healthMetricsStatsdPort); - - logsInjectionEnabled = - getBooleanSettingFromEnvironment(LOGS_INJECTION_ENABLED, DEFAULT_LOGS_INJECTION_ENABLED); - reportHostName = - getPropertyBooleanValue(properties, TRACE_REPORT_HOSTNAME, parent.reportHostName); - - traceAnnotations = properties.getProperty(TRACE_ANNOTATIONS, parent.traceAnnotations); - - traceMethods = properties.getProperty(TRACE_METHODS, parent.traceMethods); - - traceExecutorsAll = - getPropertyBooleanValue(properties, TRACE_EXECUTORS_ALL, parent.traceExecutorsAll); - traceExecutors = getPropertyListValue(properties, TRACE_EXECUTORS, parent.traceExecutors); - - traceAnalyticsEnabled = - getPropertyBooleanValue(properties, TRACE_ANALYTICS_ENABLED, parent.traceAnalyticsEnabled); - - traceSamplingServiceRules = - getPropertyMapValue( - properties, TRACE_SAMPLING_SERVICE_RULES, parent.traceSamplingServiceRules); - traceSamplingOperationRules = - getPropertyMapValue( - properties, TRACE_SAMPLING_OPERATION_RULES, parent.traceSamplingOperationRules); - traceSampleRate = getPropertyDoubleValue(properties, TRACE_SAMPLE_RATE, parent.traceSampleRate); - traceRateLimit = getPropertyDoubleValue(properties, TRACE_RATE_LIMIT, parent.traceRateLimit); - - profilingEnabled = - getPropertyBooleanValue(properties, PROFILING_ENABLED, parent.profilingEnabled); - profilingUrl = properties.getProperty(PROFILING_URL, parent.profilingUrl); - profilingTags = getPropertyMapValue(properties, PROFILING_TAGS, parent.profilingTags); - profilingStartDelay = - getPropertyIntegerValue(properties, PROFILING_START_DELAY, parent.profilingStartDelay); - profilingStartForceFirst = - getPropertyBooleanValue( - properties, PROFILING_START_FORCE_FIRST, parent.profilingStartForceFirst); - profilingUploadPeriod = - getPropertyIntegerValue(properties, PROFILING_UPLOAD_PERIOD, parent.profilingUploadPeriod); - profilingTemplateOverrideFile = - properties.getProperty( - PROFILING_TEMPLATE_OVERRIDE_FILE, parent.profilingTemplateOverrideFile); - profilingUploadTimeout = - getPropertyIntegerValue( - properties, PROFILING_UPLOAD_TIMEOUT, parent.profilingUploadTimeout); - profilingUploadCompression = - properties.getProperty(PROFILING_UPLOAD_COMPRESSION, parent.profilingUploadCompression); - profilingProxyHost = properties.getProperty(PROFILING_PROXY_HOST, parent.profilingProxyHost); - profilingProxyPort = - getPropertyIntegerValue(properties, PROFILING_PROXY_PORT, parent.profilingProxyPort); - profilingProxyUsername = - properties.getProperty(PROFILING_PROXY_USERNAME, parent.profilingProxyUsername); - profilingProxyPassword = - properties.getProperty(PROFILING_PROXY_PASSWORD, parent.profilingProxyPassword); - - profilingExceptionSampleLimit = - getPropertyIntegerValue( - properties, PROFILING_EXCEPTION_SAMPLE_LIMIT, parent.profilingExceptionSampleLimit); - - profilingExceptionHistogramTopItems = - getPropertyIntegerValue( - properties, - PROFILING_EXCEPTION_HISTOGRAM_TOP_ITEMS, - parent.profilingExceptionHistogramTopItems); - profilingExceptionHistogramMaxCollectionSize = - getPropertyIntegerValue( - properties, - PROFILING_EXCEPTION_HISTOGRAM_MAX_COLLECTION_SIZE, - parent.profilingExceptionHistogramMaxCollectionSize); - - } - - /** - * @return A map of tags to be applied only to the local application root span. - */ - public Map getLocalRootSpanTags() { - final Map runtimeTags = getRuntimeTags(); - final Map result = new HashMap<>(runtimeTags); - result.put(LANGUAGE_TAG_KEY, LANGUAGE_TAG_VALUE); - - if (reportHostName) { - final String hostName = getHostName(); - if (null != hostName && !hostName.isEmpty()) { - result.put(INTERNAL_HOST_NAME, hostName); - } - } - - return Collections.unmodifiableMap(result); - } - - public Map getMergedSpanTags() { - // Do not include runtimeId into span tags: we only want that added to the root span - final Map result = newHashMap(getGlobalTags().size() + spanTags.size()); - result.putAll(getGlobalTags()); - result.putAll(spanTags); - return Collections.unmodifiableMap(result); - } - - public Map getMergedJmxTags() { - final Map runtimeTags = getRuntimeTags(); - final Map result = - newHashMap( - getGlobalTags().size() + jmxTags.size() + runtimeTags.size() + 1 /* for serviceName */); - result.putAll(getGlobalTags()); - result.putAll(jmxTags); - result.putAll(runtimeTags); - // service name set here instead of getRuntimeTags because apm already manages the service tag - // and may chose to override it. - // Additionally, infra/JMX metrics require `service` rather than APM's `service.name` tag - result.put(SERVICE_TAG, serviceName); - return Collections.unmodifiableMap(result); - } - - public Map getMergedProfilingTags() { - final Map runtimeTags = getRuntimeTags(); - final String host = getHostName(); - final Map result = - newHashMap( - getGlobalTags().size() - + profilingTags.size() - + runtimeTags.size() - + 3 /* for serviceName and host and language */); - result.put(HOST_TAG, host); // Host goes first to allow to override it - result.putAll(getGlobalTags()); - result.putAll(profilingTags); - result.putAll(runtimeTags); - // service name set here instead of getRuntimeTags because apm already manages the service tag - // and may chose to override it. - result.put(SERVICE_TAG, serviceName); - result.put(LANGUAGE_TAG_KEY, LANGUAGE_TAG_VALUE); - return Collections.unmodifiableMap(result); - } - - /** - * Returns the sample rate for the specified instrumentation or {@link - * #DEFAULT_ANALYTICS_SAMPLE_RATE} if none specified. - */ - public float getInstrumentationAnalyticsSampleRate(final String... aliases) { - for (final String alias : aliases) { - final Float rate = getFloatSettingFromEnvironment(alias + ".analytics.sample-rate", null); - if (null != rate) { - return rate; - } - } - return DEFAULT_ANALYTICS_SAMPLE_RATE; - } - - /** - * Provide 'global' tags, i.e. tags set everywhere. We have to support old (dd.trace.global.tags) - * version of this setting if new (dd.tags) version has not been specified. - */ - private Map getGlobalTags() { - return tags; - } - - /** - * Return a map of tags required by the datadog backend to link runtime metrics (i.e. jmx) and - * traces. - * - *

These tags must be applied to every runtime metrics and placed on the root span of every - * trace. - * - * @return A map of tag-name -> tag-value - */ - private Map getRuntimeTags() { - final Map result = newHashMap(2); - result.put(RUNTIME_ID_TAG, runtimeId); - return Collections.unmodifiableMap(result); - } - - public String getFinalProfilingUrl() { - if (profilingUrl == null) { - return String.format(Locale.US, PROFILING_URL_TEMPLATE, site); - } else { - return profilingUrl; - } - } - - public boolean isIntegrationEnabled( - final SortedSet integrationNames, final boolean defaultEnabled) { - return integrationEnabled(integrationNames, defaultEnabled); - } - - /** - * @param integrationNames - * @param defaultEnabled - * @return - * @deprecated This method should only be used internally. Use the instance getter instead {@link - * #isIntegrationEnabled(SortedSet, boolean)}. - */ - @Deprecated - private static boolean integrationEnabled( - final SortedSet integrationNames, final boolean defaultEnabled) { - // If default is enabled, we want to enable individually, - // if default is disabled, we want to disable individually. - boolean anyEnabled = defaultEnabled; - for (final String name : integrationNames) { - final boolean configEnabled = - getBooleanSettingFromEnvironment("integration." + name + ".enabled", defaultEnabled); - if (defaultEnabled) { - anyEnabled &= configEnabled; - } else { - anyEnabled |= configEnabled; - } - } - return anyEnabled; - } - - public boolean isJmxFetchIntegrationEnabled( - final SortedSet integrationNames, final boolean defaultEnabled) { - return jmxFetchIntegrationEnabled(integrationNames, defaultEnabled); - } - - public boolean isRuleEnabled(final String name) { - return getBooleanSettingFromEnvironment("trace." + name + ".enabled", true) - && getBooleanSettingFromEnvironment("trace." + name.toLowerCase(Locale.US) + ".enabled", true); - } - - /** - * @param integrationNames - * @param defaultEnabled - * @return - * @deprecated This method should only be used internally. Use the instance getter instead {@link - * #isJmxFetchIntegrationEnabled(SortedSet, boolean)}. - */ - @Deprecated - public static boolean jmxFetchIntegrationEnabled( - final SortedSet integrationNames, final boolean defaultEnabled) { - // If default is enabled, we want to enable individually, - // if default is disabled, we want to disable individually. - boolean anyEnabled = defaultEnabled; - for (final String name : integrationNames) { - final boolean configEnabled = - getBooleanSettingFromEnvironment("jmxfetch." + name + ".enabled", defaultEnabled); - if (defaultEnabled) { - anyEnabled &= configEnabled; - } else { - anyEnabled |= configEnabled; - } - } - return anyEnabled; - } - - public boolean isTraceAnalyticsIntegrationEnabled( - final SortedSet integrationNames, final boolean defaultEnabled) { - return traceAnalyticsIntegrationEnabled(integrationNames, defaultEnabled); - } - - /** - * @param integrationNames - * @param defaultEnabled - * @return - * @deprecated This method should only be used internally. Use the instance getter instead {@link - * #isTraceAnalyticsIntegrationEnabled(SortedSet, boolean)}. - */ - @Deprecated - public static boolean traceAnalyticsIntegrationEnabled( - final SortedSet integrationNames, final boolean defaultEnabled) { - // If default is enabled, we want to enable individually, - // if default is disabled, we want to disable individually. - boolean anyEnabled = defaultEnabled; - for (final String name : integrationNames) { - final boolean configEnabled = - getBooleanSettingFromEnvironment(name + ".analytics.enabled", defaultEnabled); - if (defaultEnabled) { - anyEnabled &= configEnabled; - } else { - anyEnabled |= configEnabled; - } - } - return anyEnabled; - } - - /** - * Helper method that takes the name, adds a "dd." prefix then checks for System Properties of - * that name. If none found, the name is converted to an Environment Variable and used to check - * the env. If none of the above returns a value, then an optional properties file if checked. If - * setting is not configured in either location, defaultValue is returned. - * - * @param name - * @param defaultValue - * @return - * @deprecated This method should only be used internally. Use the explicit getter instead. - */ - @Deprecated - public static String getSettingFromEnvironment(final String name, final String defaultValue) { - String value; - final String systemPropertyName = propertyNameToSystemPropertyName(name); - - // System properties and properties provided from command line have the highest precedence - value = System.getProperties().getProperty(systemPropertyName); - if (null != value) { - return value; - } - - // getting setting from env or from config file is not supported on Android - - return defaultValue; - } - - /** - * @deprecated This method should only be used internally. Use the explicit getter instead. - */ - @Deprecated - private static Map getMapSettingFromEnvironment( - final String name, final String defaultValue) { - return parseMap( - getSettingFromEnvironment(name, defaultValue), propertyNameToSystemPropertyName(name)); - } - - /** - * Calls {@link #getSettingFromEnvironment(String, String)} and converts the result to a list by - * splitting on `,`. - * - * @deprecated This method should only be used internally. Use the explicit getter instead. - */ - @Deprecated - private static List getListSettingFromEnvironment( - final String name, final String defaultValue) { - return parseList(getSettingFromEnvironment(name, defaultValue)); - } - - /** - * Calls {@link #getSettingFromEnvironment(String, String)} and converts the result to a Boolean. - * - * @deprecated This method should only be used internally. Use the explicit getter instead. - */ - @Deprecated - public static Boolean getBooleanSettingFromEnvironment( - final String name, final Boolean defaultValue) { - return getSettingFromEnvironmentWithLog(name, Boolean.class, defaultValue); - } - - /** - * Calls {@link #getSettingFromEnvironment(String, String)} and converts the result to a Float. - * - * @deprecated This method should only be used internally. Use the explicit getter instead. - */ - @Deprecated - public static Float getFloatSettingFromEnvironment(final String name, final Float defaultValue) { - return getSettingFromEnvironmentWithLog(name, Float.class, defaultValue); - } - - /** - * Calls {@link #getSettingFromEnvironment(String, String)} and converts the result to a Double. - * - * @deprecated This method should only be used internally. Use the explicit getter instead. - */ - @Deprecated - private static Double getDoubleSettingFromEnvironment( - final String name, final Double defaultValue) { - return getSettingFromEnvironmentWithLog(name, Double.class, defaultValue); - } - - /** - * Calls {@link #getSettingFromEnvironment(String, String)} and converts the result to a Integer. - */ - private static Integer getIntegerSettingFromEnvironment( - final String name, final Integer defaultValue) { - return getSettingFromEnvironmentWithLog(name, Integer.class, defaultValue); - } - - private static T getSettingFromEnvironmentWithLog( - final String name, Class tClass, final T defaultValue) { - try { - return valueOf(getSettingFromEnvironment(name, null), tClass, defaultValue); - } catch (final NumberFormatException e) { - return defaultValue; - } - } - - /** - * Calls {@link #getSettingFromEnvironment(String, String)} and converts the result to a set of - * strings splitting by space or comma. - */ - private static Set getPropagationStyleSetSettingFromEnvironmentOrDefault( - final String name, final String defaultValue) { - final String value = getSettingFromEnvironment(name, defaultValue); - Set result = - convertStringSetToPropagationStyleSet(parseStringIntoSetOfNonEmptyStrings(value)); - - if (result.isEmpty()) { - // Treat empty parsing result as no value and use default instead - result = - convertStringSetToPropagationStyleSet(parseStringIntoSetOfNonEmptyStrings(defaultValue)); - } - - return result; - } - - private static Set getIntegerRangeSettingFromEnvironment( - final String name, final Set defaultValue) { - final String value = getSettingFromEnvironment(name, null); - try { - return value == null ? defaultValue : parseIntegerRangeSet(value, name); - } catch (final NumberFormatException e) { - return defaultValue; - } - } - - /** - * Converts the property name, e.g. 'service.name' into a public environment variable name, e.g. - * `DD_SERVICE_NAME`. - * - * @param setting The setting name, e.g. `service.name` - * @return The public facing environment variable name - */ - private static String propertyNameToEnvironmentVariableName(final String setting) { - return ENV_REPLACEMENT - .matcher(propertyNameToSystemPropertyName(setting).toUpperCase(Locale.US)) - .replaceAll("_"); - } - - /** - * Converts the property name, e.g. 'service.name' into a public system property name, e.g. - * `dd.service.name`. - * - * @param setting The setting name, e.g. `service.name` - * @return The public facing system property name - */ - private static String propertyNameToSystemPropertyName(final String setting) { - return PREFIX + setting; - } - - /** - * @param value to parse by tClass::valueOf - * @param tClass should contain static parsing method "T valueOf(String)" - * @param defaultValue - * @param - * @return value == null || value.trim().isEmpty() ? defaultValue : tClass.valueOf(value) - * @throws NumberFormatException - */ - private static T valueOf( - final String value, final Class tClass, final T defaultValue) { - if (value == null || value.trim().isEmpty()) { - return defaultValue; - } - try { - Method method = tClass.getMethod("valueOf", String.class); - return (T) method.invoke(null, value); - } catch (NumberFormatException e) { - throw e; - } catch (NoSuchMethodException | IllegalAccessException e) { - throw new NumberFormatException(e.toString()); - } catch (Throwable e) { - throw new NumberFormatException(e.toString()); - } - } - - private static Map getPropertyMapValue( - final Properties properties, final String name, final Map defaultValue) { - final String value = properties.getProperty(name); - return value == null || value.trim().isEmpty() ? defaultValue : parseMap(value, name); - } - - private static List getPropertyListValue( - final Properties properties, final String name, final List defaultValue) { - final String value = properties.getProperty(name); - return value == null || value.trim().isEmpty() ? defaultValue : parseList(value); - } - - private static Boolean getPropertyBooleanValue( - final Properties properties, final String name, final Boolean defaultValue) { - return valueOf(properties.getProperty(name), Boolean.class, defaultValue); - } - - private static Integer getPropertyIntegerValue( - final Properties properties, final String name, final Integer defaultValue) { - return valueOf(properties.getProperty(name), Integer.class, defaultValue); - } - - private static Double getPropertyDoubleValue( - final Properties properties, final String name, final Double defaultValue) { - return valueOf(properties.getProperty(name), Double.class, defaultValue); - } - - private static Set getPropagationStyleSetFromPropertyValue( - final Properties properties, final String name) { - final String value = properties.getProperty(name); - if (value != null) { - final Set result = - convertStringSetToPropagationStyleSet(parseStringIntoSetOfNonEmptyStrings(value)); - if (!result.isEmpty()) { - return result; - } - } - // null means parent value should be used - return null; - } - - private static Set getPropertyIntegerRangeValue( - final Properties properties, final String name, final Set defaultValue) { - final String value = properties.getProperty(name); - try { - return value == null ? defaultValue : parseIntegerRangeSet(value, name); - } catch (final NumberFormatException e) { - return defaultValue; - } - } - - private static Map parseMap(final String str, final String settingName) { - // If we ever want to have default values besides an empty map, this will need to change. - if (str == null || str.trim().isEmpty()) { - return Collections.emptyMap(); - } - if (!str.matches("(([^,:]+:[^,:]*,)*([^,:]+:[^,:]*),?)?")) { - return Collections.emptyMap(); - } - - final String[] tokens = str.split(",", -1); - final Map map = newHashMap(tokens.length); - - for (final String token : tokens) { - final String[] keyValue = token.split(":", -1); - if (keyValue.length == 2) { - final String key = keyValue[0].trim(); - final String value = keyValue[1].trim(); - if (value.length() <= 0) { - continue; - } - map.put(key, value); - } - } - return Collections.unmodifiableMap(map); - } - - private static Set parseIntegerRangeSet(String str, final String settingName) - throws NumberFormatException { - str = str.replaceAll("\\s", ""); - if (!str.matches("\\d{3}(?:-\\d{3})?(?:,\\d{3}(?:-\\d{3})?)*")) { - throw new NumberFormatException(); - } - - final String[] tokens = str.split(",", -1); - final Set set = new HashSet<>(); - - for (final String token : tokens) { - final String[] range = token.split("-", -1); - if (range.length == 1) { - set.add(Integer.parseInt(range[0])); - } else if (range.length == 2) { - final int left = Integer.parseInt(range[0]); - final int right = Integer.parseInt(range[1]); - final int min = Math.min(left, right); - final int max = Math.max(left, right); - for (int i = min; i <= max; i++) { - set.add(i); - } - } - } - return Collections.unmodifiableSet(set); - } - - private static Map newHashMap(final int size) { - return new HashMap<>(size + 1, 1f); - } - - /** - * @param map - * @param propNames - * @return new unmodifiable copy of {@param map} where properties are overwritten from environment - */ - private static Map getMapWithPropertiesDefinedByEnvironment( - final Map map, final String... propNames) { - final Map res = new HashMap<>(map); - for (final String propName : propNames) { - final String val = getSettingFromEnvironment(propName, null); - if (val != null) { - res.put(propName, val); - } - } - return Collections.unmodifiableMap(res); - } - - /** - * same as {@link Config#getMapWithPropertiesDefinedByEnvironment(Map, String...)} but using - * {@code properties} as source of values to overwrite inside map - * - * @param map - * @param properties - * @param keys - * @return - */ - private static Map overwriteKeysFromProperties( - final Map map, - final Properties properties, - final String... keys) { - final Map res = new HashMap<>(map); - for (final String propName : keys) { - final String val = properties.getProperty(propName, null); - if (val != null) { - res.put(propName, val); - } - } - return Collections.unmodifiableMap(res); - } - - private static List parseList(final String str) { - if (str == null || str.trim().isEmpty()) { - return Collections.emptyList(); - } - - final String[] tokens = str.split(",", -1); - // Remove whitespace from each item. - for (int i = 0; i < tokens.length; i++) { - tokens[i] = tokens[i].trim(); - } - return Collections.unmodifiableList(Arrays.asList(tokens)); - } - - private static Set parseStringIntoSetOfNonEmptyStrings(final String str) { - // Using LinkedHashSet to preserve original string order - final Set result = new LinkedHashSet<>(); - // Java returns single value when splitting an empty string. We do not need that value, so - // we need to throw it out. - for (final String value : str.split(SPLIT_BY_SPACE_OR_COMMA_REGEX)) { - if (!value.isEmpty()) { - result.add(value); - } - } - return Collections.unmodifiableSet(result); - } - - private static Set convertStringSetToPropagationStyleSet( - final Set input) { - // Using LinkedHashSet to preserve original string order - final Set result = new LinkedHashSet<>(); - for (final String value : input) { - try { - result.add(PropagationStyle.valueOf(value.toUpperCase(Locale.US))); - } catch (final IllegalArgumentException e) { - } - } - return Collections.unmodifiableSet(result); - } - - /** - * Loads the optional configuration properties file into the global {@link Properties} object. - * - * @return The {@link Properties} object. the returned instance might be empty of file does not - * exist or if it is in a wrong format. - */ - private static Properties loadConfigurationFile() { - final Properties properties = new Properties(); - - // Reading from system property first and from env after - String configurationFilePath = - System.getProperty(propertyNameToSystemPropertyName(CONFIGURATION_FILE)); - if (null == configurationFilePath) { - configurationFilePath = - System.getenv(propertyNameToEnvironmentVariableName(CONFIGURATION_FILE)); - } - if (null == configurationFilePath) { - return properties; - } - - // Normalizing tilde (~) paths for unix systems - configurationFilePath = - configurationFilePath.replaceFirst("^~", System.getProperty("user.home")); - - // Configuration properties file is optional - final File configurationFile = new File(configurationFilePath); - if (!configurationFile.exists()) { - return properties; - } - - try (final FileReader fileReader = new FileReader(configurationFile)) { - properties.load(fileReader); - } catch (final FileNotFoundException fnf) { - } catch (final IOException ioe) { - } - - return properties; - } - - /** - * Returns the detected hostname. First tries locally, then using DNS - */ - private static String getHostName() { - String possibleHostname; - - // Try environment variable. This works in almost all environments - if (System.getProperty("os.name").startsWith("Windows")) { - possibleHostname = System.getenv("COMPUTERNAME"); - } else { - possibleHostname = System.getenv("HOSTNAME"); - } - - if (possibleHostname != null && !possibleHostname.isEmpty()) { - return possibleHostname.trim(); - } - - // Try hostname command - try (final BufferedReader reader = - new BufferedReader( - new InputStreamReader(Runtime.getRuntime().exec("hostname").getInputStream()))) { - possibleHostname = reader.readLine(); - } catch (final Exception ignore) { - // Ignore. Hostname command is not always available - } - - if (possibleHostname != null && !possibleHostname.isEmpty()) { - return possibleHostname.trim(); - } - - // From DNS - try { - return InetAddress.getLocalHost().getHostName(); - } catch (final UnknownHostException e) { - // If we are not able to detect the hostname we do not throw an exception. - } - - return null; - } - - // This has to be placed after all other static fields to give them a chance to initialize - private static final Config INSTANCE = new Config(); - - public static Config get() { - return INSTANCE; - } - - public static Config get(final Properties properties) { - if (properties == null || properties.isEmpty()) { - return INSTANCE; - } else { - return new Config(properties, INSTANCE); - } - } - - // region GENERATED GETTERS - - public String getRuntimeId() { - return runtimeId; - } - - public String getSite() { - return site; - } - - public String getServiceName() { - return serviceName; - } - - public boolean isTraceEnabled() { - return traceEnabled; - } - - public boolean isIntegrationsEnabled() { - return integrationsEnabled; - } - - public String getWriterType() { - return writerType; - } - - public String getAgentHost() { - return agentHost; - } - - public int getAgentPort() { - return agentPort; - } - - public String getAgentUnixDomainSocket() { - return agentUnixDomainSocket; - } - - public boolean isPrioritySamplingEnabled() { - return prioritySamplingEnabled; - } - - public boolean isTraceResolverEnabled() { - return traceResolverEnabled; - } - - public Map getServiceMapping() { - return serviceMapping; - } - - public List getExcludedClasses() { - return excludedClasses; - } - - public Map getHeaderTags() { - return headerTags; - } - - public Set getHttpServerErrorStatuses() { - return httpServerErrorStatuses; - } - - public Set getHttpClientErrorStatuses() { - return httpClientErrorStatuses; - } - - public boolean isHttpServerTagQueryString() { - return httpServerTagQueryString; - } - - public boolean isHttpClientTagQueryString() { - return httpClientTagQueryString; - } - - public boolean isHttpClientSplitByDomain() { - return httpClientSplitByDomain; - } - - public boolean isDbClientSplitByInstance() { - return dbClientSplitByInstance; - } - - public Set getSplitByTags() { - return splitByTags; - } - - public Integer getScopeDepthLimit() { - return scopeDepthLimit; - } - - public Integer getPartialFlushMinSpans() { - return partialFlushMinSpans; - } - - public boolean isRuntimeContextFieldInjection() { - return runtimeContextFieldInjection; - } - - public Set getPropagationStylesToExtract() { - return propagationStylesToExtract; - } - - public Set getPropagationStylesToInject() { - return propagationStylesToInject; - } - - public boolean isJmxFetchEnabled() { - return jmxFetchEnabled; - } - - public String getJmxFetchConfigDir() { - return jmxFetchConfigDir; - } - - public List getJmxFetchConfigs() { - return jmxFetchConfigs; - } - - public List getJmxFetchMetricsConfigs() { - return jmxFetchMetricsConfigs; - } - - public Integer getJmxFetchCheckPeriod() { - return jmxFetchCheckPeriod; - } - - public Integer getJmxFetchRefreshBeansPeriod() { - return jmxFetchRefreshBeansPeriod; - } - - public String getJmxFetchStatsdHost() { - return jmxFetchStatsdHost; - } - - public Integer getJmxFetchStatsdPort() { - return jmxFetchStatsdPort; - } - - public boolean isHealthMetricsEnabled() { - return healthMetricsEnabled; - } - - public String getHealthMetricsStatsdHost() { - return healthMetricsStatsdHost; - } - - public Integer getHealthMetricsStatsdPort() { - return healthMetricsStatsdPort; - } - - public boolean isLogsInjectionEnabled() { - return logsInjectionEnabled; - } - - public boolean isReportHostName() { - return reportHostName; - } - - public String getTraceAnnotations() { - return traceAnnotations; - } - - public String getTraceMethods() { - return traceMethods; - } - - public boolean isTraceExecutorsAll() { - return traceExecutorsAll; - } - - public List getTraceExecutors() { - return traceExecutors; - } - - public boolean isTraceAnalyticsEnabled() { - return traceAnalyticsEnabled; - } - - public Map getTraceSamplingServiceRules() { - return traceSamplingServiceRules; - } - - public Map getTraceSamplingOperationRules() { - return traceSamplingOperationRules; - } - - public Double getTraceSampleRate() { - return traceSampleRate; - } - - public Double getTraceRateLimit() { - return traceRateLimit; - } - - public boolean isProfilingEnabled() { - return profilingEnabled; - } - - public int getProfilingStartDelay() { - return profilingStartDelay; - } - - public boolean isProfilingStartForceFirst() { - return profilingStartForceFirst; - } - - public int getProfilingUploadPeriod() { - return profilingUploadPeriod; - } - - public String getProfilingTemplateOverrideFile() { - return profilingTemplateOverrideFile; - } - - public int getProfilingUploadTimeout() { - return profilingUploadTimeout; - } - - public String getProfilingUploadCompression() { - return profilingUploadCompression; - } - - public String getProfilingProxyHost() { - return profilingProxyHost; - } - - public int getProfilingProxyPort() { - return profilingProxyPort; - } - - public String getProfilingProxyUsername() { - return profilingProxyUsername; - } - - public String getProfilingProxyPassword() { - return profilingProxyPassword; - } - - public int getProfilingExceptionSampleLimit() { - return profilingExceptionSampleLimit; - } - - public int getProfilingExceptionHistogramTopItems() { - return profilingExceptionHistogramTopItems; - } - - public int getProfilingExceptionHistogramMaxCollectionSize() { - return profilingExceptionHistogramMaxCollectionSize; - } - - // endregion - - // region GENERATED toString() - - @Override - public String toString() { - return "Config{" + - "runtimeId='" + runtimeId + '\'' + - ", site='" + site + '\'' + - ", serviceName='" + serviceName + '\'' + - ", traceEnabled=" + traceEnabled + - ", integrationsEnabled=" + integrationsEnabled + - ", writerType='" + writerType + '\'' + - ", agentHost='" + agentHost + '\'' + - ", agentPort=" + agentPort + - ", agentUnixDomainSocket='" + agentUnixDomainSocket + '\'' + - ", prioritySamplingEnabled=" + prioritySamplingEnabled + - ", traceResolverEnabled=" + traceResolverEnabled + - ", serviceMapping=" + serviceMapping + - ", tags=" + tags + - ", spanTags=" + spanTags + - ", jmxTags=" + jmxTags + - ", excludedClasses=" + excludedClasses + - ", headerTags=" + headerTags + - ", httpServerErrorStatuses=" + httpServerErrorStatuses + - ", httpClientErrorStatuses=" + httpClientErrorStatuses + - ", httpServerTagQueryString=" + httpServerTagQueryString + - ", httpClientTagQueryString=" + httpClientTagQueryString + - ", httpClientSplitByDomain=" + httpClientSplitByDomain + - ", dbClientSplitByInstance=" + dbClientSplitByInstance + - ", splitByTags=" + splitByTags + - ", scopeDepthLimit=" + scopeDepthLimit + - ", partialFlushMinSpans=" + partialFlushMinSpans + - ", runtimeContextFieldInjection=" + runtimeContextFieldInjection + - ", propagationStylesToExtract=" + propagationStylesToExtract + - ", propagationStylesToInject=" + propagationStylesToInject + - ", jmxFetchEnabled=" + jmxFetchEnabled + - ", jmxFetchConfigDir='" + jmxFetchConfigDir + '\'' + - ", jmxFetchConfigs=" + jmxFetchConfigs + - ", jmxFetchMetricsConfigs=" + jmxFetchMetricsConfigs + - ", jmxFetchCheckPeriod=" + jmxFetchCheckPeriod + - ", jmxFetchRefreshBeansPeriod=" + jmxFetchRefreshBeansPeriod + - ", jmxFetchStatsdHost='" + jmxFetchStatsdHost + '\'' + - ", jmxFetchStatsdPort=" + jmxFetchStatsdPort + - ", healthMetricsEnabled=" + healthMetricsEnabled + - ", healthMetricsStatsdHost='" + healthMetricsStatsdHost + '\'' + - ", healthMetricsStatsdPort=" + healthMetricsStatsdPort + - ", logsInjectionEnabled=" + logsInjectionEnabled + - ", reportHostName=" + reportHostName + - ", traceAnnotations='" + traceAnnotations + '\'' + - ", traceMethods='" + traceMethods + '\'' + - ", traceExecutorsAll=" + traceExecutorsAll + - ", traceExecutors=" + traceExecutors + - ", traceAnalyticsEnabled=" + traceAnalyticsEnabled + - ", traceSamplingServiceRules=" + traceSamplingServiceRules + - ", traceSamplingOperationRules=" + traceSamplingOperationRules + - ", traceSampleRate=" + traceSampleRate + - ", traceRateLimit=" + traceRateLimit + - ", profilingEnabled=" + profilingEnabled + - ", profilingUrl='" + profilingUrl + '\'' + - ", profilingTags=" + profilingTags + - ", profilingStartDelay=" + profilingStartDelay + - ", profilingStartForceFirst=" + profilingStartForceFirst + - ", profilingUploadPeriod=" + profilingUploadPeriod + - ", profilingTemplateOverrideFile='" + profilingTemplateOverrideFile + '\'' + - ", profilingUploadTimeout=" + profilingUploadTimeout + - ", profilingUploadCompression='" + profilingUploadCompression + '\'' + - ", profilingProxyHost='" + profilingProxyHost + '\'' + - ", profilingProxyPort=" + profilingProxyPort + - ", profilingProxyUsername='" + profilingProxyUsername + '\'' + - ", profilingProxyPassword='" + profilingProxyPassword + '\'' + - ", profilingExceptionSampleLimit=" + profilingExceptionSampleLimit + - ", profilingExceptionHistogramTopItems=" + profilingExceptionHistogramTopItems + - ", profilingExceptionHistogramMaxCollectionSize=" + profilingExceptionHistogramMaxCollectionSize + - '}'; - } - - - // endregion - -} diff --git a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/legacy/trace/api/DDSpanTypes.java b/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/legacy/trace/api/DDSpanTypes.java deleted file mode 100644 index a3fbf6de36..0000000000 --- a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/legacy/trace/api/DDSpanTypes.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. - * This product includes software developed at Datadog (https://www.datadoghq.com/). - * Copyright 2016-Present Datadog, Inc. - */ - -package com.datadog.legacy.trace.api; - -public class DDSpanTypes { - public static final String HTTP_CLIENT = "http"; - public static final String HTTP_SERVER = "web"; - @Deprecated public static final String WEB_SERVLET = HTTP_SERVER; - public static final String RPC = "rpc"; - public static final String CACHE = "cache"; - - public static final String SQL = "sql"; - public static final String MONGO = "mongodb"; - public static final String CASSANDRA = "cassandra"; - public static final String COUCHBASE = "db"; // Using generic for now. - public static final String REDIS = "redis"; - public static final String MEMCACHED = "memcached"; - public static final String ELASTICSEARCH = "elasticsearch"; - public static final String HIBERNATE = "hibernate"; - - public static final String MESSAGE_CLIENT = "queue"; - public static final String MESSAGE_CONSUMER = "queue"; - public static final String MESSAGE_PRODUCER = "queue"; -} diff --git a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/legacy/trace/api/DDTags.java b/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/legacy/trace/api/DDTags.java deleted file mode 100644 index 9a53bcdc70..0000000000 --- a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/legacy/trace/api/DDTags.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. - * This product includes software developed at Datadog (https://www.datadoghq.com/). - * Copyright 2016-Present Datadog, Inc. - */ - -package com.datadog.legacy.trace.api; - -public class DDTags { - public static final String SPAN_TYPE = "span.type"; - public static final String SERVICE_NAME = "service.name"; - public static final String RESOURCE_NAME = "resource.name"; - public static final String THREAD_NAME = "thread.name"; - public static final String THREAD_ID = "thread.id"; - public static final String DB_STATEMENT = "sql.query"; - - public static final String HTTP_QUERY = "http.query.string"; - public static final String HTTP_FRAGMENT = "http.fragment.string"; - - public static final String USER_NAME = "user.principal"; - - public static final String ERROR_MSG = "error.msg"; // string representing the error message - public static final String ERROR_TYPE = "error.type"; // string representing the type of the error - public static final String ERROR_STACK = "error.stack"; // human readable version of the stack - - public static final String ANALYTICS_SAMPLE_RATE = "_dd1.sr.eausr"; - @Deprecated public static final String EVENT_SAMPLE_RATE = ANALYTICS_SAMPLE_RATE; - - /** Manually force tracer to be keep the trace */ - public static final String MANUAL_KEEP = "manual.keep"; - /** Manually force tracer to be drop the trace */ - public static final String MANUAL_DROP = "manual.drop"; -} diff --git a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/legacy/trace/api/DDTraceApiInfo.java b/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/legacy/trace/api/DDTraceApiInfo.java deleted file mode 100644 index 94332b75c7..0000000000 --- a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/legacy/trace/api/DDTraceApiInfo.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. - * This product includes software developed at Datadog (https://www.datadoghq.com/). - * Copyright 2016-Present Datadog, Inc. - */ - -package com.datadog.legacy.trace.api; - -import java.io.BufferedReader; -import java.io.InputStreamReader; - -public class DDTraceApiInfo { - public static final String VERSION; - - static { - String v; - try (final BufferedReader br = - new BufferedReader( - new InputStreamReader( - DDTraceApiInfo.class.getResourceAsStream("/dd-trace-api.version"), "UTF-8"))) { - final StringBuilder sb = new StringBuilder(); - - for (int c = br.read(); c != -1; c = br.read()) sb.append((char) c); - - v = sb.toString().trim(); - } catch (final Exception e) { - v = "unknown"; - } - VERSION = v; - } - - public static void main(final String... args) { - System.out.println(VERSION); - } -} diff --git a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/legacy/trace/api/Trace.java b/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/legacy/trace/api/Trace.java deleted file mode 100644 index ca4b91026a..0000000000 --- a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/legacy/trace/api/Trace.java +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. - * This product includes software developed at Datadog (https://www.datadoghq.com/). - * Copyright 2016-Present Datadog, Inc. - */ - -package com.datadog.legacy.trace.api; - -import static java.lang.annotation.ElementType.METHOD; -import static java.lang.annotation.RetentionPolicy.RUNTIME; - -import java.lang.annotation.Retention; -import java.lang.annotation.Target; - -/** Set this annotation to a method so the dd-java-agent considers it for tracing. */ -@Retention(RUNTIME) -@Target(METHOD) -public @interface Trace { - - /** The operation name to set. By default it takes the method's name */ - String operationName() default ""; - - /** The resource name. By default it uses the same value as the operation name */ - String resourceName() default ""; -} diff --git a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/legacy/trace/api/Tracer.java b/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/legacy/trace/api/Tracer.java deleted file mode 100644 index 10b7f63947..0000000000 --- a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/legacy/trace/api/Tracer.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. - * This product includes software developed at Datadog (https://www.datadoghq.com/). - * Copyright 2016-Present Datadog, Inc. - */ - -package com.datadog.legacy.trace.api; - -import com.datadog.legacy.trace.api.interceptor.TraceInterceptor; -import com.datadog.legacy.trace.context.ScopeListener; - -/** A class with Datadog tracer features. */ -public interface Tracer { - - /** Get the trace id of the active trace. Returns 0 if there is no active trace. */ - String getTraceId(); - - /** - * Get the span id of the active span of the active trace. Returns 0 if there is no active trace. - */ - String getSpanId(); - - /** - * Add a new interceptor to the tracer. Interceptors with duplicate priority to existing ones are - * ignored. - * - * @param traceInterceptor - * @return false if an interceptor with same priority exists. - */ - boolean addTraceInterceptor(TraceInterceptor traceInterceptor); - - /** - * Attach a scope listener to the global scope manager - * - * @param listener listener to attach - */ - void addScopeListener(ScopeListener listener); -} diff --git a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/legacy/trace/api/interceptor/MutableSpan.java b/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/legacy/trace/api/interceptor/MutableSpan.java deleted file mode 100644 index d3cee1e460..0000000000 --- a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/legacy/trace/api/interceptor/MutableSpan.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. - * This product includes software developed at Datadog (https://www.datadoghq.com/). - * Copyright 2016-Present Datadog, Inc. - */ - -package com.datadog.legacy.trace.api.interceptor; - -import com.datadog.legacy.trace.api.DDTags; - -import java.util.Map; - -public interface MutableSpan { - - /** @return Start time with nanosecond scale, but millisecond resolution. */ - long getStartTime(); - - /** @return Duration with nanosecond scale. */ - long getDurationNano(); - - String getOperationName(); - - MutableSpan setOperationName(final String serviceName); - - String getServiceName(); - - MutableSpan setServiceName(final String serviceName); - - String getResourceName(); - - MutableSpan setResourceName(final String resourceName); - - Integer getSamplingPriority(); - - /** - * @deprecated Use {@link io.opentracing.Span#setTag(String, boolean)} instead using either tag - * names {@link DDTags#MANUAL_KEEP} or {@link - * DDTags#MANUAL_DROP}. - * @param newPriority - * @return - */ - @Deprecated - MutableSpan setSamplingPriority(final int newPriority); - - String getSpanType(); - - MutableSpan setSpanType(final String type); - - Map getTags(); - - MutableSpan setTag(final String tag, final String value); - - MutableSpan setTag(final String tag, final boolean value); - - MutableSpan setTag(final String tag, final Number value); - - Boolean isError(); - - MutableSpan setError(boolean value); - - /** @deprecated Use {@link #getLocalRootSpan()} instead. */ - @Deprecated - MutableSpan getRootSpan(); - - /** - * Returns the root span for current the trace fragment. In the context of distributed tracing - * this method returns the root span only for the fragment generated by the currently traced - * application. - * - * @return The root span for the current trace fragment. - */ - MutableSpan getLocalRootSpan(); - - /** - * By calling this method the span will be removed from the current active Trace without - * actually being persisted. - * - * Note: This method is meant for internal SDK usage. Be aware that if used this Span will - * be removed from the Trace and lost. - */ - public void drop(); -} diff --git a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/legacy/trace/api/interceptor/TraceInterceptor.java b/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/legacy/trace/api/interceptor/TraceInterceptor.java deleted file mode 100644 index 9bc59b4f22..0000000000 --- a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/legacy/trace/api/interceptor/TraceInterceptor.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. - * This product includes software developed at Datadog (https://www.datadoghq.com/). - * Copyright 2016-Present Datadog, Inc. - */ - -package com.datadog.legacy.trace.api.interceptor; - -import java.util.Collection; - -public interface TraceInterceptor { - - /** - * After a trace is "complete" but before it is written, it is provided to the interceptors to - * modify. The result following all interceptors is sampled then sent to the trace writer. - * - * @param trace - The collection of spans that represent a trace. Can be modified in place. Order - * of spans should not be relied upon. - * @return A potentially modified or replaced collection of spans. Must not be null. - */ - Collection onTraceComplete(Collection trace); - - /** - * @return A unique priority for sorting relative to other TraceInterceptors. Unique because - * interceptors are stored in a sorted set, so duplicates will not be added. - */ - int priority(); -} diff --git a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/legacy/trace/api/sampling/PrioritySampling.java b/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/legacy/trace/api/sampling/PrioritySampling.java deleted file mode 100644 index d4addbf5b8..0000000000 --- a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/legacy/trace/api/sampling/PrioritySampling.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. - * This product includes software developed at Datadog (https://www.datadoghq.com/). - * Copyright 2016-Present Datadog, Inc. - */ - -package com.datadog.legacy.trace.api.sampling; - -public class PrioritySampling { - /** - * Implementation detail of the client. will not be sent to the agent or propagated. - * - *

Internal value used when the priority sampling flag has not been set on the span context. - */ - public static final int UNSET = Integer.MIN_VALUE; - /** The sampler has decided to drop the trace. */ - public static final int SAMPLER_DROP = 0; - /** The sampler has decided to keep the trace. */ - public static final int SAMPLER_KEEP = 1; - /** The user has decided to drop the trace. */ - public static final int USER_DROP = -1; - /** The user has decided to keep the trace. */ - public static final int USER_KEEP = 2; - - private PrioritySampling() {} -} diff --git a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/legacy/trace/common/sampling/AbstractSampler.java b/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/legacy/trace/common/sampling/AbstractSampler.java deleted file mode 100644 index 7234793ab0..0000000000 --- a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/legacy/trace/common/sampling/AbstractSampler.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. - * This product includes software developed at Datadog (https://www.datadoghq.com/). - * Copyright 2016-Present Datadog, Inc. - */ - -package com.datadog.legacy.trace.common.sampling; - -import com.datadog.opentracing.DDSpan; - -import java.util.HashMap; -import java.util.Map; -import java.util.Map.Entry; -import java.util.regex.Pattern; - -@Deprecated -public abstract class AbstractSampler implements Sampler { - - /** Sample tags */ - protected Map skipTagsPatterns = new HashMap<>(); - - @Override - public boolean sample(final DDSpan span) { - - // Filter by tag values - for (final Entry entry : skipTagsPatterns.entrySet()) { - final Object value = span.getTags().get(entry.getKey()); - if (value != null) { - final String strValue = String.valueOf(value); - final Pattern skipPattern = entry.getValue(); - if (skipPattern.matcher(strValue).matches()) { - return false; - } - } - } - - return doSample(span); - } - - /** - * Pattern based skipping of tag values - * - * @param tag - * @param skipPattern - */ - @Deprecated - public void addSkipTagPattern(final String tag, final Pattern skipPattern) { - skipTagsPatterns.put(tag, skipPattern); - } - - protected abstract boolean doSample(DDSpan span); -} diff --git a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/legacy/trace/common/sampling/AllSampler.java b/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/legacy/trace/common/sampling/AllSampler.java deleted file mode 100644 index 5900d158c9..0000000000 --- a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/legacy/trace/common/sampling/AllSampler.java +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. - * This product includes software developed at Datadog (https://www.datadoghq.com/). - * Copyright 2016-Present Datadog, Inc. - */ - -package com.datadog.legacy.trace.common.sampling; - -import com.datadog.opentracing.DDSpan; - -/** Sampler that always says yes... */ -public class AllSampler extends AbstractSampler { - - @Override - public boolean doSample(final DDSpan span) { - return true; - } - - @Override - public String toString() { - return "AllSampler { sample=true }"; - } -} diff --git a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/legacy/trace/common/sampling/DeterministicSampler.java b/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/legacy/trace/common/sampling/DeterministicSampler.java deleted file mode 100644 index 097aa12510..0000000000 --- a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/legacy/trace/common/sampling/DeterministicSampler.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. - * This product includes software developed at Datadog (https://www.datadoghq.com/). - * Copyright 2016-Present Datadog, Inc. - */ - -package com.datadog.legacy.trace.common.sampling; - -import com.datadog.opentracing.DDSpan; -import com.datadog.opentracing.DDTracer; - -import java.math.BigDecimal; -import java.math.BigInteger; - -/** - * This implements the deterministic sampling algorithm used by the Datadog Agent as well as the - * tracers for other languages - */ -public class DeterministicSampler implements RateSampler { - private static final BigInteger KNUTH_FACTOR = new BigInteger("1111111111111111111"); - private static final BigDecimal SPAN_ID_MAX_AS_BIG_DECIMAL = - new BigDecimal(DDTracer.TRACE_ID_64_BITS_MAX); - private static final BigInteger MODULUS = new BigInteger("2").pow(64); - - private final BigInteger cutoff; - private final double rate; - - public DeterministicSampler(final double rate) { - this.rate = rate; - cutoff = new BigDecimal(rate).multiply(SPAN_ID_MAX_AS_BIG_DECIMAL).toBigInteger(); - } - - @Override - public boolean sample(final DDSpan span) { - final boolean sampled; - if (rate == 1) { - sampled = true; - } else if (rate == 0) { - sampled = false; - } else { - // we are going to use the spanId here instead as it is 64 bits and will not affect the - // sampling decision. This is the same approach used by the new CoreTracer code. - sampled = span.getSpanId().multiply(KNUTH_FACTOR).mod(MODULUS).compareTo(cutoff) < 0; - } - - return sampled; - } - - @Override - public double getSampleRate() { - return rate; - } -} diff --git a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/legacy/trace/common/sampling/PrioritySampler.java b/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/legacy/trace/common/sampling/PrioritySampler.java deleted file mode 100644 index ff893c9d7c..0000000000 --- a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/legacy/trace/common/sampling/PrioritySampler.java +++ /dev/null @@ -1,13 +0,0 @@ -/* - * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. - * This product includes software developed at Datadog (https://www.datadoghq.com/). - * Copyright 2016-Present Datadog, Inc. - */ - -package com.datadog.legacy.trace.common.sampling; - -import com.datadog.opentracing.DDSpan; - -public interface PrioritySampler { - void setSamplingPriority(DDSpan span); -} diff --git a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/legacy/trace/common/sampling/PrioritySampling.java b/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/legacy/trace/common/sampling/PrioritySampling.java deleted file mode 100644 index 5336432c5c..0000000000 --- a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/legacy/trace/common/sampling/PrioritySampling.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. - * This product includes software developed at Datadog (https://www.datadoghq.com/). - * Copyright 2016-Present Datadog, Inc. - */ - -package com.datadog.legacy.trace.common.sampling; - -/** @deprecated Replaced by {@link com.datadog.legacy.trace.api.sampling.PrioritySampling} . */ -@Deprecated -public class PrioritySampling { - /** - * Implementation detail of the client. will not be sent to the agent or propagated. - * - *

Internal value used when the priority sampling flag has not been set on the span context. - */ - public static final int UNSET = Integer.MIN_VALUE; - /** The sampler has decided to drop the trace. */ - public static final int SAMPLER_DROP = 0; - /** The sampler has decided to keep the trace. */ - public static final int SAMPLER_KEEP = 1; - /** The user has decided to drop the trace. */ - public static final int USER_DROP = -1; - /** The user has decided to keep the trace. */ - public static final int USER_KEEP = 2; - - private PrioritySampling() {} -} diff --git a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/legacy/trace/common/sampling/RateByServiceSampler.java b/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/legacy/trace/common/sampling/RateByServiceSampler.java deleted file mode 100644 index ecb785a204..0000000000 --- a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/legacy/trace/common/sampling/RateByServiceSampler.java +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. - * This product includes software developed at Datadog (https://www.datadoghq.com/). - * Copyright 2016-Present Datadog, Inc. - */ - -package com.datadog.legacy.trace.common.sampling; - -import static java.util.Collections.singletonMap; - -import com.datadog.legacy.trace.api.sampling.PrioritySampling; -import com.datadog.opentracing.DDSpan; - -import java.util.Map; - -/** - * A rate sampler which maintains different sample rates per service+env name. - * - *

The configuration of (serviceName,env)->rate is configured by the core agent. - */ -public class RateByServiceSampler implements Sampler, PrioritySampler { - public static final String SAMPLING_AGENT_RATE = "_dd.agent_psr"; - - /** Key for setting the default/baseline rate */ - private static final String DEFAULT_KEY = "service:,env:"; - - private static final double DEFAULT_RATE = 1.0; - - private volatile Map serviceRates; - - public RateByServiceSampler() { - this(DEFAULT_RATE); - } - - public RateByServiceSampler(Double defaultSampleRate) { - this.serviceRates = singletonMap(DEFAULT_KEY, createRateSampler(defaultSampleRate)); - } - - @Override - public boolean sample(final DDSpan span) { - // Priority sampling sends all traces to the core agent, including traces marked dropped. - // This allows the core agent to collect stats on all traces. - return true; - } - - /** If span is a root span, set the span context samplingPriority to keep or drop */ - @Override - public void setSamplingPriority(final DDSpan span) { - final String serviceName = span.getServiceName(); - final String env = getSpanEnv(span); - final String key = "service:" + serviceName + ",env:" + env; - - final Map rates = serviceRates; - RateSampler sampler = serviceRates.get(key); - if (sampler == null) { - sampler = rates.get(DEFAULT_KEY); - } - - final boolean priorityWasSet; - - if (sampler.sample(span)) { - priorityWasSet = span.context().setSamplingPriority(PrioritySampling.SAMPLER_KEEP); - } else { - priorityWasSet = span.context().setSamplingPriority(PrioritySampling.SAMPLER_DROP); - } - - // Only set metrics if we actually set the sampling priority - // We don't know until the call is completed because the lock is internal to DDSpanContext - if (priorityWasSet) { - span.context().setMetric(SAMPLING_AGENT_RATE, sampler.getSampleRate()); - } - } - - private static String getSpanEnv(final DDSpan span) { - return null == span.getTags().get("env") ? "" : String.valueOf(span.getTags().get("env")); - } - - private RateSampler createRateSampler(final double sampleRate) { - final double sanitizedRate; - if (sampleRate < 0) { - sanitizedRate = 1; - } else if (sampleRate > 1) { - sanitizedRate = 1; - } else { - sanitizedRate = sampleRate; - } - - return new DeterministicSampler(sanitizedRate); - } -} diff --git a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/legacy/trace/common/sampling/RateSampler.java b/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/legacy/trace/common/sampling/RateSampler.java deleted file mode 100644 index bbea484c82..0000000000 --- a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/legacy/trace/common/sampling/RateSampler.java +++ /dev/null @@ -1,11 +0,0 @@ -/* - * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. - * This product includes software developed at Datadog (https://www.datadoghq.com/). - * Copyright 2016-Present Datadog, Inc. - */ - -package com.datadog.legacy.trace.common.sampling; - -public interface RateSampler extends Sampler { - double getSampleRate(); -} diff --git a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/legacy/trace/common/sampling/Sampler.java b/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/legacy/trace/common/sampling/Sampler.java deleted file mode 100644 index 018898f29c..0000000000 --- a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/legacy/trace/common/sampling/Sampler.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. - * This product includes software developed at Datadog (https://www.datadoghq.com/). - * Copyright 2016-Present Datadog, Inc. - */ - -package com.datadog.legacy.trace.common.sampling; - -import com.datadog.legacy.trace.api.Config; -import com.datadog.opentracing.DDSpan; - -import java.util.Properties; - -/** Main interface to sample a collection of traces. */ -public interface Sampler { - - /** - * Sample a collection of traces based on the parent span - * - * @param span the parent span with its context - * @return true when the trace/spans has to be reported/written - */ - boolean sample(DDSpan span); - - final class Builder { - public static Sampler forConfig(final Config config) { - Sampler sampler; - if (config != null) { - if (config.isPrioritySamplingEnabled()) { - Double samplingRate = config.getTraceSampleRate(); - if (samplingRate != null) { - sampler = new RateByServiceSampler(config.getTraceSampleRate()); - } else { - sampler = new RateByServiceSampler(); - } - } else { - sampler = new AllSampler(); - } - } else { - sampler = new AllSampler(); - } - return sampler; - } - - public static Sampler forConfig(final Properties config) { - return forConfig(Config.get(config)); - } - - private Builder() {} - } -} diff --git a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/legacy/trace/common/sampling/SamplingRule.java b/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/legacy/trace/common/sampling/SamplingRule.java deleted file mode 100644 index e816b30568..0000000000 --- a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/legacy/trace/common/sampling/SamplingRule.java +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. - * This product includes software developed at Datadog (https://www.datadoghq.com/). - * Copyright 2016-Present Datadog, Inc. - */ - -package com.datadog.legacy.trace.common.sampling; - -import com.datadog.opentracing.DDSpan; - -import java.util.regex.Pattern; - -public abstract class SamplingRule { - private final RateSampler sampler; - - public SamplingRule(final RateSampler sampler) { - this.sampler = sampler; - } - - public abstract boolean matches(DDSpan span); - - public boolean sample(final DDSpan span) { - return sampler.sample(span); - } - - public RateSampler getSampler() { - return sampler; - } - - public static class AlwaysMatchesSamplingRule extends SamplingRule { - - public AlwaysMatchesSamplingRule(final RateSampler sampler) { - super(sampler); - } - - @Override - public boolean matches(final DDSpan span) { - return true; - } - } - - public abstract static class PatternMatchSamplingRule extends SamplingRule { - private final Pattern pattern; - - public PatternMatchSamplingRule(final String regex, final RateSampler sampler) { - super(sampler); - this.pattern = Pattern.compile(regex); - } - - @Override - public boolean matches(final DDSpan span) { - final String relevantString = getRelevantString(span); - return relevantString != null && pattern.matcher(relevantString).matches(); - } - - protected abstract String getRelevantString(DDSpan span); - } - - public static class ServiceSamplingRule extends PatternMatchSamplingRule { - public ServiceSamplingRule(final String regex, final RateSampler sampler) { - super(regex, sampler); - } - - @Override - protected String getRelevantString(final DDSpan span) { - return span.getServiceName(); - } - } - - public static class OperationSamplingRule extends PatternMatchSamplingRule { - public OperationSamplingRule(final String regex, final RateSampler sampler) { - super(regex, sampler); - } - - @Override - protected String getRelevantString(final DDSpan span) { - return span.getOperationName(); - } - } -} diff --git a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/legacy/trace/common/util/Clock.java b/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/legacy/trace/common/util/Clock.java deleted file mode 100644 index 6780148d31..0000000000 --- a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/legacy/trace/common/util/Clock.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. - * This product includes software developed at Datadog (https://www.datadoghq.com/). - * Copyright 2016-Present Datadog, Inc. - */ - -package com.datadog.legacy.trace.common.util; - -import java.util.concurrent.TimeUnit; - -/** - * A simple wrapper for system clock that aims to provide the current time - * - *

- * - *

- * - *

- * - *

The JDK provides two clocks: - *

  • one in nanoseconds, for precision, but it can only use to measure durations - *
  • one in milliseconds, for accuracy, useful to provide epoch time - * - *

    - * - *

    At this time, we are using a millis precision (converted to micros) in order to guarantee - * consistency between the span start times and the durations - */ -public class Clock { - - /** - * Get the current nanos ticks, this method can't be use for date accuracy (only duration - * calculations) - * - * @return The current nanos ticks - */ - public static long currentNanoTicks() { - return System.nanoTime(); - } - - /** - * Get the current time in micros. The actual precision is the millis - * - * @return the current epoch time in micros - */ - public static long currentMicroTime() { - return TimeUnit.MILLISECONDS.toMicros(System.currentTimeMillis()); - } - - /** - * Get the current time in nanos. The actual precision is the millis Note: this will overflow in - * ~290 years after epoch - * - * @return the current epoch time in nanos - */ - public static long currentNanoTime() { - return TimeUnit.MILLISECONDS.toNanos(System.currentTimeMillis()); - } -} diff --git a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/legacy/trace/common/writer/LoggingWriter.java b/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/legacy/trace/common/writer/LoggingWriter.java deleted file mode 100644 index e73120372e..0000000000 --- a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/legacy/trace/common/writer/LoggingWriter.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. - * This product includes software developed at Datadog (https://www.datadoghq.com/). - * Copyright 2016-Present Datadog, Inc. - */ - -package com.datadog.legacy.trace.common.writer; - -import com.datadog.opentracing.DDSpan; -import java.util.List; - -public class LoggingWriter implements Writer { - - @Override - public void write(final List trace) { - } - - @Override - public void incrementTraceCount() { - } - - @Override - public void close() { - } - - @Override - public void start() { - } - - @Override - public String toString() { - return "LoggingWriter { }"; - } -} diff --git a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/legacy/trace/common/writer/Writer.java b/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/legacy/trace/common/writer/Writer.java deleted file mode 100644 index 5063de1baa..0000000000 --- a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/legacy/trace/common/writer/Writer.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. - * This product includes software developed at Datadog (https://www.datadoghq.com/). - * Copyright 2016-Present Datadog, Inc. - */ - -package com.datadog.legacy.trace.common.writer; - -import com.datadog.opentracing.DDSpan; -import java.io.Closeable; -import java.util.List; - -/** A writer is responsible to send collected spans to some place */ -public interface Writer extends Closeable { - - /** - * Write a trace represented by the entire list of all the finished spans - * - * @param trace the list of spans to write - */ - void write(List trace); - - /** Start the writer */ - void start(); - - /** - * Indicates to the writer that no future writing will come and it should terminates all - * connections and tasks - */ - @Override - void close(); - - /** Count that a trace was captured for stats, but without reporting it. */ - void incrementTraceCount(); - -} diff --git a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/legacy/trace/context/TraceScope.java b/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/legacy/trace/context/TraceScope.java deleted file mode 100644 index e4aea21daf..0000000000 --- a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/legacy/trace/context/TraceScope.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. - * This product includes software developed at Datadog (https://www.datadoghq.com/). - * Copyright 2016-Present Datadog, Inc. - */ - -package com.datadog.legacy.trace.context; - -import java.io.Closeable; - -/** An object which can propagate a datadog trace across multiple threads. */ -public interface TraceScope extends Closeable { - /** - * Prevent the trace attached to this TraceScope from reporting until the returned Continuation - * finishes. - * - *

    Should be called on the parent thread. - */ - Continuation capture(); - - /** Close the activated context and allow any underlying spans to finish. */ - @Override - void close(); - - /** If true, this context will propagate across async boundaries. */ - boolean isAsyncPropagating(); - - /** - * Enable or disable async propagation. Async propagation is initially set to false. - * - * @param value The new propagation value. True == propagate. False == don't propagate. - */ - void setAsyncPropagation(boolean value); - - /** Used to pass async context between workers. */ - interface Continuation { - /** - * Activate the continuation. - * - *

    Should be called on the child thread. - */ - TraceScope activate(); - - /** - * Cancel the continuation. This also closes parent scope. - * - *

    FIXME: the fact that this is closing parent scope is confusing, we should review this in - * new API. - */ - void close(); - - /** - * Close the continuation. - * - * @param closeContinuationScope true iff parent scope should also be closed - */ - void close(boolean closeContinuationScope); - } -} diff --git a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/opentracing/DDSpan.java b/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/opentracing/DDSpan.java deleted file mode 100644 index 5e83de490a..0000000000 --- a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/opentracing/DDSpan.java +++ /dev/null @@ -1,430 +0,0 @@ -/* - * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. - * This product includes software developed at Datadog (https://www.datadoghq.com/). - * Copyright 2016-Present Datadog, Inc. - */ - -package com.datadog.opentracing; - -import com.datadog.android.api.InternalLogger; -import com.datadog.legacy.trace.api.DDTags; -import com.datadog.legacy.trace.api.interceptor.MutableSpan; -import com.datadog.legacy.trace.api.sampling.PrioritySampling; -import com.datadog.legacy.trace.common.util.Clock; -import io.opentracing.Span; -import io.opentracing.tag.Tag; -import java.io.PrintWriter; -import java.io.StringWriter; -import java.lang.ref.WeakReference; -import java.math.BigInteger; -import java.util.HashMap; -import java.util.Map; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicLong; - -/** - * Represents a period of time. Associated information is stored in the SpanContext. - * - *

    Spans are created by the {@link DDTracer#buildSpan}. This implementation adds some features - * according to the DD agent. - */ -public class DDSpan implements Span, MutableSpan { - - /** The context attached to the span */ - private final DDSpanContext context; - - /** - * Creation time of the span in microseconds provided by external clock. Must be greater than - * zero. - */ - private final long startTimeMicro; - - /** - * Creation time of span in nanoseconds. We use combination of millisecond-precision clock and - * nanosecond-precision offset from start of the trace. See {@link PendingTrace} for details. Must - * be greater than zero. - */ - private final long startTimeNano; - - /** - * The duration in nanoseconds computed using the startTimeMicro or startTimeNano. Span is - * considered finished when this is set. - */ - private final AtomicLong durationNano = new AtomicLong(); - - /** Delegates to for handling the logs if present. */ - private final LogHandler logHandler; - - /** Implementation detail. Stores the weak reference to this span. Used by TraceCollection. */ - volatile WeakReference ref; - - /** The internal logger to report warnings/errors in the span lifecycle. */ - private final InternalLogger internalLogger; - - /** - * Spans should be constructed using the builder, not by calling the constructor directly. - * - * @param timestampMicro if greater than zero, use this time instead of the current time - * @param context the context used for the span - */ - DDSpan(final long timestampMicro, final DDSpanContext context) { - this(timestampMicro, context, new DefaultLogHandler(), InternalLogger.Companion.getUNBOUND()); - } - - /** - * Spans should be constructed using the builder, not by calling the constructor directly. - * - * @param timestampMicro if greater than zero, use this time instead of the current time - * @param context the context used for the span - * @param logHandler as the handler where to delegate the log actions - * @param internalLogger as the internal logger to report mishaps in the span lifecycle - */ - DDSpan( - final long timestampMicro, - final DDSpanContext context, - final LogHandler logHandler, - final InternalLogger internalLogger - ) { - this.context = context; - this.logHandler = logHandler; - this.internalLogger = internalLogger; - - if (timestampMicro <= 0L) { - // record the start time - startTimeMicro = Clock.currentMicroTime(); - startTimeNano = context.getTrace().getCurrentTimeNano(); - } else { - startTimeMicro = timestampMicro; - // Timestamp have come from an external clock, so use startTimeNano as a flag - startTimeNano = 0; - } - - context.getTrace().registerSpan(this); - } - - public boolean isFinished() { - return durationNano.get() != 0; - } - - private void finishAndAddToTrace(final long durationNano) { - // ensure a min duration of 1 - if (this.durationNano.compareAndSet(0, Math.max(1, durationNano))) { - context.getTrace().addSpan(this); - } else { - internalLogger.log( - InternalLogger.Level.WARN, - InternalLogger.Target.USER, - () -> "Span " + getOperationName() + " finished but duration already set; " + - "dropped spanId:" + getSpanId() + " traceid:" + getTraceId(), - null, - false, - new HashMap<>() - ); - } - } - - @Override - public final void finish() { - if (startTimeNano > 0) { - // no external clock was used, so we can rely on nano time - finishAndAddToTrace(context.getTrace().getCurrentTimeNano() - startTimeNano); - } else { - finish(Clock.currentMicroTime()); - } - } - - @Override - public final void finish(final long stoptimeMicros) { - finishAndAddToTrace(TimeUnit.MICROSECONDS.toNanos(stoptimeMicros - startTimeMicro)); - } - - @Override - public DDSpan setError(final boolean error) { - context.setErrorFlag(error); - return this; - } - - /** - * By calling this method the span will be removed from the current active Trace without - * actually being persisted. - * - * Note: This method is meant for internal SDK usage. Be aware that if used this Span will - * be removed from the Trace and lost. - * - */ - @Override - public final void drop() { - context.getTrace().dropSpan(this); - } - - /** - * Check if the span is the root parent. It means that the traceId is the same as the spanId. In - * the context of distributed tracing this will return true if an only if this is the application - * initializing the trace. - * - * @return true if root, false otherwise - */ - public final boolean isRootSpan() { - return BigInteger.ZERO.equals(context.getParentId()); - } - - @Override - @Deprecated - public MutableSpan getRootSpan() { - return getLocalRootSpan(); - } - - @Override - public MutableSpan getLocalRootSpan() { - return context().getTrace().getRootSpan(); - } - - public void setErrorMeta(final Throwable error) { - setError(true); - - setTag(DDTags.ERROR_MSG, error.getMessage()); - setTag(DDTags.ERROR_TYPE, error.getClass().getName()); - - final StringWriter errorString = new StringWriter(); - error.printStackTrace(new PrintWriter(errorString)); - setTag(DDTags.ERROR_STACK, errorString.toString()); - } - - /* (non-Javadoc) - * @see io.opentracing.BaseSpan#setTag(java.lang.String, java.lang.String) - */ - @Override - public final DDSpan setTag(final String tag, final String value) { - context().setTag(tag, (Object) value); - return this; - } - - /* (non-Javadoc) - * @see io.opentracing.BaseSpan#setTag(java.lang.String, boolean) - */ - @Override - public final DDSpan setTag(final String tag, final boolean value) { - context().setTag(tag, (Object) value); - return this; - } - - /* (non-Javadoc) - * @see io.opentracing.BaseSpan#setTag(java.lang.String, java.lang.Number) - */ - @Override - public final DDSpan setTag(final String tag, final Number value) { - context().setTag(tag, (Object) value); - return this; - } - - @Override - public Span setTag(final Tag tag, final T value) { - context().setTag(tag.getKey(), value); - return this; - } - - /* (non-Javadoc) - * @see io.opentracing.BaseSpan#context() - */ - @Override - public final DDSpanContext context() { - return context; - } - - /* (non-Javadoc) - * @see io.opentracing.BaseSpan#getBaggageItem(java.lang.String) - */ - @Override - public final String getBaggageItem(final String key) { - return context.getBaggageItem(key); - } - - /* (non-Javadoc) - * @see io.opentracing.BaseSpan#setBaggageItem(java.lang.String, java.lang.String) - */ - @Override - public final DDSpan setBaggageItem(final String key, final String value) { - context.setBaggageItem(key, value); - return this; - } - - /* (non-Javadoc) - * @see io.opentracing.BaseSpan#setOperationName(java.lang.String) - */ - @Override - public final DDSpan setOperationName(final String operationName) { - context().setOperationName(operationName); - return this; - } - - /* (non-Javadoc) - * @see io.opentracing.BaseSpan#log(java.util.Map) - */ - @Override - public final DDSpan log(final Map map) { - logHandler.log(map, this); - return this; - } - - /* (non-Javadoc) - * @see io.opentracing.BaseSpan#log(long, java.util.Map) - */ - @Override - public final DDSpan log(final long l, final Map map) { - logHandler.log(l, map, this); - return this; - } - - /* (non-Javadoc) - * @see io.opentracing.BaseSpan#log(java.lang.String) - */ - @Override - public final DDSpan log(final String s) { - logHandler.log(s, this); - return this; - } - - /* (non-Javadoc) - * @see io.opentracing.BaseSpan#log(long, java.lang.String) - */ - @Override - public final DDSpan log(final long l, final String s) { - logHandler.log(l, s, this); - return this; - } - - @Override - public final DDSpan setServiceName(final String serviceName) { - context().setServiceName(serviceName); - return this; - } - - @Override - public final DDSpan setResourceName(final String resourceName) { - context().setResourceName(resourceName); - return this; - } - - /** - * Set the sampling priority of the root span of this span's trace - * - *

    Has no effect if the span priority has been propagated (injected or extracted). - */ - @Override - public final DDSpan setSamplingPriority(final int newPriority) { - context().setSamplingPriority(newPriority); - return this; - } - - @Override - public final DDSpan setSpanType(final String type) { - context().setSpanType(type); - return this; - } - - // Getters - - /** - * Meta merges baggage and tags (stringified values) - * - * @return merged context baggage and tags - */ - public Map getMeta() { - final Map meta = new HashMap<>(); - for (final Map.Entry entry : context().getBaggageItems().entrySet()) { - meta.put(entry.getKey(), entry.getValue()); - } - for (final Map.Entry entry : getTags().entrySet()) { - meta.put(entry.getKey(), String.valueOf(entry.getValue())); - } - return meta; - } - - /** - * Span metrics. - * - * @return metrics for this span - */ - public Map getMetrics() { - return context.getMetrics(); - } - - @Override - public long getStartTime() { - return startTimeNano > 0 ? startTimeNano : TimeUnit.MICROSECONDS.toNanos(startTimeMicro); - } - - @Override - public long getDurationNano() { - return durationNano.get(); - } - - @Override - public String getServiceName() { - return context.getServiceName(); - } - - public BigInteger getTraceId() { - return context.getTraceId(); - } - - public BigInteger getSpanId() { - return context.getSpanId(); - } - - public BigInteger getParentId() { - return context.getParentId(); - } - - @Override - public String getResourceName() { - return context.getResourceName(); - } - - @Override - public String getOperationName() { - return context.getOperationName(); - } - - @Override - public Integer getSamplingPriority() { - final int samplingPriority = context.getSamplingPriority(); - if (samplingPriority == PrioritySampling.UNSET) { - return null; - } else { - return samplingPriority; - } - } - - @Override - public String getSpanType() { - return context.getSpanType(); - } - - @Override - public Map getTags() { - return context().getTags(); - } - - public String getType() { - return context.getSpanType(); - } - - @Override - public Boolean isError() { - return context.getErrorFlag(); - } - - public int getError() { - return context.getErrorFlag() ? 1 : 0; - } - - @Override - public String toString() { - return new StringBuilder() - .append(context.toString()) - .append(", duration_ns=") - .append(durationNano) - .toString(); - } -} diff --git a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/opentracing/DDSpanContext.java b/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/opentracing/DDSpanContext.java deleted file mode 100644 index 69331f3ad1..0000000000 --- a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/opentracing/DDSpanContext.java +++ /dev/null @@ -1,408 +0,0 @@ -/* - * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. - * This product includes software developed at Datadog (https://www.datadoghq.com/). - * Copyright 2016-Present Datadog, Inc. - */ - -package com.datadog.opentracing; - -import com.datadog.android.api.InternalLogger; -import com.datadog.opentracing.decorators.AbstractDecorator; -import com.datadog.legacy.trace.api.DDTags; -import com.datadog.legacy.trace.api.sampling.PrioritySampling; -import java.math.BigInteger; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.TreeMap; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.atomic.AtomicReference; - -/** - * SpanContext represents Span state that must propagate to descendant Spans and across process - * boundaries. - * - *

    SpanContext is logically divided into two pieces: (1) the user-level "Baggage" that propagates - * across Span boundaries and (2) any Datadog fields that are needed to identify or contextualize - * the associated Span instance - */ -public class DDSpanContext implements io.opentracing.SpanContext { - public static final String PRIORITY_SAMPLING_KEY = "_sampling_priority_v1"; - public static final String SAMPLE_RATE_KEY = "_sample_rate"; - public static final String ORIGIN_KEY = "_dd.origin"; - - private static final Map EMPTY_METRICS = Collections.emptyMap(); - - // Shared with other span contexts - /** For technical reasons, the ref to the original tracer */ - private final DDTracer tracer; - - /** The collection of all span related to this one */ - private final PendingTrace trace; - - /** Baggage is associated with the whole trace and shared with other spans */ - private final Map baggageItems; - - // Not Shared with other span contexts - private final BigInteger traceId; - private final BigInteger spanId; - private final BigInteger parentId; - - /** Tags are associated to the current span, they will not propagate to the children span */ - private final Map tags = new ConcurrentHashMap<>(); - - /** The service name is required, otherwise the span are dropped by the agent */ - private volatile String serviceName; - /** The resource associated to the service (server_web, database, etc.) */ - private volatile String resourceName; - /** Each span have an operation name describing the current span */ - private volatile String operationName; - /** The type of the span. If null, the Datadog Agent will report as a custom */ - private volatile String spanType; - /** True indicates that the span reports an error */ - private volatile boolean errorFlag; - /** - * When true, the samplingPriority cannot be changed. This prevents the sampling flag from - * changing after the context has propagated. - * - *

    For thread safety, this boolean is only modified or accessed under instance lock. - */ - private boolean samplingPriorityLocked = false; - /** The origin of the trace. (eg. Synthetics) */ - private final String origin; - /** Metrics on the span */ - private final AtomicReference> metrics = new AtomicReference<>(); - - // Additional Metadata - private final String threadName = Thread.currentThread().getName(); - private final long threadId = Thread.currentThread().getId(); - - private final Map serviceNameMappings; - - private final InternalLogger internalLogger; - - public DDSpanContext( - final BigInteger traceId, - final BigInteger spanId, - final BigInteger parentId, - final String serviceName, - final String operationName, - final String resourceName, - final int samplingPriority, - final String origin, - final Map baggageItems, - final boolean errorFlag, - final String spanType, - final Map tags, - final PendingTrace trace, - final DDTracer tracer, - final Map serviceNameMappings, - final InternalLogger internalLogger - ) { - - assert tracer != null; - assert trace != null; - this.tracer = tracer; - this.trace = trace; - - assert traceId != null; - assert spanId != null; - assert parentId != null; - this.traceId = traceId; - this.spanId = spanId; - this.parentId = parentId; - - if (baggageItems == null) { - this.baggageItems = new ConcurrentHashMap<>(0); - } else { - this.baggageItems = new ConcurrentHashMap<>(baggageItems); - } - - if (tags != null) { - this.tags.putAll(tags); - } - - this.serviceNameMappings = serviceNameMappings; - setServiceName(serviceName); - this.operationName = operationName; - this.resourceName = resourceName; - this.errorFlag = errorFlag; - this.spanType = spanType; - this.origin = origin; - - if (samplingPriority != PrioritySampling.UNSET) { - setSamplingPriority(samplingPriority); - } - - if (origin != null) { - this.tags.put(ORIGIN_KEY, origin); - } - this.tags.put(DDTags.THREAD_NAME, threadName); - this.tags.put(DDTags.THREAD_ID, threadId); - this.internalLogger = internalLogger; - } - - public BigInteger getTraceId() { - return traceId; - } - - @Override - public String toTraceId() { - return traceId.toString(); - } - - public BigInteger getParentId() { - return parentId; - } - - public BigInteger getSpanId() { - return spanId; - } - - @Override - public String toSpanId() { - return spanId.toString(); - } - - public String getServiceName() { - return serviceName; - } - - public void setServiceName(final String serviceName) { - if (serviceNameMappings.containsKey(serviceName)) { - this.serviceName = serviceNameMappings.get(serviceName); - } else { - this.serviceName = serviceName; - } - } - - public String getResourceName() { - return isResourceNameSet() ? resourceName : operationName; - } - - public boolean isResourceNameSet() { - return !(resourceName == null || resourceName.isEmpty()); - } - - public boolean hasResourceName() { - return isResourceNameSet() || tags.containsKey(DDTags.RESOURCE_NAME); - } - - public void setResourceName(final String resourceName) { - this.resourceName = resourceName; - } - - public String getOperationName() { - return operationName; - } - - public void setOperationName(final String operationName) { - this.operationName = operationName; - } - - public boolean getErrorFlag() { - return errorFlag; - } - - public void setErrorFlag(final boolean errorFlag) { - this.errorFlag = errorFlag; - } - - public String getSpanType() { - return spanType; - } - - public void setSpanType(final String spanType) { - this.spanType = spanType; - } - - /** @return if sampling priority was set by this method invocation */ - public boolean setSamplingPriority(final int newPriority) { - if (newPriority == PrioritySampling.UNSET) { - internalLogger.log( - InternalLogger.Level.WARN, - InternalLogger.Target.USER, - () -> "Can't set sampling priority to unset", - null, - false, - new HashMap<>() - ); - return false; - } - - if (trace != null) { - final DDSpan rootSpan = trace.getRootSpan(); - if (null != rootSpan && rootSpan.context() != this) { - return rootSpan.context().setSamplingPriority(newPriority); - } - } - - // sync with lockSamplingPriority - synchronized (this) { - if (samplingPriorityLocked) { - return false; - } else { - setMetric(PRIORITY_SAMPLING_KEY, newPriority); - return true; - } - } - } - - /** @return the sampling priority of this span's trace, or null if no priority has been set */ - public int getSamplingPriority() { - final DDSpan rootSpan = trace.getRootSpan(); - if (null != rootSpan && rootSpan.context() != this) { - return rootSpan.context().getSamplingPriority(); - } - - final Number val = getMetrics().get(PRIORITY_SAMPLING_KEY); - return null == val ? PrioritySampling.UNSET : val.intValue(); - } - - /** - * Prevent future changes to the context's sampling priority. - * - *

    Used when a span is extracted or injected for propagation. - * - *

    Has no effect if the sampling priority is unset. - * - * @return true if the sampling priority was locked. - */ - public boolean lockSamplingPriority() { - final DDSpan rootSpan = trace.getRootSpan(); - if (null != rootSpan && rootSpan.context() != this) { - return rootSpan.context().lockSamplingPriority(); - } - - // sync with setSamplingPriority - synchronized (this) { - if (getMetrics().get(PRIORITY_SAMPLING_KEY) == null) { - internalLogger.log( - InternalLogger.Level.INFO, - InternalLogger.Target.USER, - () -> "Sampling priority unset, can't lock it", - null, - false, - new HashMap<>() - ); - } else if (samplingPriorityLocked == false) { - samplingPriorityLocked = true; - } - return samplingPriorityLocked; - } - } - - public String getOrigin() { - final DDSpan rootSpan = trace.getRootSpan(); - if (null != rootSpan) { - return rootSpan.context().origin; - } else { - return origin; - } - } - - public void setBaggageItem(final String key, final String value) { - baggageItems.put(key, value); - } - - public String getBaggageItem(final String key) { - return baggageItems.get(key); - } - - public Map getBaggageItems() { - return baggageItems; - } - - /* (non-Javadoc) - * @see io.opentracing.SpanContext#baggageItems() - */ - @Override - public Iterable> baggageItems() { - return baggageItems.entrySet(); - } - - public PendingTrace getTrace() { - return trace; - } - - @Deprecated - public DDTracer getTracer() { - return tracer; - } - - public Map getMetrics() { - final Map metrics = this.metrics.get(); - return metrics == null ? EMPTY_METRICS : metrics; - } - - public void setMetric(final String key, final Number value) { - if (metrics.get() == null) { - metrics.compareAndSet(null, new ConcurrentHashMap()); - } - if (value instanceof Float) { - metrics.get().put(key, value.doubleValue()); - } else { - metrics.get().put(key, value); - } - } - /** - * Add a tag to the span. Tags are not propagated to the children - * - * @param tag the tag-name - * @param value the value of the tag. tags with null values are ignored. - */ - public synchronized void setTag(final String tag, final Object value) { - if (value == null || (value instanceof String && ((String) value).isEmpty())) { - tags.remove(tag); - return; - } - - boolean addTag = true; - - // Call decorators - final List decorators = tracer.getSpanContextDecorators(tag); - if (decorators != null) { - for (final AbstractDecorator decorator : decorators) { - try { - addTag &= decorator.shouldSetTag(this, tag, value); - } catch (final Throwable ex) { - } - } - } - - if (addTag) { - tags.put(tag, value); - } - } - - public synchronized Map getTags() { - return Collections.unmodifiableMap(tags); - } - - @Override - public String toString() { - final StringBuilder s = - new StringBuilder() - .append("DDSpan [ t_id=") - .append(traceId) - .append(", s_id=") - .append(spanId) - .append(", p_id=") - .append(parentId) - .append("] trace=") - .append(getServiceName()) - .append("/") - .append(getOperationName()) - .append("/") - .append(getResourceName()) - .append(" metrics=") - .append(new TreeMap<>(getMetrics())); - if (errorFlag) { - s.append(" *errored*"); - } - - s.append(" tags=").append(new TreeMap<>(tags)); - return s.toString(); - } -} diff --git a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/opentracing/DDTraceOTInfo.java b/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/opentracing/DDTraceOTInfo.java deleted file mode 100644 index f0abefb8e0..0000000000 --- a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/opentracing/DDTraceOTInfo.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. - * This product includes software developed at Datadog (https://www.datadoghq.com/). - * Copyright 2016-Present Datadog, Inc. - */ - -package com.datadog.opentracing; - -import java.io.BufferedReader; -import java.io.InputStreamReader; - -public class DDTraceOTInfo { - - public static final String JAVA_VERSION = System.getProperty("java.version", "unknown"); - public static final String JAVA_VM_NAME = System.getProperty("java.vm.name", "unknown"); - public static final String JAVA_VM_VENDOR = System.getProperty("java.vm.vendor", "unknown"); - - public static final String VERSION; - - static { - String v; - try (final BufferedReader br = - new BufferedReader( - new InputStreamReader( - DDTraceOTInfo.class.getResourceAsStream("/dd-trace-ot.version"), "UTF-8"))) { - final StringBuilder sb = new StringBuilder(); - - for (int c = br.read(); c != -1; c = br.read()) sb.append((char) c); - - v = sb.toString().trim(); - } catch (final Exception e) { - v = "unknown"; - } - VERSION = v; - } - - public static void main(final String... args) { - System.out.println(VERSION); - } -} diff --git a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/opentracing/DDTracer.java b/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/opentracing/DDTracer.java deleted file mode 100644 index 27b7d24aee..0000000000 --- a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/opentracing/DDTracer.java +++ /dev/null @@ -1,771 +0,0 @@ -/* - * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. - * This product includes software developed at Datadog (https://www.datadoghq.com/). - * Copyright 2016-Present Datadog, Inc. - */ - -package com.datadog.opentracing; - -import android.os.StrictMode; - -import com.datadog.android.api.InternalLogger; -import com.datadog.opentracing.decorators.AbstractDecorator; -import com.datadog.opentracing.decorators.DDDecoratorsFactory; -import com.datadog.opentracing.jfr.DDNoopScopeEventFactory; -import com.datadog.opentracing.jfr.DDScopeEventFactory; -import com.datadog.opentracing.propagation.ExtractedContext; -import com.datadog.opentracing.propagation.HttpCodec; -import com.datadog.opentracing.propagation.TagContext; -import com.datadog.opentracing.scopemanager.ContextualScopeManager; -import com.datadog.opentracing.scopemanager.ScopeContext; -import com.datadog.legacy.trace.api.Config; -import com.datadog.legacy.trace.api.Tracer; -import com.datadog.legacy.trace.api.interceptor.MutableSpan; -import com.datadog.legacy.trace.api.interceptor.TraceInterceptor; -import com.datadog.legacy.trace.api.sampling.PrioritySampling; -import com.datadog.legacy.trace.common.sampling.PrioritySampler; -import com.datadog.legacy.trace.common.sampling.Sampler; -import com.datadog.legacy.trace.common.writer.LoggingWriter; -import com.datadog.legacy.trace.common.writer.Writer; -import com.datadog.legacy.trace.context.ScopeListener; -import com.datadog.trace.api.IdGenerationStrategy; - -import io.opentracing.References; -import io.opentracing.Scope; -import io.opentracing.ScopeManager; -import io.opentracing.Span; -import io.opentracing.SpanContext; -import io.opentracing.propagation.Format; -import io.opentracing.propagation.TextMapExtract; -import io.opentracing.propagation.TextMapInject; -import io.opentracing.tag.Tag; - -import java.io.Closeable; -import java.lang.ref.WeakReference; -import java.math.BigInteger; -import java.util.*; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentSkipListSet; - -/** - * DDTracer makes it easy to send traces and span to DD using the OpenTracing API. - */ -public class DDTracer implements io.opentracing.Tracer, Closeable, Tracer { - // UINT128 max value - public static final BigInteger TRACE_ID_128_BITS_MAX = - BigInteger.valueOf(2).pow(128).subtract(BigInteger.ONE); - - // UINT64 max value - public static final BigInteger TRACE_ID_64_BITS_MAX = - BigInteger.valueOf(2).pow(64).subtract(BigInteger.ONE); - - public static final BigInteger TRACE_ID_MIN = BigInteger.ZERO; - - /** - * Default service name if none provided on the trace or span - */ - final String serviceName; - /** - * Writer is an charge of reporting traces and spans to the desired endpoint - */ - final Writer writer; - /** - * Sampler defines the sampling policy in order to reduce the number of traces for instance - */ - final Sampler sampler; - /** - * Scope manager is in charge of managing the scopes from which spans are created - */ - final ScopeManager scopeManager; - - /** - * A set of tags that are added only to the application's root span - */ - private final Map localRootSpanTags; - /** - * A set of tags that are added to every span - */ - private final Map defaultSpanTags; - /** - * A configured mapping of service names to update with new values - */ - private final Map serviceNameMappings; - - /** - * number of spans in a pending trace before they get flushed - */ - private final int partialFlushMinSpans; - - /** - * JVM shutdown callback, keeping a reference to it to remove this if DDTracer gets destroyed - * earlier - */ - private final Thread shutdownCallback; - - /** - * Span context decorators - */ - private final Map> spanContextDecorators = - new ConcurrentHashMap<>(); - - private final SortedSet interceptors = - new ConcurrentSkipListSet<>( - new Comparator() { - @Override - public int compare(final TraceInterceptor o1, final TraceInterceptor o2) { - return Integer.compare(o1.priority(), o2.priority()); - } - }); - - private final HttpCodec.Injector injector; - private final HttpCodec.Extractor extractor; - - private final IdGenerationStrategy idGenerationStrategy = - IdGenerationStrategy.fromName("SECURE_RANDOM", true); - - // On Android, the same zygote is reused for every single application, - // meaning that the ThreadLocalRandom reuses the same exact state, - // resulting in conflicting TraceIds. - // To avoid this we will use a SecureRandom instance here to generate the trace id. - - private final Random random; - - protected DDTracer(final Config config, final Writer writer, final Random random) { - this( - config.getServiceName(), - writer, - Sampler.Builder.forConfig(config), - HttpCodec.createInjector(config), - HttpCodec.createExtractor(config, config.getHeaderTags()), - new ContextualScopeManager(Config.get().getScopeDepthLimit(), createScopeEventFactory()), - random, - config.getLocalRootSpanTags(), - config.getMergedSpanTags(), - config.getServiceMapping(), - config.getHeaderTags(), - config.getPartialFlushMinSpans()); - } - - - // These field names must be stable to ensure the builder api is stable. - private DDTracer( - final String serviceName, - final Writer writer, - final Sampler sampler, - final HttpCodec.Injector injector, - final HttpCodec.Extractor extractor, - final ScopeManager scopeManager, - final Random random, - final Map localRootSpanTags, - final Map defaultSpanTags, - final Map serviceNameMappings, - final Map taggedHeaders, - final int partialFlushMinSpans) { - - assert localRootSpanTags != null; - assert defaultSpanTags != null; - assert serviceNameMappings != null; - assert taggedHeaders != null; - - this.random = random; - this.serviceName = serviceName; - if (writer == null) { - this.writer = new LoggingWriter(); - } else { - this.writer = writer; - } - this.sampler = sampler; - this.injector = injector; - this.extractor = extractor; - this.scopeManager = scopeManager; - this.localRootSpanTags = localRootSpanTags; - this.defaultSpanTags = defaultSpanTags; - this.serviceNameMappings = serviceNameMappings; - this.partialFlushMinSpans = partialFlushMinSpans; - - this.writer.start(); - - shutdownCallback = new ShutdownHook(this); - try { - Runtime.getRuntime().addShutdownHook(shutdownCallback); - } catch (final IllegalStateException ex) { - // The JVM is already shutting down. - } - - final List decorators = DDDecoratorsFactory.createBuiltinDecorators(); - for (final AbstractDecorator decorator : decorators) { - addDecorator(decorator); - } - - registerClassLoader(ClassLoader.getSystemClassLoader()); - - // Ensure that PendingTrace.SPAN_CLEANER is initialized in this thread: - // FIXME: add test to verify the span cleaner thread is started with this call. - PendingTrace.initialize(); - } - - @Override - public void finalize() { - try { - Runtime.getRuntime().removeShutdownHook(shutdownCallback); - shutdownCallback.run(); - } catch (final Exception e) { - } - } - - /** - * Returns the list of span context decorators - * - * @return the list of span context decorators - */ - public List getSpanContextDecorators(final String tag) { - return spanContextDecorators.get(tag); - } - - /** - * Add a new decorator in the list ({@link AbstractDecorator}) - * - * @param decorator The decorator in the list - */ - public void addDecorator(final AbstractDecorator decorator) { - - List list = spanContextDecorators.get(decorator.getMatchingTag()); - if (list == null) { - list = new ArrayList<>(); - } - list.add(decorator); - - spanContextDecorators.put(decorator.getMatchingTag(), list); - } - - @Deprecated - public void addScopeContext(final ScopeContext context) { - if (scopeManager instanceof ContextualScopeManager) { - ((ContextualScopeManager) scopeManager).addScopeContext(context); - } - } - - /** - * If an application is using a non-system classloader, that classloader should be registered - * here. Due to the way Spring Boot structures its' executable jar, this might log some warnings. - * - * @param classLoader to register. - */ - public void registerClassLoader(final ClassLoader classLoader) { - try { - StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads(); - ServiceLoader serviceLoader = ServiceLoader.load(TraceInterceptor.class, classLoader); - for (final TraceInterceptor interceptor : serviceLoader) { - addTraceInterceptor(interceptor); - } - StrictMode.setThreadPolicy(oldPolicy); - } catch (final ServiceConfigurationError e) { - } - } - - @Override - public ScopeManager scopeManager() { - return scopeManager; - } - - @Override - public Span activeSpan() { - return scopeManager.activeSpan(); - } - - @Override - public Scope activateSpan(final Span span) { - return scopeManager.activate(span); - } - - @Override - public SpanBuilder buildSpan(final String operationName) { - return new DDSpanBuilder(operationName, scopeManager); - } - - @Override - public void inject(final SpanContext spanContext, final Format format, final T carrier) { - if (carrier instanceof TextMapInject) { - final DDSpanContext ddSpanContext = (DDSpanContext) spanContext; - - final DDSpan rootSpan = ddSpanContext.getTrace().getRootSpan(); - setSamplingPriorityIfNecessary(rootSpan); - - injector.inject(ddSpanContext, (TextMapInject) carrier); - } else { - } - } - - @Override - public SpanContext extract(final Format format, final T carrier) { - if (carrier instanceof TextMapExtract) { - return extractor.extract((TextMapExtract) carrier); - } else { - return null; - } - } - - /** - * We use the sampler to know if the trace has to be reported/written. The sampler is called on - * the first span (root span) of the trace. If the trace is marked as a sample, we report it. - * - * @param trace a list of the spans related to the same trace - */ - void write(final Collection trace) { - if (trace.isEmpty()) { - return; - } - final ArrayList writtenTrace; - if (interceptors.isEmpty()) { - writtenTrace = new ArrayList<>(trace); - } else { - Collection interceptedTrace = new ArrayList<>(trace); - for (final TraceInterceptor interceptor : interceptors) { - interceptedTrace = interceptor.onTraceComplete(interceptedTrace); - } - writtenTrace = new ArrayList<>(interceptedTrace.size()); - for (final MutableSpan span : interceptedTrace) { - if (span instanceof DDSpan) { - writtenTrace.add((DDSpan) span); - } - } - } - incrementTraceCount(); - - if (!writtenTrace.isEmpty()) { - final DDSpan rootSpan = (DDSpan) writtenTrace.get(0).getLocalRootSpan(); - setSamplingPriorityIfNecessary(rootSpan); - - final DDSpan spanToSample = rootSpan == null ? writtenTrace.get(0) : rootSpan; - if (sampler.sample(spanToSample)) { - writer.write(writtenTrace); - } - } - } - - void setSamplingPriorityIfNecessary(final DDSpan rootSpan) { - // There's a race where multiple threads can see PrioritySampling.UNSET here - // This check skips potential complex sampling priority logic when we know its redundant - // Locks inside DDSpanContext ensure the correct behavior in the race case - - if (sampler instanceof PrioritySampler - && rootSpan != null - && rootSpan.context().getSamplingPriority() == PrioritySampling.UNSET) { - - ((PrioritySampler) sampler).setSamplingPriority(rootSpan); - } - } - - /** - * Increment the reported trace count, but do not write a trace. - */ - void incrementTraceCount() { - writer.incrementTraceCount(); - } - - @Override - public String getTraceId() { - final Span activeSpan = activeSpan(); - if (activeSpan instanceof DDSpan) { - return ((DDSpan) activeSpan).getTraceId().toString(); - } - return "0"; - } - - @Override - public String getSpanId() { - final Span activeSpan = activeSpan(); - if (activeSpan instanceof DDSpan) { - return ((DDSpan) activeSpan).getSpanId().toString(); - } - return "0"; - } - - @Override - public boolean addTraceInterceptor(final TraceInterceptor interceptor) { - return interceptors.add(interceptor); - } - - @Override - public void addScopeListener(final ScopeListener listener) { - if (scopeManager instanceof ContextualScopeManager) { - ((ContextualScopeManager) scopeManager).addScopeListener(listener); - } - } - - @Override - public void close() { - PendingTrace.close(); - writer.close(); - } - - @Override - public String toString() { - return "DDTracer-" - + Integer.toHexString(hashCode()) - + "{ serviceName=" - + serviceName - + ", writer=" - + writer - + ", sampler=" - + sampler - + ", defaultSpanTags=" - + defaultSpanTags - + '}'; - } - - @Deprecated - private static Map customRuntimeTags( - final String runtimeId, final Map applicationRootSpanTags) { - final Map runtimeTags = new HashMap<>(applicationRootSpanTags); - runtimeTags.put(Config.RUNTIME_ID_TAG, runtimeId); - return Collections.unmodifiableMap(runtimeTags); - } - - private static DDScopeEventFactory createScopeEventFactory() { - try { - return (DDScopeEventFactory) - Class.forName("com.datadog.opentracing.jfr.openjdk.ScopeEventFactory").newInstance(); - } catch (final ClassFormatError | ReflectiveOperationException | NoClassDefFoundError e) { - } - return new DDNoopScopeEventFactory(); - } - - /** - * Spans are built using this builder - */ - public class DDSpanBuilder implements SpanBuilder { - private final ScopeManager scopeManager; - - /** - * Each span must have an operationName according to the opentracing specification - */ - private final String operationName; - - // Builder attributes - private final Map tags = new LinkedHashMap(defaultSpanTags); - private long timestampMicro; - private SpanContext parent; - private String serviceName; - private String resourceName; - private String origin; - private boolean errorFlag; - private String spanType; - private boolean ignoreScope = false; - private LogHandler logHandler = new DefaultLogHandler(); - private InternalLogger internalLogger = InternalLogger.Companion.getUNBOUND(); - - public DDSpanBuilder(final String operationName, final ScopeManager scopeManager) { - this.operationName = operationName; - this.scopeManager = scopeManager; - } - - @Override - public SpanBuilder ignoreActiveSpan() { - ignoreScope = true; - return this; - } - - private Span startSpan() { - return new DDSpan(timestampMicro, buildSpanContext(), logHandler, internalLogger); - } - - @Override - public Scope startActive(final boolean finishSpanOnClose) { - final Span span = startSpan(); - final Scope scope = scopeManager.activate(span, finishSpanOnClose); - return scope; - } - - @Override - @Deprecated - public Span startManual() { - return start(); - } - - @Override - public Span start() { - final Span span = startSpan(); - return span; - } - - @Override - public DDSpanBuilder withTag(final String tag, final Number number) { - return withTag(tag, (Object) number); - } - - @Override - public DDSpanBuilder withTag(final String tag, final String string) { - return withTag(tag, (Object) string); - } - - @Override - public DDSpanBuilder withTag(final String tag, final boolean bool) { - return withTag(tag, (Object) bool); - } - - @Override - public SpanBuilder withTag(final Tag tag, final T value) { - return withTag(tag.getKey(), value); - } - - @Override - public DDSpanBuilder withStartTimestamp(final long timestampMicroseconds) { - timestampMicro = timestampMicroseconds; - return this; - } - - public DDSpanBuilder withServiceName(final String serviceName) { - this.serviceName = serviceName; - return this; - } - - public DDSpanBuilder withResourceName(final String resourceName) { - this.resourceName = resourceName; - return this; - } - - public DDSpanBuilder withErrorFlag() { - errorFlag = true; - return this; - } - - public DDSpanBuilder withSpanType(final String spanType) { - this.spanType = spanType; - return this; - } - - public Iterable> baggageItems() { - if (parent == null) { - return Collections.emptyList(); - } - return parent.baggageItems(); - } - - public DDSpanBuilder withLogHandler(final LogHandler logHandler) { - if (logHandler != null) { - this.logHandler = logHandler; - } - return this; - } - - public DDSpanBuilder withInternalLogger(final InternalLogger internalLogger) { - if (internalLogger != null) { - this.internalLogger = internalLogger; - } - return this; - } - - @Override - public DDSpanBuilder asChildOf(final Span span) { - return asChildOf(span == null ? null : span.context()); - } - - @Override - public DDSpanBuilder asChildOf(final SpanContext spanContext) { - parent = spanContext; - return this; - } - - @Override - public DDSpanBuilder addReference(final String referenceType, final SpanContext spanContext) { - if (spanContext == null) { - return this; - } - if (!(spanContext instanceof ExtractedContext) && !(spanContext instanceof DDSpanContext)) { - return this; - } - if (References.CHILD_OF.equals(referenceType) - || References.FOLLOWS_FROM.equals(referenceType)) { - return asChildOf(spanContext); - } else { - } - return this; - } - - public DDSpanBuilder withOrigin(final String origin) { - this.origin = origin; - return this; - } - - // Private methods - private DDSpanBuilder withTag(final String tag, final Object value) { - if (value == null || (value instanceof String && ((String) value).isEmpty())) { - tags.remove(tag); - } else { - tags.put(tag, value); - } - return this; - } - - private BigInteger generateNewSpanId() { - // It is **extremely** unlikely to generate the value "0" but we still need to handle that - // case - BigInteger value; - do { - synchronized (random) { - value = new StringCachingBigInteger(63, random); - } - } while (value.signum() == 0); - - return value; - } - - private BigInteger generateNewTraceId() { - BigInteger value; - do { - synchronized (idGenerationStrategy) { - value = new BigInteger(idGenerationStrategy.generateTraceId().toHexString(), 16); - } - } while (value.signum() == 0); - - return value; - } - - /** - * Build the SpanContext, if the actual span has a parent, the following attributes must be - * propagated: - ServiceName - Baggage - Trace (a list of all spans related) - SpanType - * - * @return the context - */ - private DDSpanContext buildSpanContext() { - final BigInteger traceId; - final BigInteger spanId = generateNewSpanId(); - final BigInteger parentSpanId; - final Map baggage; - final PendingTrace parentTrace; - final int samplingPriority; - final String origin; - - final DDSpanContext context; - SpanContext parentContext = parent; - if (parentContext == null && !ignoreScope) { - // use the Scope as parent unless overridden or ignored. - final Span activeSpan = scopeManager.activeSpan(); - if (activeSpan != null) { - parentContext = activeSpan.context(); - } - } - - // Propagate internal trace. - // Note: if we are not in the context of distributed tracing and we are starting the first - // root span, parentContext will be null at this point. - if (parentContext instanceof DDSpanContext) { - final DDSpanContext ddsc = (DDSpanContext) parentContext; - traceId = ddsc.getTraceId(); - parentSpanId = ddsc.getSpanId(); - baggage = ddsc.getBaggageItems(); - parentTrace = ddsc.getTrace(); - samplingPriority = PrioritySampling.UNSET; - origin = null; - if (serviceName == null) { - serviceName = ddsc.getServiceName(); - } - - } else { - if (parentContext instanceof ExtractedContext) { - // Propagate external trace - final ExtractedContext extractedContext = (ExtractedContext) parentContext; - traceId = extractedContext.getTraceId(); - parentSpanId = extractedContext.getSpanId(); - samplingPriority = extractedContext.getSamplingPriority(); - baggage = extractedContext.getBaggage(); - } else { - // Start a new trace - traceId = generateNewTraceId(); - parentSpanId = BigInteger.ZERO; - samplingPriority = PrioritySampling.UNSET; - baggage = null; - } - - // Get header tags and set origin whether propagating or not. - if (parentContext instanceof TagContext) { - tags.putAll(((TagContext) parentContext).getTags()); - origin = ((TagContext) parentContext).getOrigin(); - } else { - origin = this.origin; - } - - tags.putAll(localRootSpanTags); - - parentTrace = new PendingTrace(DDTracer.this, traceId, internalLogger); - } - - if (serviceName == null) { - serviceName = DDTracer.this.serviceName; - } - - final String operationName = this.operationName != null ? this.operationName : resourceName; - - // some attributes are inherited from the parent - context = - new DDSpanContext( - traceId, - spanId, - parentSpanId, - serviceName, - operationName, - resourceName, - samplingPriority, - origin, - baggage, - errorFlag, - spanType, - tags, - parentTrace, - DDTracer.this, - serviceNameMappings, - internalLogger); - - // Apply Decorators to handle any tags that may have been set via the builder. - for (final Map.Entry tag : tags.entrySet()) { - if (tag.getValue() == null) { - context.setTag(tag.getKey(), null); - continue; - } - - boolean addTag = true; - - // Call decorators - final List decorators = getSpanContextDecorators(tag.getKey()); - if (decorators != null) { - for (final AbstractDecorator decorator : decorators) { - try { - addTag &= decorator.shouldSetTag(context, tag.getKey(), tag.getValue()); - } catch (final Throwable ex) { - } - } - } - - if (!addTag) { - context.setTag(tag.getKey(), null); - } - } - - return context; - } - } - - private static class ShutdownHook extends Thread { - private final WeakReference reference; - - private ShutdownHook(final DDTracer tracer) { - super("dd-tracer-shutdown-hook"); - reference = new WeakReference<>(tracer); - } - - @Override - public void run() { - final DDTracer tracer = reference.get(); - if (tracer != null) { - tracer.close(); - } - } - } - - // GENERATED GETTER - - public int getPartialFlushMinSpans() { - return partialFlushMinSpans; - } -} diff --git a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/opentracing/DefaultLogHandler.java b/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/opentracing/DefaultLogHandler.java deleted file mode 100644 index 0ba850f158..0000000000 --- a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/opentracing/DefaultLogHandler.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. - * This product includes software developed at Datadog (https://www.datadoghq.com/). - * Copyright 2016-Present Datadog, Inc. - */ - -package com.datadog.opentracing; - -import static io.opentracing.log.Fields.ERROR_OBJECT; -import static io.opentracing.log.Fields.MESSAGE; - -import com.datadog.legacy.trace.api.DDTags; -import java.util.Map; - -/** The default implementation of the LogHandler. */ -public class DefaultLogHandler implements LogHandler { - @Override - public void log(Map fields, DDSpan span) { - extractError(fields, span); - } - - @Override - public void log(long timestampMicroseconds, Map fields, DDSpan span) { - extractError(fields, span); - } - - @Override - public void log(String event, DDSpan span) { - } - - @Override - public void log(long timestampMicroseconds, String event, DDSpan span) { - } - - private void extractError(final Map map, DDSpan span) { - if (map.get(ERROR_OBJECT) instanceof Throwable) { - final Throwable error = (Throwable) map.get(ERROR_OBJECT); - span.setErrorMeta(error); - } else if (map.get(MESSAGE) instanceof String) { - span.setTag(DDTags.ERROR_MSG, (String) map.get(MESSAGE)); - } - } -} diff --git a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/opentracing/LogHandler.java b/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/opentracing/LogHandler.java deleted file mode 100644 index f44e13de3f..0000000000 --- a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/opentracing/LogHandler.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. - * This product includes software developed at Datadog (https://www.datadoghq.com/). - * Copyright 2016-Present Datadog, Inc. - */ - -package com.datadog.opentracing; - -import java.util.Map; - -public interface LogHandler { - - /** - * Handles the log implementation in the Span. - * - * @param fields key:value log fields. Tracer implementations should support String, numeric, and - * boolean values; some may also support arbitrary Objects. - * @param span from which the call was made - */ - void log(Map fields, DDSpan span); - - /** - * Handles the log implementation in the Span. - * - * @param timestampMicroseconds The explicit timestamp for the log record. Must be greater than or - * equal to the Span's start timestamp. - * @param fields key:value log fields. Tracer implementations should support String, numeric, and - * @param span from which the call was made - */ - void log(long timestampMicroseconds, Map fields, DDSpan span); - - /** - * Handles the log implementation in the Span.. - * - * @param event the event value; often a stable identifier for a moment in the Span lifecycle - * @param span from which the call was made - */ - void log(String event, DDSpan span); - - /** - * Handles the log implementation in the Span. - * - * @param timestampMicroseconds The explicit timestamp for the log record. Must be greater than or - * equal to the Span's start timestamp. - * @param event the event value; often a stable identifier for a moment in the Span lifecycle - * @param span from which the call was made - */ - void log(long timestampMicroseconds, String event, DDSpan span); -} diff --git a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/opentracing/PendingTrace.java b/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/opentracing/PendingTrace.java deleted file mode 100644 index dd407477cb..0000000000 --- a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/opentracing/PendingTrace.java +++ /dev/null @@ -1,505 +0,0 @@ -/* - * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. - * This product includes software developed at Datadog (https://www.datadoghq.com/). - * Copyright 2016-Present Datadog, Inc. - */ - -package com.datadog.opentracing; - -import androidx.annotation.VisibleForTesting; - -import com.datadog.android.api.InternalLogger; -import com.datadog.exec.CommonTaskExecutor; -import com.datadog.exec.CommonTaskExecutor.Task; -import com.datadog.legacy.trace.common.util.Clock; -import com.datadog.opentracing.scopemanager.ContinuableScope; - -import java.io.Closeable; -import java.lang.ref.Reference; -import java.lang.ref.ReferenceQueue; -import java.lang.ref.WeakReference; -import java.math.BigInteger; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.Iterator; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.concurrent.atomic.AtomicReference; - -public class PendingTrace extends LinkedList { - private static final AtomicReference SPAN_CLEANER = new AtomicReference<>(); - - private final DDTracer tracer; - private final BigInteger traceId; - - // TODO: consider moving these time fields into DDTracer to ensure that traces have precise - // relative time - /** - * Trace start time in nano seconds measured up to a millisecond accuracy - */ - private final long startTimeNano; - /** - * Nano second ticks value at trace start - */ - private final long startNanoTicks; - - private final ReferenceQueue referenceQueue = new ReferenceQueue(); - private final Set> weakReferences = - Collections.newSetFromMap(new ConcurrentHashMap, Boolean>()); - - private final AtomicInteger pendingReferenceCount = new AtomicInteger(0); - - // We must maintain a separate count because ConcurrentLinkedDeque.size() is a linear operation. - private final AtomicInteger completedSpanCount = new AtomicInteger(0); - /** - * During a trace there are cases where the root span must be accessed (e.g. priority sampling and - * trace-search tags). - * - *

    Use a weak ref because we still need to handle buggy cases where the root span is not - * correctly closed (see SpanCleaner). - * - *

    The root span will be available in non-buggy cases because it has either finished and - * strongly ref'd in this queue or is unfinished and ref'd in a ContinuableScope. - */ - private final AtomicReference> rootSpan = new AtomicReference<>(); - - /** - * Ensure a trace is never written multiple times - */ - private final AtomicBoolean isWritten = new AtomicBoolean(false); - - private final InternalLogger internalLogger; - - PendingTrace(final DDTracer tracer, final BigInteger traceId, final InternalLogger internalLogger) { - this.tracer = tracer; - this.traceId = traceId; - this.internalLogger = internalLogger; - - startTimeNano = Clock.currentNanoTime(); - startNanoTicks = Clock.currentNanoTicks(); - - addPendingTrace(); - } - - /** - * Current timestamp in nanoseconds. - * - *

    Note: it is not possible to get 'real' nanosecond time. This method uses trace start time - * (which has millisecond precision) as a reference and it gets time with nanosecond precision - * after that. This means time measured within same Trace in different Spans is relatively correct - * with nanosecond precision. - * - * @return timestamp in nanoseconds - */ - public long getCurrentTimeNano() { - return startTimeNano + Math.max(0, Clock.currentNanoTicks() - startNanoTicks); - } - - public void registerSpan(final DDSpan span) { - if (traceId == null || span.context() == null) { - internalLogger.log( - InternalLogger.Level.ERROR, - InternalLogger.Target.USER, - () -> "Span " + span.getOperationName() + " not registered because of null traceId or context; " + - "spanId:" + span.getSpanId() + " traceid:" + traceId, - null, - false, - new HashMap<>() - ); - return; - } - BigInteger spanTraceId = span.context().getTraceId(); - if (!traceId.equals(spanTraceId)) { - internalLogger.log( - InternalLogger.Level.ERROR, - InternalLogger.Target.USER, - () -> "Span " + span.getOperationName() + " not registered because of traceId mismatch; " + - "spanId:" + span.getSpanId() + " span.traceid:" + spanTraceId + " traceid:" + traceId, - null, - false, - new HashMap<>() - ); - return; - } - rootSpan.compareAndSet(null, new WeakReference<>(span)); - synchronized (span) { - if (null == span.ref) { - span.ref = new WeakReference(span, referenceQueue); - weakReferences.add(span.ref); - final int count = pendingReferenceCount.incrementAndGet(); - } else { - internalLogger.log( - InternalLogger.Level.ERROR, - InternalLogger.Target.USER, - () -> "Span " + span.getOperationName() + " not registered because it is already registered; " + - "spanId:" + span.getSpanId() + " traceid:" + traceId, - null, - false, - new HashMap<>() - ); - } - } - } - - private void expireSpan(final DDSpan span, final boolean write) { - if (traceId == null || span.context() == null) { - internalLogger.log( - InternalLogger.Level.ERROR, - InternalLogger.Target.USER, - () -> "Span " + span.getOperationName() + " not expired because of null traceId or context; " + - "spanId:" + span.getSpanId() + " traceid:" + traceId, - null, - false, - new HashMap<>() - ); - return; - } - BigInteger spanTraceId = span.context().getTraceId(); - if (!traceId.equals(spanTraceId)) { - internalLogger.log( - InternalLogger.Level.ERROR, - InternalLogger.Target.USER, - () -> "Span " + span.getOperationName() + " not expired because of traceId mismatch; " + - "spanId:" + span.getSpanId() + " span.traceid:" + spanTraceId + " traceid:" + traceId, - null, - false, - new HashMap<>() - ); - return; - } - synchronized (span) { - if (span.ref == null) { - internalLogger.log( - InternalLogger.Level.ERROR, - InternalLogger.Target.USER, - () -> "Span " + span.getOperationName() + " not expired because it's not registered; " + - "spanId:" + span.getSpanId() + " traceid:" + traceId, - null, - false, - new HashMap<>() - ); - return; - } - weakReferences.remove(span.ref); - span.ref.clear(); - span.ref = null; - if (write) { - expireReference(); - } else { - pendingReferenceCount.decrementAndGet(); - } - } - } - - public void dropSpan(final DDSpan span) { - expireSpan(span, false); - if (pendingReferenceCount.get() == 0 && weakReferences.isEmpty()) { - // this trace is empty and doesn't have any continuations - removePendingTrace(); - } - } - - public void addSpan(final DDSpan span) { - synchronized (this) { - if (span.getDurationNano() == 0) { - internalLogger.log( - InternalLogger.Level.ERROR, - InternalLogger.Target.USER, - () -> "Span " + span.getOperationName() + " not added because duration is zero; " + - "spanId:" + span.getSpanId() + " traceid:" + traceId, - null, - false, - new HashMap<>() - ); - return; - } - if (traceId == null || span.context() == null) { - internalLogger.log( - InternalLogger.Level.ERROR, - InternalLogger.Target.USER, - () -> "Span " + span.getOperationName() + " not added because of null traceId or context; " + - "spanId:" + span.getSpanId() + " traceid:" + traceId, - null, - false, - new HashMap<>() - ); - return; - } - if (!traceId.equals(span.getTraceId())) { - internalLogger.log( - InternalLogger.Level.ERROR, - InternalLogger.Target.USER, - () -> "Span " + span.getOperationName() + " not added because of traceId mismatch; " + - "spanId:" + span.getSpanId() + " traceid:" + traceId, - null, - false, - new HashMap<>() - ); - return; - } - - if (!isWritten.get()) { - addFirst(span); - } else { - internalLogger.log( - InternalLogger.Level.ERROR, - InternalLogger.Target.USER, - () -> "Span " + span.getOperationName() + " not added because trace already written; " + - "spanId:" + span.getSpanId() + " traceid:" + traceId, - null, - false, - new HashMap<>() - ); - } - expireSpan(span, true); - } - } - - public DDSpan getRootSpan() { - final WeakReference rootRef = rootSpan.get(); - return rootRef == null ? null : rootRef.get(); - } - - /** - * When using continuations, it's possible one may be used after all existing spans are otherwise - * completed, so we need to wait till continuations are de-referenced before reporting. - */ - public void registerContinuation(final ContinuableScope.Continuation continuation) { - synchronized (continuation) { - if (continuation.ref == null) { - continuation.ref = - new WeakReference(continuation, referenceQueue); - weakReferences.add(continuation.ref); - final int count = pendingReferenceCount.incrementAndGet(); - } else { - } - } - } - - public void cancelContinuation(final ContinuableScope.Continuation continuation) { - synchronized (continuation) { - if (continuation.ref == null) { - } else { - weakReferences.remove(continuation.ref); - continuation.ref.clear(); - continuation.ref = null; - expireReference(); - } - } - } - - private void expireReference() { - final int count = pendingReferenceCount.decrementAndGet(); - if (count == 0) { - write(); - } else { - if (tracer.getPartialFlushMinSpans() > 0 && size() > tracer.getPartialFlushMinSpans()) { - synchronized (this) { - if (size() > tracer.getPartialFlushMinSpans()) { - final DDSpan rootSpan = getRootSpan(); - final List partialTrace = new ArrayList(size()); - final Iterator it = iterator(); - while (it.hasNext()) { - final DDSpan span = it.next(); - if (span != rootSpan) { - partialTrace.add(span); - completedSpanCount.decrementAndGet(); - it.remove(); - } - } - tracer.write(partialTrace); - } - } - } - } - } - - private synchronized void write() { - if (isWritten.compareAndSet(false, true)) { - removePendingTrace(); - if (!isEmpty()) { - tracer.write(this); - } - } else { - internalLogger.log( - InternalLogger.Level.ERROR, - InternalLogger.Target.USER, - () -> "Trace " + traceId + " write ignored: isWritten already true", - null, - false, - new HashMap<>() - ); - } - } - - public synchronized boolean clean() { - Reference ref; - int count = 0; - while ((ref = referenceQueue.poll()) != null) { - weakReferences.remove(ref); - if (isWritten.compareAndSet(false, true)) { - removePendingTrace(); - // preserve throughput count. - // Don't report the trace because the data comes from buggy uses of the api and is suspect. - tracer.incrementTraceCount(); - } - count++; - expireReference(); - } - if (count > 0) { - // TODO attempt to flatten and report if top level spans are finished. (for accurate metrics) - } - return count > 0; - } - - @Override - public void addFirst(final DDSpan span) { - synchronized (this) { - // add(index, element) instead of addFirst here is on purpose, to prevent issues - // with old AGP being used when compiling with Android API 35. - super.add(0, span); - } - completedSpanCount.incrementAndGet(); - } - - @Override - public int size() { - return completedSpanCount.get(); - } - - private void addPendingTrace() { - final SpanCleaner cleaner = SPAN_CLEANER.get(); - if (cleaner != null) { - cleaner.addPendingTrace(this); - } - } - - private void removePendingTrace() { - final SpanCleaner cleaner = SPAN_CLEANER.get(); - if (cleaner != null) { - cleaner.removePendingTrace(this); - } - } - - static void initialize() { - final SpanCleaner oldCleaner = SPAN_CLEANER.getAndSet(new SpanCleaner()); - if (oldCleaner != null) { - oldCleaner.close(); - } - } - - static void close() { - final SpanCleaner cleaner = SPAN_CLEANER.getAndSet(null); - if (cleaner != null) { - cleaner.close(); - } - } - - @VisibleForTesting - static int getPendingTracesSize() { - final SpanCleaner cleaner = SPAN_CLEANER.get(); - if (cleaner != null) { - return cleaner.pendingTraces.size(); - } - return 0; - } - - @VisibleForTesting - static SpanCleaner getSpanCleaner() { - return SPAN_CLEANER.get(); - } - - @VisibleForTesting - static class SpanCleaner implements Runnable, Closeable { - private static final long CLEAN_FREQUENCY = 1; - - private final Map pendingTraces = new ConcurrentHashMap<>(); - - public SpanCleaner() { - CommonTaskExecutor.INSTANCE.scheduleAtFixedRate( - SpanCleanerTask.INSTANCE, - this, - 0, - CLEAN_FREQUENCY, - TimeUnit.SECONDS, - "Pending trace cleaner"); - } - - @Override - public void run() { - for (final PendingTrace trace : pendingTraces.values()) { - trace.clean(); - } - } - - @Override - public void close() { - // Make sure that whatever was left over gets cleaned up - run(); - } - - public void addPendingTrace(PendingTrace pendingTrace) { - pendingTraces.put(new IdentityKey(pendingTrace), pendingTrace); - } - - public void removePendingTrace(PendingTrace pendingTrace) { - pendingTraces.remove(new IdentityKey((pendingTrace))); - } - - @VisibleForTesting - Map getPendingTraces() { - return pendingTraces; - } - } - - /** - * This class is used to create a unique key for the pending trace map. - * It uses the identity hash code of the object to create hash code. - * In case of hash code collision, it compares the object references to ensure uniqueness. - * The JVM system guarantees that `System.identityHashCode` will return same value for the lifetime of the - * object. This will ensure that add/remove operations are legit for the same object. - */ - @VisibleForTesting - static class IdentityKey { - private final PendingTrace key; - - @VisibleForTesting - PendingTrace getKey() { - return key; - } - - IdentityKey(PendingTrace key) { - this.key = key; - } - - @Override - public boolean equals(Object obj) { - return obj instanceof IdentityKey && ((IdentityKey) obj).key == key; - } - - @Override - public int hashCode() { - return System.identityHashCode(key); - } - } - - /* - * Important to use explicit class to avoid implicit hard references to cleaners from within executor. - */ - private static class SpanCleanerTask implements Task { - - static final SpanCleanerTask INSTANCE = new SpanCleanerTask(); - - @Override - public void run(final SpanCleaner target) { - target.run(); - } - } -} diff --git a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/opentracing/StringCachingBigInteger.java b/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/opentracing/StringCachingBigInteger.java deleted file mode 100644 index a56f6a983c..0000000000 --- a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/opentracing/StringCachingBigInteger.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. - * This product includes software developed at Datadog (https://www.datadoghq.com/). - * Copyright 2016-Present Datadog, Inc. - */ - -package com.datadog.opentracing; - -import java.math.BigInteger; -import java.util.Random; - -/** - * Because we are using BigInteger for Trace and Span Id, the toString() operator may result in - * heavy computation and string allocation overhead. In order to limit this, we are caching the - * result of toString, thereby taking advantage of the immutability of BigInteger. - */ -public class StringCachingBigInteger extends BigInteger { - - private String cachedString; - - public StringCachingBigInteger(byte[] val) { - super(val); - } - - public StringCachingBigInteger(int signum, byte[] magnitude) { - super(signum, magnitude); - } - - public StringCachingBigInteger(String val, int radix) { - super(val, radix); - } - - public StringCachingBigInteger(String val) { - super(val); - } - - public StringCachingBigInteger(int numBits, Random rnd) { - super(numBits, rnd); - } - - public StringCachingBigInteger(int bitLength, int certainty, Random rnd) { - super(bitLength, certainty, rnd); - } - - @Override - public String toString() { - if (cachedString == null) { - this.cachedString = super.toString(); - } - return cachedString; - } - - @Override - public boolean equals(Object o) { - return super.equals(o); - } - - @Override - public int hashCode() { - return super.hashCode(); - } -} diff --git a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/opentracing/decorators/AbstractDecorator.java b/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/opentracing/decorators/AbstractDecorator.java deleted file mode 100644 index ec43c3b672..0000000000 --- a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/opentracing/decorators/AbstractDecorator.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. - * This product includes software developed at Datadog (https://www.datadoghq.com/). - * Copyright 2016-Present Datadog, Inc. - */ - -package com.datadog.opentracing.decorators; - -import com.datadog.opentracing.DDSpanContext; - -/** - * Span decorators are called when new tags are written and proceed to various remappings and - * enrichments - */ -public abstract class AbstractDecorator { - - private String matchingTag; - - private Object matchingValue; - - private String replacementTag; - - private String replacementValue; - - public boolean shouldSetTag(final DDSpanContext context, final String tag, final Object value) { - if (this.getMatchingValue() == null || this.getMatchingValue().equals(value)) { - final String targetTag = getReplacementTag() == null ? tag : getReplacementTag(); - final String targetValue = - getReplacementValue() == null ? String.valueOf(value) : getReplacementValue(); - - context.setTag(targetTag, targetValue); - return false; - } else { - return true; - } - } - - public String getMatchingTag() { - return matchingTag; - } - - public void setMatchingTag(final String tag) { - this.matchingTag = tag; - } - - public Object getMatchingValue() { - return matchingValue; - } - - public void setMatchingValue(final Object value) { - this.matchingValue = value; - } - - public String getReplacementTag() { - return replacementTag; - } - - public void setReplacementTag(final String targetTag) { - this.replacementTag = targetTag; - } - - public String getReplacementValue() { - return replacementValue; - } - - public void setReplacementValue(final String targetValue) { - this.replacementValue = targetValue; - } -} diff --git a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/opentracing/decorators/DDDecoratorsFactory.java b/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/opentracing/decorators/DDDecoratorsFactory.java deleted file mode 100644 index b972471456..0000000000 --- a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/opentracing/decorators/DDDecoratorsFactory.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. - * This product includes software developed at Datadog (https://www.datadoghq.com/). - * Copyright 2016-Present Datadog, Inc. - */ - -package com.datadog.opentracing.decorators; - -import com.datadog.legacy.trace.api.Config; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -/** Create DDSpanDecorators */ -public class DDDecoratorsFactory { - public static List createBuiltinDecorators() { - - final List decorators = new ArrayList<>(); - - for (final AbstractDecorator decorator : - Arrays.asList( - new ForceManualDropDecorator(), - new ForceManualKeepDecorator(), - new PeerServiceDecorator(), - new ServiceNameDecorator(), - new ServiceNameDecorator("service", false))) { - - if (Config.get().isRuleEnabled(decorator.getClass().getSimpleName())) { - decorators.add(decorator); - } - } - - // SplitByTags purposely does not check for ServiceNameDecorator being enabled - // This allows for ServiceNameDecorator to be disabled above while keeping SplitByTags - // SplitByTags can be disable by removing SplitByTags config - for (final String splitByTag : Config.get().getSplitByTags()) { - decorators.add(new ServiceNameDecorator(splitByTag, true)); - } - - return decorators; - } -} diff --git a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/opentracing/decorators/ForceManualDropDecorator.java b/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/opentracing/decorators/ForceManualDropDecorator.java deleted file mode 100644 index cd46c766de..0000000000 --- a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/opentracing/decorators/ForceManualDropDecorator.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. - * This product includes software developed at Datadog (https://www.datadoghq.com/). - * Copyright 2016-Present Datadog, Inc. - */ - -package com.datadog.opentracing.decorators; - -import com.datadog.opentracing.DDSpanContext; -import com.datadog.legacy.trace.api.DDTags; -import com.datadog.legacy.trace.api.sampling.PrioritySampling; - -/** - * Tag decorator to replace tag 'manual.drop: true' with the appropriate priority sampling value. - */ -public class ForceManualDropDecorator extends AbstractDecorator { - - public ForceManualDropDecorator() { - super(); - setMatchingTag(DDTags.MANUAL_DROP); - } - - @Override - public boolean shouldSetTag(final DDSpanContext context, final String tag, final Object value) { - if (value instanceof Boolean && (boolean) value) { - context.setSamplingPriority(PrioritySampling.USER_DROP); - } else if (value instanceof String && Boolean.parseBoolean((String) value)) { - context.setSamplingPriority(PrioritySampling.USER_DROP); - } - return false; - } -} diff --git a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/opentracing/decorators/ForceManualKeepDecorator.java b/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/opentracing/decorators/ForceManualKeepDecorator.java deleted file mode 100644 index a701d9dcea..0000000000 --- a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/opentracing/decorators/ForceManualKeepDecorator.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. - * This product includes software developed at Datadog (https://www.datadoghq.com/). - * Copyright 2016-Present Datadog, Inc. - */ - -package com.datadog.opentracing.decorators; - -import com.datadog.opentracing.DDSpanContext; -import com.datadog.legacy.trace.api.DDTags; -import com.datadog.legacy.trace.api.sampling.PrioritySampling; - -/** - * Tag decorator to replace tag 'manual.keep: true' with the appropriate priority sampling value. - */ -public class ForceManualKeepDecorator extends AbstractDecorator { - - public ForceManualKeepDecorator() { - super(); - setMatchingTag(DDTags.MANUAL_KEEP); - } - - @Override - public boolean shouldSetTag(final DDSpanContext context, final String tag, final Object value) { - if (value instanceof Boolean && (boolean) value) { - context.setSamplingPriority(PrioritySampling.USER_KEEP); - } else if (value instanceof String && Boolean.parseBoolean((String) value)) { - context.setSamplingPriority(PrioritySampling.USER_KEEP); - } - return false; - } -} diff --git a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/opentracing/decorators/PeerServiceDecorator.java b/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/opentracing/decorators/PeerServiceDecorator.java deleted file mode 100644 index 1e7adbd862..0000000000 --- a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/opentracing/decorators/PeerServiceDecorator.java +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. - * This product includes software developed at Datadog (https://www.datadoghq.com/). - * Copyright 2016-Present Datadog, Inc. - */ - -package com.datadog.opentracing.decorators; - -import com.datadog.opentracing.DDSpanContext; -import io.opentracing.tag.Tags; - -public class PeerServiceDecorator extends AbstractDecorator { - public PeerServiceDecorator() { - super(); - this.setMatchingTag(Tags.PEER_SERVICE.getKey()); - } - - @Override - public boolean shouldSetTag(final DDSpanContext context, final String tag, final Object value) { - context.setServiceName(String.valueOf(value)); - return false; - } -} diff --git a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/opentracing/decorators/ServiceNameDecorator.java b/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/opentracing/decorators/ServiceNameDecorator.java deleted file mode 100644 index b067281bec..0000000000 --- a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/opentracing/decorators/ServiceNameDecorator.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. - * This product includes software developed at Datadog (https://www.datadoghq.com/). - * Copyright 2016-Present Datadog, Inc. - */ - -package com.datadog.opentracing.decorators; - -import com.datadog.opentracing.DDSpanContext; -import com.datadog.legacy.trace.api.DDTags; - -public class ServiceNameDecorator extends AbstractDecorator { - - private final boolean setTag; - - public ServiceNameDecorator() { - this(DDTags.SERVICE_NAME, false); - } - - public ServiceNameDecorator(final String splitByTag, final boolean setTag) { - super(); - this.setTag = setTag; - setMatchingTag(splitByTag); - } - - @Override - public boolean shouldSetTag(final DDSpanContext context, final String tag, final Object value) { - context.setServiceName(String.valueOf(value)); - return setTag; - } -} diff --git a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/opentracing/jfr/DDNoopScopeEvent.java b/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/opentracing/jfr/DDNoopScopeEvent.java deleted file mode 100644 index 1f82178884..0000000000 --- a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/opentracing/jfr/DDNoopScopeEvent.java +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. - * This product includes software developed at Datadog (https://www.datadoghq.com/). - * Copyright 2016-Present Datadog, Inc. - */ - -package com.datadog.opentracing.jfr; - -/** Scope event implementation that does no reporting */ -public final class DDNoopScopeEvent implements DDScopeEvent { - - public static final DDNoopScopeEvent INSTANCE = new DDNoopScopeEvent(); - - @Override - public void start() { - // Noop - } - - @Override - public void finish() { - // Noop - } -} diff --git a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/opentracing/jfr/DDNoopScopeEventFactory.java b/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/opentracing/jfr/DDNoopScopeEventFactory.java deleted file mode 100644 index e8862613d5..0000000000 --- a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/opentracing/jfr/DDNoopScopeEventFactory.java +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. - * This product includes software developed at Datadog (https://www.datadoghq.com/). - * Copyright 2016-Present Datadog, Inc. - */ - -package com.datadog.opentracing.jfr; - -import com.datadog.opentracing.DDSpanContext; - -/** Event factory that returns {@link DDNoopScopeEvent} */ -public final class DDNoopScopeEventFactory implements DDScopeEventFactory { - @Override - public DDScopeEvent create(final DDSpanContext context) { - return DDNoopScopeEvent.INSTANCE; - } -} diff --git a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/opentracing/jfr/DDScopeEventFactory.java b/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/opentracing/jfr/DDScopeEventFactory.java deleted file mode 100644 index e481a6ceb7..0000000000 --- a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/opentracing/jfr/DDScopeEventFactory.java +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. - * This product includes software developed at Datadog (https://www.datadoghq.com/). - * Copyright 2016-Present Datadog, Inc. - */ - -package com.datadog.opentracing.jfr; - -import com.datadog.opentracing.DDSpanContext; - -/** Factory that produces scope events */ -public interface DDScopeEventFactory { - - /** - * Create new scope event for given context. - * - * @param context span context. - * @return scope event instance - */ - DDScopeEvent create(final DDSpanContext context); -} diff --git a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/opentracing/propagation/B3HttpCodec.java b/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/opentracing/propagation/B3HttpCodec.java deleted file mode 100644 index 86f82351c1..0000000000 --- a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/opentracing/propagation/B3HttpCodec.java +++ /dev/null @@ -1,160 +0,0 @@ -/* - * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. - * This product includes software developed at Datadog (https://www.datadoghq.com/). - * Copyright 2016-Present Datadog, Inc. - */ - -package com.datadog.opentracing.propagation; - -import static com.datadog.opentracing.propagation.HttpCodec.validateUInt128BitsID; -import static com.datadog.opentracing.propagation.HttpCodec.validateUInt64BitsID; - -import com.datadog.opentracing.DDSpanContext; -import com.datadog.legacy.trace.api.sampling.PrioritySampling; - -import java.math.BigInteger; -import java.util.Collections; -import java.util.HashMap; -import java.util.Locale; -import java.util.Map; - -import io.opentracing.SpanContext; -import io.opentracing.propagation.TextMapExtract; -import io.opentracing.propagation.TextMapInject; - -/** - * A codec designed for HTTP transport via headers using B3 headers - * - *

    TODO: there is fair amount of code duplication between DatadogHttpCodec and this class, - * especially in part where TagContext is handled. We may want to refactor that and avoid special - * handling of TagContext in other places (i.e. CompoundExtractor). - */ -class B3HttpCodec { - - private static final String HEADER_KEY = "b3"; - private static final String HEADER_VALUE = "%s-%s"; - private static final String HEADER_VALUE_WITH_SAMPLING = "%s-%s-%s"; - private static final String SAMPLING_PRIORITY_ACCEPT = String.valueOf(1); - private static final String SAMPLING_PRIORITY_DROP = String.valueOf(0); - private static final int HEX_RADIX = 16; - - private B3HttpCodec() { - // This class should not be created. This also makes code coverage checks happy. - } - - public static class Injector implements HttpCodec.Injector { - - @Override - public void inject(final DDSpanContext context, final TextMapInject carrier) { - try { - String traceId = context.getTraceId().toString(HEX_RADIX).toLowerCase(Locale.US); - String spanId = context.getSpanId().toString(HEX_RADIX).toLowerCase(Locale.US); - - if (context.lockSamplingPriority()) { - carrier.put(HEADER_KEY, String.format(HEADER_VALUE_WITH_SAMPLING, - traceId, - spanId, - convertSamplingPriority(context.getSamplingPriority()))); - } - else{ - carrier.put(HEADER_KEY, String.format(HEADER_VALUE, traceId, spanId)); - } - - } catch (final NumberFormatException e) { - } - } - - private String convertSamplingPriority(final int samplingPriority) { - return samplingPriority > 0 ? SAMPLING_PRIORITY_ACCEPT : SAMPLING_PRIORITY_DROP; - } - } - - public static class Extractor implements HttpCodec.Extractor { - - private final Map taggedHeaders; - - public Extractor(final Map taggedHeaders) { - this.taggedHeaders = new HashMap<>(); - for (final Map.Entry mapping : taggedHeaders.entrySet()) { - this.taggedHeaders.put(mapping.getKey().trim().toLowerCase(Locale.US), mapping.getValue()); - } - } - - @Override - public SpanContext extract(final TextMapExtract carrier) { - try { - Map tags = Collections.emptyMap(); - BigInteger traceId = BigInteger.ZERO; - BigInteger spanId = BigInteger.ZERO; - int samplingPriority = PrioritySampling.UNSET; - - for (final Map.Entry entry : carrier) { - final String key = entry.getKey().toLowerCase(Locale.US); - final String value = entry.getValue(); - - if (value == null) { - continue; - } - - if (HEADER_KEY.equalsIgnoreCase(key)) { - String[] valueParts = value.split("-"); - - if (valueParts.length < 2 || valueParts.length > 4){ - continue; - } - - final int traceIdLength = valueParts[0].length(); - String trimmedValue; - if (traceIdLength > 32) { - traceId = BigInteger.ZERO; - continue; - } else if (traceIdLength > 16) { - trimmedValue = valueParts[0].substring(traceIdLength - 16); - } else { - trimmedValue = valueParts[0]; - } - traceId = validateUInt128BitsID(trimmedValue, HEX_RADIX); - - spanId = validateUInt64BitsID(valueParts[1], HEX_RADIX); - - if(valueParts.length >= 3) { - samplingPriority = convertSamplingPriority(valueParts[2]); - } - } - - if (taggedHeaders.containsKey(key)) { - if (tags.isEmpty()) { - tags = new HashMap<>(); - } - tags.put(taggedHeaders.get(key), HttpCodec.decode(value)); - } - } - - if (!BigInteger.ZERO.equals(traceId)) { - final ExtractedContext context = - new ExtractedContext( - traceId, - spanId, - samplingPriority, - null, - Collections.emptyMap(), - tags); - context.lockSamplingPriority(); - - return context; - } else if (!tags.isEmpty()) { - return new TagContext(null, tags); - } - } catch (final RuntimeException e) { - } - - return null; - } - - private int convertSamplingPriority(final String samplingPriority) { - return Integer.parseInt(samplingPriority) == 1 - ? PrioritySampling.SAMPLER_KEEP - : PrioritySampling.SAMPLER_DROP; - } - } -} diff --git a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/opentracing/propagation/B3MHttpCodec.java b/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/opentracing/propagation/B3MHttpCodec.java deleted file mode 100644 index f1d0ef1e72..0000000000 --- a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/opentracing/propagation/B3MHttpCodec.java +++ /dev/null @@ -1,144 +0,0 @@ -/* - * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. - * This product includes software developed at Datadog (https://www.datadoghq.com/). - * Copyright 2016-Present Datadog, Inc. - */ - -package com.datadog.opentracing.propagation; - -import static com.datadog.opentracing.propagation.HttpCodec.validateUInt128BitsID; -import static com.datadog.opentracing.propagation.HttpCodec.validateUInt64BitsID; - -import com.datadog.opentracing.DDSpanContext; -import com.datadog.legacy.trace.api.sampling.PrioritySampling; -import io.opentracing.SpanContext; -import io.opentracing.propagation.TextMapExtract; -import io.opentracing.propagation.TextMapInject; -import java.math.BigInteger; -import java.util.Collections; -import java.util.HashMap; -import java.util.Locale; -import java.util.Map; - -/** - * A codec designed for HTTP transport via headers using B3M headers - * - *

    TODO: there is fair amount of code duplication between DatadogHttpCodec and this class, - * especially in part where TagContext is handled. We may want to refactor that and avoid special - * handling of TagContext in other places (i.e. CompoundExtractor). - */ -class B3MHttpCodec { - - private static final String TRACE_ID_KEY = "X-B3-TraceId"; - private static final String SPAN_ID_KEY = "X-B3-SpanId"; - private static final String SAMPLING_PRIORITY_KEY = "X-B3-Sampled"; - private static final String SAMPLING_PRIORITY_ACCEPT = String.valueOf(1); - private static final String SAMPLING_PRIORITY_DROP = String.valueOf(0); - private static final int HEX_RADIX = 16; - - private B3MHttpCodec() { - // This class should not be created. This also makes code coverage checks happy. - } - - public static class Injector implements HttpCodec.Injector { - - @Override - public void inject(final DDSpanContext context, final TextMapInject carrier) { - try { - carrier.put(TRACE_ID_KEY, context.getTraceId().toString(HEX_RADIX).toLowerCase(Locale.US)); - carrier.put(SPAN_ID_KEY, context.getSpanId().toString(HEX_RADIX).toLowerCase(Locale.US)); - - if (context.lockSamplingPriority()) { - carrier.put( - SAMPLING_PRIORITY_KEY, convertSamplingPriority(context.getSamplingPriority())); - } - } catch (final NumberFormatException e) { - } - } - - private String convertSamplingPriority(final int samplingPriority) { - return samplingPriority > 0 ? SAMPLING_PRIORITY_ACCEPT : SAMPLING_PRIORITY_DROP; - } - } - - public static class Extractor implements HttpCodec.Extractor { - - private final Map taggedHeaders; - - public Extractor(final Map taggedHeaders) { - this.taggedHeaders = new HashMap<>(); - for (final Map.Entry mapping : taggedHeaders.entrySet()) { - this.taggedHeaders.put(mapping.getKey().trim().toLowerCase(Locale.US), mapping.getValue()); - } - } - - @Override - public SpanContext extract(final TextMapExtract carrier) { - try { - Map tags = Collections.emptyMap(); - BigInteger traceId = BigInteger.ZERO; - BigInteger spanId = BigInteger.ZERO; - int samplingPriority = PrioritySampling.UNSET; - - for (final Map.Entry entry : carrier) { - final String key = entry.getKey().toLowerCase(Locale.US); - final String value = entry.getValue(); - - if (value == null) { - continue; - } - - if (TRACE_ID_KEY.equalsIgnoreCase(key)) { - final String trimmedValue; - final int length = value.length(); - if (length > 32) { - traceId = BigInteger.ZERO; - continue; - } else if (length > 16) { - trimmedValue = value.substring(length - 16); - } else { - trimmedValue = value; - } - traceId = validateUInt128BitsID(trimmedValue, HEX_RADIX); - } else if (SPAN_ID_KEY.equalsIgnoreCase(key)) { - spanId = validateUInt64BitsID(value, HEX_RADIX); - } else if (SAMPLING_PRIORITY_KEY.equalsIgnoreCase(key)) { - samplingPriority = convertSamplingPriority(value); - } - - if (taggedHeaders.containsKey(key)) { - if (tags.isEmpty()) { - tags = new HashMap<>(); - } - tags.put(taggedHeaders.get(key), HttpCodec.decode(value)); - } - } - - if (!BigInteger.ZERO.equals(traceId)) { - final ExtractedContext context = - new ExtractedContext( - traceId, - spanId, - samplingPriority, - null, - Collections.emptyMap(), - tags); - context.lockSamplingPriority(); - - return context; - } else if (!tags.isEmpty()) { - return new TagContext(null, tags); - } - } catch (final RuntimeException e) { - } - - return null; - } - - private int convertSamplingPriority(final String samplingPriority) { - return Integer.parseInt(samplingPriority) == 1 - ? PrioritySampling.SAMPLER_KEEP - : PrioritySampling.SAMPLER_DROP; - } - } -} diff --git a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/opentracing/propagation/DatadogHttpCodec.java b/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/opentracing/propagation/DatadogHttpCodec.java deleted file mode 100644 index 401a468d91..0000000000 --- a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/opentracing/propagation/DatadogHttpCodec.java +++ /dev/null @@ -1,185 +0,0 @@ -/* - * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. - * This product includes software developed at Datadog (https://www.datadoghq.com/). - * Copyright 2016-Present Datadog, Inc. - */ - -package com.datadog.opentracing.propagation; - -import static com.datadog.opentracing.propagation.HttpCodec.validateUInt128BitsID; -import static com.datadog.opentracing.propagation.HttpCodec.validateUInt64BitsID; -import static com.datadog.trace.core.propagation.HttpCodec.RUM_SESSION_ID_KEY; -import static com.datadog.opentracing.propagation.W3CHttpCodec.BAGGAGE_KEY; -import static com.datadog.opentracing.propagation.W3CHttpCodec.RUM_SESSION_ID_BAGGAGE_KEY; - -import com.datadog.android.trace.internal.domain.event.BigIntegerUtils; -import com.datadog.opentracing.DDSpanContext; -import com.datadog.legacy.trace.api.sampling.PrioritySampling; -import com.datadog.trace.api.internal.util.LongStringUtils; - -import io.opentracing.SpanContext; -import io.opentracing.propagation.TextMapExtract; -import io.opentracing.propagation.TextMapInject; - -import java.math.BigInteger; -import java.util.Collections; -import java.util.HashMap; -import java.util.Locale; -import java.util.Map; - -/** - * A codec designed for HTTP transport via headers using Datadog headers - */ -class DatadogHttpCodec { - - public static final String OT_BAGGAGE_PREFIX = "ot-baggage-"; - public static final String LEAST_SIGNIFICANT_TRACE_ID_KEY = "x-datadog-trace-id"; - public static final String MOST_SIGNIFICANT_TRACE_ID_TAG_KEY = "_dd.p.tid"; - public static final String DATADOG_TAGS_KEY = "x-datadog-tags"; - public static final String SPAN_ID_KEY = "x-datadog-parent-id"; - public static final String SAMPLING_PRIORITY_KEY = "x-datadog-sampling-priority"; - public static final String ORIGIN_KEY = "x-datadog-origin"; - - private DatadogHttpCodec() { - // This class should not be created. This also makes code coverage checks happy. - } - - public static class Injector implements HttpCodec.Injector { - - private final BigIntegerUtils bigIntegerUtils; - - public Injector(final BigIntegerUtils bigIntegerUtils) { - this.bigIntegerUtils = bigIntegerUtils; - } - - public Injector() { - this(BigIntegerUtils.INSTANCE); - } - - @Override - public void inject(final DDSpanContext context, final TextMapInject carrier) { - final BigInteger traceId = context.getTraceId(); - final String leastSignificantTraceId = bigIntegerUtils.leastSignificant64BitsAsDecimal(traceId); - final String mostSignificantTraceId = bigIntegerUtils.mostSignificant64BitsAsHex(traceId); - final String sessionId = (String) context.getTags().get(RUM_SESSION_ID_KEY); - carrier.put(LEAST_SIGNIFICANT_TRACE_ID_KEY, leastSignificantTraceId); - carrier.put(SPAN_ID_KEY, context.getSpanId().toString()); - final String origin = context.getOrigin(); - if (origin != null) { - carrier.put(ORIGIN_KEY, origin); - } - - for (final Map.Entry entry : context.baggageItems()) { - carrier.put(OT_BAGGAGE_PREFIX + entry.getKey(), HttpCodec.encode(entry.getValue())); - } - // adding the tags - - StringBuilder tags = new StringBuilder(); - tags.append(MOST_SIGNIFICANT_TRACE_ID_TAG_KEY); - tags.append('='); - tags.append(mostSignificantTraceId); - carrier.put(DATADOG_TAGS_KEY, tags.toString()); - - if(sessionId != null) { - carrier.put(BAGGAGE_KEY, RUM_SESSION_ID_BAGGAGE_KEY + '='+sessionId); - } - - if (context.lockSamplingPriority()) { - carrier.put(SAMPLING_PRIORITY_KEY, String.valueOf(context.getSamplingPriority())); - } - } - } - - public static class Extractor implements HttpCodec.Extractor { - private final Map taggedHeaders; - - public Extractor(final Map taggedHeaders) { - this.taggedHeaders = new HashMap<>(); - for (final Map.Entry mapping : taggedHeaders.entrySet()) { - this.taggedHeaders.put(mapping.getKey().trim().toLowerCase(Locale.US), mapping.getValue()); - } - } - - @Override - public SpanContext extract(final TextMapExtract carrier) { - try { - Map baggage = Collections.emptyMap(); - Map tags = Collections.emptyMap(); - BigInteger spanId = BigInteger.ZERO; - int samplingPriority = PrioritySampling.UNSET; - String origin = null; - String mostSignificant64BitsTraceIdAsHex = null; - String leastSignificant64BitsTraceIdAsDecimal = null; - - - for (final Map.Entry entry : carrier) { - final String key = entry.getKey().toLowerCase(Locale.US); - final String value = entry.getValue(); - - if (value == null) { - continue; - } - - if (LEAST_SIGNIFICANT_TRACE_ID_KEY.equalsIgnoreCase(key)) { - leastSignificant64BitsTraceIdAsDecimal = value; - } else if (DATADOG_TAGS_KEY.equalsIgnoreCase(key)) { - mostSignificant64BitsTraceIdAsHex = extractMostSignificant64BitsTraceId(value); - } else if (SPAN_ID_KEY.equalsIgnoreCase(key)) { - spanId = validateUInt64BitsID(value, 10); - } else if (SAMPLING_PRIORITY_KEY.equalsIgnoreCase(key)) { - samplingPriority = Integer.parseInt(value); - } else if (ORIGIN_KEY.equalsIgnoreCase(key)) { - origin = value; - } else if (key.startsWith(OT_BAGGAGE_PREFIX)) { - if (baggage.isEmpty()) { - baggage = new HashMap<>(); - } - baggage.put(key.replace(OT_BAGGAGE_PREFIX, ""), HttpCodec.decode(value)); - } - - if (taggedHeaders.containsKey(key)) { - if (tags.isEmpty()) { - tags = new HashMap<>(); - } - tags.put(taggedHeaders.get(key), HttpCodec.decode(value)); - } - } - if (leastSignificant64BitsTraceIdAsDecimal == null || mostSignificant64BitsTraceIdAsHex == null) { - return new TagContext(origin, tags); - } - - final long leastSignificantTraceId = - LongStringUtils.parseUnsignedLong(leastSignificant64BitsTraceIdAsDecimal); - final String traceIdAsHex = mostSignificant64BitsTraceIdAsHex + - LongStringUtils.toHexStringPadded(leastSignificantTraceId, 16); - final BigInteger traceId = validateUInt128BitsID(traceIdAsHex, 16); - if (!BigInteger.ZERO.equals(traceId)) { - final ExtractedContext context = - new ExtractedContext(traceId, spanId, samplingPriority, origin, baggage, tags); - context.lockSamplingPriority(); - - return context; - } else if (origin != null || !tags.isEmpty()) { - return new TagContext(origin, tags); - } - } catch (final RuntimeException e) { - } - - return null; - } - - private String extractMostSignificant64BitsTraceId(final String tags) { - if (tags == null) { - return null; - } - final String[] tagArray = tags.split(","); - for (String tag : tagArray) { - final String[] tagKeyValue = tag.split("="); - if (tagKeyValue.length >= 2 && MOST_SIGNIFICANT_TRACE_ID_TAG_KEY.equals(tagKeyValue[0])) { - return tagKeyValue[1]; - } - } - return null; - } - } -} diff --git a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/opentracing/propagation/ExtractedContext.java b/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/opentracing/propagation/ExtractedContext.java deleted file mode 100644 index d9ce18ea68..0000000000 --- a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/opentracing/propagation/ExtractedContext.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. - * This product includes software developed at Datadog (https://www.datadoghq.com/). - * Copyright 2016-Present Datadog, Inc. - */ - -package com.datadog.opentracing.propagation; - -import java.math.BigInteger; -import java.util.Map; -import java.util.concurrent.atomic.AtomicBoolean; - -/** - * Propagated data resulting from calling tracer.extract with header data from an incoming request. - */ -public class ExtractedContext extends TagContext { - private final BigInteger traceId; - private final BigInteger spanId; - private final int samplingPriority; - private final Map baggage; - private final AtomicBoolean samplingPriorityLocked = new AtomicBoolean(false); - - public ExtractedContext( - final BigInteger traceId, - final BigInteger spanId, - final int samplingPriority, - final String origin, - final Map baggage, - final Map tags) { - super(origin, tags); - this.traceId = traceId; - this.spanId = spanId; - this.samplingPriority = samplingPriority; - this.baggage = baggage; - } - - @Override - public Iterable> baggageItems() { - return baggage.entrySet(); - } - - public void lockSamplingPriority() { - samplingPriorityLocked.set(true); - } - - public BigInteger getTraceId() { - return traceId; - } - - public BigInteger getSpanId() { - return spanId; - } - - public int getSamplingPriority() { - return samplingPriority; - } - - public Map getBaggage() { - return baggage; - } - - public boolean getSamplingPriorityLocked() { - return samplingPriorityLocked.get(); - } -} diff --git a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/opentracing/propagation/HaystackHttpCodec.java b/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/opentracing/propagation/HaystackHttpCodec.java deleted file mode 100644 index 0df66dd396..0000000000 --- a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/opentracing/propagation/HaystackHttpCodec.java +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. - * This product includes software developed at Datadog (https://www.datadoghq.com/). - * Copyright 2016-Present Datadog, Inc. - */ - -package com.datadog.opentracing.propagation; - -import static com.datadog.opentracing.propagation.HttpCodec.validateUInt128BitsID; -import static com.datadog.opentracing.propagation.HttpCodec.validateUInt64BitsID; - -import com.datadog.opentracing.DDSpanContext; -import com.datadog.legacy.trace.api.sampling.PrioritySampling; -import io.opentracing.SpanContext; -import io.opentracing.propagation.TextMapExtract; -import io.opentracing.propagation.TextMapInject; -import java.math.BigInteger; -import java.util.Collections; -import java.util.HashMap; -import java.util.Locale; -import java.util.Map; - -/** - * A codec designed for HTTP transport via headers using Haystack headers. - * - * @author Alex Antonov - */ -public class HaystackHttpCodec { - - private static final String OT_BAGGAGE_PREFIX = "Baggage-"; - private static final String TRACE_ID_KEY = "Trace-ID"; - private static final String SPAN_ID_KEY = "Span-ID"; - private static final String PARENT_ID_KEY = "Parent_ID"; - - private HaystackHttpCodec() { - // This class should not be created. This also makes code coverage checks happy. - } - - public static class Injector implements HttpCodec.Injector { - - @Override - public void inject(final DDSpanContext context, final TextMapInject carrier) { - carrier.put(TRACE_ID_KEY, context.getTraceId().toString()); - carrier.put(SPAN_ID_KEY, context.getSpanId().toString()); - carrier.put(PARENT_ID_KEY, context.getParentId().toString()); - - for (final Map.Entry entry : context.baggageItems()) { - carrier.put(OT_BAGGAGE_PREFIX + entry.getKey(), HttpCodec.encode(entry.getValue())); - } - } - } - - public static class Extractor implements HttpCodec.Extractor { - private final Map taggedHeaders; - - /** Creates Header Extractor using Haystack propagation. */ - public Extractor(final Map taggedHeaders) { - this.taggedHeaders = new HashMap<>(); - for (final Map.Entry mapping : taggedHeaders.entrySet()) { - this.taggedHeaders.put(mapping.getKey().trim().toLowerCase(Locale.US), mapping.getValue()); - } - } - - @Override - public SpanContext extract(final TextMapExtract carrier) { - try { - Map baggage = Collections.emptyMap(); - Map tags = Collections.emptyMap(); - BigInteger traceId = BigInteger.ZERO; - BigInteger spanId = BigInteger.ZERO; - final int samplingPriority = PrioritySampling.SAMPLER_KEEP; - final String origin = null; // Always null - - for (final Map.Entry entry : carrier) { - final String key = entry.getKey().toLowerCase(Locale.US); - final String value = entry.getValue(); - - if (value == null) { - continue; - } - - if (TRACE_ID_KEY.equalsIgnoreCase(key)) { - traceId = validateUInt128BitsID(value, 10); - } else if (SPAN_ID_KEY.equalsIgnoreCase(key)) { - spanId = validateUInt64BitsID(value, 10); - } else if (key.startsWith(OT_BAGGAGE_PREFIX.toLowerCase(Locale.US))) { - if (baggage.isEmpty()) { - baggage = new HashMap<>(); - } - baggage.put(key.replace(OT_BAGGAGE_PREFIX.toLowerCase(Locale.US), ""), HttpCodec.decode(value)); - } - - if (taggedHeaders.containsKey(key)) { - if (tags.isEmpty()) { - tags = new HashMap<>(); - } - tags.put(taggedHeaders.get(key), HttpCodec.decode(value)); - } - } - - if (!BigInteger.ZERO.equals(traceId)) { - final ExtractedContext context = - new ExtractedContext(traceId, spanId, samplingPriority, origin, baggage, tags); - context.lockSamplingPriority(); - - return context; - } else if (origin != null || !tags.isEmpty()) { - return new TagContext(origin, tags); - } - } catch (final RuntimeException e) { - } - - return null; - } - } -} diff --git a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/opentracing/propagation/HttpCodec.java b/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/opentracing/propagation/HttpCodec.java deleted file mode 100644 index 0add450935..0000000000 --- a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/opentracing/propagation/HttpCodec.java +++ /dev/null @@ -1,189 +0,0 @@ -/* - * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. - * This product includes software developed at Datadog (https://www.datadoghq.com/). - * Copyright 2016-Present Datadog, Inc. - */ - -package com.datadog.opentracing.propagation; - -import com.datadog.opentracing.DDSpanContext; -import com.datadog.opentracing.DDTracer; -import com.datadog.opentracing.StringCachingBigInteger; -import com.datadog.legacy.trace.api.Config; -import io.opentracing.SpanContext; -import io.opentracing.propagation.TextMapExtract; -import io.opentracing.propagation.TextMapInject; -import java.io.UnsupportedEncodingException; -import java.math.BigInteger; -import java.net.URLDecoder; -import java.net.URLEncoder; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; - -public class HttpCodec { - public interface Injector { - - void inject(final DDSpanContext context, final TextMapInject carrier); - } - - public interface Extractor { - - SpanContext extract(final TextMapExtract carrier); - } - - public static Injector createInjector(final Config config) { - final List injectors = new ArrayList<>(); - for (final Config.PropagationStyle style : config.getPropagationStylesToInject()) { - if (style == Config.PropagationStyle.DATADOG) { - injectors.add(new DatadogHttpCodec.Injector()); - continue; - } - if (style == Config.PropagationStyle.B3) { - injectors.add(new B3HttpCodec.Injector()); - continue; - } - if (style == Config.PropagationStyle.B3MULTI) { - injectors.add(new B3MHttpCodec.Injector()); - continue; - } - if (style == Config.PropagationStyle.TRACECONTEXT) { - injectors.add(new W3CHttpCodec.Injector()); - continue; - } - if (style == Config.PropagationStyle.HAYSTACK) { - injectors.add(new HaystackHttpCodec.Injector()); - continue; - } - } - return new CompoundInjector(injectors); - } - - public static Extractor createExtractor( - final Config config, final Map taggedHeaders) { - final List extractors = new ArrayList<>(); - for (final Config.PropagationStyle style : config.getPropagationStylesToExtract()) { - if (style == Config.PropagationStyle.DATADOG) { - extractors.add(new DatadogHttpCodec.Extractor(taggedHeaders)); - continue; - } - if (style == Config.PropagationStyle.B3) { - extractors.add(new B3HttpCodec.Extractor(taggedHeaders)); - continue; - } - if (style == Config.PropagationStyle.B3MULTI) { - extractors.add(new B3MHttpCodec.Extractor(taggedHeaders)); - continue; - } - if (style == Config.PropagationStyle.TRACECONTEXT) { - extractors.add(new W3CHttpCodec.Extractor(taggedHeaders)); - continue; - } - if (style == Config.PropagationStyle.HAYSTACK) { - extractors.add(new HaystackHttpCodec.Extractor(taggedHeaders)); - continue; - } - } - return new CompoundExtractor(extractors); - } - - public static class CompoundInjector implements Injector { - - private final List injectors; - - public CompoundInjector(final List injectors) { - this.injectors = injectors; - } - - @Override - public void inject(final DDSpanContext context, final TextMapInject carrier) { - for (final Injector injector : injectors) { - injector.inject(context, carrier); - } - } - } - - public static class CompoundExtractor implements Extractor { - - private final List extractors; - - public CompoundExtractor(final List extractors) { - this.extractors = extractors; - } - - @Override - public SpanContext extract(final TextMapExtract carrier) { - SpanContext context = null; - for (final Extractor extractor : extractors) { - context = extractor.extract(carrier); - // Use incomplete TagContext only as last resort - if (context != null && (context instanceof ExtractedContext)) { - return context; - } - } - return context; - } - } - - /** - * Helper method to validate an ID String to verify within range - * - * @param value the String that contains the ID - * @param radix radix to use to parse the ID - * @return the parsed ID - * @throws IllegalArgumentException if value cannot be converted to integer or doesn't conform to - * required boundaries - */ - static BigInteger validateUInt64BitsID(final String value, final int radix) - throws IllegalArgumentException { - final BigInteger parsedValue = new StringCachingBigInteger(value, radix); - if (parsedValue.compareTo(DDTracer.TRACE_ID_MIN) < 0 - || parsedValue.compareTo(DDTracer.TRACE_ID_64_BITS_MAX) > 0) { - throw new IllegalArgumentException( - "ID out of range, must be between 0 and 2^64-1, got: " + value); - } - - return parsedValue; - } - - /** - * Helper method to validate an ID String to verify within range - * - * @param value the String that contains the ID - * @param radix radix to use to parse the ID - * @return the parsed ID - * @throws IllegalArgumentException if value cannot be converted to integer or doesn't conform to - * required boundaries - */ - static BigInteger validateUInt128BitsID(final String value, final int radix) - throws IllegalArgumentException { - final BigInteger parsedValue = new StringCachingBigInteger(value, radix); - if (parsedValue.compareTo(DDTracer.TRACE_ID_MIN) < 0 - || parsedValue.compareTo(DDTracer.TRACE_ID_128_BITS_MAX) > 0) { - throw new IllegalArgumentException( - "ID out of range, must be between 0 and 2^128-1, got: " + value); - } - - return parsedValue; - } - - /** URL encode value */ - static String encode(final String value) { - String encoded = value; - try { - encoded = URLEncoder.encode(value, "UTF-8"); - } catch (final UnsupportedEncodingException e) { - } - return encoded; - } - - /** URL decode value */ - static String decode(final String value) { - String decoded = value; - try { - decoded = URLDecoder.decode(value, "UTF-8"); - } catch (final UnsupportedEncodingException e) { - } - return decoded; - } -} diff --git a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/opentracing/propagation/TagContext.java b/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/opentracing/propagation/TagContext.java deleted file mode 100644 index dad8c3385a..0000000000 --- a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/opentracing/propagation/TagContext.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. - * This product includes software developed at Datadog (https://www.datadoghq.com/). - * Copyright 2016-Present Datadog, Inc. - */ - -package com.datadog.opentracing.propagation; - -import io.opentracing.SpanContext; -import java.util.Collections; -import java.util.Map; - -/** - * When calling extract, we allow for grabbing other configured headers as tags. Those tags are - * returned here even if the rest of the request would have returned null. - */ -public class TagContext implements SpanContext { - private final String origin; - private final Map tags; - - public TagContext(final String origin, final Map tags) { - this.origin = origin; - this.tags = tags; - } - - public String getOrigin() { - return origin; - } - - public Map getTags() { - return tags; - } - - @Override - public String toTraceId() { - return ""; - } - - @Override - public String toSpanId() { - return ""; - } - - @Override - public Iterable> baggageItems() { - return Collections.emptyList(); - } -} diff --git a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/opentracing/propagation/W3CHttpCodec.java b/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/opentracing/propagation/W3CHttpCodec.java deleted file mode 100644 index b12712ad49..0000000000 --- a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/opentracing/propagation/W3CHttpCodec.java +++ /dev/null @@ -1,232 +0,0 @@ -/* - * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. - * This product includes software developed at Datadog (https://www.datadoghq.com/). - * Copyright 2016-Present Datadog, Inc. - */ - -package com.datadog.opentracing.propagation; - -import static com.datadog.opentracing.propagation.HttpCodec.validateUInt128BitsID; -import static com.datadog.opentracing.propagation.HttpCodec.validateUInt64BitsID; -import static com.datadog.trace.core.propagation.HttpCodec.RUM_SESSION_ID_KEY; - -import com.datadog.opentracing.DDSpanContext; -import com.datadog.legacy.trace.api.sampling.PrioritySampling; - -import java.math.BigInteger; -import java.util.Collections; -import java.util.HashMap; -import java.util.Locale; -import java.util.Map; - -import io.opentracing.SpanContext; -import io.opentracing.propagation.TextMapExtract; -import io.opentracing.propagation.TextMapInject; -import kotlin.text.StringsKt; - -/** - * A codec designed for HTTP transport via headers using W3C traceparent header - * - *

    TODO: there is fair amount of code duplication between DatadogHttpCodec and this class, - * especially in part where TagContext is handled. We may want to refactor that and avoid special - * handling of TagContext in other places (i.e. CompoundExtractor). - */ -class W3CHttpCodec { - - private static final String TRACEPARENT_KEY = "traceparent"; - private static final String TRACESTATE_KEY = "tracestate"; - static final String BAGGAGE_KEY = "baggage"; - - private static final String TRACEPARENT_VALUE = "00-%s-%s-0%s"; - - private static final int TRACECONTEXT_PARENT_ID_LENGTH = 16; - private static final int TRACECONTEXT_TRACE_ID_LENGTH = 32; - private static final String SAMPLING_PRIORITY_ACCEPT = String.valueOf(1); - private static final String SAMPLING_PRIORITY_DROP = String.valueOf(0); - private static final int HEX_RADIX = 16; - - private static final String ORIGIN_TRACESTATE_TAG_VALUE = "o"; - private static final String SAMPLING_PRIORITY_TRACESTATE_TAG_VALUE = "s"; - private static final String PARENT_SPAN_ID_TRACESTATE_TAG_VALUE = "p"; - private static final String DATADOG_VENDOR_TRACESTATE_PREFIX = "dd="; - - static final String RUM_SESSION_ID_BAGGAGE_KEY = "session.id"; - - private W3CHttpCodec() { - // This class should not be created. This also makes code coverage checks happy. - } - - public static class Injector implements HttpCodec.Injector { - - @Override - public void inject(final DDSpanContext context, final TextMapInject carrier) { - try { - String traceId = context.getTraceId().toString(HEX_RADIX).toLowerCase(Locale.US); - String spanId = context.getSpanId().toString(HEX_RADIX).toLowerCase(Locale.US); - String samplingPriority = convertSamplingPriority(context.getSamplingPriority()); - String origin = context.getOrigin(); - String sessionId = (String) context.getTags().get(RUM_SESSION_ID_KEY); - - carrier.put(TRACEPARENT_KEY, String.format(TRACEPARENT_VALUE, - StringsKt.padStart(traceId, TRACECONTEXT_TRACE_ID_LENGTH, '0'), - StringsKt.padStart(spanId, TRACECONTEXT_PARENT_ID_LENGTH, '0'), - samplingPriority)); - // TODO RUM-2121 3rd party vendor information will be erased - carrier.put(TRACESTATE_KEY, createTraceStateHeader(samplingPriority, origin, spanId)); - - carrier.put(BAGGAGE_KEY, createSessionIdBaggage(sessionId)); - } catch (final NumberFormatException e) { - } - } - - private String convertSamplingPriority(final int samplingPriority) { - return samplingPriority > 0 ? SAMPLING_PRIORITY_ACCEPT : SAMPLING_PRIORITY_DROP; - } - - private String createTraceStateHeader( - String samplingPriority, - String origin, - String parentSpanId - ) { - StringBuilder sb = new StringBuilder(DATADOG_VENDOR_TRACESTATE_PREFIX) - .append(SAMPLING_PRIORITY_TRACESTATE_TAG_VALUE) - .append(':') - .append(samplingPriority) - .append(';') - .append(PARENT_SPAN_ID_TRACESTATE_TAG_VALUE) - .append(':') - .append(parentSpanId); - - if (origin != null) { - sb.append(';') - .append(ORIGIN_TRACESTATE_TAG_VALUE) - .append(':') - .append(origin.toLowerCase(Locale.US)); - } - - return sb.toString(); - } - - private String createSessionIdBaggage( - String sessionId - ) { - return RUM_SESSION_ID_BAGGAGE_KEY + "=" + sessionId; - } - } - - public static class Extractor implements HttpCodec.Extractor { - - private final Map taggedHeaders; - - public Extractor(final Map taggedHeaders) { - this.taggedHeaders = new HashMap<>(); - for (final Map.Entry mapping : taggedHeaders.entrySet()) { - this.taggedHeaders.put(mapping.getKey().trim().toLowerCase(Locale.US), mapping.getValue()); - } - } - - @Override - public SpanContext extract(final TextMapExtract carrier) { - try { - Map tags = Collections.emptyMap(); - BigInteger traceId = BigInteger.ZERO; - BigInteger spanId = BigInteger.ZERO; - int samplingPriority = PrioritySampling.UNSET; - String origin = null; - - for (final Map.Entry entry : carrier) { - final String key = entry.getKey().toLowerCase(Locale.US); - final String value = entry.getValue(); - - if (value == null) { - continue; - } - - if (TRACEPARENT_KEY.equalsIgnoreCase(key)) { - // version - traceId - parentId - traceFlags - String[] valueParts = value.split("-"); - - if (valueParts.length != 4){ - continue; - } - - if ("ff".equalsIgnoreCase(valueParts[0])){ - // ff version is forbidden - continue; - } - - final int traceIdLength = valueParts[1].length(); - String trimmedValue; - if (traceIdLength > 32) { - traceId = BigInteger.ZERO; - continue; - } else if (traceIdLength > 16) { - trimmedValue = valueParts[1].substring(traceIdLength - 16); - } else { - trimmedValue = valueParts[1]; - } - traceId = validateUInt128BitsID(trimmedValue, HEX_RADIX); - - spanId = validateUInt64BitsID(valueParts[2], HEX_RADIX); - - samplingPriority = convertSamplingPriority(valueParts[3]); - - } else if (TRACESTATE_KEY.equalsIgnoreCase(key)) { - Map datadogTraceStateTags = extractDatadogTagsFromTraceState(value); - origin = datadogTraceStateTags.get(ORIGIN_TRACESTATE_TAG_VALUE); - } - - if (taggedHeaders.containsKey(key)) { - if (tags.isEmpty()) { - tags = new HashMap<>(); - } - tags.put(taggedHeaders.get(key), HttpCodec.decode(value)); - } - } - - if (!BigInteger.ZERO.equals(traceId)) { - final ExtractedContext context = - new ExtractedContext( - traceId, - spanId, - samplingPriority, - origin, - Collections.emptyMap(), - tags); - context.lockSamplingPriority(); - - return context; - } else if (!tags.isEmpty()) { - return new TagContext(origin, tags); - } - } catch (final RuntimeException e) { - } - - return null; - } - - private int convertSamplingPriority(final String samplingPriority) { - return Integer.parseInt(samplingPriority) == 1 - ? PrioritySampling.SAMPLER_KEEP - : PrioritySampling.SAMPLER_DROP; - } - - private Map extractDatadogTagsFromTraceState(String traceState) { - String[] vendors = traceState.split(","); - Map tags = new HashMap<>(); - for (String vendor : vendors) { - if (vendor.startsWith(DATADOG_VENDOR_TRACESTATE_PREFIX)) { - String[] vendorTags = vendor.substring(DATADOG_VENDOR_TRACESTATE_PREFIX.length()) - .split(";"); - for (String vendorTag : vendorTags) { - String[] keyAndValue = vendorTag.split(":"); - if (keyAndValue.length == 2) { - tags.put(keyAndValue[0], keyAndValue[1]); - } - } - } - } - return tags; - } - } -} diff --git a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/opentracing/scopemanager/ContextualScopeManager.java b/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/opentracing/scopemanager/ContextualScopeManager.java deleted file mode 100644 index c49a3da8db..0000000000 --- a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/opentracing/scopemanager/ContextualScopeManager.java +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. - * This product includes software developed at Datadog (https://www.datadoghq.com/). - * Copyright 2016-Present Datadog, Inc. - */ - -package com.datadog.opentracing.scopemanager; - -import com.datadog.opentracing.DDSpan; -import com.datadog.opentracing.jfr.DDScopeEventFactory; -import com.datadog.legacy.trace.context.ScopeListener; -import io.opentracing.Scope; -import io.opentracing.ScopeManager; -import io.opentracing.Span; -import io.opentracing.noop.NoopScopeManager; -import java.util.Deque; -import java.util.LinkedList; -import java.util.List; -import java.util.concurrent.CopyOnWriteArrayList; - -public class ContextualScopeManager implements ScopeManager { - static final ThreadLocal tlsScope = new ThreadLocal<>(); - final Deque scopeContexts = new LinkedList<>(); - final List scopeListeners = new CopyOnWriteArrayList<>(); - - private final int depthLimit; - private final DDScopeEventFactory scopeEventFactory; - - public ContextualScopeManager(final int depthLimit, final DDScopeEventFactory scopeEventFactory) { - this.depthLimit = depthLimit; - this.scopeEventFactory = scopeEventFactory; - } - - @Override - public Scope activate(final Span span, final boolean finishOnClose) { - final Scope active = active(); - if (active instanceof DDScope) { - final int currentDepth = ((DDScope) active).depth(); - if (depthLimit <= currentDepth) { - return NoopScopeManager.NoopScope.INSTANCE; - } - } - synchronized (scopeContexts) { - for (final ScopeContext context : scopeContexts) { - if (context.inContext()) { - return context.activate(span, finishOnClose); - } - } - } - if (span instanceof DDSpan) { - return new ContinuableScope(this, (DDSpan) span, finishOnClose, scopeEventFactory); - } else { - return new SimpleScope(this, span, finishOnClose); - } - } - - @Override - public Scope activate(final Span span) { - return activate(span, false); - } - - @Override - public Scope active() { - synchronized (scopeContexts) { - for (final ScopeContext csm : scopeContexts) { - if (csm.inContext()) { - return csm.active(); - } - } - } - return tlsScope.get(); - } - - @Override - public Span activeSpan() { - synchronized (scopeContexts) { - for (final ScopeContext csm : scopeContexts) { - if (csm.inContext()) { - return csm.activeSpan(); - } - } - } - final DDScope active = tlsScope.get(); - return active == null ? null : active.span(); - } - - @Deprecated - public void addScopeContext(final ScopeContext context) { - synchronized (scopeContexts) { - scopeContexts.addFirst(context); - } - } - - /** Attach a listener to scope activation events */ - public void addScopeListener(final ScopeListener listener) { - scopeListeners.add(listener); - } -} diff --git a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/opentracing/scopemanager/ContinuableScope.java b/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/opentracing/scopemanager/ContinuableScope.java deleted file mode 100644 index bc27c742c5..0000000000 --- a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/opentracing/scopemanager/ContinuableScope.java +++ /dev/null @@ -1,193 +0,0 @@ -/* - * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. - * This product includes software developed at Datadog (https://www.datadoghq.com/). - * Copyright 2016-Present Datadog, Inc. - */ - -package com.datadog.opentracing.scopemanager; - -import com.datadog.opentracing.DDSpan; -import com.datadog.opentracing.DDSpanContext; -import com.datadog.opentracing.PendingTrace; -import com.datadog.opentracing.jfr.DDScopeEvent; -import com.datadog.opentracing.jfr.DDScopeEventFactory; -import com.datadog.legacy.trace.context.ScopeListener; -import com.datadog.legacy.trace.context.TraceScope; -import java.io.Closeable; -import java.lang.ref.WeakReference; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicInteger; - -public class ContinuableScope implements DDScope, TraceScope { - /** ScopeManager holding the thread-local to this scope. */ - private final ContextualScopeManager scopeManager; - /** - * Span contained by this scope. Async scopes will hold a reference to the parent scope's span. - */ - private final DDSpan spanUnderScope; - - private final DDScopeEventFactory eventFactory; - /** Event for this scope */ - private final DDScopeEvent event; - /** If true, finish the span when openCount hits 0. */ - private final boolean finishOnClose; - /** Count of open scope and continuations */ - private final AtomicInteger openCount; - /** Scope to placed in the thread local after close. May be null. */ - private final DDScope toRestore; - /** Continuation that created this scope. May be null. */ - private final Continuation continuation; - /** Flag to propagate this scope across async boundaries. */ - private final AtomicBoolean isAsyncPropagating = new AtomicBoolean(false); - /** depth of scope on thread */ - private final int depth; - - ContinuableScope( - final ContextualScopeManager scopeManager, - final DDSpan spanUnderScope, - final boolean finishOnClose, - final DDScopeEventFactory eventFactory) { - this(scopeManager, new AtomicInteger(1), null, spanUnderScope, finishOnClose, eventFactory); - } - - private ContinuableScope( - final ContextualScopeManager scopeManager, - final AtomicInteger openCount, - final Continuation continuation, - final DDSpan spanUnderScope, - final boolean finishOnClose, - final DDScopeEventFactory eventFactory) { - assert spanUnderScope != null : "span must not be null"; - this.scopeManager = scopeManager; - this.openCount = openCount; - this.continuation = continuation; - this.spanUnderScope = spanUnderScope; - this.finishOnClose = finishOnClose; - this.eventFactory = eventFactory; - event = eventFactory.create(spanUnderScope.context()); - event.start(); - toRestore = scopeManager.tlsScope.get(); - scopeManager.tlsScope.set(this); - depth = toRestore == null ? 0 : toRestore.depth() + 1; - for (final ScopeListener listener : scopeManager.scopeListeners) { - listener.afterScopeActivated(); - } - } - - @Override - public void close() { - // We have to scope finish event before we finish then span (which finishes span event). - // The reason is that we get span on construction and span event starts when span is created. - // This means from JFR perspective scope is included into the span. - event.finish(); - - if (null != continuation) { - spanUnderScope.context().getTrace().cancelContinuation(continuation); - } - - if (openCount.decrementAndGet() == 0 && finishOnClose) { - spanUnderScope.finish(); - } - - for (final ScopeListener listener : scopeManager.scopeListeners) { - listener.afterScopeClosed(); - } - - if (scopeManager.tlsScope.get() == this) { - scopeManager.tlsScope.set(toRestore); - if (toRestore != null) { - for (final ScopeListener listener : scopeManager.scopeListeners) { - listener.afterScopeActivated(); - } - } - } else { - } - } - - @Override - public DDSpan span() { - return spanUnderScope; - } - - @Override - public int depth() { - return depth; - } - - @Override - public boolean isAsyncPropagating() { - return isAsyncPropagating.get(); - } - - @Override - public void setAsyncPropagation(final boolean value) { - isAsyncPropagating.set(value); - } - - /** - * The continuation returned must be closed or activated or the trace will not finish. - * - * @return The new continuation, or null if this scope is not async propagating. - */ - @Override - public Continuation capture() { - if (isAsyncPropagating()) { - return new Continuation(); - } else { - return null; - } - } - - @Override - public String toString() { - return super.toString() + "->" + spanUnderScope; - } - - public class Continuation implements Closeable, TraceScope.Continuation { - public WeakReference ref; - - private final AtomicBoolean used = new AtomicBoolean(false); - private final PendingTrace trace; - - private Continuation() { - openCount.incrementAndGet(); - final DDSpanContext context = spanUnderScope.context(); - trace = context.getTrace(); - trace.registerContinuation(this); - } - - @Override - public ContinuableScope activate() { - if (used.compareAndSet(false, true)) { - final ContinuableScope scope = - new ContinuableScope( - scopeManager, openCount, this, spanUnderScope, finishOnClose, eventFactory); - return scope; - } else { - return new ContinuableScope( - scopeManager, new AtomicInteger(1), null, spanUnderScope, finishOnClose, eventFactory); - } - } - - @Override - public void close() { - close(true); - } - - @Override - public void close(final boolean closeContinuationScope) { - if (used.compareAndSet(false, true)) { - trace.cancelContinuation(this); - if (closeContinuationScope) { - ContinuableScope.this.close(); - } else { - // Same as in 'close()' above. - if (openCount.decrementAndGet() == 0 && finishOnClose) { - spanUnderScope.finish(); - } - } - } else { - } - } - } -} diff --git a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/opentracing/scopemanager/ScopeContext.java b/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/opentracing/scopemanager/ScopeContext.java deleted file mode 100644 index 49146eb41b..0000000000 --- a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/opentracing/scopemanager/ScopeContext.java +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. - * This product includes software developed at Datadog (https://www.datadoghq.com/). - * Copyright 2016-Present Datadog, Inc. - */ - -package com.datadog.opentracing.scopemanager; - -import io.opentracing.ScopeManager; - -/** Represents a ScopeManager that is only valid in certain cases such as on a specific thread. */ -@Deprecated -public interface ScopeContext extends ScopeManager { - - /** - * When multiple ScopeContexts are active, the first one to respond true will have control. - * - * @return true if this ScopeContext should be active - */ - boolean inContext(); -} diff --git a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/opentracing/scopemanager/SimpleScope.java b/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/opentracing/scopemanager/SimpleScope.java deleted file mode 100644 index 26c60b8d26..0000000000 --- a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/opentracing/scopemanager/SimpleScope.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. - * This product includes software developed at Datadog (https://www.datadoghq.com/). - * Copyright 2016-Present Datadog, Inc. - */ - -package com.datadog.opentracing.scopemanager; - -import com.datadog.legacy.trace.context.ScopeListener; -import io.opentracing.Span; - -/** Simple scope implementation which does not propagate across threads. */ -public class SimpleScope implements DDScope { - private final ContextualScopeManager scopeManager; - private final Span spanUnderScope; - private final boolean finishOnClose; - private final DDScope toRestore; - private final int depth; - - public SimpleScope( - final ContextualScopeManager scopeManager, - final Span spanUnderScope, - final boolean finishOnClose) { - assert spanUnderScope != null : "span must not be null"; - this.scopeManager = scopeManager; - this.spanUnderScope = spanUnderScope; - this.finishOnClose = finishOnClose; - toRestore = scopeManager.tlsScope.get(); - scopeManager.tlsScope.set(this); - depth = toRestore == null ? 0 : toRestore.depth() + 1; - for (final ScopeListener listener : scopeManager.scopeListeners) { - listener.afterScopeActivated(); - } - } - - @Override - public void close() { - if (finishOnClose) { - spanUnderScope.finish(); - } - for (final ScopeListener listener : scopeManager.scopeListeners) { - listener.afterScopeClosed(); - } - - if (scopeManager.tlsScope.get() == this) { - scopeManager.tlsScope.set(toRestore); - if (toRestore != null) { - for (final ScopeListener listener : scopeManager.scopeListeners) { - listener.afterScopeActivated(); - } - } - } - } - - @Override - public Span span() { - return spanUnderScope; - } - - @Override - public int depth() { - return depth; - } -} diff --git a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/api/Config.java b/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/api/Config.java index 88d3c0ecb7..3fb302ba43 100644 --- a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/api/Config.java +++ b/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/api/Config.java @@ -1,6 +1,77 @@ +/* + * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. + * This product includes software developed at Datadog (https://www.datadoghq.com/). + * Copyright 2016-Present Datadog, Inc. + */ + package com.datadog.trace.api; -import static com.datadog.trace.api.ConfigDefaults.*; +import static com.datadog.trace.api.ConfigDefaults.DEFAULT_AGENT_HOST; +import static com.datadog.trace.api.ConfigDefaults.DEFAULT_AGENT_TIMEOUT; +import static com.datadog.trace.api.ConfigDefaults.DEFAULT_AGENT_WRITER_TYPE; +import static com.datadog.trace.api.ConfigDefaults.DEFAULT_CLIENT_IP_ENABLED; +import static com.datadog.trace.api.ConfigDefaults.DEFAULT_CLOCK_SYNC_PERIOD; +import static com.datadog.trace.api.ConfigDefaults.DEFAULT_DATA_STREAMS_BUCKET_DURATION; +import static com.datadog.trace.api.ConfigDefaults.DEFAULT_DATA_STREAMS_ENABLED; +import static com.datadog.trace.api.ConfigDefaults.DEFAULT_DB_CLIENT_HOST_SPLIT_BY_HOST; +import static com.datadog.trace.api.ConfigDefaults.DEFAULT_DB_CLIENT_HOST_SPLIT_BY_INSTANCE; +import static com.datadog.trace.api.ConfigDefaults.DEFAULT_DB_CLIENT_HOST_SPLIT_BY_INSTANCE_TYPE_SUFFIX; +import static com.datadog.trace.api.ConfigDefaults.DEFAULT_DB_DBM_PROPAGATION_MODE_MODE; +import static com.datadog.trace.api.ConfigDefaults.DEFAULT_DOGSTATSD_START_DELAY; +import static com.datadog.trace.api.ConfigDefaults.DEFAULT_ELASTICSEARCH_BODY_AND_PARAMS_ENABLED; +import static com.datadog.trace.api.ConfigDefaults.DEFAULT_ELASTICSEARCH_BODY_ENABLED; +import static com.datadog.trace.api.ConfigDefaults.DEFAULT_ELASTICSEARCH_PARAMS_ENABLED; +import static com.datadog.trace.api.ConfigDefaults.DEFAULT_GRPC_CLIENT_ERROR_STATUSES; +import static com.datadog.trace.api.ConfigDefaults.DEFAULT_GRPC_SERVER_ERROR_STATUSES; +import static com.datadog.trace.api.ConfigDefaults.DEFAULT_HEALTH_METRICS_ENABLED; +import static com.datadog.trace.api.ConfigDefaults.DEFAULT_HTTP_CLIENT_ERROR_STATUSES; +import static com.datadog.trace.api.ConfigDefaults.DEFAULT_HTTP_CLIENT_SPLIT_BY_DOMAIN; +import static com.datadog.trace.api.ConfigDefaults.DEFAULT_HTTP_CLIENT_TAG_QUERY_STRING; +import static com.datadog.trace.api.ConfigDefaults.DEFAULT_HTTP_SERVER_ERROR_STATUSES; +import static com.datadog.trace.api.ConfigDefaults.DEFAULT_HTTP_SERVER_ROUTE_BASED_NAMING; +import static com.datadog.trace.api.ConfigDefaults.DEFAULT_HTTP_SERVER_TAG_QUERY_STRING; +import static com.datadog.trace.api.ConfigDefaults.DEFAULT_JAX_RS_EXCEPTION_AS_ERROR_ENABLED; +import static com.datadog.trace.api.ConfigDefaults.DEFAULT_LOGS_INJECTION_ENABLED; +import static com.datadog.trace.api.ConfigDefaults.DEFAULT_PARTIAL_FLUSH_MIN_SPANS; +import static com.datadog.trace.api.ConfigDefaults.DEFAULT_PERF_METRICS_ENABLED; +import static com.datadog.trace.api.ConfigDefaults.DEFAULT_PRIORITY_SAMPLING_ENABLED; +import static com.datadog.trace.api.ConfigDefaults.DEFAULT_PRIORITY_SAMPLING_FORCE; +import static com.datadog.trace.api.ConfigDefaults.DEFAULT_PROPAGATION_EXTRACT_LOG_HEADER_NAMES_ENABLED; +import static com.datadog.trace.api.ConfigDefaults.DEFAULT_REMOTE_CONFIG_ENABLED; +import static com.datadog.trace.api.ConfigDefaults.DEFAULT_REMOTE_CONFIG_INTEGRITY_CHECK_ENABLED; +import static com.datadog.trace.api.ConfigDefaults.DEFAULT_REMOTE_CONFIG_MAX_PAYLOAD_SIZE; +import static com.datadog.trace.api.ConfigDefaults.DEFAULT_REMOTE_CONFIG_POLL_INTERVAL_SECONDS; +import static com.datadog.trace.api.ConfigDefaults.DEFAULT_REMOTE_CONFIG_TARGETS_KEY; +import static com.datadog.trace.api.ConfigDefaults.DEFAULT_REMOTE_CONFIG_TARGETS_KEY_ID; +import static com.datadog.trace.api.ConfigDefaults.DEFAULT_SCOPE_DEPTH_LIMIT; +import static com.datadog.trace.api.ConfigDefaults.DEFAULT_SCOPE_ITERATION_KEEP_ALIVE; +import static com.datadog.trace.api.ConfigDefaults.DEFAULT_SECURE_RANDOM; +import static com.datadog.trace.api.ConfigDefaults.DEFAULT_SERVICE_NAME; +import static com.datadog.trace.api.ConfigDefaults.DEFAULT_SERVLET_ROOT_CONTEXT_SERVICE_NAME; +import static com.datadog.trace.api.ConfigDefaults.DEFAULT_SITE; +import static com.datadog.trace.api.ConfigDefaults.DEFAULT_SPARK_TASK_HISTOGRAM_ENABLED; +import static com.datadog.trace.api.ConfigDefaults.DEFAULT_STARTUP_LOGS_ENABLED; +import static com.datadog.trace.api.ConfigDefaults.DEFAULT_TELEMETRY_DEBUG_REQUESTS_ENABLED; +import static com.datadog.trace.api.ConfigDefaults.DEFAULT_TELEMETRY_DEPENDENCY_COLLECTION_ENABLED; +import static com.datadog.trace.api.ConfigDefaults.DEFAULT_TELEMETRY_EXTENDED_HEARTBEAT_INTERVAL; +import static com.datadog.trace.api.ConfigDefaults.DEFAULT_TELEMETRY_HEARTBEAT_INTERVAL; +import static com.datadog.trace.api.ConfigDefaults.DEFAULT_TELEMETRY_LOG_COLLECTION_ENABLED; +import static com.datadog.trace.api.ConfigDefaults.DEFAULT_TELEMETRY_METRICS_INTERVAL; +import static com.datadog.trace.api.ConfigDefaults.DEFAULT_TRACE_128_BIT_TRACEID_GENERATION_ENABLED; +import static com.datadog.trace.api.ConfigDefaults.DEFAULT_TRACE_AGENT_PORT; +import static com.datadog.trace.api.ConfigDefaults.DEFAULT_TRACE_AGENT_V05_ENABLED; +import static com.datadog.trace.api.ConfigDefaults.DEFAULT_TRACE_ANALYTICS_ENABLED; +import static com.datadog.trace.api.ConfigDefaults.DEFAULT_TRACE_FLUSH_INTERVAL; +import static com.datadog.trace.api.ConfigDefaults.DEFAULT_TRACE_HTTP_RESOURCE_REMOVE_TRAILING_SLASH; +import static com.datadog.trace.api.ConfigDefaults.DEFAULT_TRACE_LONG_RUNNING_ENABLED; +import static com.datadog.trace.api.ConfigDefaults.DEFAULT_TRACE_LONG_RUNNING_FLUSH_INTERVAL; +import static com.datadog.trace.api.ConfigDefaults.DEFAULT_TRACE_PROPAGATION_EXTRACT_FIRST; +import static com.datadog.trace.api.ConfigDefaults.DEFAULT_TRACE_PROPAGATION_STYLE; +import static com.datadog.trace.api.ConfigDefaults.DEFAULT_TRACE_RATE_LIMIT; +import static com.datadog.trace.api.ConfigDefaults.DEFAULT_TRACE_REPORT_HOSTNAME; +import static com.datadog.trace.api.ConfigDefaults.DEFAULT_TRACE_RESOLVER_ENABLED; +import static com.datadog.trace.api.ConfigDefaults.DEFAULT_TRACE_X_DATADOG_TAGS_MAX_LENGTH; +import static com.datadog.trace.api.ConfigDefaults.DEFAULT_WRITER_BAGGAGE_INJECT; import static com.datadog.trace.api.DDTags.INTERNAL_HOST_NAME; import static com.datadog.trace.api.DDTags.LANGUAGE_TAG_KEY; import static com.datadog.trace.api.DDTags.LANGUAGE_TAG_VALUE; @@ -135,7 +206,6 @@ import static com.datadog.trace.api.config.TracerConfig.AGENT_NAMED_PIPE; import static com.datadog.trace.api.config.TracerConfig.AGENT_PORT_LEGACY; import static com.datadog.trace.api.config.TracerConfig.AGENT_TIMEOUT; -import static com.datadog.trace.api.config.TracerConfig.AGENT_UNIX_DOMAIN_SOCKET; import static com.datadog.trace.api.config.TracerConfig.BAGGAGE_MAPPING; import static com.datadog.trace.api.config.TracerConfig.CLIENT_IP_ENABLED; import static com.datadog.trace.api.config.TracerConfig.CLOCK_SYNC_PERIOD; @@ -149,8 +219,6 @@ import static com.datadog.trace.api.config.TracerConfig.PRIORITY_SAMPLING; import static com.datadog.trace.api.config.TracerConfig.PRIORITY_SAMPLING_FORCE; import static com.datadog.trace.api.config.TracerConfig.PROPAGATION_EXTRACT_LOG_HEADER_NAMES_ENABLED; -import static com.datadog.trace.api.config.TracerConfig.PROPAGATION_STYLE_EXTRACT; -import static com.datadog.trace.api.config.TracerConfig.PROPAGATION_STYLE_INJECT; import static com.datadog.trace.api.config.TracerConfig.PROXY_NO_PROXY; import static com.datadog.trace.api.config.TracerConfig.REQUEST_HEADER_TAGS; import static com.datadog.trace.api.config.TracerConfig.REQUEST_HEADER_TAGS_COMMA_ALLOWED; @@ -201,6 +269,7 @@ import androidx.annotation.NonNull; +import com.datadog.android.trace.internal.compat.function.Function; import com.datadog.trace.api.config.GeneralConfig; import com.datadog.trace.api.config.ProfilingConfig; import com.datadog.trace.api.config.TracerConfig; @@ -229,7 +298,6 @@ import java.util.Set; import java.util.SortedSet; import java.util.UUID; -import com.datadog.android.trace.internal.compat.function.Function; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -339,8 +407,6 @@ static class HostNameHolder { private final int partialFlushMinSpans; private final boolean traceStrictWritesEnabled; private final boolean logExtractHeaderNames; - private final Set propagationStylesToExtract; - private final Set propagationStylesToInject; private final boolean tracePropagationStyleB3PaddingEnabled; private final Set tracePropagationStylesToExtract; private final Set tracePropagationStylesToInject; @@ -787,19 +853,6 @@ private Config(final ConfigProvider configProvider, final InstrumenterConfig ins tracePropagationStyleB3PaddingEnabled = isEnabled(true, TRACE_PROPAGATION_STYLE, ".b3.padding.enabled"); { - // The dd.propagation.style.(extract|inject) settings have been deprecated in - // favor of dd.trace.propagation.style(|.extract|.inject) settings. - // The different propagation settings when set will be applied in the following order of - // precedence, and warnings will be logged for both deprecation and overrides. - // * dd.trace.propagation.style.(extract|inject) - // * dd.trace.propagation.style - // * dd.propagation.style.(extract|inject) - Set deprecatedExtract = - getSettingsSetFromEnvironment( - PROPAGATION_STYLE_EXTRACT, PropagationStyle::valueOfConfigName, true); - Set deprecatedInject = - getSettingsSetFromEnvironment( - PROPAGATION_STYLE_INJECT, PropagationStyle::valueOfConfigName, true); Set common = getSettingsSetFromEnvironment( TRACE_PROPAGATION_STYLE, TracePropagationStyle::valueOfDisplayName, false); @@ -823,49 +876,15 @@ private Config(final ConfigProvider configProvider, final InstrumenterConfig ins // Check if we should use the common setting for injection if (inject.isEmpty()) { inject = common; - injectOrigin = TRACE_PROPAGATION_STYLE; } else if (!common.isEmpty()) { // The more specific settings will override the common setting, so log a warning logOverriddenSettingWarning( TRACE_PROPAGATION_STYLE, TRACE_PROPAGATION_STYLE_INJECT, inject); } - // Check if we should use the deprecated setting for extraction - if (extract.isEmpty()) { - // If we don't have a new setting, we convert the deprecated one - extract = convertSettingsSet(deprecatedExtract, PropagationStyle::getNewStyles); - if (!extract.isEmpty()) { - logDeprecatedConvertedSetting( - PROPAGATION_STYLE_EXTRACT, - deprecatedExtract, - TRACE_PROPAGATION_STYLE_EXTRACT, - extract); - } - } else if (!deprecatedExtract.isEmpty()) { - // If we have a new setting, we log a warning - logOverriddenDeprecatedSettingWarning(PROPAGATION_STYLE_EXTRACT, extractOrigin, extract); - } - // Check if we should use the deprecated setting for injection - if (inject.isEmpty()) { - // If we don't have a new setting, we convert the deprecated one - inject = convertSettingsSet(deprecatedInject, PropagationStyle::getNewStyles); - if (!inject.isEmpty()) { - logDeprecatedConvertedSetting( - PROPAGATION_STYLE_INJECT, deprecatedInject, TRACE_PROPAGATION_STYLE_INJECT, inject); - } - } else if (!deprecatedInject.isEmpty()) { - // If we have a new setting, we log a warning - logOverriddenDeprecatedSettingWarning(PROPAGATION_STYLE_INJECT, injectOrigin, inject); - } // Now we can check if we should pick the default injection/extraction tracePropagationStylesToExtract = extract.isEmpty() ? DEFAULT_TRACE_PROPAGATION_STYLE : extract; tracePropagationStylesToInject = inject.isEmpty() ? DEFAULT_TRACE_PROPAGATION_STYLE : inject; - // These setting are here for backwards compatibility until they can be removed in a major - // release of the tracer - propagationStylesToExtract = - deprecatedExtract.isEmpty() ? DEFAULT_PROPAGATION_STYLE : deprecatedExtract; - propagationStylesToInject = - deprecatedInject.isEmpty() ? DEFAULT_PROPAGATION_STYLE : deprecatedInject; } tracePropagationExtractFirst = @@ -1345,16 +1364,6 @@ public boolean isLogExtractHeaderNames() { return logExtractHeaderNames; } - @Deprecated - public Set getPropagationStylesToExtract() { - return propagationStylesToExtract; - } - - @Deprecated - public Set getPropagationStylesToInject() { - return propagationStylesToInject; - } - public boolean isTracePropagationStyleB3PaddingEnabled() { return tracePropagationStyleB3PaddingEnabled; } @@ -1794,6 +1803,10 @@ public boolean isSamplingMechanismValidationDisabled() { return configProvider.getBoolean(TracerConfig.SAMPLING_MECHANISM_VALIDATION_DISABLED, false); } + public boolean isV2CompatibilityEnabled() { + return configProvider.getBoolean(TracerConfig.SDK_V2_COMPATIBILITY_FLAG, false); + } + /** * @param integrationNames * @param defaultEnabled diff --git a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/api/ConfigDefaults.java b/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/api/ConfigDefaults.java index 884cfcd042..b2a66dec28 100644 --- a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/api/ConfigDefaults.java +++ b/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/api/ConfigDefaults.java @@ -7,7 +7,6 @@ import java.util.BitSet; import java.util.HashSet; import java.util.LinkedHashSet; -import java.util.List; import java.util.Set; public final class ConfigDefaults { @@ -67,10 +66,7 @@ public final class ConfigDefaults { static final int DEFAULT_SCOPE_ITERATION_KEEP_ALIVE = 30; // in seconds static final int DEFAULT_PARTIAL_FLUSH_MIN_SPANS = 1000; static final boolean DEFAULT_PROPAGATION_EXTRACT_LOG_HEADER_NAMES_ENABLED = false; - static final Set DEFAULT_TRACE_PROPAGATION_STYLE = - new LinkedHashSet<>(asList(DATADOG, TRACECONTEXT)); - static final Set DEFAULT_PROPAGATION_STYLE = - new LinkedHashSet<>(asList(PropagationStyle.DATADOG)); + static final Set DEFAULT_TRACE_PROPAGATION_STYLE = new LinkedHashSet<>(asList(DATADOG, TRACECONTEXT)); static final boolean DEFAULT_JMX_FETCH_ENABLED = true; static final boolean DEFAULT_TRACE_AGENT_V05_ENABLED = false; diff --git a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/api/DDSpanId.java b/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/api/DDSpanId.java index 79f0b9215a..5a18dff655 100644 --- a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/api/DDSpanId.java +++ b/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/api/DDSpanId.java @@ -36,6 +36,21 @@ public static long fromHex(String s) throws NumberFormatException { return LongStringUtils.parseUnsignedLongHex(s); } + /** + * Parse the span id from the given {@code String} hex representation of the unsigned 64 bit id. + * + * @param s String in hex of unsigned 64 bit id + * @return long created from parsing sting or defaultValue if there was NumberFormatException during parsing + */ + public static long fromHexOrDefault(String s, long defaultValue) { + try { + return fromHex(s); + } catch (NumberFormatException e) { + return defaultValue; + } + + } + /** * Parse the span id from the given {@code String} hex representation of the unsigned 64 bit id. * diff --git a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/api/DDTraceId.java b/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/api/DDTraceId.java index ea351c27f5..2c0880edab 100644 --- a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/api/DDTraceId.java +++ b/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/api/DDTraceId.java @@ -54,6 +54,23 @@ public static DDTraceId fromHex(String s) throws NumberFormatException { return s.length() > 16 ? DD128bTraceId.fromHex(s) : DD64bTraceId.fromHex(s); } + /** + * Creates a new {@link DDTraceId} from the given {@link #toHexString() hexadecimal + * representation}. + * + * @param s The hexadecimal {@link String} representation of a {@link DD128bTraceId} to parse. + * @return A new {@link DDTraceId} instance or DDTraceId.ZERO if the given {@link #toHexString() + * hexadecimal String} does not represent a valid number. + */ + public static DDTraceId fromHexOrDefault(String s, DDTraceId defaultValue) throws NumberFormatException { + try { + return fromHex(s); + } catch (NumberFormatException e) { + return defaultValue; + } + } + + /** * Returns a 64-bit only decimal {@link String} representation of the {@link DDTraceId}. The * {@link String} will be cached. diff --git a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/api/EventTracker.java b/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/api/EventTracker.java deleted file mode 100644 index a6b54f80e3..0000000000 --- a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/api/EventTracker.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.datadog.trace.api; - -import com.datadog.trace.api.internal.InternalTracer; -import com.datadog.trace.api.internal.TraceSegment; - -import java.util.Map; - -public class EventTracker { - - public static final EventTracker NO_EVENT_TRACKER = new EventTracker(null); - private final InternalTracer tracer; - - EventTracker(InternalTracer tracer) { - this.tracer = tracer; - } -} diff --git a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/api/GlobalTracer.java b/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/api/GlobalTracer.java deleted file mode 100644 index fb3f840db1..0000000000 --- a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/api/GlobalTracer.java +++ /dev/null @@ -1,99 +0,0 @@ -package com.datadog.trace.api; - -import com.datadog.trace.api.internal.InternalTracer; - -import java.util.ArrayList; -import java.util.Collection; - -import kotlin.Deprecated; -import kotlin.DeprecationLevel; -import kotlin.ReplaceWith; - -/** - * A global reference to the registered Datadog tracer. - * - *

    OpenTracing's GlobalTracer cannot be cast to its DDTracer implementation, so this class exists - * to provide a global window to datadog-specific features. - */ -@Deprecated( - message = "Use [io.opentracing.util.GlobalTracer] instead, this class will be removed soon", - replaceWith = @ReplaceWith(expression = "GlobalTracer", imports = "io.opentracing.util"), - level = DeprecationLevel.ERROR -) -public class GlobalTracer { - private static final Tracer NO_OP = - new Tracer() { - @Override - public String getTraceId() { - return "0"; - } - - @Override - public String getSpanId() { - return "0"; - } - - }; - - private static final Collection installationCallbacks = new ArrayList<>(); - private static Tracer provider = NO_OP; - private static EventTracker eventTracker = EventTracker.NO_EVENT_TRACKER; - - public static void registerIfAbsent(Tracer p) { - if (p == null || p == NO_OP) { - throw new IllegalArgumentException(); - } - - synchronized (installationCallbacks) { - if (provider == NO_OP) { - provider = p; - for (Callback callback : installationCallbacks) { - callback.installed(p); - } - } - } - } - - public static void forceRegister(Tracer tracer) { - if (tracer == null || tracer == NO_OP) { - throw new IllegalArgumentException(); - } - - synchronized (installationCallbacks) { - provider = tracer; - for (Callback callback : installationCallbacks) { - callback.installed(tracer); - } - } - } - - public static Tracer get() { - return provider; - } - - public static EventTracker getEventTracker() { - if (eventTracker == EventTracker.NO_EVENT_TRACKER) { - if (provider instanceof InternalTracer) { - eventTracker = new EventTracker((InternalTracer) provider); - } - } - return eventTracker; - } - - // -------------------------------------------------------------------------------- - // All code below is to support the callback registration in WithGlobalTracer - // -------------------------------------------------------------------------------- - static void registerInstallationCallback(Callback callback) { - synchronized (installationCallbacks) { - installationCallbacks.add(callback); - - if (provider != NO_OP) { - callback.installed(provider); - } - } - } - - interface Callback { - void installed(Tracer tracer); - } -} diff --git a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/api/PropagationStyle.java b/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/api/PropagationStyle.java deleted file mode 100644 index 3d4a562575..0000000000 --- a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/api/PropagationStyle.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.datadog.trace.api; - -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.Locale; - -/** - * These are the old propagation styles that have been deprecated in favor of the propagation styles - * in {@code TracePropagationStyle} - */ -@Deprecated -public enum PropagationStyle { - DATADOG(TracePropagationStyle.DATADOG), - B3(TracePropagationStyle.B3SINGLE, TracePropagationStyle.B3MULTI), - HAYSTACK(TracePropagationStyle.HAYSTACK), - XRAY(TracePropagationStyle.XRAY); - - private final List newStyles; - - PropagationStyle(TracePropagationStyle... newStyles) { - this.newStyles = Collections.unmodifiableList(Arrays.asList(newStyles)); - } - - public List getNewStyles() { - return newStyles; - } - - public static PropagationStyle valueOfConfigName(String configName) { - return valueOf(configName.toUpperCase(Locale.US).trim()); - } -} diff --git a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/api/Tracer.java b/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/api/Tracer.java deleted file mode 100644 index 89f904bf9c..0000000000 --- a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/api/Tracer.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.datadog.trace.api; - -/** A class with Datadog tracer features. */ -public interface Tracer { - - /** Get the trace id of the active trace. Returns 0 if there is no active trace. */ - String getTraceId(); - - /** - * Get the span id of the active span of the active trace. Returns 0 if there is no active trace. - */ - String getSpanId(); -} diff --git a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/api/config/TracerConfig.java b/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/api/config/TracerConfig.java index 0e0b7b0e53..a219466f24 100644 --- a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/api/config/TracerConfig.java +++ b/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/api/config/TracerConfig.java @@ -1,3 +1,9 @@ +/* + * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. + * This product includes software developed at Datadog (https://www.datadoghq.com/). + * Copyright 2016-Present Datadog, Inc. + */ + package com.datadog.trace.api.config; /** @@ -44,6 +50,7 @@ public final class TracerConfig { public static final String TRACE_SAMPLING_OPERATION_RULES = "trace.sampling.operation.rules"; // JSON rules public static final String TRACE_SAMPLING_RULES = "trace.sampling.rules"; + public static final String SDK_V2_COMPATIBILITY_FLAG = "v2.compatibility.enabled"; public static final String SPAN_SAMPLING_RULES = "span.sampling.rules"; public static final String SPAN_SAMPLING_RULES_FILE = "span.sampling.rules.file"; // a global rate used for all services (that don’t have a dedicated rule defined). diff --git a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/api/interceptor/MutableSpan.java b/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/api/interceptor/MutableSpan.java index 6886c8b22d..cfe38cbb10 100644 --- a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/api/interceptor/MutableSpan.java +++ b/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/api/interceptor/MutableSpan.java @@ -27,7 +27,7 @@ public interface MutableSpan { MutableSpan setResourceName(final CharSequence resourceName); @Nullable - Integer getSamplingPriority(); + Integer getTraceSamplingPriority(); /** * @param newPriority @@ -78,4 +78,6 @@ default Object getTag(String key) { * @return The root span for the current trace fragment. */ MutableSpan getLocalRootSpan(); + + void drop(); } diff --git a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/api/internal/InternalTracer.java b/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/api/internal/InternalTracer.java deleted file mode 100644 index 558ec18dbb..0000000000 --- a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/api/internal/InternalTracer.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.datadog.trace.api.internal; - -/** - * Tracer internal features. Those features are not part of public API and can change or be removed - * at any time. - */ -public interface InternalTracer { - void flush(); -} diff --git a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/bootstrap/instrumentation/api/AgentScopeManager.java b/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/bootstrap/instrumentation/api/AgentScopeManager.java index 514179f3b4..cd6e3e0cbb 100644 --- a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/bootstrap/instrumentation/api/AgentScopeManager.java +++ b/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/bootstrap/instrumentation/api/AgentScopeManager.java @@ -3,7 +3,7 @@ /** * Allows custom scope managers. See OTScopeManager, CustomScopeManager, and ContextualScopeManager */ -public interface AgentScopeManager extends ScopeStateAware { +public interface AgentScopeManager { AgentScope activate(AgentSpan span, ScopeSource source); diff --git a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/bootstrap/instrumentation/api/AgentSpan.java b/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/bootstrap/instrumentation/api/AgentSpan.java index 9bd72f7f07..3420557749 100644 --- a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/bootstrap/instrumentation/api/AgentSpan.java +++ b/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/bootstrap/instrumentation/api/AgentSpan.java @@ -1,3 +1,9 @@ +/* + * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. + * This product includes software developed at Datadog (https://www.datadoghq.com/). + * Copyright 2016-Present Datadog, Inc. + */ + package com.datadog.trace.bootstrap.instrumentation.api; import com.datadog.trace.api.DDTraceId; @@ -173,7 +179,7 @@ interface Context { * @return The trace sampling priority of the span's trace, or {@link PrioritySampling#UNSET} if * no priority has been set. */ - int getSamplingPriority(); + int getTraceSamplingPriority(); Iterable> baggageItems(); diff --git a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/bootstrap/instrumentation/api/AgentTracer.java b/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/bootstrap/instrumentation/api/AgentTracer.java index 924f7aac4c..1a8f209512 100644 --- a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/bootstrap/instrumentation/api/AgentTracer.java +++ b/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/bootstrap/instrumentation/api/AgentTracer.java @@ -4,26 +4,25 @@ import androidx.annotation.Nullable; +import com.datadog.android.trace.internal.compat.function.Consumer; import com.datadog.trace.api.DDSpanId; import com.datadog.trace.api.DDTraceId; import com.datadog.trace.api.EndpointCheckpointer; import com.datadog.trace.api.TraceConfig; -import com.datadog.trace.api.TracePropagationStyle; import com.datadog.trace.api.gateway.Flow; import com.datadog.trace.api.gateway.RequestContext; import com.datadog.trace.api.gateway.RequestContextSlot; -import com.datadog.trace.api.internal.InternalTracer; -import com.datadog.trace.api.profiling.Timer; import com.datadog.trace.api.sampling.PrioritySampling; import com.datadog.trace.api.sampling.SamplingRule; import com.datadog.trace.api.scopemanager.ScopeListener; import com.datadog.trace.bootstrap.instrumentation.api.AgentSpan.Context; +import org.jetbrains.annotations.NotNull; + import java.util.Collections; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; -import com.datadog.android.trace.internal.compat.function.Consumer; public class AgentTracer { private static final String DEFAULT_INSTRUMENTATION_NAME = "datadog"; @@ -31,8 +30,7 @@ public class AgentTracer { // Not intended to be constructed. private AgentTracer() {} - public interface TracerAPI - extends com.datadog.trace.api.Tracer, InternalTracer, EndpointCheckpointer, ScopeStateAware { + public interface TracerAPI extends EndpointCheckpointer { /** * Create and start a new span. @@ -82,22 +80,11 @@ AgentSpan startSpan( AgentScope activateSpan(AgentSpan span, ScopeSource source); AgentScope activateSpan(AgentSpan span, ScopeSource source, boolean isAsyncPropagating); - - AgentScope.Continuation captureSpan(AgentSpan span); - - void closePrevious(boolean finishSpan); - - AgentScope activateNext(AgentSpan span); - @Nullable AgentSpan activeSpan(); - AgentScope activeScope(); - AgentPropagation propagate(); - AgentSpan noopSpan(); - /** Deprecated. Use {@link #buildSpan(String, CharSequence)} instead. */ @Deprecated default SpanBuilder buildSpan(CharSequence spanName) { @@ -115,24 +102,12 @@ default SpanBuilder buildSpan(CharSequence spanName) { */ void addScopeListener(ScopeListener listener); - /** - * Registers the checkpointer - * - * @param checkpointer - */ - void registerCheckpointer(EndpointCheckpointer checkpointer); - - void registerTimer(Timer timer); - - Timer getTimer(); String getTraceId(AgentSpan span); String getSpanId(AgentSpan span); TraceConfig captureTraceConfig(); - - ProfilingContextIntegration getProfilingContext(); } public interface SpanBuilder { @@ -163,6 +138,9 @@ public interface SpanBuilder { SpanBuilder withRequestContextData(RequestContextSlot slot, T data); SpanBuilder withLink(AgentSpanLink link); + + @NotNull + SpanBuilder withOrigin(@Nullable String origin); } public static final class NoopAgentSpan implements AgentSpan { @@ -309,7 +287,7 @@ public AgentSpan setSamplingPriority(int newPriority, int samplingMechanism) { } @Override - public Integer getSamplingPriority() { + public Integer getTraceSamplingPriority() { return (int) PrioritySampling.UNSET; } @@ -415,6 +393,9 @@ public AgentSpan setBaggageItem(final String key, final String value) { return this; } + @Override + public void drop() {} + @Override public void finish() {} @@ -502,25 +483,6 @@ public boolean isAsyncPropagating() { } } - static class NoopAgentPropagation implements AgentPropagation { - static final NoopAgentPropagation INSTANCE = new NoopAgentPropagation(); - - @Override - public void inject(final AgentSpan span, final C carrier, final Setter setter) {} - - @Override - public void inject(final Context context, final C carrier, final Setter setter) {} - - @Override - public void inject( - AgentSpan span, C carrier, Setter setter, TracePropagationStyle style) {} - - @Override - public Context.Extracted extract(final C carrier, final ContextVisitor getter) { - return NoopContext.INSTANCE; - } - } - static class NoopContinuation implements AgentScope.Continuation { static final NoopContinuation INSTANCE = new NoopContinuation(); @@ -559,7 +521,7 @@ public AgentTrace getTrace() { } @Override - public int getSamplingPriority() { + public int getTraceSamplingPriority() { return PrioritySampling.UNSET; } diff --git a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/bootstrap/instrumentation/api/ScopeState.java b/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/bootstrap/instrumentation/api/ScopeState.java deleted file mode 100644 index 41d08fc787..0000000000 --- a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/bootstrap/instrumentation/api/ScopeState.java +++ /dev/null @@ -1,7 +0,0 @@ -package com.datadog.trace.bootstrap.instrumentation.api; - -public interface ScopeState { - void activate(); - - void fetchFromActive(); -} diff --git a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/bootstrap/instrumentation/api/ScopeStateAware.java b/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/bootstrap/instrumentation/api/ScopeStateAware.java deleted file mode 100644 index 1555c04b4c..0000000000 --- a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/bootstrap/instrumentation/api/ScopeStateAware.java +++ /dev/null @@ -1,5 +0,0 @@ -package com.datadog.trace.bootstrap.instrumentation.api; - -public interface ScopeStateAware { - ScopeState newScopeState(); -} diff --git a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/bootstrap/instrumentation/api/SpanLink.java b/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/bootstrap/instrumentation/api/SpanLink.java index 3eb94763ea..2f84ef2807 100644 --- a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/bootstrap/instrumentation/api/SpanLink.java +++ b/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/bootstrap/instrumentation/api/SpanLink.java @@ -44,7 +44,7 @@ public static SpanLink from(AgentSpan.Context context) { */ public static SpanLink from( AgentSpan.Context context, byte traceFlags, String traceState, Attributes attributes) { - if (context.getSamplingPriority() > 0) { + if (context.getTraceSamplingPriority() > 0) { traceFlags = (byte) (traceFlags | SAMPLED_FLAG); } return new SpanLink( diff --git a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/bootstrap/instrumentation/api/TagContext.java b/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/bootstrap/instrumentation/api/TagContext.java index 18d490f16e..d467d14add 100644 --- a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/bootstrap/instrumentation/api/TagContext.java +++ b/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/bootstrap/instrumentation/api/TagContext.java @@ -170,7 +170,7 @@ public final Map getTags() { } @Override - public final int getSamplingPriority() { + public final int getTraceSamplingPriority() { return samplingPriority; } diff --git a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/common/sampling/ForcePrioritySampler.java b/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/common/sampling/ForcePrioritySampler.java index f104f4874e..0fa648ca04 100644 --- a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/common/sampling/ForcePrioritySampler.java +++ b/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/common/sampling/ForcePrioritySampler.java @@ -1,12 +1,17 @@ package com.datadog.trace.common.sampling; +import androidx.annotation.VisibleForTesting; + import com.datadog.trace.core.CoreSpan; /** A sampler which forces the sampling priority */ public class ForcePrioritySampler implements Sampler, PrioritySampler { - private final int prioritySampling; - private final int samplingMechanism; + @VisibleForTesting + final int prioritySampling; + + @VisibleForTesting + final int samplingMechanism; public ForcePrioritySampler(final int prioritySampling, final int samplingMechanism) { this.prioritySampling = prioritySampling; diff --git a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/common/sampling/RateByServiceTraceSampler.java b/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/common/sampling/RateByServiceTraceSampler.java index 190cf0dc7e..f5ee229497 100644 --- a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/common/sampling/RateByServiceTraceSampler.java +++ b/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/common/sampling/RateByServiceTraceSampler.java @@ -1,5 +1,8 @@ package com.datadog.trace.common.sampling; +import androidx.annotation.VisibleForTesting; + +import com.datadog.android.trace.internal.compat.function.Function; import com.datadog.trace.api.cache.DDCache; import com.datadog.trace.api.cache.DDCaches; import com.datadog.trace.api.sampling.PrioritySampling; @@ -12,7 +15,6 @@ import java.util.HashMap; import java.util.Map; -import com.datadog.android.trace.internal.compat.function.Function; /** * A rate sampler which maintains different sample rates per service+env name. @@ -20,13 +22,19 @@ *

    The configuration of (serviceName,env)->rate is configured by the core agent. */ public class RateByServiceTraceSampler implements Sampler, PrioritySampler, RemoteResponseListener { + private static final double DEFAULT_RATE = 1.0; + private volatile RateSamplersByEnvAndService serviceRates; private static final Logger log = LoggerFactory.getLogger(RateByServiceTraceSampler.class); public static final String SAMPLING_AGENT_RATE = "_dd.agent_psr"; - private static final double DEFAULT_RATE = 1.0; + public RateByServiceTraceSampler() { + this(DEFAULT_RATE); + } - private volatile RateSamplersByEnvAndService serviceRates = new RateSamplersByEnvAndService(); + public RateByServiceTraceSampler(Double defaultSampleRate) { + serviceRates = new RateSamplersByEnvAndService(defaultSampleRate); + } @Override public > boolean sample(final T span) { @@ -86,10 +94,15 @@ public void onResponse( RateByServiceTraceSampler.createRateSampler(entry.getValue().doubleValue())); } } - serviceRates = new RateSamplersByEnvAndService(updatedEnvServiceRates); + serviceRates = new RateSamplersByEnvAndService(updatedEnvServiceRates, serviceRates.getSampleRate()); } } + @VisibleForTesting + public double getSampleRate() { + return serviceRates.getSampleRate(); + } + private static RateSampler createRateSampler(final double sampleRate) { final double sanitizedRate; if (sampleRate < 0) { @@ -105,16 +118,19 @@ private static RateSampler createRateSampler(final double sampleRate) { } private static final class RateSamplersByEnvAndService { - private static final RateSampler DEFAULT = createRateSampler(DEFAULT_RATE); + private final double sampleRate; + private final RateSampler defaultSampler; private final Map> envServiceRates; - RateSamplersByEnvAndService() { - this(new HashMap<>(0)); + RateSamplersByEnvAndService(double defaultSampleRate) { + this(new HashMap<>(0), defaultSampleRate); } - RateSamplersByEnvAndService(Map> envServiceRates) { + RateSamplersByEnvAndService(Map> envServiceRates, double sampleRate) { + this.sampleRate = sampleRate; this.envServiceRates = envServiceRates; + this.defaultSampler = createRateSampler(sampleRate); } // used in tests only @@ -125,10 +141,14 @@ RateSampler getSampler(EnvAndService envAndService) { public RateSampler getSampler(String env, String service) { Map serviceRates = envServiceRates.get(env); if (serviceRates == null) { - return DEFAULT; + return defaultSampler; } RateSampler sampler = serviceRates.get(service); - return null == sampler ? DEFAULT : sampler; + return null == sampler ? defaultSampler : sampler; + } + + public double getSampleRate() { + return sampleRate; } } diff --git a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/common/sampling/Sampler.java b/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/common/sampling/Sampler.java index 7c25cea467..2508a6f774 100644 --- a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/common/sampling/Sampler.java +++ b/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/common/sampling/Sampler.java @@ -42,6 +42,7 @@ public static Sampler forConfig(final Config config, final TraceConfig traceConf boolean serviceRulesDefined = serviceRules != null && !serviceRules.isEmpty(); boolean operationRulesDefined = operationRules != null && !operationRules.isEmpty(); boolean jsonTraceSamplingRulesDefined = !traceSamplingRules.isEmpty(); + boolean v2CompatibilityEnabled = config.isV2CompatibilityEnabled(); if ((serviceRulesDefined || operationRulesDefined) && jsonTraceSamplingRulesDefined) { log.warn( "Both {} and/or {} as well as {} are defined. Only {} will be used for rule-based sampling", @@ -52,22 +53,25 @@ public static Sampler forConfig(final Config config, final TraceConfig traceConf } Double traceSampleRate = null != traceConfig ? traceConfig.getTraceSampleRate() : config.getTraceSampleRate(); - if (serviceRulesDefined - || operationRulesDefined - || jsonTraceSamplingRulesDefined - || traceSampleRate != null) { + if (v2CompatibilityEnabled && (serviceRulesDefined + || operationRulesDefined + || jsonTraceSamplingRulesDefined + || traceSampleRate != null) + ) { try { - sampler = - RuleBasedTraceSampler.build( + sampler = RuleBasedTraceSampler.build( serviceRules, operationRules, traceSamplingRules, traceSampleRate, - config.getTraceRateLimit()); + config.getTraceRateLimit() + ); } catch (final IllegalArgumentException e) { log.error("Invalid sampler configuration. Using AllSampler", e); sampler = new AllSampler(); } + } else if (traceSampleRate != null) { + sampler = new RateByServiceTraceSampler(traceSampleRate); } else if (config.isPrioritySamplingEnabled()) { if (KEEP.equalsIgnoreCase(config.getPrioritySamplingForce())) { log.debug("Force Sampling Priority to: SAMPLER_KEEP."); diff --git a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/common/writer/NoOpWriter.java b/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/common/writer/NoOpWriter.java index f4b9fe39f5..c2d851b712 100644 --- a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/common/writer/NoOpWriter.java +++ b/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/common/writer/NoOpWriter.java @@ -10,16 +10,14 @@ import java.util.List; -public class NoOpWriter implements Writer{ +public class NoOpWriter implements Writer { @Override public void write(List trace) { - } @Override public void start() { - } @Override @@ -29,11 +27,9 @@ public boolean flush() { @Override public void close() { - } @Override public void incrementDropCounts(int spanCount) { - } } diff --git a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/core/CoreSpan.java b/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/core/CoreSpan.java index e834f7e53d..f357188a33 100644 --- a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/core/CoreSpan.java +++ b/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/core/CoreSpan.java @@ -85,6 +85,4 @@ T setSamplingPriority( T setMetric(CharSequence name, double value); T setFlag(CharSequence name, boolean value); - - int samplingPriority(); } diff --git a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/core/CoreTracer.java b/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/core/CoreTracer.java index 98d484d72d..622969ac8c 100644 --- a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/core/CoreTracer.java +++ b/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/core/CoreTracer.java @@ -5,10 +5,11 @@ import static com.datadog.trace.util.AgentThreadFactory.AGENT_THREAD_GROUP; import static com.datadog.trace.util.CollectionUtils.tryMakeImmutableMap; import static java.util.concurrent.TimeUnit.MILLISECONDS; -import static java.util.concurrent.TimeUnit.MINUTES; import static java.util.concurrent.TimeUnit.NANOSECONDS; import static java.util.concurrent.TimeUnit.SECONDS; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; import com.datadog.android.api.InternalLogger; @@ -16,7 +17,6 @@ import com.datadog.trace.api.DDSpanId; import com.datadog.trace.api.DDTraceId; import com.datadog.trace.api.DynamicConfig; -import com.datadog.trace.api.EndpointCheckpointer; import com.datadog.trace.api.EndpointCheckpointerHolder; import com.datadog.trace.api.EndpointTracker; import com.datadog.trace.api.IdGenerationStrategy; @@ -40,7 +40,6 @@ import com.datadog.trace.bootstrap.instrumentation.api.PathwayContext; import com.datadog.trace.bootstrap.instrumentation.api.ProfilingContextIntegration; import com.datadog.trace.bootstrap.instrumentation.api.ScopeSource; -import com.datadog.trace.bootstrap.instrumentation.api.ScopeState; import com.datadog.trace.bootstrap.instrumentation.api.TagContext; import com.datadog.trace.common.metrics.MetricsAggregator; import com.datadog.trace.common.metrics.NoOpMetricsAggregator; @@ -61,11 +60,10 @@ import com.datadog.trace.logger.LoggerFactory; import com.datadog.trace.monitor.NoOpRecording; import com.datadog.trace.monitor.Recording; -import com.datadog.trace.relocate.api.RatelimitedLogger; import java.io.IOException; import java.lang.ref.WeakReference; - import java.util.ArrayList; +import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.LinkedHashMap; @@ -206,11 +204,6 @@ public EndpointTracker onRootSpanStarted(AgentSpan root) { return endpointCheckpointer.onRootSpanStarted(root); } - @Override - public ScopeState newScopeState() { - return scopeManager.newScopeState(); - } - public static class CoreTracerBuilder { private Config config; @@ -407,7 +400,6 @@ private CoreTracer( assert baggageMapping != null; this.log = LoggerFactory.getLogger(CoreTracer.class.getSimpleName(), internalLogger); - this.rlLog = new RatelimitedLogger(log, 1, MINUTES); this.timeSource = timeSource == null ? SystemTimeSource.INSTANCE : timeSource; startTimeNano = this.timeSource.getCurrentTimeNanos(); startNanoTicks = this.timeSource.getNanoTicks(); @@ -615,22 +607,6 @@ public AgentScope activateSpan(final AgentSpan span, final ScopeSource source) { public AgentScope activateSpan(AgentSpan span, ScopeSource source, boolean isAsyncPropagating) { return scopeManager.activate(span, source, isAsyncPropagating); } - - @Override - public AgentScope.Continuation captureSpan(final AgentSpan span) { - return scopeManager.captureSpan(span); - } - - @Override - public void closePrevious(boolean finishSpan) { - scopeManager.closePrevious(finishSpan); - } - - @Override - public AgentScope activateNext(AgentSpan span) { - return scopeManager.activateNext(span); - } - public TagInterceptor getTagInterceptor() { return tagInterceptor; } @@ -644,28 +620,11 @@ public AgentSpan activeSpan() { return scopeManager.activeSpan(); } - @Override - public AgentScope activeScope() { - return scopeManager.active(); - } - @Override public AgentPropagation propagate() { return this.propagation; } - @Override - public AgentSpan noopSpan() { - return AgentTracer.NoopAgentSpan.INSTANCE; - } - - @Override - public Timer getTimer() { - return timer; - } - - private final RatelimitedLogger rlLog; - /** * We use the sampler to know if the trace has to be reported/written. The sampler is called on * the first span (root span) of the trace. If the trace is marked as a sample, we report it. @@ -715,12 +674,10 @@ private List interceptCompleteTrace(List trace) { return trace; } - @Override public String getTraceId() { return getTraceId(activeSpan()); } - @Override public String getSpanId() { return getSpanId(activeSpan()); } @@ -755,16 +712,6 @@ public void addScopeListener(final ScopeListener listener) { } } - @Override - public void registerCheckpointer(EndpointCheckpointer implementation) { - endpointCheckpointer.register(implementation); - } - - @Override - public void registerTimer(Timer timer) { - this.timer = timer; - } - @Override public void close() { pendingTraceBuffer.close(); @@ -773,27 +720,15 @@ public void close() { metricsAggregator.close(); } - @Override public void flush() { pendingTraceBuffer.flush(); writer.flush(); } - - @Override - public ProfilingContextIntegration getProfilingContext() { - return profilingContextIntegration; - } - - Recording writeTimer() { return traceWriteTimer.start(); } - private static String statsdTag(final String tagPrefix, final String tagValue) { - return tagPrefix + ":" + tagValue; - } - private static Map invertMap(Map map) { Map inverted = new HashMap<>(map.size()); for (Map.Entry entry : map.entrySet()) { @@ -823,6 +758,7 @@ public class CoreSpanBuilder implements AgentTracer.SpanBuilder { private Object builderRequestContextDataIast; private Object builderCiVisibilityContextData; private List links; + private String origin; CoreSpanBuilder( final String instrumentationName, final CharSequence operationName, CoreTracer tracer) { @@ -972,6 +908,13 @@ public AgentTracer.SpanBuilder withLink(AgentSpanLink link) { return this; } + @NonNull + @Override + public AgentTracer.SpanBuilder withOrigin(@Nullable String origin) { + this.origin = origin; + return this; + } + /** * Build the SpanContext, if the actual span has a parent, the following attributes must be * propagated: - ServiceName - Baggage - Trace (a list of all spans related) - SpanType @@ -1045,7 +988,7 @@ private DDSpanContext buildSpanContext() { final ExtractedContext extractedContext = (ExtractedContext) parentContext; traceId = extractedContext.getTraceId(); parentSpanId = extractedContext.getSpanId(); - samplingPriority = extractedContext.getSamplingPriority(); + samplingPriority = extractedContext.getTraceSamplingPriority(); endToEndStartTime = extractedContext.getEndToEndStartTime(); propagationTags = extractedContext.getPropagationTags(); } else if (parentContext != null) { @@ -1054,7 +997,7 @@ private DDSpanContext buildSpanContext() { ? idGenerationStrategy.generateTraceId() : parentContext.getTraceId(); parentSpanId = parentContext.getSpanId(); - samplingPriority = parentContext.getSamplingPriority(); + samplingPriority = parentContext.getTraceSamplingPriority(); endToEndStartTime = 0; propagationTags = propagationTagsFactory.empty(); } else { @@ -1079,9 +1022,9 @@ private DDSpanContext buildSpanContext() { requestContextDataIast = tc.getRequestContextDataIast(); ciVisibilityContextData = tc.getCiVisibilityContextData(); } else { + origin = this.origin; traceConfig = null; coreTags = null; - origin = null; baggage = null; requestContextDataAppSec = null; requestContextDataIast = null; diff --git a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/core/DDSpan.java b/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/core/DDSpan.java index a12f967746..ba72fac1f1 100644 --- a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/core/DDSpan.java +++ b/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/core/DDSpan.java @@ -322,6 +322,11 @@ public DDSpan getLocalRootSpan() { return context.getTrace().getRootSpan(); } + @Override + public void drop() { + context.getTrace().unregisterSpan(this); + } + /** * Checks whether the span is also the local root span * @@ -547,7 +552,7 @@ public final DDSpan setResourceName(final CharSequence resourceName, byte priori @Override public boolean eligibleForDropping() { - int samplingPriority = context.getSamplingPriority(); + int samplingPriority = context.getTraceSamplingPriority(); return samplingPriority == USER_DROP || samplingPriority == SAMPLER_DROP; } @@ -564,7 +569,7 @@ public Integer forceSamplingDecision() { if (rootSpan == null) { return null; } - return rootSpan.getSamplingPriority(); + return rootSpan.getTraceSamplingPriority(); } @Deprecated @@ -669,8 +674,9 @@ public byte getResourceNamePriority() { } @Override - public Integer getSamplingPriority() { - final int samplingPriority = context.getSamplingPriority(); + @Nullable + public Integer getTraceSamplingPriority() { + final int samplingPriority = context.getTraceSamplingPriority(); if (samplingPriority == PrioritySampling.UNSET) { return null; } else { @@ -678,9 +684,8 @@ public Integer getSamplingPriority() { } } - @Override - public int samplingPriority() { - return context.getSamplingPriority(); + public int getSpanSamplingPriority() { + return context.getSpanSamplingPriority(); } @Override diff --git a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/core/DDSpanContext.java b/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/core/DDSpanContext.java index 4166b94ed1..052dc7d6a1 100644 --- a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/core/DDSpanContext.java +++ b/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/core/DDSpanContext.java @@ -558,10 +558,14 @@ private boolean validateSamplingPriority(final int newPriority, final int newMec } @Override - public int getSamplingPriority() { + public int getTraceSamplingPriority() { return getRootSpanContextOrThis().samplingPriority; } + public int getSpanSamplingPriority() { + return samplingPriority; + } + public void setSpanSamplingPriority(double rate, int limit) { synchronized (unsafeTags) { unsafeSetTag(SPAN_SAMPLING_MECHANISM_TAG, SamplingMechanism.SPAN_SAMPLING_RATE); @@ -806,7 +810,7 @@ public void processTagsAndBaggage( threadName, tags, baggageItemsWithPropagationTags, - samplingPriority != PrioritySampling.UNSET ? samplingPriority : getSamplingPriority(), + samplingPriority != PrioritySampling.UNSET ? samplingPriority : getTraceSamplingPriority(), measured, topLevel, httpStatusCode == 0 ? null : HTTP_STATUSES.get(httpStatusCode), diff --git a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/core/DDSpanLink.java b/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/core/DDSpanLink.java index fc1e778721..2727766025 100644 --- a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/core/DDSpanLink.java +++ b/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/core/DDSpanLink.java @@ -50,7 +50,7 @@ public static SpanLink from(ExtractedContext context) { * @return A span link to the given context with custom attributes. */ public static SpanLink from(ExtractedContext context, Attributes attributes) { - byte traceFlags = context.getSamplingPriority() > 0 ? SAMPLED_FLAG : DEFAULT_FLAGS; + byte traceFlags = context.getTraceSamplingPriority() > 0 ? SAMPLED_FLAG : DEFAULT_FLAGS; String traceState = context.getPropagationTags() == null ? "" diff --git a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/core/PendingTrace.java b/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/core/PendingTrace.java index 7f15ea39f8..280d477ed1 100644 --- a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/core/PendingTrace.java +++ b/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/core/PendingTrace.java @@ -223,6 +223,13 @@ void registerSpan(final DDSpan span) { } } + void unregisterSpan(final DDSpan span){ + PENDING_REFERENCE_COUNT.decrementAndGet(this); + if (pendingTraceBuffer.longRunningSpansEnabled()){ + spans.remove(span); + } + } + void trackRunningTrace(final DDSpan span) { if (!compareAndSetLongRunningState( LongRunningTracesTracker.UNDEFINED, LongRunningTracesTracker.TO_TRACK)) { @@ -239,7 +246,7 @@ public Integer evaluateSamplingPriority() { if (span == null) { return null; } - Integer prio = span.getSamplingPriority(); + Integer prio = span.getTraceSamplingPriority(); if (prio == null) { prio = span.forceSamplingDecision(); } @@ -323,7 +330,7 @@ private PublishState decrementRefAndMaybeWrite(boolean isRootSpan) { // Finished root with pending work ... delay write pendingTraceBuffer.enqueue(this); return PublishState.ROOT_BUFFERED; - } else if (partialFlushMinSpans > 0 && size() >= partialFlushMinSpans) { + } else if (partialFlushMinSpans > 0 && size() > partialFlushMinSpans) { // Trace is getting too big, write anything completed. partialFlush(); return PublishState.PARTIAL_FLUSH; @@ -516,7 +523,7 @@ public void setSamplingPriorityIfNecessary() { if (traceConfig.sampler instanceof PrioritySampler && rootSpan != null - && rootSpan.context().getSamplingPriority() == PrioritySampling.UNSET) { + && rootSpan.context().getTraceSamplingPriority() == PrioritySampling.UNSET) { ((PrioritySampler) traceConfig.sampler).setSamplingPriority(rootSpan); } } diff --git a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/core/propagation/B3HttpCodec.java b/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/core/propagation/B3HttpCodec.java index 919537ba60..64c7d0376a 100644 --- a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/core/propagation/B3HttpCodec.java +++ b/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/core/propagation/B3HttpCodec.java @@ -120,7 +120,7 @@ public void inject( setter.set(carrier, SPAN_ID_KEY, injectedSpanId); if (context.lockSamplingPriority()) { final String injectedSamplingPriority = - convertSamplingPriority(context.getSamplingPriority()); + convertSamplingPriority(context.getTraceSamplingPriority()); setter.set(carrier, SAMPLING_PRIORITY_KEY, injectedSamplingPriority); } log.debug( @@ -146,7 +146,7 @@ public void inject( if (context.lockSamplingPriority()) { final String injectedSamplingPriority = - convertSamplingPriority(context.getSamplingPriority()); + convertSamplingPriority(context.getTraceSamplingPriority()); injectedB3IdBuilder.append('-').append(injectedSamplingPriority); } String injectedB3Id = injectedB3IdBuilder.toString(); diff --git a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/core/propagation/DatadogHttpCodec.java b/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/core/propagation/DatadogHttpCodec.java index 820d8de60b..5b3e606dc4 100644 --- a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/core/propagation/DatadogHttpCodec.java +++ b/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/core/propagation/DatadogHttpCodec.java @@ -62,7 +62,7 @@ public void inject( setter.set(carrier, TRACE_ID_KEY, context.getTraceId().toString()); setter.set(carrier, SPAN_ID_KEY, DDSpanId.toString(context.getSpanId())); if (context.lockSamplingPriority()) { - setter.set(carrier, SAMPLING_PRIORITY_KEY, String.valueOf(context.getSamplingPriority())); + setter.set(carrier, SAMPLING_PRIORITY_KEY, String.valueOf(context.getTraceSamplingPriority())); } final CharSequence origin = context.getOrigin(); if (origin != null) { diff --git a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/core/propagation/ExtractedContext.java b/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/core/propagation/ExtractedContext.java index 829c6f17ec..71ab991de9 100644 --- a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/core/propagation/ExtractedContext.java +++ b/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/core/propagation/ExtractedContext.java @@ -96,8 +96,8 @@ public String toString() { if (getBaggage() != null) { builder.append("baggage=").append(getBaggage()).append(", "); } - if (getSamplingPriority() != PrioritySampling.UNSET) { - builder.append("samplingPriority=").append(getSamplingPriority()).append(", "); + if (getTraceSamplingPriority() != PrioritySampling.UNSET) { + builder.append("samplingPriority=").append(getTraceSamplingPriority()).append(", "); } return builder.append('}').toString(); } diff --git a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/core/propagation/HttpCodec.java b/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/core/propagation/HttpCodec.java index 9639ac1f9e..36256fdb7a 100644 --- a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/core/propagation/HttpCodec.java +++ b/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/core/propagation/HttpCodec.java @@ -2,6 +2,9 @@ import static com.datadog.trace.api.TracePropagationStyle.TRACECONTEXT; +import androidx.annotation.Nullable; + +import com.datadog.android.trace.internal.compat.function.Supplier; import com.datadog.trace.api.Config; import com.datadog.trace.api.DD128bTraceId; import com.datadog.trace.api.DD64bTraceId; @@ -24,7 +27,6 @@ import java.util.List; import java.util.Map; import java.util.Set; -import com.datadog.android.trace.internal.compat.function.Supplier; public class HttpCodec { @@ -66,6 +68,7 @@ public interface Extractor { * @return {@code null} for failed context extraction, a {@link TagContext} instance for partial * context extraction or an {@link ExtractedContext} for complete context extraction. */ + @Nullable TagContext extract(final C carrier, final AgentPropagation.ContextVisitor getter); /** diff --git a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/core/propagation/TagContextExtractor.java b/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/core/propagation/TagContextExtractor.java index 259ab7462a..11d8daa368 100644 --- a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/core/propagation/TagContextExtractor.java +++ b/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/core/propagation/TagContextExtractor.java @@ -1,32 +1,47 @@ package com.datadog.trace.core.propagation; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.datadog.android.trace.internal.compat.function.Supplier; import com.datadog.trace.api.TraceConfig; import com.datadog.trace.bootstrap.instrumentation.api.AgentPropagation; import com.datadog.trace.bootstrap.instrumentation.api.TagContext; -import com.datadog.android.trace.internal.compat.function.Supplier; - public class TagContextExtractor implements HttpCodec.Extractor { private final Supplier traceConfigSupplier; private final ThreadLocal ctxInterpreter; + private final ContextInterpreter.Factory factory; public TagContextExtractor( final Supplier traceConfigSupplier, final ContextInterpreter.Factory factory) { + this.factory = factory; this.traceConfigSupplier = traceConfigSupplier; this.ctxInterpreter = new ThreadLocal<>(); - this.ctxInterpreter.set(factory.create()); } @Override + public void cleanup() { + ctxInterpreter.remove(); + } + + @Override + @Nullable public TagContext extract(final C carrier, final AgentPropagation.ContextVisitor getter) { - ContextInterpreter interpreter = this.ctxInterpreter.get().reset(traceConfigSupplier.get()); + ContextInterpreter interpreter = resolveContextInterpreter().reset(traceConfigSupplier.get()); getter.forEachKey(carrier, interpreter); return interpreter.build(); } - @Override - public void cleanup() { - ctxInterpreter.remove(); + @NonNull + private ContextInterpreter resolveContextInterpreter() { + ContextInterpreter contextInterpreter = ctxInterpreter.get(); + if (contextInterpreter == null) { + contextInterpreter = factory.create(); + ctxInterpreter.set(contextInterpreter); + } + + return contextInterpreter; } } diff --git a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/core/propagation/W3CHttpCodec.java b/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/core/propagation/W3CHttpCodec.java index 46bb7c70af..7cd8e90bea 100644 --- a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/core/propagation/W3CHttpCodec.java +++ b/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/core/propagation/W3CHttpCodec.java @@ -81,7 +81,7 @@ private void injectTraceParent( sb.append(context.getTraceId().toHexString()); sb.append("-"); sb.append(DDSpanId.toHexStringPadded(context.getSpanId())); - sb.append(context.getSamplingPriority() > 0 ? "-01" : "-00"); + sb.append(context.getTraceSamplingPriority() > 0 ? "-01" : "-00"); setter.set(carrier, TRACE_PARENT_KEY, sb.toString()); } diff --git a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/core/propagation/XRayHttpCodec.java b/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/core/propagation/XRayHttpCodec.java index 37220a56d7..5e0408d013 100644 --- a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/core/propagation/XRayHttpCodec.java +++ b/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/core/propagation/XRayHttpCodec.java @@ -89,7 +89,7 @@ public void inject(DDSpanContext context, C carrier, AgentPropagation.Setter if (context.lockSamplingPriority()) { buf.append(';' + SAMPLED_PREFIX) - .append(convertSamplingPriority(context.getSamplingPriority())); + .append(convertSamplingPriority(context.getTraceSamplingPriority())); } int maxCapacity = buf.length() + MAX_ADDITIONAL_BYTES; diff --git a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/core/scopemanager/ContinuableScopeManager.java b/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/core/scopemanager/ContinuableScopeManager.java index 7efa1b378e..76a7fdb051 100644 --- a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/core/scopemanager/ContinuableScopeManager.java +++ b/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/core/scopemanager/ContinuableScopeManager.java @@ -17,7 +17,6 @@ import com.datadog.trace.bootstrap.instrumentation.api.ProfilerContext; import com.datadog.trace.bootstrap.instrumentation.api.ProfilingContextIntegration; import com.datadog.trace.bootstrap.instrumentation.api.ScopeSource; -import com.datadog.trace.bootstrap.instrumentation.api.ScopeState; import com.datadog.trace.core.monitor.HealthMetrics; import com.datadog.trace.logger.Logger; import com.datadog.trace.logger.LoggerFactory; @@ -320,26 +319,6 @@ ScopeStack scopeStack() { return this.tlsScopeStack.get(); } - @Override - public ScopeState newScopeState() { - return new ContinuableScopeState(); - } - - private class ContinuableScopeState implements ScopeState { - - private ScopeStack localScopeStack = tlsScopeStack.initialValue(); - - @Override - public void activate() { - tlsScopeStack.set(localScopeStack); - } - - @Override - public void fetchFromActive() { - localScopeStack = tlsScopeStack.get(); - } - } - static final class ScopeStackThreadLocal extends ThreadLocal { private final ProfilingContextIntegration profilingContextIntegration; diff --git a/features/dd-sdk-android-trace-internal/src/test/kotlin/com/datadog/opentracing/DDSpanContextTest.kt b/features/dd-sdk-android-trace-internal/src/test/kotlin/com/datadog/opentracing/DDSpanContextTest.kt deleted file mode 100644 index 1fa6891a95..0000000000 --- a/features/dd-sdk-android-trace-internal/src/test/kotlin/com/datadog/opentracing/DDSpanContextTest.kt +++ /dev/null @@ -1,613 +0,0 @@ -/* - * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. - * This product includes software developed at Datadog (https://www.datadoghq.com/). - * Copyright 2016-Present Datadog, Inc. - */ - -package com.datadog.opentracing - -import com.datadog.android.api.InternalLogger -import com.datadog.android.internal.utils.safeGetThreadId -import com.datadog.legacy.trace.api.DDTags -import com.datadog.legacy.trace.api.sampling.PrioritySampling -import com.datadog.opentracing.assertj.DDSpanContextAssert.Companion.assertThat -import com.datadog.opentracing.decorators.AbstractDecorator -import com.datadog.utils.forge.Configurator -import fr.xgouchet.elmyr.Forge -import fr.xgouchet.elmyr.annotation.BoolForgery -import fr.xgouchet.elmyr.annotation.Forgery -import fr.xgouchet.elmyr.annotation.IntForgery -import fr.xgouchet.elmyr.annotation.StringForgery -import fr.xgouchet.elmyr.junit5.ForgeConfiguration -import fr.xgouchet.elmyr.junit5.ForgeExtension -import org.assertj.core.api.Assertions.assertThat -import org.junit.jupiter.api.BeforeEach -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.extension.ExtendWith -import org.junit.jupiter.api.extension.Extensions -import org.mockito.Mock -import org.mockito.junit.jupiter.MockitoExtension -import org.mockito.junit.jupiter.MockitoSettings -import org.mockito.kotlin.mock -import org.mockito.kotlin.verify -import org.mockito.kotlin.whenever -import org.mockito.quality.Strictness -import java.math.BigInteger -import java.util.concurrent.CountDownLatch -import java.util.concurrent.TimeUnit - -@Extensions( - ExtendWith(MockitoExtension::class), - ExtendWith(ForgeExtension::class) -) -@MockitoSettings(strictness = Strictness.LENIENT) -@ForgeConfiguration(value = Configurator::class) -internal class DDSpanContextTest { - - @Forgery - lateinit var fakeTraceId: BigInteger - - @Forgery - lateinit var fakeSpanId: BigInteger - - @Forgery - lateinit var fakeParentId: BigInteger - - @StringForgery - lateinit var fakeServiceName: String - - @StringForgery - lateinit var fakeOperationName: String - - @StringForgery - lateinit var fakeResourceName: String - - @StringForgery - lateinit var fakeOrigin: String - - @StringForgery - lateinit var fakeSpanType: String - - @IntForgery(min = 0) - var fakeSamplingPriority: Int = 0 - - @BoolForgery - var fakeErrorFlag: Boolean = false - - @Mock - lateinit var mockedPendingTrace: PendingTrace - - @Mock - lateinit var mockedTracer: DDTracer - - @Mock - lateinit var mockRootSpan: DDSpan - - @Mock - lateinit var mockRootSpanContext: DDSpanContext - - @Mock - lateinit var mockInternalLogger: InternalLogger - - lateinit var fakeBaggageItems: Map - lateinit var fakeTags: Map - lateinit var fakeServiceNamesMapping: Map - - lateinit var testedContext: DDSpanContext - - @BeforeEach - fun `set up`(forge: Forge) { - fakeBaggageItems = forge.aMap { - forge.anAlphabeticalString() to forge.anAlphaNumericalString() - } - fakeTags = forge.aMap { - forge.anAlphabeticalString() to forge.anAlphaNumericalString() - } - fakeServiceNamesMapping = emptyMap() - testedContext = DDSpanContext( - fakeTraceId, - fakeSpanId, - fakeParentId, - fakeServiceName, - fakeOperationName, - fakeResourceName, - PrioritySampling.UNSET, - fakeOrigin, - fakeBaggageItems, - fakeErrorFlag, - fakeSpanType, - fakeTags, - mockedPendingTrace, - mockedTracer, - fakeServiceNamesMapping, - mockInternalLogger - ) - } - - @Test - fun `M initialize correctly all the fields W initialized`() { - assertThat(testedContext) - .hasTraceId(fakeTraceId) - .hasSpanId(fakeSpanId) - .hasParentId(fakeParentId) - .hasServiceName(fakeServiceName) - .hasOperationName(fakeOperationName) - .hasResourceName(fakeResourceName) - .hasOrigin(fakeOrigin) - .hasExactlyBaggageItems(fakeBaggageItems) - .hasErrorFlag(fakeErrorFlag) - .hasSpanType(fakeSpanType) - .containsTags(fakeTags) - } - - @Test - fun `M add the threadName and threadId as tags W initialized`() { - assertThat(testedContext).containsTags( - mapOf( - DDTags.THREAD_ID to Thread.currentThread().safeGetThreadId(), - DDTags.THREAD_NAME to Thread.currentThread().name - ) - ) - } - - @Test - fun `M add the origin as tag W not null`() { - assertThat(testedContext).containsTags( - mapOf( - DDSpanContext.ORIGIN_KEY to fakeOrigin - ) - ) - } - - @Test - fun `M not add the origin as tag W null`() { - // WHEN - testedContext = DDSpanContext( - fakeTraceId, - fakeSpanId, - fakeParentId, - fakeServiceName, - fakeOperationName, - fakeResourceName, - fakeSamplingPriority, - null, - fakeBaggageItems, - fakeErrorFlag, - fakeSpanType, - fakeTags, - mockedPendingTrace, - mockedTracer, - fakeServiceNamesMapping, - mockInternalLogger - ) - - // THEN - assertThat(testedContext).doesNotContainTags(DDSpanContext.ORIGIN_KEY) - } - - @Test - fun `M add sampling W initialised {samplingPriority != UNSET}`() { - // WHEN - testedContext = DDSpanContext( - fakeTraceId, - fakeSpanId, - fakeParentId, - fakeServiceName, - fakeOperationName, - fakeResourceName, - fakeSamplingPriority, - fakeOrigin, - fakeBaggageItems, - fakeErrorFlag, - fakeSpanType, - fakeTags, - mockedPendingTrace, - mockedTracer, - fakeServiceNamesMapping, - mockInternalLogger - ) - - assertThat(testedContext).hasSamplingPriority(fakeSamplingPriority) - } - - @Test - fun `M not add sampling W initialised {samplingPriority == UNSET}`() { - assertThat(testedContext).hasSamplingPriority(PrioritySampling.UNSET) - } - - @Test - fun `M add sampling on context W setSamplingPriority { root == null}`( - forge: Forge - ) { - // GIVEN - val fakeNewSamplingPriority = forge.anInt(min = 0) - - // WHEN - testedContext.samplingPriority = fakeNewSamplingPriority - - // THEN - assertThat(testedContext).hasSamplingPriority(fakeNewSamplingPriority) - } - - @Test - fun `M add sampling on context W setSamplingPriority { isRootContext }`(forge: Forge) { - // GIVEN - whenever(mockRootSpan.context()).thenReturn(testedContext) - val fakeNewSamplingPriority = forge.anInt(min = 0) - whenever(mockedPendingTrace.rootSpan).thenReturn(mockRootSpan) - - // WHEN - testedContext.samplingPriority = fakeNewSamplingPriority - - // THEN - assertThat(testedContext).hasSamplingPriority(fakeNewSamplingPriority) - } - - @Test - fun `M add sampling on root W setSamplingPriority { isNotRootContext }`( - forge: Forge - ) { - // GIVEN - whenever(mockRootSpan.context()).thenReturn(mockRootSpanContext) - val fakeNewSamplingPriority = forge.anInt(min = 0) - whenever(mockedPendingTrace.rootSpan).thenReturn(mockRootSpan) - - // WHEN - testedContext.samplingPriority = fakeNewSamplingPriority - - // THEN - verify(mockRootSpanContext).setSamplingPriority(fakeNewSamplingPriority) - } - - @Test - fun `M do nothing W setSamplingPriority {samplingPriority == UNSET}`() { - // WHEN - assertThat(testedContext.setSamplingPriority(PrioritySampling.UNSET)).isFalse() - - // THEN - assertThat(testedContext).hasSamplingPriority(PrioritySampling.UNSET) - } - - @Test - fun `M do nothing W setSamplingPriority {samplingPriority == SET, locked}`( - forge: Forge - ) { - // GIVEN - testedContext.setSamplingPriority(fakeSamplingPriority) - val fakeNewSamplingPriority = forge.anInt(min = 0) - testedContext.lockSamplingPriority() - - // WHEN - assertThat(testedContext.setSamplingPriority(fakeNewSamplingPriority)).isFalse() - - // THEN - assertThat(testedContext).hasSamplingPriority(fakeSamplingPriority) - } - - @Test - fun `M add sampling on Context W setSamplingPriority {samplingPriority == UNSET, locked}`() { - // GIVEN - testedContext.lockSamplingPriority() - - // WHEN - assertThat(testedContext.setSamplingPriority(fakeSamplingPriority)).isTrue() - - // THEN - assertThat(testedContext).hasSamplingPriority(fakeSamplingPriority) - } - - @Test - fun `M delegate to rootContext W setSamplingPriority {locked, isNotRootContext}`(forge: Forge) { - // GIVEN - whenever(mockRootSpan.context()).thenReturn(mockRootSpanContext) - val fakeNewSamplingPriority = forge.anInt(min = 0) - whenever(mockedPendingTrace.rootSpan).thenReturn(mockRootSpan) - - // WHEN - assertThat(testedContext.setSamplingPriority(fakeNewSamplingPriority)).isFalse() - - // THEN - verify(mockRootSpanContext).setSamplingPriority(fakeNewSamplingPriority) - } - - @Test - fun `M support concurrency W setSamplingPriority {samplingPriority != UNSET}`( - forge: Forge - ) { - // GIVEN - testedContext.setSamplingPriority(fakeSamplingPriority) - val fakeNewSamplingPriority = forge.anInt(min = 0) - val countDownLatch = CountDownLatch(2) - - // WHEN - Thread { - testedContext.lockSamplingPriority() - countDownLatch.countDown() - }.start() - Thread { - Thread.sleep(40) // Give it some time here to have the first thread running already - testedContext.setSamplingPriority(fakeNewSamplingPriority) - countDownLatch.countDown() - }.start() - - // THEN - countDownLatch.await(10, TimeUnit.SECONDS) - assertThat(testedContext).hasSamplingPriority(fakeSamplingPriority) - } - - @Test - fun `M use the mapped serviceName W setService and key exists in mapping`(forge: Forge) { - // GIVEN - fakeServiceNamesMapping = forge.aMap(size = forge.anInt(min = 1, max = 10)) { - forge.anAlphabeticalString() to forge.anAlphaNumericalString() - } - testedContext = DDSpanContext( - fakeTraceId, - fakeSpanId, - fakeParentId, - fakeServiceName, - fakeOperationName, - fakeResourceName, - fakeSamplingPriority, - fakeOrigin, - fakeBaggageItems, - fakeErrorFlag, - fakeSpanType, - fakeTags, - mockedPendingTrace, - mockedTracer, - fakeServiceNamesMapping, - mockInternalLogger - ) - - // WHEN - val mappedServiceName = fakeServiceNamesMapping.keys.toTypedArray()[0] - testedContext.serviceName = mappedServiceName - - // THEN - assertThat(testedContext).hasServiceName(fakeServiceNamesMapping[mappedServiceName]) - } - - @Test - fun `M set serviceName W setService and key does not exists in mapping`(forge: Forge) { - // GIVEN - val fakeNewServiceName = forge.anAlphabeticalString() - - // WHEN - testedContext.serviceName = fakeNewServiceName - - // THEN - assertThat(testedContext).hasServiceName(fakeNewServiceName) - } - - @Test - fun `M return Context origin W getOrigin {rootContext == null}`() { - // THEN - assertThat(testedContext).hasOrigin(fakeOrigin) - } - - @Test - fun `M return root Context origin W getOrigin {rootContext != null}`(forge: Forge) { - // GIVEN - val fakeRootOrigin = forge.anAlphabeticalString() - val fakeRootSpanContext = DDSpanContext( - fakeTraceId, - fakeSpanId, - fakeParentId, - fakeServiceName, - fakeOperationName, - fakeResourceName, - fakeSamplingPriority, - fakeRootOrigin, - fakeBaggageItems, - fakeErrorFlag, - fakeSpanType, - fakeTags, - mockedPendingTrace, - mockedTracer, - fakeServiceNamesMapping, - mockInternalLogger - ) - whenever(mockRootSpan.context()).thenReturn(fakeRootSpanContext) - whenever(mockedPendingTrace.rootSpan).thenReturn(mockRootSpan) - - // THEN - assertThat(testedContext).hasOrigin(fakeRootOrigin) - } - - @Test - fun `M return Empty W getMetrics and no metric was added`() { - // GIVEN - testedContext = DDSpanContext( - fakeTraceId, - fakeSpanId, - fakeParentId, - fakeServiceName, - fakeOperationName, - fakeResourceName, - PrioritySampling.UNSET, - fakeOrigin, - fakeBaggageItems, - fakeErrorFlag, - fakeSpanType, - fakeTags, - mockedPendingTrace, - mockedTracer, - fakeServiceNamesMapping, - mockInternalLogger - ) - - assertThat(testedContext.metrics).isEmpty() - } - - @Test - fun `M add the metric W setMetrics`(forge: Forge) { - // GIVEN - val fakeMetricKey = forge.anAlphabeticalString() - val fakeMetricValue = forge.anInt() - - // WHEN - testedContext.setMetric(fakeMetricKey, fakeMetricValue) - - // THEN - assertThat(testedContext).hasExactlyMetrics( - mapOf( - fakeMetricKey to fakeMetricValue - ) - ) - } - - @Test - fun `M add the metric W setMetrics from multiple threads`(forge: Forge) { - // GIVEN - val fakeMetricKey1 = forge.anAlphabeticalString() - val fakeMetricValue1 = forge.anInt() - val fakeMetricKey2 = forge.anAlphabeticalString() - val fakeMetricValue2 = forge.anInt() - val fakeMetricKey3 = forge.anAlphabeticalString() - val fakeMetricValue3 = forge.anInt() - val countDownLatch = CountDownLatch(3) - - // WHEN - Thread { - testedContext.setMetric(fakeMetricKey1, fakeMetricValue1) - countDownLatch.countDown() - }.start() - Thread { - testedContext.setMetric(fakeMetricKey2, fakeMetricValue2) - countDownLatch.countDown() - }.start() - Thread { - testedContext.setMetric(fakeMetricKey3, fakeMetricValue3) - countDownLatch.countDown() - }.start() - - // THEN - countDownLatch.await(10, TimeUnit.SECONDS) - assertThat(testedContext).hasExactlyMetrics( - mapOf( - fakeMetricKey1 to fakeMetricValue1, - fakeMetricKey2 to fakeMetricValue2, - fakeMetricKey3 to fakeMetricValue3 - ) - ) - } - - @Test - fun `M add tag W setTag and there are no tag decorators`(forge: Forge) { - // GIVEN - testedContext = contextWithEmptyTags() - val fakeTagKey = forge.anAlphabeticalString() - val fakeTagValue = forge.anAlphabeticalString() - - // WHEN - testedContext.setTag(fakeTagKey, fakeTagValue) - - // THEN - assertThat(testedContext).hasExactlyTags( - mapOf( - DDTags.THREAD_NAME to Thread.currentThread().name, - DDTags.THREAD_ID to Thread.currentThread().safeGetThreadId(), - DDSpanContext.ORIGIN_KEY to fakeOrigin, - fakeTagKey to fakeTagValue - ) - ) - } - - @Test - fun `M add tag W setTag and there decorators accept it`(forge: Forge) { - // GIVEN - testedContext = contextWithEmptyTags() - val fakeTagKey = forge.anAlphabeticalString() - val fakeTagValue = forge.anAlphabeticalString() - val mockDecorator: AbstractDecorator = mock() - whenever(mockedTracer.getSpanContextDecorators(fakeTagKey)).thenReturn( - listOf(mockDecorator) - ) - whenever(mockDecorator.shouldSetTag(testedContext, fakeTagKey, fakeTagValue)).thenReturn( - true - ) - - // WHEN - testedContext.setTag(fakeTagKey, fakeTagValue) - - // THEN - assertThat(testedContext).hasExactlyTags( - mapOf( - DDTags.THREAD_NAME to Thread.currentThread().name, - DDTags.THREAD_ID to Thread.currentThread().safeGetThreadId(), - DDSpanContext.ORIGIN_KEY to fakeOrigin, - fakeTagKey to fakeTagValue - ) - ) - } - - @Test - fun `M do nothing W setTag and there decorators do not accept it`(forge: Forge) { - // GIVEN - testedContext = contextWithEmptyTags() - val fakeTagKey = forge.anAlphabeticalString() - val fakeTagValue = forge.anAlphabeticalString() - val mockDecorator: AbstractDecorator = mock() - whenever(mockedTracer.getSpanContextDecorators(fakeTagKey)).thenReturn( - listOf(mockDecorator) - ) - whenever(mockDecorator.shouldSetTag(testedContext, fakeTagKey, fakeTagValue)).thenReturn( - false - ) - - // WHEN - testedContext.setTag(fakeTagKey, fakeTagValue) - - // THEN - assertThat(testedContext).hasExactlyTags( - mapOf( - DDTags.THREAD_NAME to Thread.currentThread().name, - DDTags.THREAD_ID to Thread.currentThread().safeGetThreadId(), - DDSpanContext.ORIGIN_KEY to fakeOrigin - ) - ) - } - - @Test - fun `M remove tag W setTag with same key and empty value`(forge: Forge) { - // GIVEN - testedContext = contextWithEmptyTags() - val fakeTagKey = forge.anAlphabeticalString() - val fakeTagValue = forge.anAlphabeticalString() - testedContext.setTag(fakeTagKey, fakeTagValue) - - // WHEN - testedContext.setTag(fakeTagKey, "") - - // THEN - assertThat(testedContext).hasExactlyTags( - mapOf( - DDTags.THREAD_NAME to Thread.currentThread().name, - DDTags.THREAD_ID to Thread.currentThread().safeGetThreadId(), - DDSpanContext.ORIGIN_KEY to fakeOrigin - ) - ) - } - - private fun contextWithEmptyTags(): DDSpanContext { - return DDSpanContext( - fakeTraceId, - fakeSpanId, - fakeParentId, - fakeServiceName, - fakeOperationName, - fakeResourceName, - PrioritySampling.UNSET, - fakeOrigin, - fakeBaggageItems, - fakeErrorFlag, - fakeSpanType, - emptyMap(), - mockedPendingTrace, - mockedTracer, - fakeServiceNamesMapping, - mockInternalLogger - ) - } -} diff --git a/features/dd-sdk-android-trace-internal/src/test/kotlin/com/datadog/opentracing/IdentityKeyTest.kt b/features/dd-sdk-android-trace-internal/src/test/kotlin/com/datadog/opentracing/IdentityKeyTest.kt deleted file mode 100644 index ca4aed1fe1..0000000000 --- a/features/dd-sdk-android-trace-internal/src/test/kotlin/com/datadog/opentracing/IdentityKeyTest.kt +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. - * This product includes software developed at Datadog (https://www.datadoghq.com/). - * Copyright 2016-Present Datadog, Inc. - */ - -package com.datadog.opentracing - -import com.datadog.android.api.InternalLogger -import com.datadog.opentracing.PendingTrace.IdentityKey -import com.datadog.tools.unit.ObjectTest -import fr.xgouchet.elmyr.Forge -import org.mockito.kotlin.mock -import java.math.BigInteger - -internal class IdentityKeyTest : ObjectTest() { - - override fun createInstance(forge: Forge): IdentityKey { - val mockTracer: DDTracer = mock() - val mockInternalLogger: InternalLogger = mock() - val fakeTraceId = BigInteger.valueOf(forge.aLong()) - return IdentityKey(PendingTrace(mockTracer, fakeTraceId, mockInternalLogger)) - } - - override fun createEqualInstance(source: IdentityKey, forge: Forge): IdentityKey { - return IdentityKey(source.key) - } - - override fun createUnequalInstance(source: IdentityKey, forge: Forge): IdentityKey { - val mockTracer: DDTracer = mock() - val mockInternalLogger: InternalLogger = mock() - val fakeTraceId = BigInteger.valueOf(forge.aLong()) - return IdentityKey(PendingTrace(mockTracer, fakeTraceId, mockInternalLogger)) - } -} diff --git a/features/dd-sdk-android-trace-internal/src/test/kotlin/com/datadog/opentracing/PendingTraceTest.kt b/features/dd-sdk-android-trace-internal/src/test/kotlin/com/datadog/opentracing/PendingTraceTest.kt deleted file mode 100644 index 21c96b826c..0000000000 --- a/features/dd-sdk-android-trace-internal/src/test/kotlin/com/datadog/opentracing/PendingTraceTest.kt +++ /dev/null @@ -1,430 +0,0 @@ -/* - * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. - * This product includes software developed at Datadog (https://www.datadoghq.com/). - * Copyright 2016-Present Datadog, Inc. - */ - -package com.datadog.opentracing - -import com.datadog.android.api.InternalLogger -import com.datadog.legacy.trace.api.sampling.PrioritySampling -import com.datadog.opentracing.scopemanager.ContinuableScope.Continuation -import com.datadog.tools.unit.createInstance -import com.datadog.utils.forge.Configurator -import fr.xgouchet.elmyr.Forge -import fr.xgouchet.elmyr.junit5.ForgeConfiguration -import fr.xgouchet.elmyr.junit5.ForgeExtension -import org.assertj.core.api.Assertions.assertThat -import org.junit.jupiter.api.AfterEach -import org.junit.jupiter.api.BeforeEach -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.extension.ExtendWith -import org.junit.jupiter.api.extension.Extensions -import org.mockito.junit.jupiter.MockitoExtension -import org.mockito.junit.jupiter.MockitoSettings -import org.mockito.kotlin.argumentCaptor -import org.mockito.kotlin.doReturn -import org.mockito.kotlin.mock -import org.mockito.kotlin.verify -import org.mockito.kotlin.whenever -import org.mockito.quality.Strictness -import java.math.BigInteger -import java.util.ConcurrentModificationException -import java.util.concurrent.CountDownLatch -import java.util.concurrent.TimeUnit - -@Extensions( - ExtendWith(MockitoExtension::class), - ExtendWith(ForgeExtension::class) -) -@MockitoSettings(strictness = Strictness.LENIENT) -@ForgeConfiguration(value = Configurator::class) -class PendingTraceTest { - - @BeforeEach - fun `set up`() { - PendingTrace.initialize() - } - - @AfterEach - fun `tear down`() { - PendingTrace.close() - } - - @Test - fun `PendingTrace is thread safe`(forge: Forge) { - // Given - val mockTracer: DDTracer = mock() - val mockInternalLogger: InternalLogger = mock() - whenever(mockTracer.partialFlushMinSpans) doReturn 1 - val fakeTraceId = BigInteger.valueOf(forge.aLong()) - val pendingTrace = createInstance( - PendingTrace::class.java, - mockTracer, - fakeTraceId, - mockInternalLogger - ) - val rootSpan = - forge.fakeSpan(pendingTrace, mockTracer, fakeTraceId, BigInteger.ZERO, "rootSpan", mockInternalLogger) - val countDownLatch = CountDownLatch(CONCURRENT_THREAD) - - // When - val runnables = Array(CONCURRENT_THREAD) { - StressTestRunnable(pendingTrace, mockTracer, rootSpan, forge, countDownLatch, mockInternalLogger) - } - runnables.forEach { Thread(it).start() } - countDownLatch.await(20, TimeUnit.SECONDS) - - // Then - assertThat(countDownLatch.count).isZero() - runnables.forEach { - assertThat(it.cme).isNull() - } - } - - @Test - fun `M not leak the PendingTraces W finish`( - forge: Forge - ) { - // Given - val mockTracer: DDTracer = mock() - val mockInternalLogger: InternalLogger = mock() - whenever(mockTracer.partialFlushMinSpans) doReturn 1 - - repeat(forge.anInt(min = 10, max = 30)) { - val fakeTraceId = BigInteger.valueOf(forge.aLong()) - - val pendingTrace = createInstance( - PendingTrace::class.java, - mockTracer, - fakeTraceId, - mockInternalLogger - ) - - val rootSpan = - forge.fakeSpan( - pendingTrace, - mockTracer, - fakeTraceId, - BigInteger.ZERO, - forge.anAlphabeticalString(), - mockInternalLogger - ) - Thread.sleep(1) - rootSpan.finish() - } - - // When - PendingTrace.getSpanCleaner().run() - - // Then - assertThat(PendingTrace.getPendingTracesSize()).isZero() - } - - @Test - fun `M not leak the PendingTraces W finishSpan from different threads`( - forge: Forge - ) { - // Given - val mockTracer: DDTracer = mock() - val mockInternalLogger: InternalLogger = mock() - whenever(mockTracer.partialFlushMinSpans) doReturn 1 - val repeatCount = forge.anInt(min = 10, max = 30) - val countDownLatch = CountDownLatch(repeatCount) - repeat(repeatCount) { - Thread { - val fakeTraceId = BigInteger.valueOf(forge.aLong()) - - val pendingTrace = createInstance( - PendingTrace::class.java, - mockTracer, - fakeTraceId, - mockInternalLogger - ) - - val rootSpan = - forge.fakeSpan( - pendingTrace, - mockTracer, - fakeTraceId, - BigInteger.ZERO, - forge.anAlphabeticalString(), - mockInternalLogger - ) - Thread.sleep(1) - rootSpan.finish() - countDownLatch.countDown() - }.start() - } - countDownLatch.await(2, TimeUnit.SECONDS) - - // When - Thread { - PendingTrace.getSpanCleaner().run() - }.apply { - start() - join(3) - } - - // Then - assertThat(PendingTrace.getPendingTracesSize()).isEqualTo(0) - } - - @Test - fun `M not leak the PendingTraces W drop`( - forge: Forge - ) { - // Given - val mockTracer: DDTracer = mock() - val mockInternalLogger: InternalLogger = mock() - whenever(mockTracer.partialFlushMinSpans) doReturn 1 - val repeatTimes = forge.anInt(min = 10, max = 30) - repeat(repeatTimes) { - val fakeTraceId = BigInteger.valueOf(forge.aLong()) - - val pendingTrace = createInstance( - PendingTrace::class.java, - mockTracer, - fakeTraceId, - mockInternalLogger - ) - - val rootSpan = - forge.fakeSpan( - pendingTrace, - mockTracer, - fakeTraceId, - BigInteger.ZERO, - forge.anAlphabeticalString(), - mockInternalLogger - ) - Thread.sleep(1) - rootSpan.drop() - } - - // When - PendingTrace.getSpanCleaner().run() - - // Then - assertThat(PendingTrace.getPendingTracesSize()).isZero() - } - - @Test - fun `M write the PendingTrace W addParentSpan and drop some spans`( - forge: Forge - ) { - // Given - val mockTracer: DDTracer = mock() - val mockInternalLogger: InternalLogger = mock() - whenever(mockTracer.partialFlushMinSpans) doReturn 1 - val fakeParentTraceId = BigInteger.valueOf(forge.aLong()) - val pendingTrace = createInstance( - PendingTrace::class.java, - mockTracer, - fakeParentTraceId, - mockInternalLogger - ) - val rootSpan = - forge.fakeSpan( - pendingTrace, - mockTracer, - fakeParentTraceId, - BigInteger.ZERO, - forge.anAlphabeticalString(), - mockInternalLogger - ) - val childSpan1 = DDSpan(forge.aPositiveLong(), rootSpan.context()) - val childSpan2 = DDSpan(forge.aPositiveLong(), rootSpan.context()) - - // When - childSpan1.drop() - childSpan2.finish() - rootSpan.finish() - PendingTrace.getSpanCleaner().run() - - // Then - assertThat(PendingTrace.getPendingTracesSize()).isZero() - argumentCaptor>().apply { - verify(mockTracer).write(capture()) - assertThat(firstValue).hasSize(2) - assertThat(firstValue).containsExactlyInAnyOrder(rootSpan, childSpan2) - } - } - - @Test - fun `M write the PendingTrace W addParentSpan and drop the only child`( - forge: Forge - ) { - // Given - val mockTracer: DDTracer = mock() - val mockInternalLogger: InternalLogger = mock() - whenever(mockTracer.partialFlushMinSpans) doReturn 1 - val fakeParentTraceId = BigInteger.valueOf(forge.aLong()) - val pendingTrace = createInstance( - PendingTrace::class.java, - mockTracer, - fakeParentTraceId, - mockInternalLogger - ) - val rootSpan = - forge.fakeSpan( - pendingTrace, - mockTracer, - fakeParentTraceId, - BigInteger.ZERO, - forge.anAlphabeticalString(), - mockInternalLogger - ) - val childSpan1 = DDSpan(forge.aPositiveLong(), rootSpan.context()) - - // When - childSpan1.drop() - // add intermediary cleaner just to make sure we're testing this case - PendingTrace.getSpanCleaner().run() - rootSpan.finish() - PendingTrace.getSpanCleaner().run() - - // Then - assertThat(PendingTrace.getPendingTracesSize()).isZero() - argumentCaptor>().apply { - verify(mockTracer).write(capture()) - assertThat(firstValue).hasSize(1) - assertThat(firstValue).containsExactlyInAnyOrder(rootSpan) - } - } - - @Test - fun `M not drop the PendingTrace W active continuable scope and dropSpan`( - forge: Forge - ) { - // Given - val mockTracer: DDTracer = mock() - val mockInternalLogger: InternalLogger = mock() - whenever(mockTracer.partialFlushMinSpans) doReturn 1 - val fakeParentTraceId = BigInteger.valueOf(forge.aLong()) - val pendingTrace = createInstance( - PendingTrace::class.java, - mockTracer, - fakeParentTraceId, - mockInternalLogger - ) - val rootSpan = - forge.fakeSpan( - pendingTrace, - mockTracer, - fakeParentTraceId, - BigInteger.ZERO, - forge.anAlphabeticalString(), - mockInternalLogger - ) - val continuation: Continuation = mock() - pendingTrace.registerContinuation(continuation) - - // When - rootSpan.drop() - PendingTrace.getSpanCleaner().run() - - // Then - assertThat(PendingTrace.getPendingTracesSize()).isEqualTo(1) - } - - @Test - fun `M not drop the PendingTrace W continuable scope stopped and dropSpan`( - forge: Forge - ) { - // Given - val mockTracer: DDTracer = mock() - val mockInternalLogger: InternalLogger = mock() - whenever(mockTracer.partialFlushMinSpans) doReturn 1 - val fakeParentTraceId = BigInteger.valueOf(forge.aLong()) - val pendingTrace = createInstance( - PendingTrace::class.java, - mockTracer, - fakeParentTraceId, - mockInternalLogger - ) - val rootSpan = - forge.fakeSpan( - pendingTrace, - mockTracer, - fakeParentTraceId, - BigInteger.ZERO, - forge.anAlphabeticalString(), - mockInternalLogger - ) - val continuation: Continuation = mock() - pendingTrace.registerContinuation(continuation) - - // When - pendingTrace.cancelContinuation(continuation) - rootSpan.drop() - - // Then - assertThat(PendingTrace.getPendingTracesSize()).isZero - } - - private class StressTestRunnable( - val pendingTrace: PendingTrace, - val tracer: DDTracer, - val parentSpan: DDSpan, - val forge: Forge, - val countDownLatch: CountDownLatch, - val internalLogger: InternalLogger - ) : Runnable { - - var cme: ConcurrentModificationException? = null - - override fun run() { - try { - for (i in 0..10000) { - val span = forge.fakeSpan( - pendingTrace, - tracer, - parentSpan.traceId, - parentSpan.spanId, - "childSpan_$i", - internalLogger - ) - span.finish() - } - } catch (e: ConcurrentModificationException) { - cme = e - } - countDownLatch.countDown() - } - } - - companion object { - const val CONCURRENT_THREAD = 4 - } -} - -private fun Forge.fakeSpan( - pendingTrace: PendingTrace, - tracer: DDTracer, - traceId: BigInteger, - parentSpanId: BigInteger, - operationName: String, - internalLogger: InternalLogger -): DDSpan { - val ddSpanContext = DDSpanContext( - traceId, - BigInteger.valueOf(aLong()), - parentSpanId, - anAlphabeticalString(), // service - operationName, - anAlphabeticalString(), // resourceName, - PrioritySampling.SAMPLER_KEEP, - anAlphabeticalString(), // origin - emptyMap(), // baggageItems - aBool(), // errorFlag - anAlphabeticalString(), // spanType - emptyMap(), // tags - pendingTrace, - tracer, - emptyMap(), // serviceNameMappings - internalLogger - ) - - return DDSpan(aPositiveLong(), ddSpanContext) -} diff --git a/features/dd-sdk-android-trace-internal/src/test/kotlin/com/datadog/opentracing/SpanCleanerTest.kt b/features/dd-sdk-android-trace-internal/src/test/kotlin/com/datadog/opentracing/SpanCleanerTest.kt deleted file mode 100644 index 648589b70f..0000000000 --- a/features/dd-sdk-android-trace-internal/src/test/kotlin/com/datadog/opentracing/SpanCleanerTest.kt +++ /dev/null @@ -1,204 +0,0 @@ -/* - * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. - * This product includes software developed at Datadog (https://www.datadoghq.com/). - * Copyright 2016-Present Datadog, Inc. - */ - -package com.datadog.opentracing - -import com.datadog.android.api.InternalLogger -import com.datadog.opentracing.PendingTrace.SpanCleaner -import com.datadog.tools.unit.createInstance -import com.datadog.utils.forge.Configurator -import fr.xgouchet.elmyr.Forge -import fr.xgouchet.elmyr.junit5.ForgeConfiguration -import fr.xgouchet.elmyr.junit5.ForgeExtension -import org.assertj.core.api.Assertions.assertThat -import org.junit.jupiter.api.Assumptions -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.extension.ExtendWith -import org.junit.jupiter.api.extension.Extensions -import org.mockito.junit.jupiter.MockitoExtension -import org.mockito.junit.jupiter.MockitoSettings -import org.mockito.kotlin.mock -import org.mockito.kotlin.verify -import org.mockito.quality.Strictness -import java.math.BigInteger -import java.util.concurrent.CountDownLatch -import java.util.concurrent.TimeUnit - -@Extensions( - ExtendWith(MockitoExtension::class), - ExtendWith(ForgeExtension::class) -) -@MockitoSettings(strictness = Strictness.LENIENT) -@ForgeConfiguration(value = Configurator::class) -internal class SpanCleanerTest { - - private var testedCleaner: SpanCleaner = SpanCleaner() - - // region cleanup - - @Test - fun `M remove random traces W add and remove`(forge: Forge) { - // Given - val pendingTraces = forge.aList(size = forge.anInt(min = 10, max = 50)) { mock() } - pendingTraces.forEach { - testedCleaner.addPendingTrace(it) - } - Assumptions.assumeTrue(testedCleaner.pendingTraces.values.size == pendingTraces.size) - Assumptions.assumeTrue(testedCleaner.pendingTraces.values.containsAll(pendingTraces)) - - // When - pendingTraces.shuffled().forEach { - testedCleaner.removePendingTrace(it) - } - - // Then - assertThat(testedCleaner.pendingTraces).isEmpty() - } - - @Test - fun `M cleanup random traces W add and close`(forge: Forge) { - // Given - val pendingTraces = forge.aList(size = forge.anInt(min = 10, max = 50)) { mock() } - pendingTraces.forEach { - testedCleaner.addPendingTrace(it) - } - assertThat(testedCleaner.pendingTraces.values).containsExactlyInAnyOrderElementsOf(pendingTraces) - - // When - testedCleaner.close() - - // Then - pendingTraces.forEach { - verify(it).clean() - } - } - - // endregion - - // region `equals` contract - - @Test - fun `M contain only one entry W add same PendingTrace twice`(forge: Forge) { - // Given - val pendingTrace = mockPendingTrace(forge) - testedCleaner.addPendingTrace(pendingTrace) - testedCleaner.addPendingTrace(pendingTrace) - - // Then - assertThat(testedCleaner.pendingTraces).hasSize(1) - assertThat(testedCleaner.pendingTraces.values).containsExactly(pendingTrace) - } - - @Test - fun `M contain only one entry W add {same PendingTrace mutated, twice}`(forge: Forge) { - // Given - val pendingTrace = mockPendingTrace(forge) - testedCleaner.addPendingTrace(pendingTrace) - pendingTrace.addSpan(mock()) - pendingTrace.addSpan(mock()) - testedCleaner.addPendingTrace(pendingTrace) - - // Then - assertThat(testedCleaner.pendingTraces).hasSize(1) - assertThat(testedCleaner.pendingTraces.values).containsExactly(pendingTrace) - - // When - testedCleaner.removePendingTrace(pendingTrace) - - // Then - assertThat(testedCleaner.pendingTraces).isEmpty() - } - - @Test - fun `M contain 2 entries W add {2 PendingTrace instances with same root span}`(forge: Forge) { - // Given - val pendingTrace1 = mockPendingTrace(forge) - val pendingTrace2 = mockPendingTrace(forge) - val mock = mock() - pendingTrace1.addSpan(mock) - pendingTrace2.addSpan(mock) - testedCleaner.addPendingTrace(pendingTrace1) - testedCleaner.addPendingTrace(pendingTrace2) - - // Then - assertThat(testedCleaner.pendingTraces).hasSize(2) - assertThat(testedCleaner.pendingTraces.values).containsExactlyInAnyOrder(pendingTrace1, pendingTrace2) - - // When - testedCleaner.removePendingTrace(pendingTrace1) - testedCleaner.removePendingTrace(pendingTrace2) - - // Then - assertThat(testedCleaner.pendingTraces).isEmpty() - } - - // endregion - - // region concurrency - - @Test - fun `M not leak any PendingTrace W add, mutate and remove { different threads }`(forge: Forge) { - // Given - val pendingTraces = forge.aList(size = forge.anInt(min = 10, max = 50)) { mockPendingTrace(forge) } - val addCountDownLatch = CountDownLatch(pendingTraces.size) - val removeCountDownLatch = CountDownLatch(pendingTraces.size) - val half = pendingTraces.size / 2 - pendingTraces.take(half).forEach { pendingTrace -> - Thread { - testedCleaner.addPendingTrace(pendingTrace) - repeat(forge.anInt(min = 1, max = 10)) { - pendingTrace.addSpan(mock()) - } - addCountDownLatch.countDown() - }.start() - } - pendingTraces.subList(half, pendingTraces.size).forEach { pendingTrace -> - Thread { - if (forge.aBool()) { - repeat(forge.anInt(min = 1, max = 10)) { - pendingTrace.addSpan(mock()) - } - } - testedCleaner.addPendingTrace(pendingTrace) - repeat(forge.anInt(min = 1, max = 10)) { - pendingTrace.addSpan(mock()) - } - addCountDownLatch.countDown() - }.start() - } - addCountDownLatch.await(3, TimeUnit.SECONDS) - - // When - pendingTraces.forEach { - Thread { - testedCleaner.removePendingTrace(it) - removeCountDownLatch.countDown() - }.start() - } - removeCountDownLatch.await(3, TimeUnit.SECONDS) - - // Then - assertThat(testedCleaner.pendingTraces).isEmpty() - } - - // endregion - - // region Internal - - private fun mockPendingTrace(forge: Forge): PendingTrace { - val mockTracer: DDTracer = mock() - val mockInternalLogger: InternalLogger = mock() - val fakeTraceId = BigInteger.valueOf(forge.aLong()) - return createInstance( - PendingTrace::class.java, - mockTracer, - fakeTraceId, - mockInternalLogger - ) - } - - // endregion -} diff --git a/features/dd-sdk-android-trace-internal/src/test/kotlin/com/datadog/opentracing/assertj/DDSpanContextAssert.kt b/features/dd-sdk-android-trace-internal/src/test/kotlin/com/datadog/opentracing/assertj/DDSpanContextAssert.kt deleted file mode 100644 index 25e9ee096f..0000000000 --- a/features/dd-sdk-android-trace-internal/src/test/kotlin/com/datadog/opentracing/assertj/DDSpanContextAssert.kt +++ /dev/null @@ -1,170 +0,0 @@ -/* - * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. - * This product includes software developed at Datadog (https://www.datadoghq.com/). - * Copyright 2016-Present Datadog, Inc. - */ - -package com.datadog.opentracing.assertj - -import com.datadog.opentracing.DDSpanContext -import org.assertj.core.api.AbstractObjectAssert -import org.assertj.core.api.Assertions.assertThat -import java.math.BigInteger - -internal class DDSpanContextAssert(actual: DDSpanContext) : - AbstractObjectAssert( - actual, - DDSpanContextAssert::class.java - ) { - - fun hasSpanId(spanId: BigInteger): DDSpanContextAssert { - assertThat(actual.spanId) - .overridingErrorMessage( - "Expected span context to have spanId: $spanId" + - " but instead was: ${actual.spanId}" - ) - .isEqualTo(spanId) - return this - } - - fun hasTraceId(traceId: BigInteger): DDSpanContextAssert { - assertThat(actual.traceId) - .overridingErrorMessage( - "Expected span context to have traceId: $traceId" + - " but instead was: ${actual.traceId}" - ) - .isEqualTo(traceId) - return this - } - - fun hasParentId(parentId: BigInteger): DDSpanContextAssert { - assertThat(actual.parentId) - .overridingErrorMessage( - "Expected span context to have parentId: $parentId" + - " but instead was: ${actual.parentId}" - ) - .isEqualTo(parentId) - return this - } - - fun hasServiceName(serviceName: String?): DDSpanContextAssert { - assertThat(actual.serviceName) - .overridingErrorMessage( - "Expected span context to have serviceName: $serviceName" + - " but instead was: ${actual.serviceName}" - ) - .isEqualTo(serviceName) - return this - } - - fun hasResourceName(resourceName: String): DDSpanContextAssert { - assertThat(actual.resourceName) - .overridingErrorMessage( - "Expected span context to have resourceName: $resourceName" + - " but instead was: ${actual.resourceName}" - ) - .isEqualTo(resourceName) - return this - } - - fun hasOperationName(operationName: String): DDSpanContextAssert { - assertThat(actual.operationName) - .overridingErrorMessage( - "Expected span context to have operationName: $operationName" + - " but instead was: ${actual.operationName}" - ) - .isEqualTo(operationName) - return this - } - - fun hasOrigin(origin: String): DDSpanContextAssert { - assertThat(actual.origin) - .overridingErrorMessage( - "Expected span context to have origin: $origin" + - " but instead was: ${actual.origin}" - ) - .isEqualTo(origin) - return this - } - - fun hasSpanType(spanType: String): DDSpanContextAssert { - assertThat(actual.spanType) - .overridingErrorMessage( - "Expected span context to have spanType: $spanType" + - " but instead was: ${actual.spanType}" - ) - .isEqualTo(spanType) - return this - } - - fun hasErrorFlag(errorFlag: Boolean): DDSpanContextAssert { - assertThat(actual.errorFlag) - .overridingErrorMessage( - "Expected span context to have errorFlag: $errorFlag" + - " but instead was: ${actual.errorFlag}" - ) - .isEqualTo(errorFlag) - return this - } - - fun hasSamplingPriority(samplingPriority: Int): DDSpanContextAssert { - assertThat(actual.samplingPriority) - .overridingErrorMessage( - "Expected span context to have samplingPriority: $samplingPriority" + - " but instead was: ${actual.samplingPriority}" - ) - .isEqualTo(samplingPriority) - return this - } - - fun hasExactlyBaggageItems(attributes: Map): DDSpanContextAssert { - assertThat(actual.baggageItems) - .hasSameSizeAs(attributes) - .containsAllEntriesOf(attributes) - return this - } - - fun containsBaggageItems(baggageItems: Map): DDSpanContextAssert { - assertThat(actual.baggageItems) - .containsAllEntriesOf(baggageItems) - return this - } - - fun hasExactlyTags(tags: Map): DDSpanContextAssert { - assertThat(actual.tags) - .hasSameSizeAs(tags) - .containsAllEntriesOf(tags) - return this - } - - fun containsTags(tags: Map): DDSpanContextAssert { - assertThat(actual.tags) - .containsAllEntriesOf(tags) - return this - } - - fun doesNotContainTags(vararg keys: String): DDSpanContextAssert { - assertThat(actual.tags) - .doesNotContainKeys(*keys) - return this - } - - fun hasExactlyMetrics(metrics: Map): DDSpanContextAssert { - assertThat(actual.metrics) - .hasSameSizeAs(metrics) - .containsAllEntriesOf(metrics) - return this - } - - fun doesNotContainMetrics(vararg keys: String): DDSpanContextAssert { - assertThat(actual.metrics) - .doesNotContainKeys(*keys) - return this - } - - companion object { - internal fun assertThat(actual: DDSpanContext): DDSpanContextAssert { - return DDSpanContextAssert(actual) - } - } -} diff --git a/features/dd-sdk-android-trace-internal/src/test/kotlin/com/datadog/opentracing/propagation/DatadogHttpCodecTest.kt b/features/dd-sdk-android-trace-internal/src/test/kotlin/com/datadog/opentracing/propagation/DatadogHttpCodecTest.kt deleted file mode 100644 index 5c9a0a0b54..0000000000 --- a/features/dd-sdk-android-trace-internal/src/test/kotlin/com/datadog/opentracing/propagation/DatadogHttpCodecTest.kt +++ /dev/null @@ -1,362 +0,0 @@ -/* - * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. - * This product includes software developed at Datadog (https://www.datadoghq.com/). - * Copyright 2016-Present Datadog, Inc. - */ - -package com.datadog.opentracing.propagation - -import com.datadog.android.trace.internal.domain.event.BigIntegerUtils -import com.datadog.legacy.trace.api.sampling.PrioritySampling -import com.datadog.opentracing.DDSpanContext -import com.datadog.tools.unit.setFieldValue -import com.datadog.utils.forge.Configurator -import fr.xgouchet.elmyr.Forge -import fr.xgouchet.elmyr.annotation.Forgery -import fr.xgouchet.elmyr.annotation.StringForgery -import fr.xgouchet.elmyr.junit5.ForgeConfiguration -import fr.xgouchet.elmyr.junit5.ForgeExtension -import org.assertj.core.api.Assertions.assertThat -import org.junit.jupiter.api.BeforeEach -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.extension.ExtendWith -import org.junit.jupiter.api.extension.Extensions -import org.junit.jupiter.params.ParameterizedTest -import org.junit.jupiter.params.provider.MethodSource -import org.mockito.Mock -import org.mockito.junit.jupiter.MockitoExtension -import org.mockito.junit.jupiter.MockitoSettings -import org.mockito.kotlin.whenever -import org.mockito.quality.Strictness -import java.math.BigInteger -import java.util.UUID - -@Extensions( - ExtendWith(MockitoExtension::class), - ExtendWith(ForgeExtension::class) -) -@MockitoSettings(strictness = Strictness.LENIENT) -@ForgeConfiguration(value = Configurator::class) -internal class DatadogHttpCodecTest { - - @Forgery - lateinit var fakeDDSpanContext: DDSpanContext - - private lateinit var testedInjector: DatadogHttpCodec.Injector - - private lateinit var testedExtractor: DatadogHttpCodec.Extractor - - @Mock - lateinit var mockBigIntegerUtils: BigIntegerUtils - - @StringForgery(regex = "[0-9]+") - lateinit var fakeLeastSignificant64BitsTraceId: String - - @StringForgery(regex = "[0-9a-f]{16}") - lateinit var fakeMostSignificant64BitsTraceId: String - - @Forgery - lateinit var fakeSessionId: UUID - - private lateinit var fakeTaggedHeaders: Map - - private lateinit var fakeIdAsHexString: String - - @BeforeEach - fun `set up`(forge: Forge) { - fakeIdAsHexString = fakeDDSpanContext.traceId.toString(16) - fakeTaggedHeaders = forge.aMap(size = forge.anInt(min = 1, max = 10)) { - anAlphabeticalString() to anAlphabeticalString() - } - testedInjector = DatadogHttpCodec.Injector(mockBigIntegerUtils) - whenever(mockBigIntegerUtils.leastSignificant64BitsAsDecimal(fakeDDSpanContext.traceId)) - .thenReturn(fakeLeastSignificant64BitsTraceId) - whenever(mockBigIntegerUtils.mostSignificant64BitsAsHex(fakeDDSpanContext.traceId)) - .thenReturn(fakeMostSignificant64BitsTraceId) - testedExtractor = DatadogHttpCodec.Extractor(fakeTaggedHeaders) - } - - // region Injector - - @Test - fun `M inject the required headers W inject`() { - // Given - val headers = mutableMapOf() - - // When - testedInjector.inject(fakeDDSpanContext) { key, value -> - headers[key] = value - } - - // Then - assertThat(headers[DatadogHttpCodec.ORIGIN_KEY]).isEqualTo(fakeDDSpanContext.origin) - assertThat( - headers[DatadogHttpCodec.LEAST_SIGNIFICANT_TRACE_ID_KEY] - ).isEqualTo(fakeLeastSignificant64BitsTraceId) - assertThat(headers[DatadogHttpCodec.DATADOG_TAGS_KEY]).isEqualTo(expectedInjectedTags()) - assertThat(headers[DatadogHttpCodec.SAMPLING_PRIORITY_KEY]) - .let { - if (fakeDDSpanContext.samplingPriority != PrioritySampling.UNSET) { - it.isEqualTo(fakeDDSpanContext.samplingPriority.toString()) - } else { - it.isNull() - } - } - assertThat(headers[DatadogHttpCodec.SPAN_ID_KEY]) - .isEqualTo(fakeDDSpanContext.spanId.toString()) - fakeDDSpanContext.baggageItems.forEach { (key, value) -> - assertThat(headers[DatadogHttpCodec.OT_BAGGAGE_PREFIX + key]).isEqualTo(HttpCodec.encode(value)) - } - } - - @Test - fun `M inject the required headers W inject {with sessionId}`() { - // Given - val headers = mutableMapOf() - fakeDDSpanContext.setTag("session_id", fakeSessionId.toString()) - - // When - testedInjector.inject(fakeDDSpanContext) { key, value -> - headers[key] = value - } - - // Then - assertThat(headers[DatadogHttpCodec.ORIGIN_KEY]).isEqualTo(fakeDDSpanContext.origin) - assertThat( - headers[DatadogHttpCodec.LEAST_SIGNIFICANT_TRACE_ID_KEY] - ).isEqualTo(fakeLeastSignificant64BitsTraceId) - assertThat(headers[DatadogHttpCodec.DATADOG_TAGS_KEY]).isEqualTo(expectedInjectedTags()) - assertThat(headers[W3CHttpCodec.BAGGAGE_KEY]).isEqualTo(expectedInjectedBaggage()) - assertThat(headers[DatadogHttpCodec.SAMPLING_PRIORITY_KEY]) - .let { - if (fakeDDSpanContext.samplingPriority != PrioritySampling.UNSET) { - it.isEqualTo(fakeDDSpanContext.samplingPriority.toString()) - } else { - it.isNull() - } - } - assertThat(headers[DatadogHttpCodec.SPAN_ID_KEY]) - .isEqualTo(fakeDDSpanContext.spanId.toString()) - fakeDDSpanContext.baggageItems.forEach { (key, value) -> - assertThat(headers[DatadogHttpCodec.OT_BAGGAGE_PREFIX + key]).isEqualTo(HttpCodec.encode(value)) - } - } - - // endregion - - @ParameterizedTest - @MethodSource("spanContextValues") - fun `M extract the required headers W extract`(spanContext: DDSpanContext, forge: Forge) { - // Given - val headers = resolveExtractedHeadersFromSpanContext(spanContext, forge) - - // When - val extractedSpanContext = testedExtractor.extract { - headers.iterator() - } as? ExtractedContext - - // Then - checkNotNull(extractedSpanContext) - assertThat(extractedSpanContext.origin).isEqualTo(spanContext.origin) - assertThat(extractedSpanContext.traceId).isEqualTo(spanContext.traceId) - assertThat(extractedSpanContext.spanId).isEqualTo(spanContext.spanId) - assertThat(extractedSpanContext.samplingPriority).isEqualTo(spanContext.samplingPriority) - assertThat(extractedSpanContext.baggageItems()) - .containsExactlyInAnyOrder(*spanContext.baggageItems.entries.toTypedArray()) - } - - @Test - fun `M extract the TagContext W extract { traceId 0 }`(forge: Forge) { - // Given - fakeDDSpanContext.setFieldValue("traceId", BigInteger.ZERO) - val headers = resolveExtractedHeadersFromSpanContext(fakeDDSpanContext, forge) - - // When - val extractedSpanContext = testedExtractor.extract { - headers.iterator() - } as? TagContext - - // Then - checkNotNull(extractedSpanContext) - assertThat(extractedSpanContext.origin).isEqualTo(fakeDDSpanContext.origin) - } - - @Test - fun `M extract the TagContext W extract { traceId is missing }`(forge: Forge) { - // Given - val headers = resolveExtractedHeadersFromSpanContext(fakeDDSpanContext, forge).apply { - remove(DatadogHttpCodec.LEAST_SIGNIFICANT_TRACE_ID_KEY) - } - - // When - val extractedSpanContext = testedExtractor.extract { - headers.iterator() - } as? TagContext - - // Then - checkNotNull(extractedSpanContext) - assertThat(extractedSpanContext.origin).isEqualTo(fakeDDSpanContext.origin) - } - - @Test - fun `M return null W extract { most significant trace id tag is broken }`(forge: Forge) { - // Given - val headers = resolveExtractedHeadersFromSpanContext(fakeDDSpanContext, forge).apply { - remove(DatadogHttpCodec.DATADOG_TAGS_KEY) - set(DatadogHttpCodec.DATADOG_TAGS_KEY, DatadogHttpCodec.MOST_SIGNIFICANT_TRACE_ID_TAG_KEY + "=broken") - } - - // When - val extractedSpanContext = testedExtractor.extract { - headers.iterator() - } - - // Then - assertThat(extractedSpanContext).isNull() - } - - @Test - fun `M return TagContext W extract { most significant trace id value is missing }`(forge: Forge) { - // Given - val headers = resolveExtractedHeadersFromSpanContext(fakeDDSpanContext, forge).apply { - remove(DatadogHttpCodec.DATADOG_TAGS_KEY) - set(DatadogHttpCodec.DATADOG_TAGS_KEY, DatadogHttpCodec.MOST_SIGNIFICANT_TRACE_ID_TAG_KEY) - } - - // When - val extractedSpanContext = testedExtractor.extract { - headers.iterator() - } as? TagContext - - // Then - checkNotNull(extractedSpanContext) - assertThat(extractedSpanContext.origin).isEqualTo(fakeDDSpanContext.origin) - } - - @Test - fun `M return null W extract { origin is null traceId is 0 }`(forge: Forge) { - // Given - fakeDDSpanContext.setFieldValue("traceId", BigInteger.ZERO) - val headers = resolveExtractedHeadersFromSpanContext(fakeDDSpanContext, forge).apply { - remove(DatadogHttpCodec.ORIGIN_KEY) - } - - // When - val extractedSpanContext = testedExtractor.extract { - headers.iterator() - } - - // Then - assertThat(extractedSpanContext).isNull() - } - - @Test - fun `M return null W extract { out of bounds least significant trace id }`(forge: Forge) { - // Given - val outOfBoundsSize = forge.anInt(min = 17, max = 32) - val fakeOutOfBoundsLeastSignificantTraceId = - BigInteger(forge.aStringMatching("[0-9][a-f]{$outOfBoundsSize}"), 16).toString() - val headers = resolveExtractedHeadersFromSpanContext(fakeDDSpanContext, forge).apply { - set(DatadogHttpCodec.LEAST_SIGNIFICANT_TRACE_ID_KEY, fakeOutOfBoundsLeastSignificantTraceId) - } - - // When - val extractedSpanContext = testedExtractor.extract { - headers.iterator() - } - - // Then - assertThat(extractedSpanContext).isNull() - } - - @Test - fun `M return null W extract { out of bounds most significant trace id }`(forge: Forge) { - // Given - val outOfBoundsSize = forge.anInt(min = 17, max = 32) - val fakeOutOfBoundsMostSignificantTraceId = - BigInteger(forge.aStringMatching("[0-9][a-f]{$outOfBoundsSize}"), 16).toString(16) - val traceIdTagsAndNoise = traceIdTagsAndNoise( - fakeOutOfBoundsMostSignificantTraceId, - forge - ) - val headers = resolveExtractedHeadersFromSpanContext(fakeDDSpanContext, forge).apply { - set(DatadogHttpCodec.DATADOG_TAGS_KEY, traceIdTagsAndNoise) - } - - // When - val extractedSpanContext = testedExtractor.extract { - headers.iterator() - } - - // Then - assertThat(extractedSpanContext).isNull() - } - - // region internal - - private fun resolveExtractedHeadersFromSpanContext( - spanContext: DDSpanContext, - forge: Forge - ): MutableMap { - val headers = mutableMapOf() - headers[DatadogHttpCodec.ORIGIN_KEY] = spanContext.origin - headers[DatadogHttpCodec.LEAST_SIGNIFICANT_TRACE_ID_KEY] = BigIntegerUtils.leastSignificant64BitsAsDecimal( - spanContext.traceId - ) - val traceIdTagsAndNoise = traceIdTagsAndNoise( - BigIntegerUtils.mostSignificant64BitsAsHex(spanContext.traceId), - forge - ) - headers[DatadogHttpCodec.DATADOG_TAGS_KEY] = traceIdTagsAndNoise - headers[DatadogHttpCodec.SAMPLING_PRIORITY_KEY] = spanContext.samplingPriority.toString() - headers[DatadogHttpCodec.SPAN_ID_KEY] = spanContext.spanId.toString() - spanContext.baggageItems.forEach { (key, value) -> - headers[DatadogHttpCodec.OT_BAGGAGE_PREFIX + key] = HttpCodec.encode(value) - } - return headers - } - - private fun expectedInjectedTags(): String { - return "_dd.p.tid=$fakeMostSignificant64BitsTraceId" - } - - private fun expectedInjectedBaggage(): String { - return "session.id=$fakeSessionId" - } - - private fun traceIdTagsAndNoise(traceId: String, forge: Forge): String { - val noiseTags = forge.aMap(size = forge.anInt(min = 0, max = 10)) { - anAlphabeticalString() to anAlphabeticalString() - } - return "_dd.p.tid=$traceId" + - "," + noiseTags.map { "${it.key}=${it.value}" }.joinToString(separator = ",") - } - - // endregion - - companion object { - val forge = Forge().apply { - Configurator().configure(this) - } - - @JvmStatic - @MethodSource("spanContextValues") - fun spanContextValues(): List { - val max128BitsHex = forge.aStringMatching("[f]{32}") - val minLeastSignificant128BitsHex = forge.aStringMatching("[0]{31}[1]{1}") - val minMostSignificant128BitsHex = forge.aStringMatching("[0]{15}[1]{1}[0]{16}") - return listOf( - forge.getForgery(), - forge.getForgery().apply { - setFieldValue("traceId", BigInteger(max128BitsHex, 16)) - }, - forge.getForgery().apply { - setFieldValue("traceId", BigInteger(minLeastSignificant128BitsHex, 16)) - }, - forge.getForgery().apply { - setFieldValue("traceId", BigInteger(minMostSignificant128BitsHex, 16)) - } - ) - } - } -} diff --git a/features/dd-sdk-android-trace-internal/src/test/kotlin/com/datadog/trace/common/sampling/SamplerTest.kt b/features/dd-sdk-android-trace-internal/src/test/kotlin/com/datadog/trace/common/sampling/SamplerTest.kt new file mode 100644 index 0000000000..9d14aec345 --- /dev/null +++ b/features/dd-sdk-android-trace-internal/src/test/kotlin/com/datadog/trace/common/sampling/SamplerTest.kt @@ -0,0 +1,139 @@ +/* + * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. + * This product includes software developed at Datadog (https://www.datadoghq.com/). + * Copyright 2016-Present Datadog, Inc. + */ +package com.datadog.trace.common.sampling + +import com.datadog.trace.api.Config +import com.datadog.trace.api.sampling.PrioritySampling +import com.datadog.trace.api.sampling.SamplingMechanism +import com.datadog.trace.bootstrap.instrumentation.api.SamplerConstants +import com.datadog.utils.forge.Configurator +import fr.xgouchet.elmyr.annotation.DoubleForgery +import fr.xgouchet.elmyr.junit5.ForgeConfiguration +import fr.xgouchet.elmyr.junit5.ForgeExtension +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith +import org.junit.jupiter.api.extension.Extensions +import org.mockito.junit.jupiter.MockitoExtension +import org.mockito.junit.jupiter.MockitoSettings +import org.mockito.kotlin.doReturn +import org.mockito.kotlin.mock +import org.mockito.kotlin.whenever +import org.mockito.quality.Strictness + +@Extensions( + ExtendWith(MockitoExtension::class), + ExtendWith(ForgeExtension::class) +) +@MockitoSettings(strictness = Strictness.LENIENT) +@ForgeConfiguration(value = Configurator::class) +class SamplerTest { + + private lateinit var mockConfig: Config + + @BeforeEach + fun `set up`() { + mockConfig = mock { + on { traceSampleRate } doReturn null + on { traceSamplingRules } doReturn null + on { traceSamplingServiceRules } doReturn null + on { isV2CompatibilityEnabled } doReturn false + on { isPrioritySamplingEnabled } doReturn false + on { traceSamplingOperationRules } doReturn null + } + } + + @Test + fun `M return RuleBasedTraceSampler W forConfig {v2 compatible, sampleRate provided}`( + @DoubleForgery(min = 0.1, max = 1.0) fakeSampleRate: Double + ) { + // Given + whenever(mockConfig.isV2CompatibilityEnabled).thenReturn(true) + whenever(mockConfig.traceSampleRate).thenReturn(fakeSampleRate) + + // When + val actual = Sampler.Builder.forConfig(mockConfig, null) + + // Then + assertThat(actual).isInstanceOf(RuleBasedTraceSampler::class.java) + } + + @Test + fun `M return RateByServiceTraceSampler W forConfig {v2 incompatible, sampleRate provided}`( + @DoubleForgery(min = 0.1, max = 1.0) fakeSampleRate: Double + ) { + // Given + whenever(mockConfig.traceSampleRate).thenReturn(fakeSampleRate) + + // When + val actual = Sampler.Builder.forConfig(mockConfig, null) + + // Then + assertThat(actual).isInstanceOf(RateByServiceTraceSampler::class.java) + assertThat((actual as RateByServiceTraceSampler).sampleRate).isEqualTo(fakeSampleRate) + } + + @Test + fun `M return AllSampler W forConfig {v2 incompatible, sampleRate not provided}`() { + // When + val actual = Sampler.Builder.forConfig(mockConfig, null) + + // Then + assertThat(actual).isInstanceOf(AllSampler::class.java) + } + + @Test + fun `M return ForcePrioritySampler W forConfig {priority sampling, prioritySamplingForce = KEEP}`() { + // Given + whenever(mockConfig.isPrioritySamplingEnabled).thenReturn(true) + whenever(mockConfig.prioritySamplingForce).thenReturn(SamplerConstants.KEEP) + + // When + val actual = Sampler.Builder.forConfig(mockConfig, null) + + // Then + assertThat(actual).isInstanceOf(ForcePrioritySampler::class.java) + assertThat((actual as ForcePrioritySampler).prioritySampling).isEqualTo(PrioritySampling.SAMPLER_KEEP.toInt()) + assertThat(actual.samplingMechanism).isEqualTo(SamplingMechanism.DEFAULT.toInt()) + } + + @Test + fun `M return ForcePrioritySampler W forConfig {priority sampling, prioritySamplingForce = DROP}`() { + // Given + whenever(mockConfig.isPrioritySamplingEnabled).thenReturn(true) + whenever(mockConfig.prioritySamplingForce).thenReturn(SamplerConstants.DROP) + + // When + val actual = Sampler.Builder.forConfig(mockConfig, null) + + // Then + assertThat(actual).isInstanceOf(ForcePrioritySampler::class.java) + assertThat((actual as ForcePrioritySampler).prioritySampling).isEqualTo(PrioritySampling.SAMPLER_DROP.toInt()) + assertThat(actual.samplingMechanism).isEqualTo(SamplingMechanism.DEFAULT.toInt()) + } + + @Test + fun `M return RateByServiceTraceSampler W forConfig {priority sampling, prioritySamplingForce is null}`() { + // Given + whenever(mockConfig.isPrioritySamplingEnabled).thenReturn(true) + + // When + val actual = Sampler.Builder.forConfig(mockConfig, null) + + // Then + assertThat(actual).isInstanceOf(RateByServiceTraceSampler::class.java) + } + + @Test + fun `M return AllSampler W forConfig {config is null}`() { + // When + val actual = Sampler.Builder.forConfig(null, null) + + // Then + assertThat(actual).isInstanceOf(AllSampler::class.java) + } +} diff --git a/features/dd-sdk-android-trace-internal/src/test/kotlin/com/datadog/trace/core/CoreSpanBuilderTest.kt b/features/dd-sdk-android-trace-internal/src/test/kotlin/com/datadog/trace/core/CoreSpanBuilderTest.kt index 0fb8477766..00c4080759 100644 --- a/features/dd-sdk-android-trace-internal/src/test/kotlin/com/datadog/trace/core/CoreSpanBuilderTest.kt +++ b/features/dd-sdk-android-trace-internal/src/test/kotlin/com/datadog/trace/core/CoreSpanBuilderTest.kt @@ -92,6 +92,7 @@ internal class CoreSpanBuilderTest : DDCoreSpecification() { val expectedResource = "fakeResource" val expectedService = "fakeService" val expectedType = "fakeType" + val expectedOrigin = "fakeOrigin" // When val thirdSpan = tracer @@ -99,6 +100,7 @@ internal class CoreSpanBuilderTest : DDCoreSpecification() { .withServiceName("foo") .withResourceName(expectedResource) .withServiceName(expectedService) + .withOrigin(expectedOrigin) .withErrorFlag() .withSpanType(expectedType) .start() @@ -110,6 +112,7 @@ internal class CoreSpanBuilderTest : DDCoreSpecification() { assertThat(context.errorFlag).isTrue assertThat(context.serviceName).isEqualTo(expectedService) assertThat(context.spanType).isEqualTo(expectedType) + assertThat(context.origin).isEqualTo(expectedOrigin) } @ParameterizedTest @@ -324,7 +327,7 @@ internal class CoreSpanBuilderTest : DDCoreSpecification() { // Then assertThat(span.traceId).isEqualTo(extractedContext.traceId) assertThat(span.parentId).isEqualTo(extractedContext.spanId) - assertThat(span.samplingPriority).isEqualTo(extractedContext.samplingPriority) + assertThat(span.traceSamplingPriority).isEqualTo(extractedContext.traceSamplingPriority) assertThat(span.context().origin).isEqualTo(extractedContext.origin) assertThat(span.context().baggageItems).isEqualTo(extractedContext.baggage) // check the extracted context has been copied into the span tags. Intercepted tags will be skipped from @@ -350,7 +353,7 @@ internal class CoreSpanBuilderTest : DDCoreSpecification() { // Then assertThat(span.traceId).isNotEqualTo(DDTraceId.ZERO) assertThat(span.parentId).isEqualTo(DDSpanId.ZERO) - assertThat(span.samplingPriority).isNull() + assertThat(span.traceSamplingPriority).isNull() assertThat(span.context().origin).isEqualTo(tagContext.origin) assertThat(span.context().baggageItems).isEqualTo(emptyMap()) assertThat(span.context().tags).containsExactlyInAnyOrderEntriesOf( diff --git a/features/dd-sdk-android-trace-internal/src/test/kotlin/com/datadog/trace/core/CoreTracerTest.kt b/features/dd-sdk-android-trace-internal/src/test/kotlin/com/datadog/trace/core/CoreTracerTest.kt index 83a8a54964..70f32c1277 100644 --- a/features/dd-sdk-android-trace-internal/src/test/kotlin/com/datadog/trace/core/CoreTracerTest.kt +++ b/features/dd-sdk-android-trace-internal/src/test/kotlin/com/datadog/trace/core/CoreTracerTest.kt @@ -150,7 +150,7 @@ internal class CoreTracerTest : DDCoreSpecification() { writer.waitForTraces(1) // Then - assertThat(span.samplingPriority).isEqualTo(PrioritySampling.SAMPLER_KEEP.toInt()) + assertThat(span.traceSamplingPriority).isEqualTo(PrioritySampling.SAMPLER_KEEP.toInt()) // Tear down tracer.close() @@ -168,15 +168,15 @@ internal class CoreTracerTest : DDCoreSpecification() { root.finish() // Then - assertThat(child.samplingPriority).isNull() + assertThat(child.traceSamplingPriority).isNull() // Tear down child.finish() writer.waitForTraces(1) // Then - assertThat(root.samplingPriority).isEqualTo(PrioritySampling.SAMPLER_KEEP.toInt()) - assertThat(root.samplingPriority).isEqualTo(child.samplingPriority) + assertThat(root.traceSamplingPriority).isEqualTo(PrioritySampling.SAMPLER_KEEP.toInt()) + assertThat(root.traceSamplingPriority).isEqualTo(child.traceSamplingPriority) // Tear down tracer.close() diff --git a/features/dd-sdk-android-trace-internal/src/test/kotlin/com/datadog/trace/core/DDSpanContextTest.kt b/features/dd-sdk-android-trace-internal/src/test/kotlin/com/datadog/trace/core/DDSpanContextTest.kt index 7b8ce441ea..967d6e387a 100644 --- a/features/dd-sdk-android-trace-internal/src/test/kotlin/com/datadog/trace/core/DDSpanContextTest.kt +++ b/features/dd-sdk-android-trace-internal/src/test/kotlin/com/datadog/trace/core/DDSpanContextTest.kt @@ -154,7 +154,7 @@ internal class DDSpanContextTest : DDCoreSpecification() { context.setSamplingPriority(PrioritySampling.SAMPLER_DROP.toInt(), SamplingMechanism.DEFAULT.toInt()) // Then: "priority should be set" - assertThat(context.samplingPriority).isEqualTo(PrioritySampling.SAMPLER_DROP.toInt()) + assertThat(context.traceSamplingPriority).isEqualTo(PrioritySampling.SAMPLER_DROP.toInt()) // When: "sampling priority locked" context.lockSamplingPriority() @@ -164,13 +164,13 @@ internal class DDSpanContextTest : DDCoreSpecification() { context.setSamplingPriority(PrioritySampling.USER_DROP.toInt(), SamplingMechanism.MANUAL.toInt()) assertThat(samplingPriority) .isFalse() - assertThat(context.samplingPriority).isEqualTo(PrioritySampling.SAMPLER_DROP.toInt()) + assertThat(context.traceSamplingPriority).isEqualTo(PrioritySampling.SAMPLER_DROP.toInt()) // When context.forceKeep() // Then: "lock is bypassed and priority set to USER_KEEP" - assertThat(context.samplingPriority).isEqualTo(PrioritySampling.USER_KEEP.toInt()) + assertThat(context.traceSamplingPriority).isEqualTo(PrioritySampling.USER_KEEP.toInt()) // Tear down span.finish() @@ -187,7 +187,7 @@ internal class DDSpanContextTest : DDCoreSpecification() { val context = span.context() as DDSpanContext // Then - assertThat(context.samplingPriority).isEqualTo(PrioritySampling.UNSET.toInt()) + assertThat(context.traceSamplingPriority).isEqualTo(PrioritySampling.UNSET.toInt()) // Given context.setSpanSamplingPriority(rate, limit) @@ -200,7 +200,7 @@ internal class DDSpanContextTest : DDCoreSpecification() { assertThat(context.getTag(DDSpanContext.SPAN_SAMPLING_MAX_PER_SECOND_TAG)) .isEqualTo(if (limit == Int.MAX_VALUE) null else limit) // single span sampling should not change the trace sampling priority - assertThat(context.samplingPriority).isEqualTo(PrioritySampling.UNSET.toInt()) + assertThat(context.traceSamplingPriority).isEqualTo(PrioritySampling.UNSET.toInt()) // make sure the `_dd.p.dm` tag has not been set by single span sampling assertThat(context.propagationTags.createTagMap().containsKey("_dd.p.dm")).isFalse() } diff --git a/features/dd-sdk-android-trace-internal/src/test/kotlin/com/datadog/trace/core/DDSpanTest.kt b/features/dd-sdk-android-trace-internal/src/test/kotlin/com/datadog/trace/core/DDSpanTest.kt index 464db52566..5bd609bc1c 100644 --- a/features/dd-sdk-android-trace-internal/src/test/kotlin/com/datadog/trace/core/DDSpanTest.kt +++ b/features/dd-sdk-android-trace-internal/src/test/kotlin/com/datadog/trace/core/DDSpanTest.kt @@ -30,6 +30,8 @@ import org.junit.jupiter.api.Test import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.Arguments import org.junit.jupiter.params.provider.MethodSource +import org.mockito.kotlin.any +import org.mockito.kotlin.doReturn import org.mockito.kotlin.mock import org.mockito.kotlin.times import org.mockito.kotlin.verify @@ -93,18 +95,18 @@ internal class DDSpanTest : DDCoreSpecification() { // When span.setSamplingPriority(PrioritySampling.UNSET.toInt()) // Then - assertThat(span.samplingPriority).isNull() + assertThat(span.traceSamplingPriority).isNull() // When span.setSamplingPriority(PrioritySampling.SAMPLER_KEEP.toInt()) // Then - assertThat(span.samplingPriority).isEqualTo(PrioritySampling.SAMPLER_KEEP.toInt()) + assertThat(span.traceSamplingPriority).isEqualTo(PrioritySampling.SAMPLER_KEEP.toInt()) // When (span.context() as DDSpanContext).lockSamplingPriority() span.setSamplingPriority(PrioritySampling.USER_KEEP.toInt()) // Then - assertThat(span.samplingPriority).isEqualTo(PrioritySampling.SAMPLER_KEEP.toInt()) + assertThat(span.traceSamplingPriority).isEqualTo(PrioritySampling.SAMPLER_KEEP.toInt()) } @Test @@ -299,10 +301,10 @@ internal class DDSpanTest : DDCoreSpecification() { parent.finish() // Then - assertThat(parent.context().samplingPriority).isEqualTo(PrioritySampling.SAMPLER_KEEP.toInt()) - assertThat(parent.samplingPriority).isEqualTo(PrioritySampling.SAMPLER_KEEP.toInt()) - assertThat(child1.samplingPriority).isEqualTo(parent.samplingPriority) - assertThat(child2.samplingPriority).isEqualTo(parent.samplingPriority) + assertThat(parent.context().traceSamplingPriority).isEqualTo(PrioritySampling.SAMPLER_KEEP.toInt()) + assertThat(parent.traceSamplingPriority).isEqualTo(PrioritySampling.SAMPLER_KEEP.toInt()) + assertThat(child1.traceSamplingPriority).isEqualTo(parent.traceSamplingPriority) + assertThat(child2.traceSamplingPriority).isEqualTo(parent.traceSamplingPriority) } @ParameterizedTest @@ -497,7 +499,7 @@ internal class DDSpanTest : DDCoreSpecification() { // Then val expectedLimit = if (limit == Int.MAX_VALUE) null else limit - assertThat(span.samplingPriority()).isEqualTo(PrioritySampling.UNSET.toInt()) + assertThat(span.traceSamplingPriority).isNull() // When span.setSpanSamplingPriority(rate, limit) @@ -507,7 +509,28 @@ internal class DDSpanTest : DDCoreSpecification() { .isEqualTo(SamplingMechanism.SPAN_SAMPLING_RATE) assertThat(span.getTag(DDSpanContext.SPAN_SAMPLING_RULE_RATE_TAG)).isEqualTo(rate) assertThat(span.getTag(DDSpanContext.SPAN_SAMPLING_MAX_PER_SECOND_TAG)).isEqualTo(expectedLimit) - assertThat(span.samplingPriority()).isEqualTo(PrioritySampling.UNSET.toInt()) + assertThat(span.traceSamplingPriority).isNull() + } + + @Test + fun `W drop T unregisterSpan is called`() { + val mockTrace = mock { + on { rootSpan } doReturn mock() + } + + val mockContext = mock { + on { traceId } doReturn mock() + on { trace } doReturn mockTrace + } + + val span = tracer + .buildSpan(instrumentationName, "testSpan") + .asChildOf(mockContext) + .start() as DDSpan + + span.drop() + + verify(mockTrace).unregisterSpan(any()) } @Test diff --git a/features/dd-sdk-android-trace-internal/src/test/kotlin/com/datadog/trace/core/PendingTraceTest.kt b/features/dd-sdk-android-trace-internal/src/test/kotlin/com/datadog/trace/core/PendingTraceTest.kt index 38c176da66..c412228589 100644 --- a/features/dd-sdk-android-trace-internal/src/test/kotlin/com/datadog/trace/core/PendingTraceTest.kt +++ b/features/dd-sdk-android-trace-internal/src/test/kotlin/com/datadog/trace/core/PendingTraceTest.kt @@ -187,6 +187,37 @@ internal class PendingTraceTest : PendingTraceTestBase() { assertThat(trace.spans).containsExactlyInAnyOrder(unfinishedSpan, unfinishedSpan2) } + @Test + fun `do not drop root span when drop is called`() { + // Given + val tracer = mock() + val traceConfig = mock() + val buffer = mock() + val healthMetrics = mock() + whenever(tracer.captureTraceConfig()).thenReturn(traceConfig) + whenever(traceConfig.serviceMapping).thenReturn(emptyMap()) + val trace = createInstanceWithoutTypeCheck( + PendingTrace::class.java, + tracer, + DDTraceId.from(0), + buffer, + mock(), + mock(), + false, + healthMetrics + ) + rootSpan = createSimpleSpan(trace) + trace.registerSpan(rootSpan) + + // When + trace.unregisterSpan(rootSpan) + + // Then + assertThat(trace.pendingReferenceCount).isEqualTo(0) + assertThat(trace.spans).isEmpty() + assertThat(trace.rootSpan).isEqualTo(rootSpan) + } + private fun createSimpleSpan(trace: PendingTrace): DDSpan { return createSimpleSpanWithID(trace, 1) } diff --git a/features/dd-sdk-android-trace-internal/src/test/kotlin/com/datadog/trace/core/PendingTraceTestBase.kt b/features/dd-sdk-android-trace-internal/src/test/kotlin/com/datadog/trace/core/PendingTraceTestBase.kt index 637aaedfad..ff18570dd6 100644 --- a/features/dd-sdk-android-trace-internal/src/test/kotlin/com/datadog/trace/core/PendingTraceTestBase.kt +++ b/features/dd-sdk-android-trace-internal/src/test/kotlin/com/datadog/trace/core/PendingTraceTestBase.kt @@ -144,7 +144,7 @@ internal abstract class PendingTraceTestBase : DDCoreSpecification() { @Test fun `partial flush`() { // Given - val quickTracer = tracerBuilder().writer(writer).partialFlushMinSpans(2).build() + val quickTracer = tracerBuilder().writer(writer).partialFlushMinSpans(1).build() val rootSpan = quickTracer.buildSpan(instrumentationName, "root").start() as DDSpan val trace = rootSpan.context().trace as PendingTrace val child1 = quickTracer.buildSpan(instrumentationName, "child1") @@ -192,7 +192,7 @@ internal abstract class PendingTraceTestBase : DDCoreSpecification() { @Test fun `partial flush with root span closed last`() { // Given - val quickTracer = tracerBuilder().writer(writer).partialFlushMinSpans(2).build() as CoreTracer + val quickTracer = tracerBuilder().writer(writer).partialFlushMinSpans(1).build() as CoreTracer val rootSpan = quickTracer.buildSpan(instrumentationName, "root").start() as DDSpan val trace = rootSpan.context().trace val child1 = quickTracer diff --git a/features/dd-sdk-android-trace-internal/src/test/kotlin/com/datadog/utils/forge/Configurator.kt b/features/dd-sdk-android-trace-internal/src/test/kotlin/com/datadog/utils/forge/Configurator.kt index d81530607f..ffd4303d50 100644 --- a/features/dd-sdk-android-trace-internal/src/test/kotlin/com/datadog/utils/forge/Configurator.kt +++ b/features/dd-sdk-android-trace-internal/src/test/kotlin/com/datadog/utils/forge/Configurator.kt @@ -18,9 +18,6 @@ internal class Configurator : BaseConfigurator() { // Core forge.useCoreFactories() - // APM - forge.addFactory(DDSpanContextForgeryFactory()) - // MISC forge.addFactory(BigIntegerFactory()) } diff --git a/features/dd-sdk-android-trace-internal/src/test/kotlin/com/datadog/utils/forge/DDSpanContextForgeryFactory.kt b/features/dd-sdk-android-trace-internal/src/test/kotlin/com/datadog/utils/forge/DDSpanContextForgeryFactory.kt deleted file mode 100644 index 498cd5e8e3..0000000000 --- a/features/dd-sdk-android-trace-internal/src/test/kotlin/com/datadog/utils/forge/DDSpanContextForgeryFactory.kt +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. - * This product includes software developed at Datadog (https://www.datadoghq.com/). - * Copyright 2016-Present Datadog, Inc. - */ - -package com.datadog.utils.forge - -import com.datadog.legacy.trace.api.sampling.PrioritySampling -import com.datadog.opentracing.DDSpanContext -import fr.xgouchet.elmyr.Forge -import fr.xgouchet.elmyr.ForgeryFactory -import org.mockito.kotlin.mock -import java.math.BigInteger - -internal class DDSpanContextForgeryFactory : ForgeryFactory { - - override fun getForgery(forge: Forge): DDSpanContext { - val traceIdAsHexa = forge.aStringMatching("[0-9a-f]{32}") - val traceId = BigInteger(traceIdAsHexa, 16) - val spanIdAsHexa = forge.aStringMatching("[0-9a-f]{16}") - val spanId = BigInteger(spanIdAsHexa, 16) - val parentSpanIdAsHexa = forge.aStringMatching("[0-9a-f]{16}") - val parentId = BigInteger(parentSpanIdAsHexa, 16) - val operationName = forge.anAlphabeticalString() - val resourceName = forge.anAlphabeticalString() - val serviceName = forge.anAlphabeticalString() - val spanType = forge.anAlphabeticalString() - val origin = forge.anAlphabeticalString() - val samplingPriority = forge.anElementFrom( - PrioritySampling.UNSET, - PrioritySampling.SAMPLER_DROP, - PrioritySampling.USER_DROP, - PrioritySampling.SAMPLER_KEEP, - PrioritySampling.USER_KEEP - ) - val baggageItems = forge.aMap(size = forge.anInt(min = 0, max = 10)) { - anAlphabeticalString() to anAlphabeticalString() - } - - return DDSpanContext( - traceId, - spanId, - parentId, - serviceName, - operationName, - resourceName, - samplingPriority, - origin, - baggageItems, - forge.aBool(), - spanType, - emptyMap(), - mock(), - mock(), - mock(), - mock() - ) - } -} diff --git a/features/dd-sdk-android-trace-internal/transitiveDependencies b/features/dd-sdk-android-trace-internal/transitiveDependencies index 1a6159fdd1..998404520a 100644 --- a/features/dd-sdk-android-trace-internal/transitiveDependencies +++ b/features/dd-sdk-android-trace-internal/transitiveDependencies @@ -3,9 +3,6 @@ Dependencies List androidx.annotation:annotation-jvm:1.9.1 : 59 Kb com.google.code.gson:gson:2.10.1 : 276 Kb com.google.re2j:re2j:1.7 : 111 Kb -io.opentracing:opentracing-api:0.32.0 : 18 Kb -io.opentracing:opentracing-noop:0.32.0 : 10 Kb -io.opentracing:opentracing-util:0.32.0 : 10 Kb org.jctools:jctools-core:3.3.0 : 328 Kb org.jetbrains.kotlin:kotlin-stdlib:2.0.21 : 1706 Kb org.jetbrains:annotations:13.0 : 17 Kb diff --git a/features/dd-sdk-android-trace-otel/api/apiSurface b/features/dd-sdk-android-trace-otel/api/apiSurface index 4c1ff05373..e467826eeb 100644 --- a/features/dd-sdk-android-trace-otel/api/apiSurface +++ b/features/dd-sdk-android-trace-otel/api/apiSurface @@ -1,3 +1,7 @@ +class com.datadog.android.trace.opentelemetry.DatadogOpenTelemetry : io.opentelemetry.api.OpenTelemetry + constructor(String) + override fun getTracerProvider(): io.opentelemetry.api.trace.TracerProvider + override fun getPropagators(): io.opentelemetry.context.propagation.ContextPropagators class com.datadog.android.trace.opentelemetry.OtelTracerProvider : io.opentelemetry.api.trace.TracerProvider override fun get(String): io.opentelemetry.api.trace.Tracer override fun get(String, String): io.opentelemetry.api.trace.Tracer diff --git a/features/dd-sdk-android-trace-otel/api/dd-sdk-android-trace-otel.api b/features/dd-sdk-android-trace-otel/api/dd-sdk-android-trace-otel.api index aa92fbd185..3abf206948 100644 --- a/features/dd-sdk-android-trace-otel/api/dd-sdk-android-trace-otel.api +++ b/features/dd-sdk-android-trace-otel/api/dd-sdk-android-trace-otel.api @@ -6,6 +6,12 @@ public final class com/datadog/android/trace/opentelemetry/BuildConfig { public fun ()V } +public final class com/datadog/android/trace/opentelemetry/DatadogOpenTelemetry : io/opentelemetry/api/OpenTelemetry { + public fun (Ljava/lang/String;)V + public fun getPropagators ()Lio/opentelemetry/context/propagation/ContextPropagators; + public fun getTracerProvider ()Lio/opentelemetry/api/trace/TracerProvider; +} + public final class com/datadog/android/trace/opentelemetry/OtelTracerProvider : io/opentelemetry/api/trace/TracerProvider { public fun get (Ljava/lang/String;)Lio/opentelemetry/api/trace/Tracer; public fun get (Ljava/lang/String;Ljava/lang/String;)Lio/opentelemetry/api/trace/Tracer; @@ -37,30 +43,31 @@ public final class com/datadog/opentelemetry/context/propagation/TraceStateHelpe } public final class com/datadog/opentelemetry/trace/OtelConventions { - public static fun applyNamingConvention (Lcom/datadog/trace/bootstrap/instrumentation/api/AgentSpan;)V - public static fun applyReservedAttribute (Lcom/datadog/trace/bootstrap/instrumentation/api/AgentSpan;Lio/opentelemetry/api/common/AttributeKey;Ljava/lang/Object;)Z + public static fun applyNamingConvention (Lcom/datadog/android/trace/api/span/DatadogSpan;)V + public static fun applyReservedAttribute (Lcom/datadog/android/trace/api/span/DatadogSpan;Lio/opentelemetry/api/common/AttributeKey;Ljava/lang/Object;)Z public static fun toOtelSpanKind (Ljava/lang/String;)Lio/opentelemetry/api/trace/SpanKind; public static fun toSpanKindTagValue (Lio/opentelemetry/api/trace/SpanKind;)Ljava/lang/String; } -public class com/datadog/opentelemetry/trace/OtelExtractedContext : com/datadog/trace/bootstrap/instrumentation/api/AgentSpan$Context { - public fun baggageItems ()Ljava/lang/Iterable; - public static fun extract (Lio/opentelemetry/context/Context;Lcom/datadog/android/api/InternalLogger;)Lcom/datadog/trace/bootstrap/instrumentation/api/AgentSpan$Context; - public fun getPathwayContext ()Lcom/datadog/trace/bootstrap/instrumentation/api/PathwayContext; +public class com/datadog/opentelemetry/trace/OtelExtractedContext : com/datadog/android/trace/api/span/DatadogSpanContext { + public static fun extract (Lio/opentelemetry/context/Context;Lcom/datadog/android/api/InternalLogger;)Lcom/datadog/android/trace/api/span/DatadogSpanContext; public fun getSamplingPriority ()I public fun getSpanId ()J - public fun getTrace ()Lcom/datadog/trace/bootstrap/instrumentation/api/AgentTrace; - public fun getTraceId ()Lcom/datadog/trace/api/DDTraceId; + public fun getTags ()Ljava/util/Map; + public fun getTraceId ()Lcom/datadog/android/trace/api/trace/DatadogTraceId; + public fun setMetric (Ljava/lang/CharSequence;D)V + public fun setSamplingPriority (I)Z } public class com/datadog/opentelemetry/trace/OtelSpan : io/opentelemetry/api/trace/Span { - public fun (Lcom/datadog/trace/bootstrap/instrumentation/api/AgentSpan;Lcom/datadog/trace/bootstrap/instrumentation/api/AgentTracer$TracerAPI;)V - public fun activate ()Lcom/datadog/trace/bootstrap/instrumentation/api/AgentScope; + public fun (Lcom/datadog/android/trace/api/span/DatadogSpan;Lcom/datadog/android/trace/api/tracer/DatadogTracer;)V + public fun activate ()Lcom/datadog/android/trace/api/scope/DatadogScope; public fun addEvent (Ljava/lang/String;Lio/opentelemetry/api/common/Attributes;)Lio/opentelemetry/api/trace/Span; public fun addEvent (Ljava/lang/String;Lio/opentelemetry/api/common/Attributes;JLjava/util/concurrent/TimeUnit;)Lio/opentelemetry/api/trace/Span; public fun end ()V public fun end (JLjava/util/concurrent/TimeUnit;)V - public fun getAgentSpanContext ()Lcom/datadog/trace/bootstrap/instrumentation/api/AgentSpan$Context; + public fun getDatadogSpan ()Lcom/datadog/android/trace/api/span/DatadogSpan; + public fun getDatadogSpanContext ()Lcom/datadog/android/trace/api/span/DatadogSpanContext; public fun getSpanContext ()Lio/opentelemetry/api/trace/SpanContext; public fun getStatusCode ()Lio/opentelemetry/api/trace/StatusCode; public static fun invalid ()Lio/opentelemetry/api/trace/Span; @@ -72,7 +79,7 @@ public class com/datadog/opentelemetry/trace/OtelSpan : io/opentelemetry/api/tra } public class com/datadog/opentelemetry/trace/OtelSpanBuilder : io/opentelemetry/api/trace/SpanBuilder { - public fun (Lcom/datadog/trace/bootstrap/instrumentation/api/AgentTracer$SpanBuilder;Lcom/datadog/trace/bootstrap/instrumentation/api/AgentTracer$TracerAPI;Lcom/datadog/android/api/InternalLogger;)V + public fun (Lcom/datadog/android/trace/api/span/DatadogSpanBuilder;Lcom/datadog/android/trace/api/tracer/DatadogTracer;Lcom/datadog/android/api/InternalLogger;)V public fun addLink (Lio/opentelemetry/api/trace/SpanContext;)Lio/opentelemetry/api/trace/SpanBuilder; public fun addLink (Lio/opentelemetry/api/trace/SpanContext;Lio/opentelemetry/api/common/Attributes;)Lio/opentelemetry/api/trace/SpanBuilder; public fun setAttribute (Lio/opentelemetry/api/common/AttributeKey;Ljava/lang/Object;)Lio/opentelemetry/api/trace/SpanBuilder; @@ -80,7 +87,6 @@ public class com/datadog/opentelemetry/trace/OtelSpanBuilder : io/opentelemetry/ public fun setAttribute (Ljava/lang/String;J)Lio/opentelemetry/api/trace/SpanBuilder; public fun setAttribute (Ljava/lang/String;Ljava/lang/String;)Lio/opentelemetry/api/trace/SpanBuilder; public fun setAttribute (Ljava/lang/String;Z)Lio/opentelemetry/api/trace/SpanBuilder; - public fun setLazyDatadogContext (Lcom/datadog/android/internal/concurrent/CompletableFuture;)Lio/opentelemetry/api/trace/SpanBuilder; public fun setNoParent ()Lio/opentelemetry/api/trace/SpanBuilder; public fun setParent (Lio/opentelemetry/context/Context;)Lio/opentelemetry/api/trace/SpanBuilder; public fun setSpanKind (Lio/opentelemetry/api/trace/SpanKind;)Lio/opentelemetry/api/trace/SpanBuilder; @@ -89,10 +95,10 @@ public class com/datadog/opentelemetry/trace/OtelSpanBuilder : io/opentelemetry/ } public class com/datadog/opentelemetry/trace/OtelSpanContext : io/opentelemetry/api/trace/SpanContext { - public fun (Lcom/datadog/trace/bootstrap/instrumentation/api/AgentSpan$Context;ZZLio/opentelemetry/api/trace/TraceState;)V - public static fun fromLocalSpan (Lcom/datadog/trace/bootstrap/instrumentation/api/AgentSpan;)Lio/opentelemetry/api/trace/SpanContext; - public static fun fromRemote (Lcom/datadog/trace/bootstrap/instrumentation/api/AgentSpan$Context;Lio/opentelemetry/api/trace/TraceState;)Lio/opentelemetry/api/trace/SpanContext; - public fun getDelegate ()Lcom/datadog/trace/bootstrap/instrumentation/api/AgentSpan$Context; + public fun (Lcom/datadog/android/trace/api/span/DatadogSpanContext;ZZLio/opentelemetry/api/trace/TraceState;)V + public static fun fromLocalSpan (Lcom/datadog/android/trace/api/span/DatadogSpan;)Lio/opentelemetry/api/trace/SpanContext; + public static fun fromRemote (Lcom/datadog/android/trace/api/span/DatadogSpanContext;Lio/opentelemetry/api/trace/TraceState;)Lio/opentelemetry/api/trace/SpanContext; + public fun getDelegate ()Lcom/datadog/android/trace/api/span/DatadogSpanContext; public fun getSpanId ()Ljava/lang/String; public fun getTraceFlags ()Lio/opentelemetry/api/trace/TraceFlags; public fun getTraceId ()Ljava/lang/String; @@ -101,19 +107,24 @@ public class com/datadog/opentelemetry/trace/OtelSpanContext : io/opentelemetry/ public fun toString ()Ljava/lang/String; } -public class com/datadog/opentelemetry/trace/OtelSpanLink : com/datadog/trace/bootstrap/instrumentation/api/SpanLink { +public class com/datadog/opentelemetry/trace/OtelSpanLink : com/datadog/android/trace/api/span/DatadogSpanLink { public fun (Lio/opentelemetry/api/trace/SpanContext;)V public fun (Lio/opentelemetry/api/trace/SpanContext;Lio/opentelemetry/api/common/Attributes;)V + public fun getAttributes ()Ljava/util/Map; + public fun getSampled ()Z + public fun getSpanId ()J + public fun getTraceId ()Lcom/datadog/android/trace/api/trace/DatadogTraceId; + public fun getTraceStrace ()Ljava/lang/String; } public class com/datadog/opentelemetry/trace/OtelTracer : io/opentelemetry/api/trace/Tracer { - public fun (Ljava/lang/String;Lcom/datadog/trace/bootstrap/instrumentation/api/AgentTracer$TracerAPI;Lcom/datadog/android/api/InternalLogger;)V - public fun (Ljava/lang/String;Lcom/datadog/trace/bootstrap/instrumentation/api/AgentTracer$TracerAPI;Lcom/datadog/android/api/InternalLogger;Lcom/datadog/opentelemetry/compat/function/Function;)V + public fun (Ljava/lang/String;Lcom/datadog/android/trace/api/tracer/DatadogTracer;Lcom/datadog/android/api/InternalLogger;)V + public fun (Ljava/lang/String;Lcom/datadog/android/trace/api/tracer/DatadogTracer;Lcom/datadog/android/api/InternalLogger;Lcom/datadog/opentelemetry/compat/function/Function;)V public fun spanBuilder (Ljava/lang/String;)Lio/opentelemetry/api/trace/SpanBuilder; } public class com/datadog/opentelemetry/trace/OtelTracerBuilder : io/opentelemetry/api/trace/TracerBuilder { - public fun (Ljava/lang/String;Lcom/datadog/trace/bootstrap/instrumentation/api/AgentTracer$TracerAPI;Lcom/datadog/android/api/InternalLogger;Lcom/datadog/opentelemetry/compat/function/Function;)V + public fun (Ljava/lang/String;Lcom/datadog/android/trace/api/tracer/DatadogTracer;Lcom/datadog/android/api/InternalLogger;)V public fun build ()Lio/opentelemetry/api/trace/Tracer; public fun setInstrumentationVersion (Ljava/lang/String;)Lio/opentelemetry/api/trace/TracerBuilder; public fun setSchemaUrl (Ljava/lang/String;)Lio/opentelemetry/api/trace/TracerBuilder; diff --git a/features/dd-sdk-android-trace-otel/build.gradle.kts b/features/dd-sdk-android-trace-otel/build.gradle.kts index c8f1cb6a5c..105443ddba 100644 --- a/features/dd-sdk-android-trace-otel/build.gradle.kts +++ b/features/dd-sdk-android-trace-otel/build.gradle.kts @@ -55,13 +55,14 @@ android { } dependencies { - api(project(":dd-sdk-android-core")) - api(project(":features:dd-sdk-android-trace")) - implementation(project(":dd-sdk-android-internal")) api(libs.openTelemetryApi) implementation(libs.kotlin) implementation(libs.androidXAnnotation) + api(project(":dd-sdk-android-core")) + implementation(project(":dd-sdk-android-internal")) + implementation(project(":features:dd-sdk-android-trace")) + testImplementation(project(":tools:unit")) { attributes { attribute( @@ -71,6 +72,7 @@ dependencies { } } testImplementation(testFixtures(project(":dd-sdk-android-core"))) + testImplementation(testFixtures(project(":features:dd-sdk-android-trace"))) testImplementation(libs.bundles.jUnit5) testImplementation(libs.bundles.testTools) testImplementation(libs.systemStubsJupiter) diff --git a/features/dd-sdk-android-trace-otel/src/main/java/com/datadog/opentelemetry/trace/OtelConventions.java b/features/dd-sdk-android-trace-otel/src/main/java/com/datadog/opentelemetry/trace/OtelConventions.java index 3718641f84..c242aeacc7 100644 --- a/features/dd-sdk-android-trace-otel/src/main/java/com/datadog/opentelemetry/trace/OtelConventions.java +++ b/features/dd-sdk-android-trace-otel/src/main/java/com/datadog/opentelemetry/trace/OtelConventions.java @@ -6,24 +6,24 @@ package com.datadog.opentelemetry.trace; -import static com.datadog.trace.api.DDTags.ANALYTICS_SAMPLE_RATE; -import static com.datadog.trace.bootstrap.instrumentation.api.Tags.SPAN_KIND; -import static com.datadog.trace.bootstrap.instrumentation.api.Tags.SPAN_KIND_CLIENT; -import static com.datadog.trace.bootstrap.instrumentation.api.Tags.SPAN_KIND_CONSUMER; -import static com.datadog.trace.bootstrap.instrumentation.api.Tags.SPAN_KIND_PRODUCER; -import static com.datadog.trace.bootstrap.instrumentation.api.Tags.SPAN_KIND_SERVER; +import static com.datadog.android.trace.api.DatadogTracingConstants.Tags.KEY_ANALYTICS_SAMPLE_RATE; +import static com.datadog.android.trace.api.DatadogTracingConstants.Tags.VALUE_SPAN_KIND_CLIENT; +import static com.datadog.android.trace.api.DatadogTracingConstants.Tags.VALUE_SPAN_KIND_CONSUMER; +import static com.datadog.android.trace.api.DatadogTracingConstants.Tags.VALUE_SPAN_KIND_PRODUCER; +import static com.datadog.android.trace.api.DatadogTracingConstants.Tags.VALUE_SPAN_KIND_SERVER; +import static java.lang.Boolean.parseBoolean; +import static java.util.Locale.ROOT; import static io.opentelemetry.api.trace.SpanKind.CLIENT; import static io.opentelemetry.api.trace.SpanKind.CONSUMER; import static io.opentelemetry.api.trace.SpanKind.INTERNAL; import static io.opentelemetry.api.trace.SpanKind.PRODUCER; import static io.opentelemetry.api.trace.SpanKind.SERVER; -import static java.lang.Boolean.parseBoolean; -import static java.util.Locale.ROOT; import androidx.annotation.Nullable; -import com.datadog.trace.bootstrap.instrumentation.api.AgentSpan; -import com.datadog.trace.bootstrap.instrumentation.api.Tags; +import com.datadog.android.trace.api.DatadogTracingConstants; +import com.datadog.android.trace.api.span.DatadogSpan; + import io.opentelemetry.api.common.AttributeKey; import io.opentelemetry.api.trace.SpanKind; @@ -34,21 +34,21 @@ public final class OtelConventions { private OtelConventions() {} /** - * Convert OpenTelemetry {@link SpanKind} to {@link Tags#SPAN_KIND} value. + * Convert OpenTelemetry {@link SpanKind} to {@link DatadogTracingConstants.Tags#KEY_SPAN_KIND} value. * * @param spanKind The OpenTelemetry span kind to convert. - * @return The {@link Tags#SPAN_KIND} value. + * @return The {@link DatadogTracingConstants.Tags#KEY_SPAN_KIND} value. */ public static String toSpanKindTagValue(SpanKind spanKind) { switch (spanKind) { case CLIENT: - return SPAN_KIND_CLIENT; + return VALUE_SPAN_KIND_CLIENT; case SERVER: - return SPAN_KIND_SERVER; + return VALUE_SPAN_KIND_SERVER; case PRODUCER: - return SPAN_KIND_PRODUCER; + return VALUE_SPAN_KIND_PRODUCER; case CONSUMER: - return SPAN_KIND_CONSUMER; + return VALUE_SPAN_KIND_CONSUMER; case INTERNAL: return SPAN_KIND_INTERNAL; default: @@ -57,9 +57,9 @@ public static String toSpanKindTagValue(SpanKind spanKind) { } /** - * Convert {@link Tags#SPAN_KIND} value to OpenTelemetry {@link SpanKind}. + * Convert {@link DatadogTracingConstants.Tags#KEY_SPAN_KIND} value to OpenTelemetry {@link SpanKind}. * - * @param spanKind The {@link Tags#SPAN_KIND} value to convert. + * @param spanKind The {@link DatadogTracingConstants.Tags#KEY_SPAN_KIND} value to convert. * @return The related OpenTelemetry {@link SpanKind}. */ public static SpanKind toOtelSpanKind(String spanKind) { @@ -67,13 +67,13 @@ public static SpanKind toOtelSpanKind(String spanKind) { return INTERNAL; } switch (spanKind) { - case SPAN_KIND_CLIENT: + case VALUE_SPAN_KIND_CLIENT: return CLIENT; - case SPAN_KIND_SERVER: + case VALUE_SPAN_KIND_SERVER: return SERVER; - case SPAN_KIND_PRODUCER: + case VALUE_SPAN_KIND_PRODUCER: return PRODUCER; - case SPAN_KIND_CONSUMER: + case VALUE_SPAN_KIND_CONSUMER: return CONSUMER; default: return INTERNAL; @@ -91,7 +91,7 @@ public static SpanKind toOtelSpanKind(String spanKind) { * @return {@code true} if the attributes is a reserved attribute applied to the span, {@code * false} otherwise. */ - public static boolean applyReservedAttribute(AgentSpan span, AttributeKey key, T value) { + public static boolean applyReservedAttribute(DatadogSpan span, AttributeKey key, T value) { String name = key.getKey(); switch (key.getType()) { case STRING: @@ -99,27 +99,27 @@ public static boolean applyReservedAttribute(AgentSpan span, AttributeKey span.setOperationName(((String) value).toLowerCase(ROOT)); return true; } else if (ANALYTICS_EVENT_SPECIFIC_ATTRIBUTES.equals(name) && value instanceof String) { - span.setMetric(ANALYTICS_SAMPLE_RATE, parseBoolean((String) value) ? 1 : 0); + span.setMetric(KEY_ANALYTICS_SAMPLE_RATE, parseBoolean((String) value) ? 1 : 0); return true; } case BOOLEAN: if (ANALYTICS_EVENT_SPECIFIC_ATTRIBUTES.equals(name) && value instanceof Boolean) { - span.setMetric(ANALYTICS_SAMPLE_RATE, ((Boolean) value) ? 1 : 0); + span.setMetric(KEY_ANALYTICS_SAMPLE_RATE, ((Boolean) value) ? 1 : 0); return true; } } return false; } - public static void applyNamingConvention(AgentSpan span) { + public static void applyNamingConvention(DatadogSpan span) { // Check if span operation name is unchanged from its default value - if (span.getOperationName() == SPAN_KIND_INTERNAL) { + if (SPAN_KIND_INTERNAL.equals(span.getOperationName())) { span.setOperationName(computeOperationName(span).toLowerCase(ROOT)); } } - private static String computeOperationName(AgentSpan span) { - Object spanKingTag = span.getTag(SPAN_KIND); + private static String computeOperationName(DatadogSpan span) { + Object spanKingTag = span.getTag(DatadogTracingConstants.Tags.KEY_SPAN_KIND); SpanKind spanKind = spanKingTag instanceof String ? toOtelSpanKind((String) spanKingTag) : INTERNAL; /* @@ -210,7 +210,7 @@ private static String computeOperationName(AgentSpan span) { } @Nullable - private static String getStringAttribute(AgentSpan span, String key) { + private static String getStringAttribute(DatadogSpan span, String key) { Object tag = span.getTag(key); if (tag == null) { return null; diff --git a/features/dd-sdk-android-trace-otel/src/main/java/com/datadog/opentelemetry/trace/OtelExtractedContext.java b/features/dd-sdk-android-trace-otel/src/main/java/com/datadog/opentelemetry/trace/OtelExtractedContext.java index b68535f329..32da1b92c2 100644 --- a/features/dd-sdk-android-trace-otel/src/main/java/com/datadog/opentelemetry/trace/OtelExtractedContext.java +++ b/features/dd-sdk-android-trace-otel/src/main/java/com/datadog/opentelemetry/trace/OtelExtractedContext.java @@ -6,35 +6,38 @@ package com.datadog.opentelemetry.trace; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + import com.datadog.android.api.InternalLogger; -import com.datadog.trace.api.DDSpanId; -import com.datadog.trace.api.DDTraceId; -import com.datadog.trace.api.sampling.PrioritySampling; -import com.datadog.trace.bootstrap.instrumentation.api.AgentSpan; -import com.datadog.trace.bootstrap.instrumentation.api.AgentTrace; -import com.datadog.trace.bootstrap.instrumentation.api.AgentTracer; -import com.datadog.trace.bootstrap.instrumentation.api.PathwayContext; +import com.datadog.android.trace.api.DatadogTracingConstants; +import com.datadog.android.trace.api.span.DatadogSpanContext; +import com.datadog.android.trace.api.trace.DatadogTraceId; +import com.datadog.android.trace.internal.DatadogTraceExtKt; +import com.datadog.android.trace.internal.DatadogTracingToolkit; + +import java.util.Collections; +import java.util.Locale; +import java.util.Map; import io.opentelemetry.api.trace.Span; import io.opentelemetry.api.trace.SpanContext; import io.opentelemetry.context.Context; -import java.util.Locale; -import java.util.Map; - -public class OtelExtractedContext implements AgentSpan.Context { - private final DDTraceId traceId; +public class OtelExtractedContext implements DatadogSpanContext { + private final DatadogTraceId traceId; private final long spanId; private final int prioritySampling; private OtelExtractedContext(SpanContext context) { - this.traceId = DDTraceId.fromHex(context.getTraceId()); - this.spanId = DDSpanId.fromHex(context.getSpanId()); - this.prioritySampling = - context.isSampled() ? PrioritySampling.SAMPLER_KEEP : PrioritySampling.UNSET; + traceId = DatadogTraceExtKt.fromHex(DatadogTraceId.Companion, context.getTraceId()); + spanId = DatadogTracingToolkit.spanIdConverter.fromHex(context.getSpanId()); + prioritySampling = context.isSampled() + ? DatadogTracingConstants.PrioritySampling.SAMPLER_KEEP + : DatadogTracingConstants.PrioritySampling.UNSET; } - public static AgentSpan.Context extract(Context context, InternalLogger logger) { + public static DatadogSpanContext extract(Context context, InternalLogger logger) { Span span = Span.fromContext(context); SpanContext spanContext = span.getSpanContext(); if (spanContext instanceof OtelSpanContext) { @@ -57,33 +60,37 @@ public static AgentSpan.Context extract(Context context, InternalLogger logger) return null; } + @NonNull @Override - public DDTraceId getTraceId() { - return this.traceId; + public DatadogTraceId getTraceId() { + return traceId; } @Override public long getSpanId() { - return this.spanId; + return spanId; } @Override - public AgentTrace getTrace() { - return AgentTracer.NoopAgentTrace.INSTANCE; + public int getSamplingPriority() { + return prioritySampling; } + @NonNull @Override - public int getSamplingPriority() { - return this.prioritySampling; + public Map getTags() { + // Do nothing + return Collections.emptyMap(); } @Override - public Iterable> baggageItems() { - return null; + public boolean setSamplingPriority(int samplingPriority) { + // Do nothing + return false; } @Override - public PathwayContext getPathwayContext() { - return null; + public void setMetric(@Nullable CharSequence key, double value) { + // Do nothing } } diff --git a/features/dd-sdk-android-trace-otel/src/main/java/com/datadog/opentelemetry/trace/OtelSpan.java b/features/dd-sdk-android-trace-otel/src/main/java/com/datadog/opentelemetry/trace/OtelSpan.java index c717fc1184..fe509d99e3 100644 --- a/features/dd-sdk-android-trace-otel/src/main/java/com/datadog/opentelemetry/trace/OtelSpan.java +++ b/features/dd-sdk-android-trace-otel/src/main/java/com/datadog/opentelemetry/trace/OtelSpan.java @@ -6,18 +6,24 @@ package com.datadog.opentelemetry.trace; -import static com.datadog.trace.api.ConfigDefaults.DEFAULT_ASYNC_PROPAGATING; +import static com.datadog.android.trace.api.DatadogTracingConstants.DEFAULT_ASYNC_PROPAGATING; import static com.datadog.opentelemetry.trace.OtelConventions.applyNamingConvention; import static com.datadog.opentelemetry.trace.OtelConventions.applyReservedAttribute; import static io.opentelemetry.api.trace.StatusCode.ERROR; import static io.opentelemetry.api.trace.StatusCode.OK; import static io.opentelemetry.api.trace.StatusCode.UNSET; -import com.datadog.trace.bootstrap.instrumentation.api.AgentScope; -import com.datadog.trace.bootstrap.instrumentation.api.AgentSpan; -import com.datadog.trace.bootstrap.instrumentation.api.AgentTracer; -import com.datadog.trace.bootstrap.instrumentation.api.ErrorPriorities; -import com.datadog.trace.bootstrap.instrumentation.api.ScopeSource; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.datadog.android.trace.api.DatadogTracingConstants; +import com.datadog.android.trace.api.scope.DatadogScope; +import com.datadog.android.trace.api.span.DatadogSpan; +import com.datadog.android.trace.api.span.DatadogSpanContext; +import com.datadog.android.trace.api.tracer.DatadogTracer; + +import java.util.List; +import java.util.concurrent.TimeUnit; import io.opentelemetry.api.common.AttributeKey; import io.opentelemetry.api.common.Attributes; @@ -26,17 +32,15 @@ import io.opentelemetry.api.trace.StatusCode; import io.opentelemetry.api.trace.TraceFlags; import io.opentelemetry.api.trace.TraceState; -import java.util.List; -import java.util.concurrent.TimeUnit; public class OtelSpan implements Span { - private final AgentSpan delegate; + private final DatadogSpan delegate; private StatusCode statusCode; private boolean recording; - private final AgentTracer.TracerAPI agentTracer; + private final DatadogTracer agentTracer; - public OtelSpan(AgentSpan delegate, AgentTracer.TracerAPI agentTracer) { + public OtelSpan(DatadogSpan delegate, DatadogTracer agentTracer) { this.delegate = delegate; this.statusCode = UNSET; this.recording = true; @@ -48,7 +52,7 @@ public static Span invalid() { } @Override - public Span setAttribute(AttributeKey key, T value) { + public Span setAttribute(@NonNull AttributeKey key, @Nullable T value) { if (this.recording && !applyReservedAttribute(this.delegate, key, value)) { switch (key.getType()) { case STRING_ARRAY: @@ -110,7 +114,7 @@ public StatusCode getStatusCode() { public Span recordException(Throwable exception, Attributes additionalAttributes) { if (this.recording) { // Store exception as span tags as span events are not supported yet - this.delegate.addThrowable(exception, ErrorPriorities.UNSET); + this.delegate.addThrowable(exception, DatadogTracingConstants.ErrorPriorities.UNSET); } return this; } @@ -147,11 +151,15 @@ public boolean isRecording() { return this.recording; } - public AgentScope activate() { - return agentTracer.activateSpan(this.delegate, ScopeSource.INSTRUMENTATION, DEFAULT_ASYNC_PROPAGATING); + public DatadogScope activate() { + return agentTracer.activateSpan(this.delegate, DEFAULT_ASYNC_PROPAGATING); + } + + public DatadogSpan getDatadogSpan() { + return this.delegate; } - public AgentSpan.Context getAgentSpanContext() { + public DatadogSpanContext getDatadogSpanContext() { return this.delegate.context(); } diff --git a/features/dd-sdk-android-trace-otel/src/main/java/com/datadog/opentelemetry/trace/OtelSpanBuilder.java b/features/dd-sdk-android-trace-otel/src/main/java/com/datadog/opentelemetry/trace/OtelSpanBuilder.java index c42698684f..770c1fa7c1 100644 --- a/features/dd-sdk-android-trace-otel/src/main/java/com/datadog/opentelemetry/trace/OtelSpanBuilder.java +++ b/features/dd-sdk-android-trace-otel/src/main/java/com/datadog/opentelemetry/trace/OtelSpanBuilder.java @@ -6,24 +6,29 @@ package com.datadog.opentelemetry.trace; -import static com.datadog.trace.api.DDTags.ANALYTICS_SAMPLE_RATE; -import static com.datadog.trace.bootstrap.instrumentation.api.Tags.SPAN_KIND; import static com.datadog.opentelemetry.trace.OtelConventions.ANALYTICS_EVENT_SPECIFIC_ATTRIBUTES; import static com.datadog.opentelemetry.trace.OtelConventions.OPERATION_NAME_SPECIFIC_ATTRIBUTE; import static com.datadog.opentelemetry.trace.OtelConventions.toSpanKindTagValue; import static com.datadog.opentelemetry.trace.OtelExtractedContext.extract; -import static io.opentelemetry.api.trace.SpanKind.INTERNAL; import static java.lang.Boolean.parseBoolean; import static java.util.Locale.ROOT; +import static io.opentelemetry.api.trace.SpanKind.INTERNAL; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import com.datadog.android.api.InternalLogger; import com.datadog.android.api.context.DatadogContext; import com.datadog.android.internal.concurrent.CompletableFuture; +import com.datadog.android.trace.api.DatadogTracingConstants; +import com.datadog.android.trace.api.span.DatadogSpan; +import com.datadog.android.trace.api.span.DatadogSpanBuilder; +import com.datadog.android.trace.api.span.DatadogSpanContext; +import com.datadog.android.trace.api.tracer.DatadogTracer; import com.datadog.android.trace.internal.SpanAttributes; -import com.datadog.trace.bootstrap.instrumentation.api.AgentSpan; -import com.datadog.trace.bootstrap.instrumentation.api.AgentTracer; + +import java.util.List; +import java.util.concurrent.TimeUnit; import io.opentelemetry.api.common.AttributeKey; import io.opentelemetry.api.common.Attributes; @@ -33,12 +38,9 @@ import io.opentelemetry.api.trace.SpanKind; import io.opentelemetry.context.Context; -import java.util.List; -import java.util.concurrent.TimeUnit; - public class OtelSpanBuilder implements SpanBuilder { - private final AgentTracer.SpanBuilder delegate; - private final AgentTracer.TracerAPI agentTracer; + private final DatadogSpanBuilder delegate; + private final DatadogTracer agentTracer; private boolean spanKindSet; /** @@ -57,8 +59,8 @@ public class OtelSpanBuilder implements SpanBuilder { private final InternalLogger logger; public OtelSpanBuilder( - AgentTracer.SpanBuilder delegate, - AgentTracer.TracerAPI agentTracer, + DatadogSpanBuilder delegate, + DatadogTracer agentTracer, @NonNull InternalLogger logger) { this.delegate = delegate; this.spanKindSet = false; @@ -70,16 +72,16 @@ public OtelSpanBuilder( @Override public SpanBuilder setParent(Context context) { - AgentSpan.Context extractedContext = extract(context, logger); + DatadogSpanContext extractedContext = extract(context, logger); if (extractedContext != null) { - this.delegate.asChildOf(extractedContext); + this.delegate.withParentContext(extractedContext); } return this; } @Override public SpanBuilder setNoParent() { - this.delegate.asChildOf(null); + this.delegate.withParentContext(null); this.delegate.ignoreActiveSpan(); return this; } @@ -101,7 +103,7 @@ public SpanBuilder addLink(SpanContext spanContext, Attributes attributes) { } @Override - public SpanBuilder setAttribute(String key, String value) { + public SpanBuilder setAttribute(@NonNull String key, @Nullable String value) { // Check reserved attributes if (OPERATION_NAME_SPECIFIC_ATTRIBUTE.equals(key) && value != null) { this.overriddenOperationName = value.toLowerCase(ROOT); @@ -111,24 +113,24 @@ public SpanBuilder setAttribute(String key, String value) { return this; } // Store as object to prevent delegate to remove tag when value is empty - this.delegate.withTag(key, (Object) value); + this.delegate.withTag(key, value); return this; } @Override - public SpanBuilder setAttribute(String key, long value) { + public SpanBuilder setAttribute(@NonNull String key, long value) { this.delegate.withTag(key, value); return this; } @Override - public SpanBuilder setAttribute(String key, double value) { + public SpanBuilder setAttribute(@NonNull String key, double value) { this.delegate.withTag(key, value); return this; } @Override - public SpanBuilder setAttribute(String key, boolean value) { + public SpanBuilder setAttribute(@NonNull String key, boolean value) { // Check reserved attributes if (ANALYTICS_EVENT_SPECIFIC_ATTRIBUTES.equals(key)) { this.overriddenAnalyticsSampleRate = value ? 1 : 0; @@ -139,7 +141,7 @@ public SpanBuilder setAttribute(String key, boolean value) { } @Override - public SpanBuilder setAttribute(AttributeKey key, T value) { + public SpanBuilder setAttribute(AttributeKey key, @Nullable T value) { switch (key.getType()) { case STRING_ARRAY: case BOOLEAN_ARRAY: @@ -164,18 +166,10 @@ public SpanBuilder setAttribute(AttributeKey key, T value) { return this; } - /** - * This is internal method and should not be used outside of SDK. - */ - public SpanBuilder setLazyDatadogContext(CompletableFuture context) { - this.delegate.withTag(SpanAttributes.DATADOG_INITIAL_CONTEXT, context); - return this; - } - @Override - public SpanBuilder setSpanKind(SpanKind spanKind) { + public SpanBuilder setSpanKind(@Nullable SpanKind spanKind) { if (spanKind != null) { - this.delegate.withTag(SPAN_KIND, toSpanKindTagValue(spanKind)); + this.delegate.withTag(DatadogTracingConstants.Tags.KEY_SPAN_KIND, toSpanKindTagValue(spanKind)); this.spanKindSet = true; } return this; @@ -193,13 +187,13 @@ public Span startSpan() { if (!this.spanKindSet) { setSpanKind(INTERNAL); } - AgentSpan delegate = this.delegate.start(); + DatadogSpan delegate = this.delegate.start(); // Apply overrides if (this.overriddenOperationName != null) { delegate.setOperationName(this.overriddenOperationName); } if (this.overriddenAnalyticsSampleRate != -1) { - delegate.setMetric(ANALYTICS_SAMPLE_RATE, this.overriddenAnalyticsSampleRate); + delegate.setMetric(DatadogTracingConstants.Tags.KEY_ANALYTICS_SAMPLE_RATE, this.overriddenAnalyticsSampleRate); } return new OtelSpan(delegate, agentTracer); } diff --git a/features/dd-sdk-android-trace-otel/src/main/java/com/datadog/opentelemetry/trace/OtelSpanContext.java b/features/dd-sdk-android-trace-otel/src/main/java/com/datadog/opentelemetry/trace/OtelSpanContext.java index 8ed0e0a7ad..b06e086036 100644 --- a/features/dd-sdk-android-trace-otel/src/main/java/com/datadog/opentelemetry/trace/OtelSpanContext.java +++ b/features/dd-sdk-android-trace-otel/src/main/java/com/datadog/opentelemetry/trace/OtelSpanContext.java @@ -8,14 +8,16 @@ import androidx.annotation.VisibleForTesting; -import com.datadog.trace.api.DDSpanId; -import com.datadog.trace.bootstrap.instrumentation.api.AgentSpan; +import com.datadog.android.trace.api.span.DatadogSpan; +import com.datadog.android.trace.api.span.DatadogSpanContext; +import com.datadog.android.trace.internal.DatadogTracingToolkit; + import io.opentelemetry.api.trace.SpanContext; import io.opentelemetry.api.trace.TraceFlags; import io.opentelemetry.api.trace.TraceState; public class OtelSpanContext implements SpanContext { - final AgentSpan.Context delegate; + final DatadogSpanContext delegate; private final boolean sampled; private final boolean remote; private final TraceState traceState; @@ -23,29 +25,29 @@ public class OtelSpanContext implements SpanContext { private String spanId; public OtelSpanContext( - AgentSpan.Context delegate, boolean sampled, boolean remote, TraceState traceState) { + DatadogSpanContext delegate, boolean sampled, boolean remote, TraceState traceState) { this.delegate = delegate; this.sampled = sampled; this.remote = remote; this.traceState = traceState; } - public static SpanContext fromLocalSpan(AgentSpan span) { - AgentSpan.Context delegate = span.context(); - AgentSpan localRootSpan = span.getLocalRootSpan(); - Integer samplingPriority = localRootSpan.getSamplingPriority(); + public static SpanContext fromLocalSpan(DatadogSpan span) { + DatadogSpanContext delegate = span.context(); + DatadogSpan localRootSpan = span.getLocalRootSpan(); + Integer samplingPriority = localRootSpan != null ? localRootSpan.getSamplingPriority() : null; boolean sampled = samplingPriority != null && samplingPriority > 0; return new OtelSpanContext(delegate, sampled, false, TraceState.getDefault()); } - public static SpanContext fromRemote(AgentSpan.Context extracted, TraceState traceState) { + public static SpanContext fromRemote(DatadogSpanContext extracted, TraceState traceState) { return new OtelSpanContext(extracted, extracted.getSamplingPriority() > 0, true, traceState); } @Override public String getTraceId() { if (this.traceId == null) { - this.traceId = this.delegate.getTraceId().toHexString(); + traceId = delegate.getTraceId().toHexString(); } return this.traceId; } @@ -53,13 +55,13 @@ public String getTraceId() { @Override public String getSpanId() { if (this.spanId == null) { - this.spanId = DDSpanId.toHexStringPadded(this.delegate.getSpanId()); + this.spanId = DatadogTracingToolkit.spanIdConverter.toHexStringPadded(this.delegate.getSpanId()); } return this.spanId; } @VisibleForTesting - public AgentSpan.Context getDelegate() { + public DatadogSpanContext getDelegate() { return delegate; } diff --git a/features/dd-sdk-android-trace-otel/src/main/java/com/datadog/opentelemetry/trace/OtelSpanLink.java b/features/dd-sdk-android-trace-otel/src/main/java/com/datadog/opentelemetry/trace/OtelSpanLink.java index d09a3b61aa..3d80af3b81 100644 --- a/features/dd-sdk-android-trace-otel/src/main/java/com/datadog/opentelemetry/trace/OtelSpanLink.java +++ b/features/dd-sdk-android-trace-otel/src/main/java/com/datadog/opentelemetry/trace/OtelSpanLink.java @@ -6,67 +6,119 @@ package com.datadog.opentelemetry.trace; -import com.datadog.trace.api.DDSpanId; -import com.datadog.trace.api.DDTraceId; -import com.datadog.trace.bootstrap.instrumentation.api.SpanLink; -import com.datadog.trace.bootstrap.instrumentation.api.SpanLinkAttributes; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.datadog.android.trace.api.span.DatadogSpanLink; +import com.datadog.android.trace.api.trace.DatadogTraceId; +import com.datadog.android.trace.internal.DatadogTraceExtKt; +import com.datadog.android.trace.internal.DatadogTracingToolkit; import com.datadog.opentelemetry.context.propagation.TraceStateHelper; -import io.opentelemetry.api.trace.SpanContext; + +import java.util.Collections; +import java.util.HashMap; import java.util.List; +import java.util.Map; + +import io.opentelemetry.api.trace.SpanContext; + +public class OtelSpanLink implements DatadogSpanLink { + private final long spanId; + private final boolean sampled; + private final String traceState; + private final DatadogTraceId traceId; + private final Map attributes; -public class OtelSpanLink extends SpanLink { public OtelSpanLink(SpanContext spanContext) { this(spanContext, io.opentelemetry.api.common.Attributes.empty()); } public OtelSpanLink(SpanContext spanContext, io.opentelemetry.api.common.Attributes attributes) { - super( - DDTraceId.fromHex(spanContext.getTraceId()), - DDSpanId.fromHex(spanContext.getSpanId()), - spanContext.isSampled() ? SAMPLED_FLAG : DEFAULT_FLAGS, - TraceStateHelper.encodeHeader(spanContext.getTraceState()), - convertAttributes(attributes)); + traceId = DatadogTraceExtKt.fromHex(DatadogTraceId.Companion, spanContext.getTraceId()); + spanId = DatadogTracingToolkit.spanIdConverter.fromHex(spanContext.getSpanId()); + sampled = spanContext.isSampled(); + traceState = TraceStateHelper.encodeHeader(spanContext.getTraceState()); + this.attributes = convertAttributes(attributes); + } + + private static Map convertAttributes(io.opentelemetry.api.common.Attributes attributes) { + if (attributes.isEmpty()) return Collections.emptyMap(); + + + Map bundle = new HashMap<>(); + attributes.forEach((attributeKey, value) -> { + switch (attributeKey.getType()) { + case STRING: + bundle.put(attributeKey.getKey(), (String) value); + break; + case BOOLEAN: + bundle.put(attributeKey.getKey(), value.toString()); + break; + case LONG: + bundle.put(attributeKey.getKey(), Long.toString((long) value)); + break; + case DOUBLE: + bundle.put(attributeKey.getKey(), Double.toString((double) value)); + break; + case STRING_ARRAY: + // noinspection unchecked + putArray(bundle, attributeKey.getKey(), (List) value); + break; + case BOOLEAN_ARRAY: + //noinspection unchecked,DuplicateBranchesInSwitch + putArray(bundle, attributeKey.getKey(), (List) value); + break; + case LONG_ARRAY: + //noinspection unchecked,DuplicateBranchesInSwitch + putArray(bundle, attributeKey.getKey(), (List) value); + break; + case DOUBLE_ARRAY: + //noinspection unchecked,DuplicateBranchesInSwitch + putArray(bundle, attributeKey.getKey(), (List) value); + break; + } + }); + + return bundle; + } + + @NonNull + @Override + public DatadogTraceId getTraceId() { + return traceId; + } + + @Override + public long getSpanId() { + return spanId; + } + + @Nullable + @Override + public Map getAttributes() { + return attributes; + } + + @Override + public boolean getSampled() { + return sampled; + } + + @NonNull + @Override + public String getTraceStrace() { + return traceState; } - private static Attributes convertAttributes(io.opentelemetry.api.common.Attributes attributes) { - if (attributes.isEmpty()) { - return SpanLinkAttributes.EMPTY; + private static void putArray(Map attributes, String key, List array) { + if (key != null && array != null) { + for (int index = 0; index < array.size(); index++) { + Object value = array.get(index); + if (value != null) { + attributes.put(key + "." + index, value.toString()); + } + } } - SpanLinkAttributes.Builder builder = SpanLinkAttributes.builder(); - attributes.forEach( - (attributeKey, value) -> { - String key = attributeKey.getKey(); - switch (attributeKey.getType()) { - case STRING: - builder.put(key, (String) value); - break; - case BOOLEAN: - builder.put(key, (boolean) value); - break; - case LONG: - builder.put(key, (long) value); - break; - case DOUBLE: - builder.put(key, (double) value); - break; - case STRING_ARRAY: - //noinspection unchecked - builder.putStringArray(key, (List) value); - break; - case BOOLEAN_ARRAY: - //noinspection unchecked - builder.putBooleanArray(key, (List) value); - break; - case LONG_ARRAY: - //noinspection unchecked - builder.putLongArray(key, (List) value); - break; - case DOUBLE_ARRAY: - //noinspection unchecked - builder.putDoubleArray(key, (List) value); - break; - } - }); - return builder.build(); } } diff --git a/features/dd-sdk-android-trace-otel/src/main/java/com/datadog/opentelemetry/trace/OtelTracer.java b/features/dd-sdk-android-trace-otel/src/main/java/com/datadog/opentelemetry/trace/OtelTracer.java index 36dea23289..e29687c391 100644 --- a/features/dd-sdk-android-trace-otel/src/main/java/com/datadog/opentelemetry/trace/OtelTracer.java +++ b/features/dd-sdk-android-trace-otel/src/main/java/com/datadog/opentelemetry/trace/OtelTracer.java @@ -9,8 +9,9 @@ import androidx.annotation.NonNull; import com.datadog.android.api.InternalLogger; +import com.datadog.android.trace.api.span.DatadogSpanBuilder; +import com.datadog.android.trace.api.tracer.DatadogTracer; import com.datadog.opentelemetry.compat.function.Function; -import com.datadog.trace.bootstrap.instrumentation.api.AgentTracer; import io.opentelemetry.api.trace.SpanBuilder; import io.opentelemetry.api.trace.Tracer; @@ -20,7 +21,7 @@ public class OtelTracer implements Tracer { private static final Function NO_OP_DECORATOR = spanBuilder -> spanBuilder; @NonNull - private final AgentTracer.TracerAPI tracer; + private final DatadogTracer tracer; @NonNull private final String instrumentationScopeName; @NonNull @@ -31,7 +32,7 @@ public class OtelTracer implements Tracer { public OtelTracer( @NonNull String instrumentationScopeName, - @NonNull AgentTracer.TracerAPI tracer, + @NonNull DatadogTracer tracer, @NonNull InternalLogger logger, @NonNull Function spanBuilderDecorator) { this.instrumentationScopeName = instrumentationScopeName; @@ -42,17 +43,17 @@ public OtelTracer( public OtelTracer( @NonNull String instrumentationScopeName, - @NonNull AgentTracer.TracerAPI tracer, + @NonNull DatadogTracer tracer, @NonNull InternalLogger logger) { this(instrumentationScopeName, tracer, logger, NO_OP_DECORATOR); } @Override - public SpanBuilder spanBuilder(String spanName) { - AgentTracer.SpanBuilder delegate = - this.tracer + public SpanBuilder spanBuilder(@NonNull String spanName) { + DatadogSpanBuilder delegate = tracer .buildSpan(instrumentationScopeName, OtelConventions.SPAN_KIND_INTERNAL) .withResourceName(spanName); + return spanBuilderDecorator.apply(new OtelSpanBuilder(delegate, tracer, logger)); } } diff --git a/features/dd-sdk-android-trace-otel/src/main/java/com/datadog/opentelemetry/trace/OtelTracerBuilder.java b/features/dd-sdk-android-trace-otel/src/main/java/com/datadog/opentelemetry/trace/OtelTracerBuilder.java index f7cd33d38f..10d1931bcb 100644 --- a/features/dd-sdk-android-trace-otel/src/main/java/com/datadog/opentelemetry/trace/OtelTracerBuilder.java +++ b/features/dd-sdk-android-trace-otel/src/main/java/com/datadog/opentelemetry/trace/OtelTracerBuilder.java @@ -10,8 +10,8 @@ import androidx.annotation.NonNull; import com.datadog.android.api.InternalLogger; +import com.datadog.android.trace.api.tracer.DatadogTracer; import com.datadog.opentelemetry.compat.function.Function; -import com.datadog.trace.bootstrap.instrumentation.api.AgentTracer; import io.opentelemetry.api.trace.SpanBuilder; import io.opentelemetry.api.trace.Tracer; @@ -22,24 +22,19 @@ public class OtelTracerBuilder implements TracerBuilder { private final String instrumentationScopeName; @NonNull - private final AgentTracer.TracerAPI coreTracer; + private final DatadogTracer datadogTracer; @NonNull private final InternalLogger logger; - @NonNull - private final Function spanBuilderDecorator; - public OtelTracerBuilder( @NonNull String instrumentationScopeName, - @NonNull AgentTracer.TracerAPI coreTracer, - @NonNull InternalLogger logger, - @NonNull Function spanBuilderDecorator) { - this.coreTracer = coreTracer; + @NonNull DatadogTracer datadogTracer, + @NonNull InternalLogger logger) { + this.datadogTracer = datadogTracer; this.instrumentationScopeName = instrumentationScopeName; this.logger = logger; - this.spanBuilderDecorator = spanBuilderDecorator; } @Override @@ -56,6 +51,6 @@ public TracerBuilder setInstrumentationVersion(String instrumentationScopeVersio @Override public Tracer build() { - return new OtelTracer(this.instrumentationScopeName, this.coreTracer, this.logger, this.spanBuilderDecorator); + return new OtelTracer(this.instrumentationScopeName, this.datadogTracer, this.logger); } } \ No newline at end of file diff --git a/features/dd-sdk-android-trace-otel/src/main/kotlin/com/datadog/android/trace/opentelemetry/DatadogOpenTelemetry.kt b/features/dd-sdk-android-trace-otel/src/main/kotlin/com/datadog/android/trace/opentelemetry/DatadogOpenTelemetry.kt new file mode 100644 index 0000000000..4a274a8399 --- /dev/null +++ b/features/dd-sdk-android-trace-otel/src/main/kotlin/com/datadog/android/trace/opentelemetry/DatadogOpenTelemetry.kt @@ -0,0 +1,24 @@ +/* + * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. + * This product includes software developed at Datadog (https://www.datadoghq.com/). + * Copyright 2016-Present Datadog, Inc. + */ +package com.datadog.android.trace.opentelemetry + +import io.opentelemetry.api.OpenTelemetry +import io.opentelemetry.api.trace.TracerProvider +import io.opentelemetry.context.propagation.ContextPropagators + +/** + * Default implementation of the OpenTelemetry interface that integrates with Datadog. + */ +class DatadogOpenTelemetry(serviceName: String) : OpenTelemetry { + + private val tracerProvider = OtelTracerProvider.Builder() + .setService(serviceName) + .build() + + override fun getTracerProvider(): TracerProvider = tracerProvider + + override fun getPropagators(): ContextPropagators = ContextPropagators.noop() +} diff --git a/features/dd-sdk-android-trace-otel/src/main/kotlin/com/datadog/android/trace/opentelemetry/OtelTracerProvider.kt b/features/dd-sdk-android-trace-otel/src/main/kotlin/com/datadog/android/trace/opentelemetry/OtelTracerProvider.kt index 02a512060a..b4dda27ceb 100644 --- a/features/dd-sdk-android-trace-otel/src/main/kotlin/com/datadog/android/trace/opentelemetry/OtelTracerProvider.kt +++ b/features/dd-sdk-android-trace-otel/src/main/kotlin/com/datadog/android/trace/opentelemetry/OtelTracerProvider.kt @@ -11,31 +11,22 @@ import androidx.annotation.IntRange import com.datadog.android.Datadog import com.datadog.android.api.InternalLogger import com.datadog.android.api.SdkCore -import com.datadog.android.api.context.DatadogContext import com.datadog.android.api.feature.Feature import com.datadog.android.api.feature.FeatureSdkCore -import com.datadog.android.internal.concurrent.CompletableFuture +import com.datadog.android.trace.DatadogTracing import com.datadog.android.trace.InternalCoreWriterProvider import com.datadog.android.trace.TracingHeaderType +import com.datadog.android.trace.api.tracer.DatadogTracer +import com.datadog.android.trace.internal.DatadogTracingToolkit +import com.datadog.android.trace.internal.DatadogTracingToolkit.setTraceId128BitGenerationEnabled import com.datadog.android.trace.opentelemetry.internal.DatadogContextStorageWrapper -import com.datadog.android.trace.opentelemetry.internal.NoOpCoreTracerWriter -import com.datadog.android.trace.opentelemetry.internal.addActiveTraceToContext import com.datadog.android.trace.opentelemetry.internal.executeIfJavaFunctionPackageExists -import com.datadog.android.trace.opentelemetry.internal.removeActiveTraceFromContext -import com.datadog.opentelemetry.trace.OtelSpanBuilder import com.datadog.opentelemetry.trace.OtelTracerBuilder -import com.datadog.trace.api.IdGenerationStrategy -import com.datadog.trace.api.config.TracerConfig -import com.datadog.trace.api.scopemanager.ScopeListener -import com.datadog.trace.bootstrap.instrumentation.api.AgentTracer -import com.datadog.trace.core.CoreTracer -import io.opentelemetry.api.trace.SpanBuilder import io.opentelemetry.api.trace.Tracer import io.opentelemetry.api.trace.TracerBuilder import io.opentelemetry.api.trace.TracerProvider import io.opentelemetry.context.ContextStorage import java.util.Locale -import java.util.Properties /** * A class enabling Datadog OpenTelemetry features. @@ -46,10 +37,8 @@ import java.util.Properties * */ class OtelTracerProvider internal constructor( - private val sdkCore: FeatureSdkCore, - private val coreTracer: AgentTracer.TracerAPI, - private val internalLogger: InternalLogger, - private val bundleWithRumEnabled: Boolean + private val datadogTracer: DatadogTracer, + private val internalLogger: InternalLogger ) : TracerProvider { private val tracers: MutableMap = mutableMapOf() @@ -92,9 +81,8 @@ class OtelTracerProvider internal constructor( val resolvedInstrumentationScopeName = resolveInstrumentationScopeName(instrumentationScopeName) return OtelTracerBuilder( resolvedInstrumentationScopeName, - coreTracer, - internalLogger, - resolveSpanBuilderDecorator() + datadogTracer, + internalLogger ) } @@ -104,11 +92,15 @@ class OtelTracerProvider internal constructor( class Builder internal constructor( private val sdkCore: FeatureSdkCore ) { - - private var tracingHeaderTypes: Set = - setOf(TracingHeaderType.DATADOG, TracingHeaderType.TRACECONTEXT) - private var sampleRate: Double? = null - private var traceRateLimit = Int.MAX_VALUE + private val builderDelegate = DatadogTracing.newTracerBuilder(sdkCore) + .withPartialFlushMinSpans(DEFAULT_PARTIAL_MIN_FLUSH) + .withTracingHeadersTypes( + setOf( + TracingHeaderType.DATADOG, + TracingHeaderType.TRACECONTEXT + ) + ) + .also { setTraceId128BitGenerationEnabled(it) } private var serviceName: String = "" get() { @@ -124,9 +116,6 @@ class OtelTracerProvider internal constructor( service } } - private var partialFlushThreshold = DEFAULT_PARTIAL_MIN_FLUSH - private val globalTags: MutableMap = mutableMapOf() - private var bundleWithRumEnabled: Boolean = true /** * @param sdkCore SDK instance to bind to. If not provided, default instance will be used. @@ -144,22 +133,9 @@ class OtelTracerProvider internal constructor( sdkCore.internalLogger, TracerProvider.noop() ) { - val tracingFeature = sdkCore.getFeature(Feature.TRACING_FEATURE_NAME) - ?.unwrap() + val tracingFeature = sdkCore.getFeature(Feature.TRACING_FEATURE_NAME)?.unwrap() val internalCoreWriterProvider = tracingFeature as? InternalCoreWriterProvider - if (tracingFeature == null) { - sdkCore.internalLogger.log( - InternalLogger.Level.ERROR, - InternalLogger.Target.USER, - { TRACING_NOT_ENABLED_ERROR_MESSAGE } - ) - } else if (internalCoreWriterProvider == null) { - sdkCore.internalLogger.log( - InternalLogger.Level.ERROR, - InternalLogger.Target.MAINTAINER, - { WRITER_PROVIDER_INTERFACE_NOT_IMPLEMENTED_ERROR_MESSAGE } - ) - } else { + if (tracingFeature != null && internalCoreWriterProvider != null) { // update meta for the configuration telemetry reporting, can be done directly from this thread sdkCore.updateFeatureContext(Feature.TRACING_FEATURE_NAME, useContextThread = false) { it[IS_OPENTELEMETRY_ENABLED_CONFIG_KEY] = true @@ -167,37 +143,26 @@ class OtelTracerProvider internal constructor( BuildConfig.OPENTELEMETRY_API_VERSION_NAME } } - val coreTracer = CoreTracer.CoreTracerBuilder(sdkCore.internalLogger) - .withProperties(properties()) - .serviceName(serviceName) - .writer(internalCoreWriterProvider?.getCoreTracerWriter() ?: NoOpCoreTracerWriter()) - .partialFlushMinSpans(partialFlushThreshold) - .idGenerationStrategy(IdGenerationStrategy.fromName("SECURE_RANDOM", true)) - .build() - coreTracer.addScopeListener(object : ScopeListener { - override fun afterScopeActivated() { - val activeSpanContext = coreTracer.activeSpan()?.context() - if (activeSpanContext != null) { - val activeSpanId = activeSpanContext.spanId.toString() - val activeTraceId = activeSpanContext.traceId.toHexString() - sdkCore.addActiveTraceToContext(activeTraceId, activeSpanId) - } - } - override fun afterScopeClosed() { - sdkCore.removeActiveTraceFromContext() - } - }) - OtelTracerProvider(sdkCore, coreTracer, sdkCore.internalLogger, bundleWithRumEnabled) + OtelTracerProvider(createDatadogTracer(), sdkCore.internalLogger) } } + private fun createDatadogTracer(): DatadogTracer { + val datadogTracer = builderDelegate + .withServiceName(serviceName) + .also(DatadogTracingToolkit::setSdkV2Compatible) + .build() + + return datadogTracer + } + /** * Sets the tracing header styles that may be injected by this tracer. * @param headerTypes the list of header types injected (default = datadog style headers) */ fun setTracingHeaderTypes(headerTypes: Set): Builder { - this.tracingHeaderTypes = headerTypes + builderDelegate.withTracingHeadersTypes(headerTypes) return this } @@ -217,7 +182,7 @@ class OtelTracerProvider internal constructor( * @param threshold the threshold value (default = 5) */ fun setPartialFlushThreshold(threshold: Int): Builder { - this.partialFlushThreshold = threshold + builderDelegate.withPartialFlushMinSpans(threshold) return this } @@ -227,7 +192,7 @@ class OtelTracerProvider internal constructor( * @param value the tag value */ fun addTag(key: String, value: String): Builder { - this.globalTags[key] = value + builderDelegate.withTag(key, value) return this } @@ -238,7 +203,7 @@ class OtelTracerProvider internal constructor( fun setSampleRate( @FloatRange(from = 0.0, to = 100.0) sampleRate: Double ): Builder { - this.sampleRate = sampleRate + builderDelegate.withSampleRate(sampleRate) return this } @@ -251,7 +216,7 @@ class OtelTracerProvider internal constructor( fun setTraceRateLimit( @IntRange(from = 1, to = Int.MAX_VALUE.toLong()) traceRateLimit: Int ): Builder { - this.traceRateLimit = traceRateLimit + builderDelegate.setTraceRateLimit(traceRateLimit) return this } @@ -262,42 +227,10 @@ class OtelTracerProvider internal constructor( * @param enabled true by default */ fun setBundleWithRumEnabled(enabled: Boolean): Builder { - bundleWithRumEnabled = enabled + builderDelegate.setBundleWithRumEnabled(enabled) return this } - internal fun properties(): Properties { - val properties = Properties() - properties.setProperty( - TracerConfig.SPAN_TAGS, - globalTags.map { "${it.key}:${it.value}" }.joinToString(",") - ) - properties.setProperty(TracerConfig.TRACE_RATE_LIMIT, traceRateLimit.toString()) - - // In case the sample rate is not set we should not specify it. The agent code under the hood - // will provide different sampler based on this property and also different sampling priorities used - // in the metrics - // -1 MANUAL_DROP User indicated to drop the trace via configuration (sampling rate). - // 0 AUTO_DROP Sampler indicated to drop the trace using a sampling rate provided by the Agent through - // a remote configuration. The Agent API is not used in Android so this `sampling_priority:0` will never - // be used. - // 1 AUTO_KEEP Sampler indicated to keep the trace using a sampling rate from the default configuration - // which right now is 100.0 - // (Default sampling priority value. or in our case no specified sample rate will be considered as 100) - // 2 MANUAL_KEEP User indicated to keep the trace, either manually or via configuration (sampling rate) - sampleRate?.let { - properties.setProperty( - TracerConfig.TRACE_SAMPLE_RATE, - (it / KEEP_ALL_SAMPLE_RATE_PERCENT).toString() - ) - } - val propagationStyles = tracingHeaderTypes.joinToString(",") - properties.setProperty(TracerConfig.PROPAGATION_STYLE_EXTRACT, propagationStyles) - properties.setProperty(TracerConfig.PROPAGATION_STYLE_INJECT, propagationStyles) - - return properties - } - // endregion } @@ -311,26 +244,6 @@ class OtelTracerProvider internal constructor( } } - private fun resolveSpanBuilderDecorator(): (SpanBuilder) -> SpanBuilder { - return if (bundleWithRumEnabled) { - resolveDecoratorWithRumContext() - } else { - NO_OP_SPAN_BUILDER_DECORATOR - } - } - - private fun resolveDecoratorWithRumContext(): (SpanBuilder) -> SpanBuilder = { spanBuilder -> - val rumFeature = sdkCore.getFeature(Feature.RUM_FEATURE_NAME) - if (rumFeature != null) { - val lazyContext = CompletableFuture() - rumFeature.withContext(withFeatureContexts = setOf(Feature.RUM_FEATURE_NAME)) { - lazyContext.complete(it) - } - (spanBuilder as? OtelSpanBuilder)?.setLazyDatadogContext(lazyContext) - } - spanBuilder - } - override fun toString(): String { return "OtelTracerProvider/${super.toString()}" } @@ -360,20 +273,11 @@ class OtelTracerProvider internal constructor( internal const val RUM_SESSION_ID_KEY = "session_id" internal const val RUM_VIEW_ID_KEY = "view_id" internal const val RUM_ACTION_ID_KEY = "action_id" - internal val NO_OP_SPAN_BUILDER_DECORATOR: (SpanBuilder) -> SpanBuilder = { it } internal const val TRACER_ALREADY_EXISTS_WARNING_MESSAGE = "Tracer for %s already exists. Returning existing instance." internal const val DEFAULT_TRACER_NAME = "android" internal const val KEEP_ALL_SAMPLE_RATE_PERCENT = 100.0 - internal const val TRACING_NOT_ENABLED_ERROR_MESSAGE = - "You're trying to create an OtelTracerProvider instance, " + - "but either the SDK was not initialized or the Tracing feature was " + - "not registered. No tracing data will be sent." - - internal const val WRITER_PROVIDER_INTERFACE_NOT_IMPLEMENTED_ERROR_MESSAGE = - "The Tracing feature is not implementing the InternalCoreWriterProvider interface." + - " No tracing data will be sent." internal const val DEFAULT_SERVICE_NAME_IS_MISSING_ERROR_MESSAGE = "Default service name is missing during" + " OtelTracerProvider creation, did you initialize SDK?" diff --git a/features/dd-sdk-android-trace-otel/src/main/kotlin/com/datadog/android/trace/opentelemetry/internal/NoOpCoreTracerWriter.kt b/features/dd-sdk-android-trace-otel/src/main/kotlin/com/datadog/android/trace/opentelemetry/internal/NoOpCoreTracerWriter.kt deleted file mode 100644 index f80ab3acde..0000000000 --- a/features/dd-sdk-android-trace-otel/src/main/kotlin/com/datadog/android/trace/opentelemetry/internal/NoOpCoreTracerWriter.kt +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. - * This product includes software developed at Datadog (https://www.datadoghq.com/). - * Copyright 2016-Present Datadog, Inc. - */ - -package com.datadog.android.trace.opentelemetry.internal - -import com.datadog.trace.common.writer.Writer - -internal class NoOpCoreTracerWriter : Writer { - override fun close() { - } - - override fun write(p0: MutableList?) { - } - - override fun start() { - } - - override fun flush(): Boolean { - return true - } - - override fun incrementDropCounts(p0: Int) { - } -} diff --git a/features/dd-sdk-android-trace-otel/src/main/kotlin/com/datadog/android/trace/opentelemetry/internal/OtelScope.kt b/features/dd-sdk-android-trace-otel/src/main/kotlin/com/datadog/android/trace/opentelemetry/internal/OtelScope.kt index eec4e43896..45a3091e56 100644 --- a/features/dd-sdk-android-trace-otel/src/main/kotlin/com/datadog/android/trace/opentelemetry/internal/OtelScope.kt +++ b/features/dd-sdk-android-trace-otel/src/main/kotlin/com/datadog/android/trace/opentelemetry/internal/OtelScope.kt @@ -6,10 +6,10 @@ package com.datadog.android.trace.opentelemetry.internal -import com.datadog.trace.bootstrap.instrumentation.api.AgentScope +import com.datadog.android.trace.api.scope.DatadogScope import io.opentelemetry.context.Scope -internal class OtelScope(internal val scope: Scope, internal val delegate: AgentScope) : Scope { +internal class OtelScope(internal val scope: Scope, internal val delegate: DatadogScope) : Scope { override fun close() { delegate.close() scope.close() diff --git a/features/dd-sdk-android-trace-otel/src/test/kotlin/com/datadog/android/trace/opentelemetry/OtelTracerBuilderProviderTest.kt b/features/dd-sdk-android-trace-otel/src/test/kotlin/com/datadog/android/trace/opentelemetry/OtelTracerBuilderProviderTest.kt index dd7239c778..ee1a08b8e4 100644 --- a/features/dd-sdk-android-trace-otel/src/test/kotlin/com/datadog/android/trace/opentelemetry/OtelTracerBuilderProviderTest.kt +++ b/features/dd-sdk-android-trace-otel/src/test/kotlin/com/datadog/android/trace/opentelemetry/OtelTracerBuilderProviderTest.kt @@ -14,26 +14,22 @@ import com.datadog.android.api.feature.FeatureScope import com.datadog.android.api.feature.FeatureSdkCore import com.datadog.android.internal.concurrent.CompletableFuture import com.datadog.android.trace.InternalCoreWriterProvider -import com.datadog.android.trace.TracingHeaderType +import com.datadog.android.trace.api.DatadogTracingConstants +import com.datadog.android.trace.api.forceSamplingDecision +import com.datadog.android.trace.api.partialFlushMinSpans +import com.datadog.android.trace.api.resourceName +import com.datadog.android.trace.api.serviceName +import com.datadog.android.trace.api.span.DatadogSpan +import com.datadog.android.trace.api.span.DatadogSpanContext +import com.datadog.android.trace.api.span.DatadogSpanWriter +import com.datadog.android.trace.api.tracer.DatadogTracer +import com.datadog.android.trace.api.tracer.DatadogTracerBuilder import com.datadog.android.trace.internal.SpanAttributes -import com.datadog.android.trace.opentelemetry.internal.NoOpCoreTracerWriter +import com.datadog.android.trace.opentelemetry.utils.forge.Configurator import com.datadog.android.trace.opentelemetry.utils.verifyLog -import com.datadog.android.utils.forge.Configurator import com.datadog.opentelemetry.trace.OtelSpan import com.datadog.opentelemetry.trace.OtelSpanContext -import com.datadog.opentelemetry.trace.OtelTracer import com.datadog.tools.unit.getFieldValue -import com.datadog.tools.unit.setFieldValue -import com.datadog.trace.api.Config -import com.datadog.trace.api.config.TracerConfig -import com.datadog.trace.api.sampling.PrioritySampling -import com.datadog.trace.bootstrap.instrumentation.api.AgentScopeManager -import com.datadog.trace.bootstrap.instrumentation.api.AgentTracer -import com.datadog.trace.bootstrap.instrumentation.api.ScopeSource -import com.datadog.trace.common.writer.Writer -import com.datadog.trace.core.CoreTracer -import com.datadog.trace.core.DDSpan -import com.datadog.trace.core.DDSpanContext import fr.xgouchet.elmyr.Forge import fr.xgouchet.elmyr.annotation.DoubleForgery import fr.xgouchet.elmyr.annotation.Forgery @@ -42,8 +38,10 @@ import fr.xgouchet.elmyr.annotation.StringForgery import fr.xgouchet.elmyr.annotation.StringForgeryType import fr.xgouchet.elmyr.junit5.ForgeConfiguration import fr.xgouchet.elmyr.junit5.ForgeExtension +import io.opentelemetry.api.trace.Span +import io.opentelemetry.api.trace.Tracer +import io.opentelemetry.api.trace.TracerProvider import org.assertj.core.api.Assertions.assertThat -import org.assertj.core.api.Assertions.offset import org.assertj.core.data.Offset import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test @@ -77,6 +75,12 @@ internal class OtelTracerBuilderProviderTest { lateinit var testedOtelTracerProviderBuilder: OtelTracerProvider.Builder lateinit var fakeServiceName: String + val mockDatadogTracerBuilder: DatadogTracerBuilder = mock { + on { withServiceName(any()) } doReturn it + on { withTracingHeadersTypes(any()) } doReturn it + on { withPartialFlushMinSpans(any()) } doReturn it + } + @Mock lateinit var mockTracingFeatureScope: FeatureScope @@ -96,7 +100,7 @@ internal class OtelTracerBuilderProviderTest { lateinit var fakeOperationName: String @Mock - lateinit var mockTraceWriter: Writer + lateinit var mockTraceWriter: DatadogSpanWriter lateinit var fakeRumContext: MutableMap @@ -135,87 +139,6 @@ internal class OtelTracerBuilderProviderTest { // region feature checks - @Test - fun `M log a user error W build { TracingFeature not enabled }`() { - // GIVEN - whenever(mockSdkCore.getFeature(Feature.TRACING_FEATURE_NAME)) doReturn null - - // WHEN - val tracer = testedOtelTracerProviderBuilder.build() - - // THEN - assertThat(tracer).isNotNull - mockInternalLogger.verifyLog( - InternalLogger.Level.ERROR, - InternalLogger.Target.USER, - OtelTracerProvider.TRACING_NOT_ENABLED_ERROR_MESSAGE - ) - } - - @Test - fun `M log a maintainer error W build { TracingFeature not implementing InternalCoreTracerWriterProvider }`() { - // GIVEN - whenever(mockTracingFeatureScope.unwrap()) doReturn mock() - - // WHEN - val tracer = testedOtelTracerProviderBuilder.build() - - // THEN - assertThat(tracer).isNotNull - mockInternalLogger.verifyLog( - InternalLogger.Level.ERROR, - InternalLogger.Target.MAINTAINER, - OtelTracerProvider.WRITER_PROVIDER_INTERFACE_NOT_IMPLEMENTED_ERROR_MESSAGE - ) - } - - @Test - fun `M use a NoOpCoreTracerWriter W build { TracingFeature not enabled }`() { - // GIVEN - whenever(mockSdkCore.getFeature(Feature.TRACING_FEATURE_NAME)) doReturn null - - // WHEN - val tracer = testedOtelTracerProviderBuilder.build() - - // THEN - assertThat(tracer).isNotNull - val coreTracer: CoreTracer = tracer.getFieldValue("coreTracer") - val writer: Writer = coreTracer.getFieldValue("writer") - assertThat(writer).isInstanceOf(NoOpCoreTracerWriter::class.java) - } - - @Test - fun `M use the feature writer W build { TracingFeature enabled }`() { - // WHEN - val tracer = testedOtelTracerProviderBuilder.build() - - // THEN - assertThat(tracer).isNotNull - val coreTracer: CoreTracer = tracer.getFieldValue("coreTracer") - val writer: Writer = coreTracer.getFieldValue("writer") - assertThat(writer).isSameAs(mockTraceWriter) - } - - @Test - fun `M log a user error W build { default service name not available }`() { - // GIVEN - whenever(mockSdkCore.service) doReturn "" - - // WHEN - testedOtelTracerProviderBuilder.build() - - // THEN - mockInternalLogger.verifyLog( - InternalLogger.Level.ERROR, - InternalLogger.Target.USER, - OtelTracerProvider.DEFAULT_SERVICE_NAME_IS_MISSING_ERROR_MESSAGE - ) - } - - // endregion - - // region ID generation - @Test fun `M build tracers which generate Spans with 64 bits long ids W build`( @StringForgery(type = StringForgeryType.ALPHA_NUMERICAL) spanName: String @@ -332,7 +255,7 @@ internal class OtelTracerBuilderProviderTest { span.end() // Then - val agentContext = (span.spanContext as OtelSpanContext).delegate as DDSpanContext + val agentContext = (span.spanContext as OtelSpanContext).delegate as DatadogSpanContext assertThat(agentContext.resourceName).isEqualTo(spanName) } @@ -352,7 +275,7 @@ internal class OtelTracerBuilderProviderTest { span.end() // Then - val agentContext = (span.spanContext as OtelSpanContext).delegate as DDSpanContext + val agentContext = (span.spanContext as OtelSpanContext).delegate as DatadogSpanContext assertThat(agentContext.serviceName).isEqualTo(fakeServiceName) } @@ -373,7 +296,7 @@ internal class OtelTracerBuilderProviderTest { span.end() // Then - val agentContext = (span.spanContext as OtelSpanContext).delegate as DDSpanContext + val agentContext = (span.spanContext as OtelSpanContext).delegate as DatadogSpanContext assertThat(agentContext.serviceName).isEqualTo(fakeCustomServiceName) } @@ -384,7 +307,7 @@ internal class OtelTracerBuilderProviderTest { .tracerBuilder(fakeInstrumentationName).build() // When - val coreTracer: CoreTracer = tracer.getFieldValue("tracer") + val coreTracer: DatadogTracer = tracer.delegate // Then assertThat(coreTracer.partialFlushMinSpans).isEqualTo(OtelTracerProvider.DEFAULT_PARTIAL_MIN_FLUSH) @@ -397,65 +320,12 @@ internal class OtelTracerBuilderProviderTest { .tracerBuilder(fakeInstrumentationName).build() // When - val coreTracer: CoreTracer = tracer.getFieldValue("tracer") + val coreTracer: DatadogTracer = tracer.delegate // Then assertThat(coreTracer.partialFlushMinSpans).isEqualTo(threshold) } - @Test - fun `M set correct propagating style W setting tracing header types`(forge: Forge) { - // Given - val tracingHeaderStyles = forge.aList { aValueFrom(TracingHeaderType::class.java) }.toSet() - val tracerProvider = testedOtelTracerProviderBuilder - .setTracingHeaderTypes(tracingHeaderStyles) - .build() - - // Then - tracerProvider.tracerBuilder(fakeInstrumentationName).build() - val properties = testedOtelTracerProviderBuilder.properties() - - val injectionStyles = properties - .getProperty(TracerConfig.PROPAGATION_STYLE_INJECT) - .toString() - .split(",") - .toSet() - val extractionStyles = properties - .getProperty(TracerConfig.PROPAGATION_STYLE_EXTRACT) - .toString() - .split(",") - .toSet() - - assertThat(injectionStyles).isEqualTo(tracingHeaderStyles.map { it.headerType }.toSet()) - assertThat(extractionStyles).isEqualTo(tracingHeaderStyles.map { it.headerType }.toSet()) - } - - @Test - fun `M use default propagating style W build`() { - // Given - val expectedDefaultPropagationStyles = setOf(TracingHeaderType.DATADOG, TracingHeaderType.TRACECONTEXT) - val tracerProvider = testedOtelTracerProviderBuilder - .build() - - // Then - tracerProvider.tracerBuilder(fakeInstrumentationName).build() - val properties = testedOtelTracerProviderBuilder.properties() - - val injectionStyles = properties - .getProperty(TracerConfig.PROPAGATION_STYLE_INJECT) - .toString() - .split(",") - .toSet() - val extractionStyles = properties - .getProperty(TracerConfig.PROPAGATION_STYLE_EXTRACT) - .toString() - .split(",") - .toSet() - - assertThat(injectionStyles).isEqualTo(expectedDefaultPropagationStyles.map { it.headerType }.toSet()) - assertThat(extractionStyles).isEqualTo(expectedDefaultPropagationStyles.map { it.headerType }.toSet()) - } - @Test fun `M build a valid Tracer with tags W addTag`( @StringForgery operation: String, @@ -471,57 +341,14 @@ internal class OtelTracerBuilderProviderTest { // Then assertThat(tracer).isNotNull() val span = tracer.spanBuilder(operation).startSpan() as OtelSpan - val agentSpanContext = span.agentSpanContext as DDSpanContext + val agentSpanContext = span.datadogSpanContext as DatadogSpanContext assertThat(agentSpanContext.tags).containsEntry(key, value) } - @Test - fun `M use the internalLogger in the CoreTracer W build`() { - // When - val tracerProvider = testedOtelTracerProviderBuilder - .build() - val tracer = tracerProvider.tracerBuilder(fakeInstrumentationName).build() as OtelTracer - - // Then - val coreTracer: CoreTracer = tracer.getFieldValue("tracer") - val internalLogger: InternalLogger = coreTracer.getFieldValue("internalLogger") - assertThat(internalLogger).isSameAs(mockInternalLogger) - } - // endregion // region Sampling priority - @Test - fun `M not add a sample rate by default W creating a tracer`() { - // Given - val tracer = testedOtelTracerProviderBuilder.build() - .tracerBuilder(fakeInstrumentationName).build() - - // When - val coreTracer: CoreTracer = tracer.getFieldValue("tracer") - - // Then - val config: Config = coreTracer.getFieldValue("initialConfig") - val traceSampleRate: Double? = config.traceSampleRate - assertThat(traceSampleRate).isNull() - } - - @Test - fun `M use the sample rate W setSampleRate`(@DoubleForgery(min = 0.0, max = 100.0) sampleRate: Double) { - // Given - val expectedNormalizedSampleRate = sampleRate / 100.0 - val tracer = testedOtelTracerProviderBuilder.setSampleRate(sampleRate).build() - .tracerBuilder(fakeInstrumentationName).build() - - // When - val coreTracer: CoreTracer = tracer.getFieldValue("tracer") - - // Then - val config: Config = coreTracer.getFieldValue("initialConfig") - assertThat(config.traceSampleRate).isCloseTo(expectedNormalizedSampleRate, offset(0.005)) - } - @Test fun `M use user-keep priority W buildSpan { provided keep sample rate }`() { // Given @@ -536,13 +363,13 @@ internal class OtelTracerBuilderProviderTest { val span = tracer .spanBuilder(fakeOperationName) .startSpan() - val delegateSpan: DDSpan = span.getFieldValue("delegate") + val delegateSpan: DatadogSpan = span.delegate delegateSpan.forceSamplingDecision() span.end() // Then val priority = delegateSpan.samplingPriority - assertThat(priority).isEqualTo(PrioritySampling.USER_KEEP.toInt()) + assertThat(priority).isEqualTo(DatadogTracingConstants.PrioritySampling.USER_KEEP) } @Test @@ -559,13 +386,13 @@ internal class OtelTracerBuilderProviderTest { val span = tracer .spanBuilder(fakeOperationName) .startSpan() - val delegateSpan: DDSpan = span.getFieldValue("delegate") + val delegateSpan: DatadogSpan = span.delegate delegateSpan.forceSamplingDecision() span.end() // Then val priority = delegateSpan.samplingPriority - assertThat(priority).isEqualTo(PrioritySampling.USER_DROP.toInt()) + assertThat(priority).isEqualTo(DatadogTracingConstants.PrioritySampling.USER_DROP) } @Test @@ -590,13 +417,15 @@ internal class OtelTracerBuilderProviderTest { tracer.spanBuilder(forge.anAlphabeticalString()).startSpan() } val delegatedSpans = spans.map { - val delegatedSpan: DDSpan = it.getFieldValue("delegate") + val delegatedSpan: DatadogSpan = it.delegate delegatedSpan.forceSamplingDecision() delegatedSpan } spans.forEach { it.end() } - val droppedSpans = delegatedSpans.filter { it.samplingPriority == PrioritySampling.USER_DROP.toInt() } - val keptSpans = delegatedSpans.filter { it.samplingPriority == PrioritySampling.USER_KEEP.toInt() } + val droppedSpans = + delegatedSpans.filter { it.samplingPriority == DatadogTracingConstants.PrioritySampling.USER_DROP } + val keptSpans = + delegatedSpans.filter { it.samplingPriority == DatadogTracingConstants.PrioritySampling.USER_KEEP } // Then assertThat(droppedSpans.size + keptSpans.size).isEqualTo(numberOfSpans) @@ -620,46 +449,12 @@ internal class OtelTracerBuilderProviderTest { val span = tracer .spanBuilder(fakeOperationName) .startSpan() - val delegateSpan: DDSpan = span.getFieldValue("delegate") - delegateSpan.forceSamplingDecision() + val delegateSpan: DatadogSpan = span.delegate span.end() // Then val priority = delegateSpan.samplingPriority - assertThat(priority).isEqualTo(PrioritySampling.SAMPLER_KEEP.toInt()) - } - - // endregion - - // region trace rate limit - - @Test - fun `M use the trace rate limit W setTraceRateLimit`( - @IntForgery(min = 1, max = Int.MAX_VALUE) traceRateLimit: Int - ) { - // Given - val tracer = testedOtelTracerProviderBuilder.setTraceRateLimit(traceRateLimit).build() - .tracerBuilder(fakeInstrumentationName).build() - - // When - val coreTracer: CoreTracer = tracer.getFieldValue("tracer") - - // Then - val config: Config = coreTracer.getFieldValue("initialConfig") - assertThat(config.traceRateLimit).isEqualTo(traceRateLimit) - } - - @Test - fun `M use the default rate limit W build { if not provided }`() { - // Given - val tracer = testedOtelTracerProviderBuilder.build().tracerBuilder(fakeInstrumentationName).build() - - // When - val coreTracer: CoreTracer = tracer.getFieldValue("tracer") - - // Then - val config: Config = coreTracer.getFieldValue("initialConfig") - assertThat(config.traceRateLimit).isEqualTo(Int.MAX_VALUE) + assertThat(priority).isEqualTo(DatadogTracingConstants.PrioritySampling.SAMPLER_KEEP) } // endregion @@ -686,7 +481,7 @@ internal class OtelTracerBuilderProviderTest { val span = tracer .spanBuilder(fakeOperationName) .startSpan() - val delegateSpan: DDSpan = span.getFieldValue("delegate") + val delegateSpan: DatadogSpan = span.delegate val context = delegateSpan.context() span.end() @@ -709,7 +504,7 @@ internal class OtelTracerBuilderProviderTest { val span = tracer .spanBuilder(fakeOperationName) .startSpan() - val delegateSpan: DDSpan = span.getFieldValue("delegate") + val delegateSpan: DatadogSpan = span.delegate val context = delegateSpan.context() span.end() @@ -731,7 +526,7 @@ internal class OtelTracerBuilderProviderTest { val span = tracer .spanBuilder(fakeOperationName) .startSpan() - val delegateSpan: DDSpan = span.getFieldValue("delegate") + val delegateSpan: DatadogSpan = span.delegate val context = delegateSpan.context() span.end() @@ -753,18 +548,17 @@ internal class OtelTracerBuilderProviderTest { .build() .tracerBuilder(fakeInstrumentationName) .build() - val delegatedTracer: AgentTracer.TracerAPI = tracer.getFieldValue("tracer") - val scopeManager: AgentScopeManager = delegatedTracer.getFieldValue("scopeManager") + val delegatedTracer: DatadogTracer = tracer.delegate val span = tracer .spanBuilder(fakeOperationName) .startSpan() - val delegateSpan: DDSpan = span.getFieldValue("delegate") + val delegateSpan: DatadogSpan = span.delegate val expectedTraceId = delegateSpan.context().traceId.toHexString() val expectedSpanId = delegateSpan.context().spanId.toString() // When - val scope = scopeManager.activate(delegateSpan, ScopeSource.INSTRUMENTATION) - scope.close() + val scope = delegatedTracer.activateSpan(delegateSpan, false) + scope?.close() span.end() // Then @@ -786,29 +580,28 @@ internal class OtelTracerBuilderProviderTest { // Given val expectedThreadName = Thread.currentThread().name val expectedActiveTraceContextName = "context@$expectedThreadName" - val tracer = testedOtelTracerProviderBuilder - .build() + val tracer = testedOtelTracerProviderBuilder.build() .tracerBuilder(fakeInstrumentationName) .build() - val delegatedTracer: AgentTracer.TracerAPI = tracer.getFieldValue("tracer") - val scopeManager: AgentScopeManager = spy(delegatedTracer.getFieldValue("scopeManager")) { + + val delegatedTracer: DatadogTracer = tracer.delegate + spy(delegatedTracer) { whenever(it.activeSpan()).thenReturn(null) } - delegatedTracer.setFieldValue("scopeManager", scopeManager) val span = tracer .spanBuilder(fakeOperationName) .startSpan() - val delegateSpan: DDSpan = span.getFieldValue("delegate") // When - val scope = scopeManager.activate(delegateSpan, ScopeSource.INSTRUMENTATION) - scope.close() + val delegateSpan: DatadogSpan = span.delegate + val scope = delegatedTracer.activateSpan(delegateSpan, false) + scope?.close() span.end() // Then argumentCaptor<(MutableMap) -> Unit> { val traceContext: MutableMap = mutableMapOf() - verify(mockSdkCore, times(2)) + verify(mockSdkCore, times(3)) .updateFeatureContext(eq(Feature.TRACING_FEATURE_NAME), any(), capture()) lastValue.invoke(traceContext) assertThat(traceContext).doesNotContainKey(expectedActiveTraceContextName) @@ -849,23 +642,24 @@ internal class OtelTracerBuilderProviderTest { // endregion class StubTracingFeature : Feature, InternalCoreWriterProvider { - override val name: String - get() = "" + override val name: String = "" + override fun onStop() = Unit + override fun onInitialize(appContext: Context) = Unit + override fun getCoreTracerWriter(): DatadogSpanWriter = mock() + } - override fun onInitialize(appContext: Context) { - } + companion object { - override fun onStop() { - } + private val forge = Forge() - override fun getCoreTracerWriter(): Writer { - return mock() - } - } + private val Tracer.delegate: DatadogTracer + get() = getFieldValue("tracer") - companion object { + private val Span.delegate: DatadogSpan + get() = getFieldValue("delegate") - val forge = Forge() + private val TracerProvider.delegate: DatadogTracer + get() = getFieldValue("datadogTracer") @JvmStatic fun brokenRumContextProvider(): List> { diff --git a/features/dd-sdk-android-trace-otel/src/test/kotlin/com/datadog/android/trace/opentelemetry/internal/MiscUtilsTest.kt b/features/dd-sdk-android-trace-otel/src/test/kotlin/com/datadog/android/trace/opentelemetry/internal/MiscUtilsTest.kt index 02d82dd757..9d118dbe73 100644 --- a/features/dd-sdk-android-trace-otel/src/test/kotlin/com/datadog/android/trace/opentelemetry/internal/MiscUtilsTest.kt +++ b/features/dd-sdk-android-trace-otel/src/test/kotlin/com/datadog/android/trace/opentelemetry/internal/MiscUtilsTest.kt @@ -7,8 +7,8 @@ package com.datadog.android.trace.opentelemetry.internal import com.datadog.android.api.InternalLogger +import com.datadog.android.trace.opentelemetry.utils.forge.Configurator import com.datadog.android.trace.opentelemetry.utils.verifyLog -import com.datadog.android.utils.forge.Configurator import com.datadog.tools.unit.annotations.TestTargetApi import com.datadog.tools.unit.extensions.ApiLevelExtension import fr.xgouchet.elmyr.annotation.StringForgery diff --git a/features/dd-sdk-android-trace-otel/src/test/kotlin/com/datadog/android/trace/opentelemetry/internal/OtelContextTest.kt b/features/dd-sdk-android-trace-otel/src/test/kotlin/com/datadog/android/trace/opentelemetry/internal/OtelContextTest.kt index 7c8ae3c696..627839875f 100644 --- a/features/dd-sdk-android-trace-otel/src/test/kotlin/com/datadog/android/trace/opentelemetry/internal/OtelContextTest.kt +++ b/features/dd-sdk-android-trace-otel/src/test/kotlin/com/datadog/android/trace/opentelemetry/internal/OtelContextTest.kt @@ -6,9 +6,9 @@ package com.datadog.android.trace.opentelemetry.internal -import com.datadog.android.utils.forge.Configurator +import com.datadog.android.trace.api.scope.DatadogScope +import com.datadog.android.trace.opentelemetry.utils.forge.Configurator import com.datadog.opentelemetry.trace.OtelSpan -import com.datadog.trace.bootstrap.instrumentation.api.AgentScope import fr.xgouchet.elmyr.Forge import fr.xgouchet.elmyr.junit5.ForgeConfiguration import fr.xgouchet.elmyr.junit5.ForgeExtension @@ -172,7 +172,7 @@ internal class OtelContextTest { @Test fun `M return OtelScope W makeCurrent { currentSpan is OtelSpan }`() { // Given - val mockAgentScope: AgentScope = mock() + val mockAgentScope: DatadogScope = mock() val mockOtelSpan: OtelSpan = mock { on { activate() }.thenReturn(mockAgentScope) } diff --git a/features/dd-sdk-android-trace-otel/src/test/kotlin/com/datadog/android/trace/opentelemetry/internal/OtelScopeTest.kt b/features/dd-sdk-android-trace-otel/src/test/kotlin/com/datadog/android/trace/opentelemetry/internal/OtelScopeTest.kt index f2cd080466..5b24a3b937 100644 --- a/features/dd-sdk-android-trace-otel/src/test/kotlin/com/datadog/android/trace/opentelemetry/internal/OtelScopeTest.kt +++ b/features/dd-sdk-android-trace-otel/src/test/kotlin/com/datadog/android/trace/opentelemetry/internal/OtelScopeTest.kt @@ -6,7 +6,7 @@ package com.datadog.android.trace.opentelemetry.internal -import com.datadog.trace.bootstrap.instrumentation.api.AgentScope +import com.datadog.android.trace.api.scope.DatadogScope import io.opentelemetry.context.Scope import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test @@ -28,7 +28,7 @@ internal class OtelScopeTest { lateinit var mockWrappedScope: Scope @Mock - lateinit var mockAgentScope: AgentScope + lateinit var mockAgentScope: DatadogScope lateinit var testedScope: OtelScope diff --git a/features/dd-sdk-android-trace-otel/src/test/kotlin/com/datadog/android/trace/opentelemetry/utils/forge/Configurator.kt b/features/dd-sdk-android-trace-otel/src/test/kotlin/com/datadog/android/trace/opentelemetry/utils/forge/Configurator.kt index 0dcbfddee5..7ba39fd838 100644 --- a/features/dd-sdk-android-trace-otel/src/test/kotlin/com/datadog/android/trace/opentelemetry/utils/forge/Configurator.kt +++ b/features/dd-sdk-android-trace-otel/src/test/kotlin/com/datadog/android/trace/opentelemetry/utils/forge/Configurator.kt @@ -4,7 +4,7 @@ * Copyright 2016-Present Datadog, Inc. */ -package com.datadog.android.utils.forge +package com.datadog.android.trace.opentelemetry.utils.forge import com.datadog.android.tests.elmyr.useCoreFactories import com.datadog.tools.unit.forge.BaseConfigurator diff --git a/features/dd-sdk-android-trace-otel/src/test/kotlin/com/datadog/opentelemetry/trace/OtelSpanTest.kt b/features/dd-sdk-android-trace-otel/src/test/kotlin/com/datadog/opentelemetry/trace/OtelSpanTest.kt index 8187ebd660..10d6185898 100644 --- a/features/dd-sdk-android-trace-otel/src/test/kotlin/com/datadog/opentelemetry/trace/OtelSpanTest.kt +++ b/features/dd-sdk-android-trace-otel/src/test/kotlin/com/datadog/opentelemetry/trace/OtelSpanTest.kt @@ -6,12 +6,11 @@ package com.datadog.opentelemetry.trace -import com.datadog.android.utils.forge.Configurator -import com.datadog.trace.api.ConfigDefaults -import com.datadog.trace.bootstrap.instrumentation.api.AgentSpan -import com.datadog.trace.bootstrap.instrumentation.api.AgentTracer -import com.datadog.trace.bootstrap.instrumentation.api.ErrorPriorities -import com.datadog.trace.bootstrap.instrumentation.api.ScopeSource +import com.datadog.android.trace.api.DatadogTracingConstants.DEFAULT_ASYNC_PROPAGATING +import com.datadog.android.trace.api.DatadogTracingConstants.ErrorPriorities +import com.datadog.android.trace.api.span.DatadogSpan +import com.datadog.android.trace.api.tracer.DatadogTracer +import com.datadog.android.trace.opentelemetry.utils.forge.Configurator import fr.xgouchet.elmyr.annotation.Forgery import fr.xgouchet.elmyr.annotation.StringForgery import fr.xgouchet.elmyr.junit5.ForgeConfiguration @@ -49,10 +48,10 @@ import org.mockito.quality.Strictness internal class OtelSpanTest { @Mock - lateinit var mockAgentTracer: AgentTracer.TracerAPI + lateinit var mockAgentTracer: DatadogTracer @Mock - lateinit var mockAgentSpan: AgentSpan + lateinit var mockAgentSpan: DatadogSpan lateinit var testedSpan: OtelSpan @BeforeEach @@ -142,7 +141,7 @@ internal class OtelSpanTest { testedSpan.setStatus(fakeStatusCode, fakeDescription) // Then - verify(mockAgentSpan).setError(expectedIsError) + verify(mockAgentSpan).isError = expectedIsError verify(mockAgentSpan).setErrorMessage(expectedErrorMessage) assertThat(testedSpan.statusCode).isEqualTo(fakeStatusCode) } @@ -160,7 +159,7 @@ internal class OtelSpanTest { testedSpan.setStatus(fakeStatusCode, fakeDescription) // Then - verify(mockAgentSpan, never()).setError(any()) + verify(mockAgentSpan, never()).isError = any() verify(mockAgentSpan, never()).setErrorMessage(anyOrNull()) assertThat(testedSpan.statusCode).isEqualTo(StatusCode.UNSET) } @@ -177,7 +176,7 @@ internal class OtelSpanTest { testedSpan.setStatus(StatusCode.OK, fakeDescription2) // Then - verify(mockAgentSpan).setError(false) + verify(mockAgentSpan).isError = false verify(mockAgentSpan).setErrorMessage(null) assertThat(testedSpan.statusCode).isEqualTo(StatusCode.ERROR) } @@ -199,7 +198,7 @@ internal class OtelSpanTest { testedSpan.setStatus(fakeStatusCode2, fakeDescription2) // Then - verify(mockAgentSpan).setError(expectedIsError) + verify(mockAgentSpan).isError = expectedIsError verify(mockAgentSpan).setErrorMessage(expectedErrorMessage) assertThat(testedSpan.statusCode).isEqualTo(fakeStatusCode1) verifyNoMoreInteractions(mockAgentSpan) @@ -248,8 +247,7 @@ internal class OtelSpanTest { // Then verify(mockAgentTracer).activateSpan( mockAgentSpan, - ScopeSource.INSTRUMENTATION, - ConfigDefaults.DEFAULT_ASYNC_PROPAGATING + DEFAULT_ASYNC_PROPAGATING ) } diff --git a/features/dd-sdk-android-trace-otel/src/test/kotlin/com/datadog/opentelemetry/trace/OtelTracerTest.kt b/features/dd-sdk-android-trace-otel/src/test/kotlin/com/datadog/opentelemetry/trace/OtelTracerTest.kt index 13bd0d1e48..ad16e57ad2 100644 --- a/features/dd-sdk-android-trace-otel/src/test/kotlin/com/datadog/opentelemetry/trace/OtelTracerTest.kt +++ b/features/dd-sdk-android-trace-otel/src/test/kotlin/com/datadog/opentelemetry/trace/OtelTracerTest.kt @@ -7,9 +7,9 @@ package com.datadog.opentelemetry.trace import com.datadog.android.api.InternalLogger -import com.datadog.android.utils.forge.Configurator -import com.datadog.trace.bootstrap.instrumentation.api.AgentTracer -import com.datadog.trace.bootstrap.instrumentation.api.AgentTracer.TracerAPI +import com.datadog.android.trace.api.span.DatadogSpanBuilder +import com.datadog.android.trace.api.tracer.DatadogTracer +import com.datadog.android.trace.opentelemetry.utils.forge.Configurator import fr.xgouchet.elmyr.annotation.StringForgery import fr.xgouchet.elmyr.junit5.ForgeConfiguration import fr.xgouchet.elmyr.junit5.ForgeExtension @@ -38,7 +38,7 @@ internal class OtelTracerTest { private lateinit var testedTracer: OtelTracer @Mock - lateinit var mockDelegateTracer: TracerAPI + lateinit var mockDelegateTracer: DatadogTracer @StringForgery lateinit var fakeIntstrumentationName: String @@ -50,7 +50,7 @@ internal class OtelTracerTest { lateinit var mockLogger: InternalLogger @Mock - lateinit var mockDelegateSpanBuilder: AgentTracer.SpanBuilder + lateinit var mockDelegateSpanBuilder: DatadogSpanBuilder // region Unit Tests diff --git a/features/dd-sdk-android-trace-otel/transitiveDependencies b/features/dd-sdk-android-trace-otel/transitiveDependencies index 5d9052ff9a..8372b1583e 100644 --- a/features/dd-sdk-android-trace-otel/transitiveDependencies +++ b/features/dd-sdk-android-trace-otel/transitiveDependencies @@ -1,13 +1,10 @@ Dependencies List androidx.annotation:annotation-jvm:1.9.1 : 59 Kb -io.opentelemetry:opentelemetry-api:1.4.0 : 78 Kb -io.opentelemetry:opentelemetry-context:1.4.0 : 42 Kb -io.opentracing:opentracing-api:0.32.0 : 18 Kb -io.opentracing:opentracing-noop:0.32.0 : 10 Kb -io.opentracing:opentracing-util:0.32.0 : 10 Kb +io.opentelemetry:opentelemetry-api:1.40.0 : 138 Kb +io.opentelemetry:opentelemetry-context:1.40.0 : 46 Kb org.jetbrains.kotlin:kotlin-stdlib:2.0.21 : 1706 Kb org.jetbrains:annotations:13.0 : 17 Kb -Total transitive dependencies size : 1943 Kb +Total transitive dependencies size : 1967 Kb diff --git a/features/dd-sdk-android-trace/api/apiSurface b/features/dd-sdk-android-trace/api/apiSurface index c689f4a845..d1032344a0 100644 --- a/features/dd-sdk-android-trace/api/apiSurface +++ b/features/dd-sdk-android-trace/api/apiSurface @@ -1,24 +1,13 @@ -class com.datadog.android.trace.AndroidTracer : com.datadog.opentracing.DDTracer - override fun buildSpan(String): DDSpanBuilder - class Builder - constructor(com.datadog.android.api.SdkCore = Datadog.getInstance()) - fun build(): AndroidTracer - fun setTracingHeaderTypes(Set): Builder - fun setService(String): Builder - fun setPartialFlushThreshold(Int): Builder - DEPRECATED fun addGlobalTag(String, String): Builder - fun addTag(String, String): Builder - fun setBundleWithRumEnabled(Boolean): Builder - fun setSampleRate(Double): Builder - override fun toString(): String - companion object - fun logThrowable(io.opentracing.Span, Throwable) - fun logErrorMessage(io.opentracing.Span, String) +object com.datadog.android.trace.DatadogTracing + fun newTracerBuilder(com.datadog.android.api.SdkCore = Datadog.getInstance()): com.datadog.android.trace.api.tracer.DatadogTracerBuilder +object com.datadog.android.trace.GlobalDatadogTracer + fun registerIfAbsent(com.datadog.android.trace.api.tracer.DatadogTracer): Boolean + fun get(): com.datadog.android.trace.api.tracer.DatadogTracer + fun getOrNull(): com.datadog.android.trace.api.tracer.DatadogTracer? + fun clear() interface com.datadog.android.trace.InternalCoreWriterProvider - fun getCoreTracerWriter(): com.datadog.trace.common.writer.Writer -fun io.opentracing.Span.setError(Throwable) -fun io.opentracing.Span.setError(String) -fun withinSpan(String, io.opentracing.Span? = null, Boolean = true, io.opentracing.Span.() -> T): T + fun getCoreTracerWriter(): com.datadog.android.trace.api.span.DatadogSpanWriter +fun withinSpan(String, com.datadog.android.trace.api.span.DatadogSpan? = null, Boolean = true, com.datadog.android.trace.api.span.DatadogSpan.() -> T): T object com.datadog.android.trace.Trace fun enable(TraceConfiguration, com.datadog.android.api.SdkCore = Datadog.getInstance()) data class com.datadog.android.trace.TraceConfiguration @@ -29,28 +18,29 @@ data class com.datadog.android.trace.TraceConfiguration fun build(): TraceConfiguration interface com.datadog.android.trace.event.SpanEventMapper : com.datadog.android.event.EventMapper override fun map(com.datadog.android.trace.model.SpanEvent): com.datadog.android.trace.model.SpanEvent +class com.datadog.android.trace.internal.DatadogPropagationHelper + fun isExtractedContext(com.datadog.android.trace.api.span.DatadogSpanContext): Boolean + fun createExtractedContext(String, String, Int): com.datadog.android.trace.api.span.DatadogSpanContext +class com.datadog.android.trace.internal.DatadogSpanIdConverter + fun fromHex(String): Long + fun toHexStringPadded(Long): String +fun com.datadog.android.trace.api.trace.DatadogTraceId.Companion.fromHex(String): com.datadog.android.trace.api.trace.DatadogTraceId +object com.datadog.android.trace.internal.DatadogTracingToolkit + val spanIdConverter: DatadogSpanIdConverter + var propagationHelper: DatadogPropagationHelper + fun setTracingSamplingPriorityIfNecessary(com.datadog.android.trace.api.span.DatadogSpanContext) + fun setTraceId128BitGenerationEnabled(com.datadog.android.trace.api.tracer.DatadogTracerBuilder): com.datadog.android.trace.api.tracer.DatadogTracerBuilder + fun setSdkV2Compatible(com.datadog.android.trace.api.tracer.DatadogTracerBuilder): com.datadog.android.trace.api.tracer.DatadogTracerBuilder object com.datadog.android.trace.internal.SpanAttributes const val DATADOG_INITIAL_CONTEXT: String -fun android.database.sqlite.SQLiteDatabase.transactionTraced(String, Boolean = true, io.opentracing.Span.(android.database.sqlite.SQLiteDatabase) -> T): T +fun android.database.sqlite.SQLiteDatabase.transactionTraced(String, Boolean = true, com.datadog.android.trace.api.span.DatadogSpan.(android.database.sqlite.SQLiteDatabase) -> T): T data class com.datadog.android.trace.model.SpanEvent - constructor(Device, Os, kotlin.String, kotlin.String, kotlin.String, kotlin.String, kotlin.String, kotlin.String, kotlin.Long, kotlin.Long, kotlin.Long = 0L, Metrics, Meta) + constructor(kotlin.String, kotlin.String, kotlin.String, kotlin.String, kotlin.String, kotlin.String, kotlin.Long, kotlin.Long, kotlin.Long = 0L, Metrics, Meta) val type: kotlin.String fun toJson(): com.google.gson.JsonElement companion object fun fromJson(kotlin.String): SpanEvent fun fromJsonObject(com.google.gson.JsonObject): SpanEvent - data class Device - constructor(Type? = null, kotlin.String? = null, kotlin.String? = null, kotlin.String? = null, kotlin.String? = null, kotlin.String? = null, kotlin.collections.List? = null, kotlin.String? = null, kotlin.Number? = null, kotlin.Boolean? = null, kotlin.Number? = null) - fun toJson(): com.google.gson.JsonElement - companion object - fun fromJson(kotlin.String): Device - fun fromJsonObject(com.google.gson.JsonObject): Device - data class Os - constructor(kotlin.String, kotlin.String, kotlin.String? = null, kotlin.String) - fun toJson(): com.google.gson.JsonElement - companion object - fun fromJson(kotlin.String): Os - fun fromJsonObject(com.google.gson.JsonObject): Os data class Metrics constructor(kotlin.Long? = null, kotlin.collections.Map = mapOf()) fun toJson(): com.google.gson.JsonElement @@ -58,7 +48,7 @@ data class com.datadog.android.trace.model.SpanEvent fun fromJson(kotlin.String): Metrics fun fromJsonObject(com.google.gson.JsonObject): Metrics data class Meta - constructor(kotlin.String, Dd, Span, Tracer, Usr, Account? = null, Network? = null, kotlin.collections.Map = mapOf()) + constructor(kotlin.String, Dd, Span, Tracer, Usr, Account? = null, Network? = null, Device, Os, kotlin.collections.Map = mapOf()) fun toJson(): com.google.gson.JsonElement companion object fun fromJson(kotlin.String): Meta @@ -99,6 +89,18 @@ data class com.datadog.android.trace.model.SpanEvent companion object fun fromJson(kotlin.String): Network fun fromJsonObject(com.google.gson.JsonObject): Network + data class Device + constructor(Type? = null, kotlin.String? = null, kotlin.String? = null, kotlin.String? = null, kotlin.String? = null, kotlin.String? = null, kotlin.collections.List? = null, kotlin.String? = null, kotlin.Number? = null, kotlin.Boolean? = null, kotlin.Number? = null) + fun toJson(): com.google.gson.JsonElement + companion object + fun fromJson(kotlin.String): Device + fun fromJsonObject(com.google.gson.JsonObject): Device + data class Os + constructor(kotlin.String, kotlin.String, kotlin.String? = null, kotlin.String) + fun toJson(): com.google.gson.JsonElement + companion object + fun fromJson(kotlin.String): Os + fun fromJsonObject(com.google.gson.JsonObject): Os data class Application constructor(kotlin.String? = null) fun toJson(): com.google.gson.JsonElement diff --git a/features/dd-sdk-android-trace/api/dd-sdk-android-trace.api b/features/dd-sdk-android-trace/api/dd-sdk-android-trace.api index 922deea0b9..d99dc33785 100644 --- a/features/dd-sdk-android-trace/api/dd-sdk-android-trace.api +++ b/features/dd-sdk-android-trace/api/dd-sdk-android-trace.api @@ -1,40 +1,24 @@ -public final class com/datadog/android/trace/AndroidTracer : com/datadog/opentracing/DDTracer { - public static final field Companion Lcom/datadog/android/trace/AndroidTracer$Companion; - public fun buildSpan (Ljava/lang/String;)Lcom/datadog/opentracing/DDTracer$DDSpanBuilder; - public synthetic fun buildSpan (Ljava/lang/String;)Lio/opentracing/Tracer$SpanBuilder; - public static final fun logErrorMessage (Lio/opentracing/Span;Ljava/lang/String;)V - public static final fun logThrowable (Lio/opentracing/Span;Ljava/lang/Throwable;)V - public fun toString ()Ljava/lang/String; +public final class com/datadog/android/trace/DatadogTracing { + public static final field INSTANCE Lcom/datadog/android/trace/DatadogTracing; + public final fun newTracerBuilder (Lcom/datadog/android/api/SdkCore;)Lcom/datadog/android/trace/api/tracer/DatadogTracerBuilder; + public static synthetic fun newTracerBuilder$default (Lcom/datadog/android/trace/DatadogTracing;Lcom/datadog/android/api/SdkCore;ILjava/lang/Object;)Lcom/datadog/android/trace/api/tracer/DatadogTracerBuilder; } -public final class com/datadog/android/trace/AndroidTracer$Builder { - public fun ()V - public fun (Lcom/datadog/android/api/SdkCore;)V - public synthetic fun (Lcom/datadog/android/api/SdkCore;ILkotlin/jvm/internal/DefaultConstructorMarker;)V - public final fun addGlobalTag (Ljava/lang/String;Ljava/lang/String;)Lcom/datadog/android/trace/AndroidTracer$Builder; - public final fun addTag (Ljava/lang/String;Ljava/lang/String;)Lcom/datadog/android/trace/AndroidTracer$Builder; - public final fun build ()Lcom/datadog/android/trace/AndroidTracer; - public final fun setBundleWithRumEnabled (Z)Lcom/datadog/android/trace/AndroidTracer$Builder; - public final fun setPartialFlushThreshold (I)Lcom/datadog/android/trace/AndroidTracer$Builder; - public final fun setSampleRate (D)Lcom/datadog/android/trace/AndroidTracer$Builder; - public final fun setService (Ljava/lang/String;)Lcom/datadog/android/trace/AndroidTracer$Builder; - public final fun setTracingHeaderTypes (Ljava/util/Set;)Lcom/datadog/android/trace/AndroidTracer$Builder; -} - -public final class com/datadog/android/trace/AndroidTracer$Companion { - public final fun logErrorMessage (Lio/opentracing/Span;Ljava/lang/String;)V - public final fun logThrowable (Lio/opentracing/Span;Ljava/lang/Throwable;)V +public final class com/datadog/android/trace/GlobalDatadogTracer { + public static final field INSTANCE Lcom/datadog/android/trace/GlobalDatadogTracer; + public final fun clear ()V + public final fun get ()Lcom/datadog/android/trace/api/tracer/DatadogTracer; + public final fun getOrNull ()Lcom/datadog/android/trace/api/tracer/DatadogTracer; + public final fun registerIfAbsent (Lcom/datadog/android/trace/api/tracer/DatadogTracer;)Z } public abstract interface class com/datadog/android/trace/InternalCoreWriterProvider { - public abstract fun getCoreTracerWriter ()Lcom/datadog/trace/common/writer/Writer; + public abstract fun getCoreTracerWriter ()Lcom/datadog/android/trace/api/span/DatadogSpanWriter; } public final class com/datadog/android/trace/SpanExtKt { - public static final fun setError (Lio/opentracing/Span;Ljava/lang/String;)V - public static final fun setError (Lio/opentracing/Span;Ljava/lang/Throwable;)V - public static final fun withinSpan (Ljava/lang/String;Lio/opentracing/Span;ZLkotlin/jvm/functions/Function1;)Ljava/lang/Object; - public static synthetic fun withinSpan$default (Ljava/lang/String;Lio/opentracing/Span;ZLkotlin/jvm/functions/Function1;ILjava/lang/Object;)Ljava/lang/Object; + public static final fun withinSpan (Ljava/lang/String;Lcom/datadog/android/trace/api/span/DatadogSpan;ZLkotlin/jvm/functions/Function1;)Ljava/lang/Object; + public static synthetic fun withinSpan$default (Ljava/lang/String;Lcom/datadog/android/trace/api/span/DatadogSpan;ZLkotlin/jvm/functions/Function1;ILjava/lang/Object;)Ljava/lang/Object; } public final class com/datadog/android/trace/Trace { @@ -64,6 +48,29 @@ public abstract interface class com/datadog/android/trace/event/SpanEventMapper public abstract fun map (Lcom/datadog/android/trace/model/SpanEvent;)Lcom/datadog/android/trace/model/SpanEvent; } +public final class com/datadog/android/trace/internal/DatadogPropagationHelper { + public final fun createExtractedContext (Ljava/lang/String;Ljava/lang/String;I)Lcom/datadog/android/trace/api/span/DatadogSpanContext; + public final fun isExtractedContext (Lcom/datadog/android/trace/api/span/DatadogSpanContext;)Z +} + +public final class com/datadog/android/trace/internal/DatadogSpanIdConverter { + public final fun fromHex (Ljava/lang/String;)J + public final fun toHexStringPadded (J)Ljava/lang/String; +} + +public final class com/datadog/android/trace/internal/DatadogTraceExtKt { + public static final fun fromHex (Lcom/datadog/android/trace/api/trace/DatadogTraceId$Companion;Ljava/lang/String;)Lcom/datadog/android/trace/api/trace/DatadogTraceId; +} + +public final class com/datadog/android/trace/internal/DatadogTracingToolkit { + public static final field INSTANCE Lcom/datadog/android/trace/internal/DatadogTracingToolkit; + public static final field spanIdConverter Lcom/datadog/android/trace/internal/DatadogSpanIdConverter; + public final fun getPropagationHelper ()Lcom/datadog/android/trace/internal/DatadogPropagationHelper; + public final fun setSdkV2Compatible (Lcom/datadog/android/trace/api/tracer/DatadogTracerBuilder;)Lcom/datadog/android/trace/api/tracer/DatadogTracerBuilder; + public final fun setTraceId128BitGenerationEnabled (Lcom/datadog/android/trace/api/tracer/DatadogTracerBuilder;)Lcom/datadog/android/trace/api/tracer/DatadogTracerBuilder; + public final fun setTracingSamplingPriorityIfNecessary (Lcom/datadog/android/trace/api/span/DatadogSpanContext;)V +} + public final class com/datadog/android/trace/internal/SpanAttributes { public static final field DATADOG_INITIAL_CONTEXT Ljava/lang/String; public static final field INSTANCE Lcom/datadog/android/trace/internal/SpanAttributes; @@ -71,33 +78,29 @@ public final class com/datadog/android/trace/internal/SpanAttributes { public final class com/datadog/android/trace/model/SpanEvent { public static final field Companion Lcom/datadog/android/trace/model/SpanEvent$Companion; - public fun (Lcom/datadog/android/trace/model/SpanEvent$Device;Lcom/datadog/android/trace/model/SpanEvent$Os;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;JJJLcom/datadog/android/trace/model/SpanEvent$Metrics;Lcom/datadog/android/trace/model/SpanEvent$Meta;)V - public synthetic fun (Lcom/datadog/android/trace/model/SpanEvent$Device;Lcom/datadog/android/trace/model/SpanEvent$Os;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;JJJLcom/datadog/android/trace/model/SpanEvent$Metrics;Lcom/datadog/android/trace/model/SpanEvent$Meta;ILkotlin/jvm/internal/DefaultConstructorMarker;)V - public final fun component1 ()Lcom/datadog/android/trace/model/SpanEvent$Device; - public final fun component10 ()J - public final fun component11 ()J - public final fun component12 ()Lcom/datadog/android/trace/model/SpanEvent$Metrics; - public final fun component13 ()Lcom/datadog/android/trace/model/SpanEvent$Meta; - public final fun component2 ()Lcom/datadog/android/trace/model/SpanEvent$Os; + public fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;JJJLcom/datadog/android/trace/model/SpanEvent$Metrics;Lcom/datadog/android/trace/model/SpanEvent$Meta;)V + public synthetic fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;JJJLcom/datadog/android/trace/model/SpanEvent$Metrics;Lcom/datadog/android/trace/model/SpanEvent$Meta;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun component1 ()Ljava/lang/String; + public final fun component10 ()Lcom/datadog/android/trace/model/SpanEvent$Metrics; + public final fun component11 ()Lcom/datadog/android/trace/model/SpanEvent$Meta; + public final fun component2 ()Ljava/lang/String; public final fun component3 ()Ljava/lang/String; public final fun component4 ()Ljava/lang/String; public final fun component5 ()Ljava/lang/String; public final fun component6 ()Ljava/lang/String; - public final fun component7 ()Ljava/lang/String; - public final fun component8 ()Ljava/lang/String; + public final fun component7 ()J + public final fun component8 ()J public final fun component9 ()J - public final fun copy (Lcom/datadog/android/trace/model/SpanEvent$Device;Lcom/datadog/android/trace/model/SpanEvent$Os;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;JJJLcom/datadog/android/trace/model/SpanEvent$Metrics;Lcom/datadog/android/trace/model/SpanEvent$Meta;)Lcom/datadog/android/trace/model/SpanEvent; - public static synthetic fun copy$default (Lcom/datadog/android/trace/model/SpanEvent;Lcom/datadog/android/trace/model/SpanEvent$Device;Lcom/datadog/android/trace/model/SpanEvent$Os;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;JJJLcom/datadog/android/trace/model/SpanEvent$Metrics;Lcom/datadog/android/trace/model/SpanEvent$Meta;ILjava/lang/Object;)Lcom/datadog/android/trace/model/SpanEvent; + public final fun copy (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;JJJLcom/datadog/android/trace/model/SpanEvent$Metrics;Lcom/datadog/android/trace/model/SpanEvent$Meta;)Lcom/datadog/android/trace/model/SpanEvent; + public static synthetic fun copy$default (Lcom/datadog/android/trace/model/SpanEvent;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;JJJLcom/datadog/android/trace/model/SpanEvent$Metrics;Lcom/datadog/android/trace/model/SpanEvent$Meta;ILjava/lang/Object;)Lcom/datadog/android/trace/model/SpanEvent; public fun equals (Ljava/lang/Object;)Z public static final fun fromJson (Ljava/lang/String;)Lcom/datadog/android/trace/model/SpanEvent; public static final fun fromJsonObject (Lcom/google/gson/JsonObject;)Lcom/datadog/android/trace/model/SpanEvent; - public final fun getDevice ()Lcom/datadog/android/trace/model/SpanEvent$Device; public final fun getDuration ()J public final fun getError ()J public final fun getMeta ()Lcom/datadog/android/trace/model/SpanEvent$Meta; public final fun getMetrics ()Lcom/datadog/android/trace/model/SpanEvent$Metrics; public final fun getName ()Ljava/lang/String; - public final fun getOs ()Lcom/datadog/android/trace/model/SpanEvent$Os; public final fun getParentId ()Ljava/lang/String; public final fun getResource ()Ljava/lang/String; public final fun getService ()Ljava/lang/String; @@ -267,25 +270,29 @@ public final class com/datadog/android/trace/model/SpanEvent$Device$Companion { public final class com/datadog/android/trace/model/SpanEvent$Meta { public static final field Companion Lcom/datadog/android/trace/model/SpanEvent$Meta$Companion; - public fun (Ljava/lang/String;Lcom/datadog/android/trace/model/SpanEvent$Dd;Lcom/datadog/android/trace/model/SpanEvent$Span;Lcom/datadog/android/trace/model/SpanEvent$Tracer;Lcom/datadog/android/trace/model/SpanEvent$Usr;Lcom/datadog/android/trace/model/SpanEvent$Account;Lcom/datadog/android/trace/model/SpanEvent$Network;Ljava/util/Map;)V - public synthetic fun (Ljava/lang/String;Lcom/datadog/android/trace/model/SpanEvent$Dd;Lcom/datadog/android/trace/model/SpanEvent$Span;Lcom/datadog/android/trace/model/SpanEvent$Tracer;Lcom/datadog/android/trace/model/SpanEvent$Usr;Lcom/datadog/android/trace/model/SpanEvent$Account;Lcom/datadog/android/trace/model/SpanEvent$Network;Ljava/util/Map;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun (Ljava/lang/String;Lcom/datadog/android/trace/model/SpanEvent$Dd;Lcom/datadog/android/trace/model/SpanEvent$Span;Lcom/datadog/android/trace/model/SpanEvent$Tracer;Lcom/datadog/android/trace/model/SpanEvent$Usr;Lcom/datadog/android/trace/model/SpanEvent$Account;Lcom/datadog/android/trace/model/SpanEvent$Network;Lcom/datadog/android/trace/model/SpanEvent$Device;Lcom/datadog/android/trace/model/SpanEvent$Os;Ljava/util/Map;)V + public synthetic fun (Ljava/lang/String;Lcom/datadog/android/trace/model/SpanEvent$Dd;Lcom/datadog/android/trace/model/SpanEvent$Span;Lcom/datadog/android/trace/model/SpanEvent$Tracer;Lcom/datadog/android/trace/model/SpanEvent$Usr;Lcom/datadog/android/trace/model/SpanEvent$Account;Lcom/datadog/android/trace/model/SpanEvent$Network;Lcom/datadog/android/trace/model/SpanEvent$Device;Lcom/datadog/android/trace/model/SpanEvent$Os;Ljava/util/Map;ILkotlin/jvm/internal/DefaultConstructorMarker;)V public final fun component1 ()Ljava/lang/String; + public final fun component10 ()Ljava/util/Map; public final fun component2 ()Lcom/datadog/android/trace/model/SpanEvent$Dd; public final fun component3 ()Lcom/datadog/android/trace/model/SpanEvent$Span; public final fun component4 ()Lcom/datadog/android/trace/model/SpanEvent$Tracer; public final fun component5 ()Lcom/datadog/android/trace/model/SpanEvent$Usr; public final fun component6 ()Lcom/datadog/android/trace/model/SpanEvent$Account; public final fun component7 ()Lcom/datadog/android/trace/model/SpanEvent$Network; - public final fun component8 ()Ljava/util/Map; - public final fun copy (Ljava/lang/String;Lcom/datadog/android/trace/model/SpanEvent$Dd;Lcom/datadog/android/trace/model/SpanEvent$Span;Lcom/datadog/android/trace/model/SpanEvent$Tracer;Lcom/datadog/android/trace/model/SpanEvent$Usr;Lcom/datadog/android/trace/model/SpanEvent$Account;Lcom/datadog/android/trace/model/SpanEvent$Network;Ljava/util/Map;)Lcom/datadog/android/trace/model/SpanEvent$Meta; - public static synthetic fun copy$default (Lcom/datadog/android/trace/model/SpanEvent$Meta;Ljava/lang/String;Lcom/datadog/android/trace/model/SpanEvent$Dd;Lcom/datadog/android/trace/model/SpanEvent$Span;Lcom/datadog/android/trace/model/SpanEvent$Tracer;Lcom/datadog/android/trace/model/SpanEvent$Usr;Lcom/datadog/android/trace/model/SpanEvent$Account;Lcom/datadog/android/trace/model/SpanEvent$Network;Ljava/util/Map;ILjava/lang/Object;)Lcom/datadog/android/trace/model/SpanEvent$Meta; + public final fun component8 ()Lcom/datadog/android/trace/model/SpanEvent$Device; + public final fun component9 ()Lcom/datadog/android/trace/model/SpanEvent$Os; + public final fun copy (Ljava/lang/String;Lcom/datadog/android/trace/model/SpanEvent$Dd;Lcom/datadog/android/trace/model/SpanEvent$Span;Lcom/datadog/android/trace/model/SpanEvent$Tracer;Lcom/datadog/android/trace/model/SpanEvent$Usr;Lcom/datadog/android/trace/model/SpanEvent$Account;Lcom/datadog/android/trace/model/SpanEvent$Network;Lcom/datadog/android/trace/model/SpanEvent$Device;Lcom/datadog/android/trace/model/SpanEvent$Os;Ljava/util/Map;)Lcom/datadog/android/trace/model/SpanEvent$Meta; + public static synthetic fun copy$default (Lcom/datadog/android/trace/model/SpanEvent$Meta;Ljava/lang/String;Lcom/datadog/android/trace/model/SpanEvent$Dd;Lcom/datadog/android/trace/model/SpanEvent$Span;Lcom/datadog/android/trace/model/SpanEvent$Tracer;Lcom/datadog/android/trace/model/SpanEvent$Usr;Lcom/datadog/android/trace/model/SpanEvent$Account;Lcom/datadog/android/trace/model/SpanEvent$Network;Lcom/datadog/android/trace/model/SpanEvent$Device;Lcom/datadog/android/trace/model/SpanEvent$Os;Ljava/util/Map;ILjava/lang/Object;)Lcom/datadog/android/trace/model/SpanEvent$Meta; public fun equals (Ljava/lang/Object;)Z public static final fun fromJson (Ljava/lang/String;)Lcom/datadog/android/trace/model/SpanEvent$Meta; public static final fun fromJsonObject (Lcom/google/gson/JsonObject;)Lcom/datadog/android/trace/model/SpanEvent$Meta; public final fun getAccount ()Lcom/datadog/android/trace/model/SpanEvent$Account; public final fun getAdditionalProperties ()Ljava/util/Map; public final fun getDd ()Lcom/datadog/android/trace/model/SpanEvent$Dd; + public final fun getDevice ()Lcom/datadog/android/trace/model/SpanEvent$Device; public final fun getNetwork ()Lcom/datadog/android/trace/model/SpanEvent$Network; + public final fun getOs ()Lcom/datadog/android/trace/model/SpanEvent$Os; public final fun getSpan ()Lcom/datadog/android/trace/model/SpanEvent$Span; public final fun getTracer ()Lcom/datadog/android/trace/model/SpanEvent$Tracer; public final fun getUsr ()Lcom/datadog/android/trace/model/SpanEvent$Usr; diff --git a/features/dd-sdk-android-trace/build.gradle.kts b/features/dd-sdk-android-trace/build.gradle.kts index c063ceddc7..265715ffb0 100644 --- a/features/dd-sdk-android-trace/build.gradle.kts +++ b/features/dd-sdk-android-trace/build.gradle.kts @@ -45,13 +45,17 @@ android { consumerProguardFiles("consumer-rules.pro") } namespace = "com.datadog.android.trace" + + testFixtures { + enable = true + } } dependencies { api(project(":dd-sdk-android-core")) + api(project(":features:dd-sdk-android-trace-api")) implementation(project(":dd-sdk-android-internal")) - // TODO RUM-9902 This is temporary for compilation. Gonna change to implementation after removing opentracing code - api(project(":features:dd-sdk-android-trace-internal")) + implementation(project(":features:dd-sdk-android-trace-internal")) implementation(libs.kotlin) implementation(libs.gson) implementation(libs.androidXAnnotation) @@ -60,9 +64,11 @@ dependencies { // Generate NoOp implementations ksp(project(":tools:noopfactory")) - // OpenTracing - api(libs.bundles.openTracing) - + testImplementation(testFixtures(project(":dd-sdk-android-core"))) + testImplementation(libs.okHttp) + testImplementation(libs.bundles.jUnit5) + testImplementation(libs.bundles.testTools) + testImplementation(libs.systemStubsJupiter) testImplementation(project(":tools:unit")) { attributes { attribute( @@ -71,13 +77,16 @@ dependencies { ) } } - testImplementation(testFixtures(project(":dd-sdk-android-core"))) - testImplementation(libs.okHttp) - testImplementation(libs.bundles.jUnit5) - testImplementation(libs.bundles.testTools) - testImplementation(libs.systemStubsJupiter) unmock(libs.robolectric) + + // Test Fixtures + testFixturesImplementation(libs.gson) + testFixturesImplementation(libs.kotlin) + testFixturesImplementation(libs.okHttp) + testFixturesImplementation(libs.bundles.jUnit5) + testFixturesImplementation(libs.bundles.testTools) + testFixturesImplementation(project(":features:dd-sdk-android-trace-internal")) } unMock { diff --git a/features/dd-sdk-android-trace/consumer-rules.pro b/features/dd-sdk-android-trace/consumer-rules.pro index d6ab88b50b..8f3cd5b51d 100644 --- a/features/dd-sdk-android-trace/consumer-rules.pro +++ b/features/dd-sdk-android-trace/consumer-rules.pro @@ -1,4 +1,5 @@ --keepnames class io.opentracing.util.GlobalTracer { - public boolean isRegistered(); +-keepnames class com.datadog.android.trace.GlobalDatadogTracer { + public com.datadog.android.trace.api.tracer.DatadogTracer getOrNull(); + public static com.datadog.android.trace.GlobalDatadogTracer INSTANCE; } -keepclassmembernames class org.jctools.** { *; } diff --git a/features/dd-sdk-android-trace/src/main/json/trace/span-schema.json b/features/dd-sdk-android-trace/src/main/json/trace/span-schema.json index 1184e879b5..8701c68e21 100644 --- a/features/dd-sdk-android-trace/src/main/json/trace/span-schema.json +++ b/features/dd-sdk-android-trace/src/main/json/trace/span-schema.json @@ -16,17 +16,9 @@ "error", "type", "meta", - "metrics", - "device", - "os" + "metrics" ], "properties": { - "device": { - "$ref": "_common-schema.json#/properties/device" - }, - "os": { - "$ref": "_common-schema.json#/properties/os" - }, "trace_id": { "type": "string", "description": "The id of the trace this Span belongs to", @@ -260,6 +252,12 @@ } }, "readOnly": true + }, + "device": { + "$ref": "_common-schema.json#/properties/device" + }, + "os": { + "$ref": "_common-schema.json#/properties/os" } }, "required": [ diff --git a/features/dd-sdk-android-trace/src/main/kotlin/com/datadog/android/trace/AndroidTracer.kt b/features/dd-sdk-android-trace/src/main/kotlin/com/datadog/android/trace/AndroidTracer.kt deleted file mode 100644 index eaaee2d75d..0000000000 --- a/features/dd-sdk-android-trace/src/main/kotlin/com/datadog/android/trace/AndroidTracer.kt +++ /dev/null @@ -1,341 +0,0 @@ -/* - * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. - * This product includes software developed at Datadog (https://www.datadoghq.com/). - * Copyright 2016-Present Datadog, Inc. - */ - -package com.datadog.android.trace - -import android.util.Log -import androidx.annotation.FloatRange -import com.datadog.android.Datadog -import com.datadog.android.api.InternalLogger -import com.datadog.android.api.SdkCore -import com.datadog.android.api.context.DatadogContext -import com.datadog.android.api.feature.Feature -import com.datadog.android.api.feature.FeatureSdkCore -import com.datadog.android.internal.concurrent.CompletableFuture -import com.datadog.android.trace.internal.SpanAttributes -import com.datadog.android.trace.internal.TracingFeature -import com.datadog.android.trace.internal.addActiveTraceToContext -import com.datadog.android.trace.internal.data.NoOpWriter -import com.datadog.android.trace.internal.handlers.AndroidSpanLogsHandler -import com.datadog.android.trace.internal.removeActiveTraceFromContext -import com.datadog.android.trace.internal.utils.traceIdAsHexString -import com.datadog.legacy.trace.api.Config -import com.datadog.legacy.trace.common.writer.Writer -import com.datadog.legacy.trace.context.ScopeListener -import com.datadog.opentracing.DDTracer -import com.datadog.opentracing.LogHandler -import io.opentracing.Span -import io.opentracing.log.Fields -import io.opentracing.tag.Tag -import java.security.SecureRandom -import java.util.Properties -import java.util.Random - -/** - * A class enabling Datadog tracing features. - * - * It allows you to create [DDSpan] and send them to Datadog servers. - * - * You can have multiple tracers configured in your application, each with their own settings. - * - */ -class AndroidTracer internal constructor( - private val sdkCore: FeatureSdkCore, - config: Config, - writer: Writer, - random: Random, - private val logsHandler: LogHandler, - private val bundleWithRum: Boolean -) : DDTracer(config, writer, random) { - - init { - addScopeListener(object : ScopeListener { - override fun afterScopeActivated() { - val activeContext = activeSpan()?.context() - if (activeContext != null) { - val activeSpanId = activeContext.toSpanId() - val activeTraceId = activeContext.traceIdAsHexString() - sdkCore.addActiveTraceToContext(activeTraceId, activeSpanId) - } - } - - override fun afterScopeClosed() { - sdkCore.removeActiveTraceFromContext() - } - }) - } - - // region Tracer - - override fun buildSpan(operationName: String): DDSpanBuilder { - return DDSpanBuilder(operationName, scopeManager()) - .withLogHandler(logsHandler) - .withInternalLogger(sdkCore.internalLogger) - .withRumContext() - } - - // endregion - - /** - * Builds a [AndroidTracer] instance. - */ - class Builder - internal constructor( - private val sdkCore: FeatureSdkCore, - private val logsHandler: LogHandler - ) { - - private var tracingHeaderTypes: Set = - setOf(TracingHeaderType.DATADOG, TracingHeaderType.TRACECONTEXT) - private var bundleWithRumEnabled: Boolean = true - private var sampleRate: Double = DEFAULT_SAMPLE_RATE - - // TODO RUM-3786 should have a nicer call chain - private var serviceName: String = "" - get() { - return field.ifEmpty { - val service = sdkCore.service - if (service.isEmpty()) { - sdkCore.internalLogger.log( - InternalLogger.Level.ERROR, - InternalLogger.Target.USER, - { DEFAULT_SERVICE_NAME_IS_MISSING_ERROR_MESSAGE } - ) - } - service - } - } - private var partialFlushThreshold = DEFAULT_PARTIAL_MIN_FLUSH - private var random: Random = SecureRandom() - - private val globalTags: MutableMap = mutableMapOf() - - /** - * @param sdkCore SDK instance to bind to. If not provided, default instance will be used. - */ - @JvmOverloads - constructor(sdkCore: SdkCore = Datadog.getInstance()) : this( - sdkCore as FeatureSdkCore, - AndroidSpanLogsHandler(sdkCore) - ) - - // region Public API - - /** - * Builds a [AndroidTracer] based on the current state of this Builder. - */ - fun build(): AndroidTracer { - val tracingFeature = sdkCore.getFeature(Feature.TRACING_FEATURE_NAME) - ?.unwrap() - - if (tracingFeature == null) { - sdkCore.internalLogger.log( - InternalLogger.Level.ERROR, - InternalLogger.Target.USER, - { TRACING_NOT_ENABLED_ERROR_MESSAGE } - ) - } - - return AndroidTracer( - sdkCore, - config(), - tracingFeature?.legacyTracerWriter ?: NoOpWriter(), - random, - logsHandler, - bundleWithRumEnabled - ) - } - - /** - * Sets the tracing header styles that may be injected by this tracer. - * @param headerTypes the list of header types injected (default = datadog style headers) - */ - fun setTracingHeaderTypes(headerTypes: Set): Builder { - this.tracingHeaderTypes = headerTypes - return this - } - - /** - * Sets the service name that will appear in your traces. - * @param service the service name (default = application package name) - */ - fun setService(service: String): Builder { - this.serviceName = service - return this - } - - /** - * Sets the partial flush threshold. When this threshold is reached (you have a specific - * amount of spans closed waiting) the flush mechanism will be triggered and all the pending - * closed spans will be processed in order to be sent to the intake. - * @param threshold the threshold value (default = 5) - */ - fun setPartialFlushThreshold(threshold: Int): Builder { - this.partialFlushThreshold = threshold - return this - } - - /** - * Adds a global tag which will be appended to all spans created with the built tracer. - * - * @param key the tag key - * @param value the tag value - */ - @Deprecated( - replaceWith = ReplaceWith("addTag"), - message = "addGlobalTag is deprecated, please use addTag instead", - level = DeprecationLevel.WARNING - ) - fun addGlobalTag(key: String, value: String): Builder { - return addTag(key, value) - } - - /** - * Adds a global tag which will be appended to all spans created with the built tracer. - * @param key the tag key - * @param value the tag value - */ - fun addTag(key: String, value: String): Builder { - this.globalTags[key] = value - return this - } - - /** - * Enables the trace bundling with the current active View. If this feature is enabled all - * the spans from this moment on will be bundled with the current view information and you - * will be able to see all the traces sent during a specific view in the Rum Explorer. - * @param enabled true by default - */ - fun setBundleWithRumEnabled(enabled: Boolean): Builder { - bundleWithRumEnabled = enabled - return this - } - - /** - * Sets the sample rate of spans. - * @param sampleRate the sample rate as a percentage between 0 and 100 (default is 100%) - */ - fun setSampleRate( - @FloatRange(from = 0.0, to = 100.0) sampleRate: Double - ): Builder { - this.sampleRate = sampleRate - return this - } - - // endregion - - // region Internal - - internal fun withRandom(random: Random): Builder { - this.random = random - return this - } - - internal fun properties(): Properties { - val properties = Properties() - properties.setProperty(Config.SERVICE_NAME, serviceName) - properties.setProperty( - Config.PARTIAL_FLUSH_MIN_SPANS, - partialFlushThreshold.toString() - ) - properties.setProperty( - Config.TAGS, - globalTags.map { "${it.key}:${it.value}" }.joinToString(",") - ) - properties.setProperty( - Config.TRACE_SAMPLE_RATE, - (sampleRate / DEFAULT_SAMPLE_RATE).toString() - ) - - val propagationStyles = tracingHeaderTypes.joinToString(",") - properties.setProperty(Config.PROPAGATION_STYLE_EXTRACT, propagationStyles) - properties.setProperty(Config.PROPAGATION_STYLE_INJECT, propagationStyles) - - return properties - } - - private fun config(): Config { - return Config.get(properties()) - } - - // endregion - } - - // region Internal - - private fun DDSpanBuilder.withRumContext(): DDSpanBuilder { - if (bundleWithRum) { - val rumFeature = sdkCore.getFeature(Feature.RUM_FEATURE_NAME) - if (rumFeature != null) { - val lazyContext = CompletableFuture() - rumFeature.withContext(withFeatureContexts = setOf(Feature.RUM_FEATURE_NAME)) { - lazyContext.complete(it) - } - withTag(DATADOG_CONTEXT_TAG, lazyContext) - } - } - return this - } - - // endregion - - override fun toString(): String { - return "AndroidTracer/${super.toString()}" - } - - companion object { - internal const val DEFAULT_SAMPLE_RATE = 100.0 - - internal const val TRACING_NOT_ENABLED_ERROR_MESSAGE = - "You're trying to create an AndroidTracer instance, " + - "but either the SDK was not initialized or the Tracing feature was " + - "not registered/initialized. No tracing data will be sent." - internal const val DEFAULT_SERVICE_NAME_IS_MISSING_ERROR_MESSAGE = - "Default service name is missing during" + - " AndroidTracer.Builder creation, did you initialize SDK?" - - // the minimum closed spans required for triggering a flush and deliver - // everything to the writer - internal const val DEFAULT_PARTIAL_MIN_FLUSH = 5 - - internal const val SPAN_ID_BIT_SIZE = 63 - - internal const val LOG_STATUS = "status" - - internal val DATADOG_CONTEXT_TAG = object : Tag> { - override fun getKey(): String = SpanAttributes.DATADOG_INITIAL_CONTEXT - - override fun set(span: Span?, value: CompletableFuture?) { - span?.setTag(this, value) - } - } - - /** - * Helper method to attach a Throwable to a specific Span. - * The Throwable information (class name, message and stacktrace) will be added to the - * provided Span as standard Error Tags. - * @param span the active Span - * @param throwable the Throwable you wan to log - */ - @JvmStatic - fun logThrowable(span: Span, throwable: Throwable) { - val fieldsMap = mapOf(Fields.ERROR_OBJECT to throwable) - span.log(fieldsMap) - } - - /** - * Helper method to attach an error message to a specific Span. - * The error message will be logged with ERROR status and can be seen in logs attached to the span. - * @param span the active Span - * @param message the error message you want to attach - */ - @JvmStatic - fun logErrorMessage(span: Span, message: String) { - val fieldsMap = mapOf(Fields.MESSAGE to message, LOG_STATUS to Log.ERROR) - span.log(fieldsMap) - } - } -} diff --git a/features/dd-sdk-android-trace/src/main/kotlin/com/datadog/android/trace/DatadogTracing.kt b/features/dd-sdk-android-trace/src/main/kotlin/com/datadog/android/trace/DatadogTracing.kt new file mode 100644 index 0000000000..08c69c9426 --- /dev/null +++ b/features/dd-sdk-android-trace/src/main/kotlin/com/datadog/android/trace/DatadogTracing.kt @@ -0,0 +1,105 @@ +/* + * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. + * This product includes software developed at Datadog (https://www.datadoghq.com/). + * Copyright 2016-Present Datadog, Inc. + */ +package com.datadog.android.trace + +import com.datadog.android.Datadog +import com.datadog.android.api.InternalLogger +import com.datadog.android.api.SdkCore +import com.datadog.android.api.feature.Feature +import com.datadog.android.api.feature.FeatureSdkCore +import com.datadog.android.trace.api.tracer.DatadogTracerBuilder +import com.datadog.android.trace.api.tracer.NoOpDatadogTracerBuilder +import com.datadog.android.trace.internal.DatadogSpanWriterWrapper +import com.datadog.android.trace.internal.DatadogTracerBuilderAdapter +import com.datadog.android.trace.internal.DatadogTracingToolkit +import com.datadog.trace.common.writer.NoOpWriter +import com.datadog.trace.core.CoreTracer + +/** + * Object responsible for providing tracing capabilities in the Datadog SDK. + */ +@SuppressWarnings("UndocumentedPublicFunction") +object DatadogTracing { + /** + * Creates and returns a new instance of [DatadogTracerBuilder]. This method initializes and configures the + * tracer builder based on the provided SDK core instance and its setup. If certain required configurations + * are missing, a no-operation tracer builder is returned instead. + * + * @param sdkCore An instance of [SdkCore], which provides configuration and features needed for initializing + * the tracer builder. + * @return A configured instance of [DatadogTracerBuilder], or a no-operation implementation if the necessary + * configurations or features are unavailable. + */ + fun newTracerBuilder(sdkCore: SdkCore = Datadog.getInstance()): DatadogTracerBuilder = when { + DatadogTracingToolkit.testBuilderProvider != null -> { + DatadogTracingToolkit.testBuilderProvider as DatadogTracerBuilder + } + sdkCore !is FeatureSdkCore -> { + NoOpDatadogTracerBuilder() + } + else -> { + val internalLogger = sdkCore.internalLogger + val tracingFeature = sdkCore.getFeature(Feature.TRACING_FEATURE_NAME)?.unwrap() + val internalCoreWriterProvider = tracingFeature as? InternalCoreWriterProvider + val writer = (internalCoreWriterProvider?.getCoreTracerWriter() as? DatadogSpanWriterWrapper)?.delegate + + when { + null == tracingFeature -> internalLogger.log( + InternalLogger.Level.ERROR, + InternalLogger.Target.USER, + { ErrorMessages.TRACING_NOT_ENABLED_ERROR_MESSAGE } + ) + + null == internalCoreWriterProvider -> internalLogger.log( + InternalLogger.Level.ERROR, + InternalLogger.Target.MAINTAINER, + { ErrorMessages.WRITER_PROVIDER_INTERFACE_NOT_IMPLEMENTED_ERROR_MESSAGE } + ) + + null == writer -> internalLogger.log( + InternalLogger.Level.ERROR, + InternalLogger.Target.USER, + { + ErrorMessages.buildWrongWrapperMessage( + internalCoreWriterProvider.getCoreTracerWriter().javaClass + ) + } + ) + + sdkCore.service.isEmpty() -> internalLogger.log( + InternalLogger.Level.ERROR, + InternalLogger.Target.USER, + { ErrorMessages.DEFAULT_SERVICE_NAME_IS_MISSING_ERROR_MESSAGE } + ) + } + + DatadogTracerBuilderAdapter( + sdkCore = sdkCore, + serviceName = sdkCore.service, + delegate = CoreTracer.CoreTracerBuilder(sdkCore.internalLogger).writer(writer ?: NoOpWriter()) + ) + } + } + + internal object ErrorMessages { + const val TRACING_NOT_ENABLED_ERROR_MESSAGE: String = + "You're trying to create an DatadogTracer instance, " + + "but either the SDK was not initialized or the Tracing feature was " + + "not registered. No tracing data will be sent." + + const val WRITER_PROVIDER_INTERFACE_NOT_IMPLEMENTED_ERROR_MESSAGE: String = + "The Tracing feature is not implementing the InternalCoreWriterProvider interface." + + " No tracing data will be sent." + + const val DEFAULT_SERVICE_NAME_IS_MISSING_ERROR_MESSAGE: String = + "Default service name is missing during DatadogTracerBuilder creation, did you initialize SDK?" + + fun buildWrongWrapperMessage(cls: Class<*>?): String { + return "You're trying to create an DatadogTracer instance, " + + "but provided ${cls?.canonicalName} writer wrapper is not supported." + } + } +} diff --git a/features/dd-sdk-android-trace/src/main/kotlin/com/datadog/android/trace/GlobalDatadogTracer.kt b/features/dd-sdk-android-trace/src/main/kotlin/com/datadog/android/trace/GlobalDatadogTracer.kt new file mode 100644 index 0000000000..88f1426e37 --- /dev/null +++ b/features/dd-sdk-android-trace/src/main/kotlin/com/datadog/android/trace/GlobalDatadogTracer.kt @@ -0,0 +1,59 @@ +/* + * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. + * This product includes software developed at Datadog (https://www.datadoghq.com/). + * Copyright 2016-Present Datadog, Inc. + */ +package com.datadog.android.trace + +import com.datadog.android.trace.api.tracer.DatadogTracer +import com.datadog.android.trace.api.tracer.NoOpDatadogTracer +import java.util.concurrent.atomic.AtomicReference + +/** + * A holder object for managing and retrieving a global instance of the [DatadogTracer]. + * + * This object is used to share same instance of [DatadogTracer] across different integrations such as + * `OkHttp`, Kotlin's coroutines, ect. + */ +object GlobalDatadogTracer { + + private val instance = AtomicReference() + + /** + * Registers the provided tracer as the global tracer if no tracer is currently registered. + * + * @param tracer The tracer to register as the global tracer. + * @return `true` if the tracer was successfully registered, or `false` if a tracer was already registered. + */ + @Synchronized + fun registerIfAbsent(tracer: DatadogTracer): Boolean { + return instance.compareAndSet(null, tracer) + } + + /** + * Retrieves the current active tracer for Datadog, or a no-operation tracer if none is active. + * + * @return The current instance of [DatadogTracer] if available. Otherwise, an instance of + * [NoOpDatadogTracer] that performs no operations. + */ + fun get(): DatadogTracer = getOrNull() ?: NoOpDatadogTracer() + + /** + * Retrieves the current instance of the DatadogTracer, if available. + * + * @return An instance of [DatadogTracer] or null. + */ + fun getOrNull(): DatadogTracer? = instance.get() + + /** + * Clears the current instance of the global Datadog tracer. + * + * This method sets the internal tracer instance to null, effectively + * removing any active tracer currently held in the global state. + * The general purpose is to use it for test implementation. + */ + @Synchronized + fun clear() { + instance.set(null) + } +} diff --git a/features/dd-sdk-android-trace/src/main/kotlin/com/datadog/android/trace/InternalCoreWriterProvider.kt b/features/dd-sdk-android-trace/src/main/kotlin/com/datadog/android/trace/InternalCoreWriterProvider.kt index 8773551a36..b0f2a30f78 100644 --- a/features/dd-sdk-android-trace/src/main/kotlin/com/datadog/android/trace/InternalCoreWriterProvider.kt +++ b/features/dd-sdk-android-trace/src/main/kotlin/com/datadog/android/trace/InternalCoreWriterProvider.kt @@ -7,6 +7,7 @@ package com.datadog.android.trace import com.datadog.android.lint.InternalApi +import com.datadog.android.trace.api.span.DatadogSpanWriter /** * Internal interface to provide the core writer to the tracer. @@ -22,5 +23,5 @@ interface InternalCoreWriterProvider { /** * Returns the core writer used by the tracer. */ - fun getCoreTracerWriter(): com.datadog.trace.common.writer.Writer + fun getCoreTracerWriter(): DatadogSpanWriter } diff --git a/features/dd-sdk-android-trace/src/main/kotlin/com/datadog/android/trace/SpanExt.kt b/features/dd-sdk-android-trace/src/main/kotlin/com/datadog/android/trace/SpanExt.kt index 8befe8c20c..cb1bc0cce0 100644 --- a/features/dd-sdk-android-trace/src/main/kotlin/com/datadog/android/trace/SpanExt.kt +++ b/features/dd-sdk-android-trace/src/main/kotlin/com/datadog/android/trace/SpanExt.kt @@ -6,49 +6,29 @@ package com.datadog.android.trace -import io.opentracing.Span -import io.opentracing.util.GlobalTracer +import com.datadog.android.trace.api.span.DatadogSpan /** - * Helper method to attach a Throwable to this [Span]. - * The Throwable information (class name, message and stacktrace) will be added to - * this [Span] as standard Error Tags. - * @param throwable the [Throwable] you wan to log - */ -fun Span.setError(throwable: Throwable) { - AndroidTracer.logThrowable(this, throwable) -} - -/** - * Helper method to attach an error message to this [Span]. - * The error message will be logged with ERROR status and can be seen in logs attached to the span. - * @param message the error message you want to attach. - */ -fun Span.setError(message: String) { - AndroidTracer.logErrorMessage(this, message) -} - -/** - * Wraps the provided lambda within a [Span]. + * Wraps the provided lambda within a [DatadogSpan]. * @param T the type returned by the lambda - * @param operationName the name of the [Span] created around the lambda - * @param parentSpan the parent [Span] (default is `null`) - * @param activate whether the created [Span] should be made active for the current thread + * @param operationName the name of the [DatadogSpan] created around the lambda + * @param parentSpan the parent [DatadogSpan] (default is `null`) + * @param activate whether the created [DatadogSpan] should be made active for the current thread * (default is `true`) - * @param block the lambda function traced by this newly created [Span] + * @param block the lambda function traced by this newly created [DatadogSpan] * */ @SuppressWarnings("TooGenericExceptionCaught") inline fun withinSpan( operationName: String, - parentSpan: Span? = null, + parentSpan: DatadogSpan? = null, activate: Boolean = true, - block: Span.() -> T + block: DatadogSpan.() -> T ): T { - val tracer = GlobalTracer.get() + val tracer = GlobalDatadogTracer.get() val span = tracer.buildSpan(operationName) - .asChildOf(parentSpan) + .withParentSpan(parentSpan) .start() val scope = if (activate) tracer.activateSpan(span) else null @@ -56,7 +36,7 @@ inline fun withinSpan( return try { span.block() } catch (e: Throwable) { - span.setError(e) + span.logThrowable(e) throw e } finally { span.finish() diff --git a/features/dd-sdk-android-trace/src/main/kotlin/com/datadog/android/trace/internal/DatadogPropagationAdapter.kt b/features/dd-sdk-android-trace/src/main/kotlin/com/datadog/android/trace/internal/DatadogPropagationAdapter.kt new file mode 100644 index 0000000000..6a7aad0e17 --- /dev/null +++ b/features/dd-sdk-android-trace/src/main/kotlin/com/datadog/android/trace/internal/DatadogPropagationAdapter.kt @@ -0,0 +1,46 @@ +/* + * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. + * This product includes software developed at Datadog (https://www.datadoghq.com/). + * Copyright 2016-Present Datadog, Inc. + */ + +package com.datadog.android.trace.internal + +import com.datadog.android.api.InternalLogger +import com.datadog.android.trace.api.propagation.DatadogPropagation +import com.datadog.android.trace.api.span.DatadogSpanContext +import com.datadog.trace.bootstrap.instrumentation.api.AgentPropagation +import kotlin.reflect.KClass + +internal class DatadogPropagationAdapter( + private val internalLogger: InternalLogger, + private val delegate: AgentPropagation +) : DatadogPropagation { + + override fun inject( + context: DatadogSpanContext, + carrier: C, + setter: (carrier: C, key: String, value: String) -> Unit + ) { + if (context !is DatadogSpanContextAdapter) { + internalLogger.log( + InternalLogger.Level.ERROR, + InternalLogger.Target.USER, + { constructErrorMessage(context::class) } + ) + return + } + delegate.inject(context.delegate, carrier, setter) + } + + override fun extract( + carrier: C, + getter: (carrier: C, classifier: (String, String) -> Boolean) -> Unit + ): DatadogSpanContext? { + return delegate.extract(carrier) { car, cls -> getter(car, cls::accept) } + ?.let { DatadogSpanContextAdapter(it) } + } + + private fun constructErrorMessage(klass: KClass<*>) = "DatadogPropagationAdapter supports only" + + " DatadogSpanContextAdapter instancies for injection but ${klass.simpleName} is given" +} diff --git a/features/dd-sdk-android-trace/src/main/kotlin/com/datadog/android/trace/internal/DatadogPropagationHelper.kt b/features/dd-sdk-android-trace/src/main/kotlin/com/datadog/android/trace/internal/DatadogPropagationHelper.kt new file mode 100644 index 0000000000..67bee9a3f6 --- /dev/null +++ b/features/dd-sdk-android-trace/src/main/kotlin/com/datadog/android/trace/internal/DatadogPropagationHelper.kt @@ -0,0 +1,53 @@ +/* + * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. + * This product includes software developed at Datadog (https://www.datadoghq.com/). + * Copyright 2016-Present Datadog, Inc. + */ +package com.datadog.android.trace.internal + +import com.datadog.android.lint.InternalApi +import com.datadog.android.trace.api.span.DatadogSpanContext +import com.datadog.trace.api.DDSpanId +import com.datadog.trace.api.DDTraceId +import com.datadog.trace.core.propagation.ExtractedContext + +/** + * For internal usage only. + * Helper class for handling Datadog context propagation. + */ +@InternalApi +class DatadogPropagationHelper internal constructor() { + /** + * Determines if the provided [DatadogSpanContext] represents an extracted context. + * + * @param context The [DatadogSpanContext] to be evaluated. + * @return True if the context is identified as an extracted context, otherwise false. + */ + fun isExtractedContext(context: DatadogSpanContext): Boolean { + if (context !is DatadogSpanContextAdapter) return false + return context.delegate is ExtractedContext + } + + /** + * Creates a [DatadogSpanContext] object that represents an extracted context from the given parameters. + * + * @param traceId The unique identifier for the trace. + * @param spanId The unique identifier for the span. + * @param samplingPriority The sampling priority value for determining the trace's sampling behavior. + * @return A [DatadogSpanContext] instance containing the extracted context. + */ + fun createExtractedContext( + traceId: String, + spanId: String, + samplingPriority: Int + ): DatadogSpanContext = DatadogSpanContextAdapter( + ExtractedContext( + DDTraceId.fromHexOrDefault(traceId, DDTraceId.ZERO), + DDSpanId.fromHexOrDefault(spanId, DDSpanId.ZERO), + samplingPriority, + null, + null, + null + ) + ) +} diff --git a/features/dd-sdk-android-trace/src/main/kotlin/com/datadog/android/trace/internal/DatadogScopeAdapter.kt b/features/dd-sdk-android-trace/src/main/kotlin/com/datadog/android/trace/internal/DatadogScopeAdapter.kt new file mode 100644 index 0000000000..0b5f7d6c44 --- /dev/null +++ b/features/dd-sdk-android-trace/src/main/kotlin/com/datadog/android/trace/internal/DatadogScopeAdapter.kt @@ -0,0 +1,15 @@ +/* + * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. + * This product includes software developed at Datadog (https://www.datadoghq.com/). + * Copyright 2016-Present Datadog, Inc. + */ +package com.datadog.android.trace.internal + +import com.datadog.android.trace.api.scope.DatadogScope +import com.datadog.trace.bootstrap.instrumentation.api.AgentScope + +internal class DatadogScopeAdapter( + internal val delegate: AgentScope +) : DatadogScope { + override fun close() = delegate.close() +} diff --git a/features/dd-sdk-android-trace/src/main/kotlin/com/datadog/android/trace/internal/DatadogScopeListenerAdapter.kt b/features/dd-sdk-android-trace/src/main/kotlin/com/datadog/android/trace/internal/DatadogScopeListenerAdapter.kt new file mode 100644 index 0000000000..e70eb8d21f --- /dev/null +++ b/features/dd-sdk-android-trace/src/main/kotlin/com/datadog/android/trace/internal/DatadogScopeListenerAdapter.kt @@ -0,0 +1,16 @@ +/* + * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. + * This product includes software developed at Datadog (https://www.datadoghq.com/). + * Copyright 2016-Present Datadog, Inc. + */ +package com.datadog.android.trace.internal + +import com.datadog.android.trace.api.scope.DatadogScopeListener +import com.datadog.trace.api.scopemanager.ScopeListener + +internal class DatadogScopeListenerAdapter( + internal val delegate: DatadogScopeListener +) : ScopeListener { + override fun afterScopeClosed() = delegate.afterScopeClosed() + override fun afterScopeActivated() = delegate.afterScopeActivated() +} diff --git a/features/dd-sdk-android-trace/src/main/kotlin/com/datadog/android/trace/internal/DatadogSpanAdapter.kt b/features/dd-sdk-android-trace/src/main/kotlin/com/datadog/android/trace/internal/DatadogSpanAdapter.kt new file mode 100644 index 0000000000..57cf1734bf --- /dev/null +++ b/features/dd-sdk-android-trace/src/main/kotlin/com/datadog/android/trace/internal/DatadogSpanAdapter.kt @@ -0,0 +1,116 @@ +/* + * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. + * This product includes software developed at Datadog (https://www.datadoghq.com/). + * Copyright 2016-Present Datadog, Inc. + */ +package com.datadog.android.trace.internal + +import com.datadog.android.trace.api.span.DatadogSpan +import com.datadog.android.trace.api.trace.DatadogTraceId +import com.datadog.trace.bootstrap.instrumentation.api.AgentSpan +import com.datadog.trace.core.DDSpan + +internal class DatadogSpanAdapter( + internal val delegate: AgentSpan, + private val spanLogger: DatadogSpanLogger +) : DatadogSpan { + + override val isRootSpan: Boolean get() = delegate is DDSpan && delegate.isRootSpan + + override val traceId: DatadogTraceId get() = DatadogTraceIdAdapter(delegate.traceId) + + override val parentSpanId: Long? get() = (delegate as? DDSpan)?.parentId + + override val samplingPriority: Int? get() = delegate.traceSamplingPriority + + override val durationNano: Long get() = delegate.durationNano + + override val startTimeNanos: Long get() = delegate.startTime + + override val localRootSpan: DatadogSpan? get() = delegate.localRootSpan?.let { DatadogSpanAdapter(it, spanLogger) } + + override var isError: Boolean? + get() = delegate.isError + set(value) { + if (value == null) return + delegate.isError = value + } + + override var resourceName: String? + get() = delegate.resourceName?.toString() + set(value) { + delegate.resourceName = value + } + + override var serviceName: String + get() = delegate.serviceName + set(value) { + delegate.serviceName = value + } + + override var operationName: String + get() = delegate.operationName.toString() + set(value) { + delegate.operationName = value + } + + override fun drop() = delegate.drop() + + override fun finish() = delegate.finish() + + override fun finish(finishMicros: Long) = delegate.finish(finishMicros) + + override fun context() = DatadogSpanContextAdapter(delegate.context()) + + override fun setTag(tag: String?, value: String?) { + delegate.setTag(tag, value) + } + + override fun setTag(tag: String?, value: Boolean) { + delegate.setTag(tag, value) + } + + override fun setTag(tag: String?, value: Number?) { + delegate.setTag(tag, value) + } + + override fun setTag(tag: String?, value: Any?) { + delegate.setTag(tag, value) + } + + override fun getTag(tag: String?): Any? { + return delegate.getTag(tag) + } + + override fun setMetric(key: String, value: Int) { + delegate.setMetric(key, value) + } + + override fun setErrorMessage(message: String?) { + delegate.setErrorMessage(message) + } + + override fun addThrowable(throwable: Throwable) { + delegate.addThrowable(throwable) + } + + override fun addThrowable(throwable: Throwable, errorPriority: Byte) { + delegate.addThrowable(throwable, errorPriority) + } + + override fun logThrowable(throwable: Throwable) { + spanLogger.log(throwable, this) + } + + override fun logErrorMessage(message: String) { + spanLogger.logErrorMessage(message, this) + } + + override fun logMessage(message: String) { + spanLogger.log(message, this) + } + + override fun logAttributes(attributes: Map) { + spanLogger.log(attributes, this) + } +} diff --git a/features/dd-sdk-android-trace/src/main/kotlin/com/datadog/android/trace/internal/DatadogSpanBuilderAdapter.kt b/features/dd-sdk-android-trace/src/main/kotlin/com/datadog/android/trace/internal/DatadogSpanBuilderAdapter.kt new file mode 100644 index 0000000000..7476f2c45d --- /dev/null +++ b/features/dd-sdk-android-trace/src/main/kotlin/com/datadog/android/trace/internal/DatadogSpanBuilderAdapter.kt @@ -0,0 +1,46 @@ +/* + * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. + * This product includes software developed at Datadog (https://www.datadoghq.com/). + * Copyright 2016-Present Datadog, Inc. + */ +package com.datadog.android.trace.internal + +import com.datadog.android.trace.api.span.DatadogSpan +import com.datadog.android.trace.api.span.DatadogSpanBuilder +import com.datadog.android.trace.api.span.DatadogSpanContext +import com.datadog.android.trace.api.span.DatadogSpanLink +import com.datadog.trace.bootstrap.instrumentation.api.AgentTracer + +internal class DatadogSpanBuilderAdapter( + private val delegate: AgentTracer.SpanBuilder, + private val spanLogger: DatadogSpanLogger +) : DatadogSpanBuilder { + + override fun ignoreActiveSpan() = apply { delegate.ignoreActiveSpan() } + + override fun start(): DatadogSpan = DatadogSpanAdapter(delegate.start(), spanLogger) + + override fun withOrigin(origin: String?) = apply { delegate.withOrigin(origin) } + + override fun withStartTimestamp(micros: Long) = apply { delegate.withStartTimestamp(micros) } + + override fun withTag(key: String, value: Double?): DatadogSpanBuilder = apply { delegate.withTag(key, value) } + + override fun withTag(key: String, value: Long?): DatadogSpanBuilder = apply { delegate.withTag(key, value) } + + override fun withTag(key: String, value: Any?): DatadogSpanBuilder = apply { delegate.withTag(key, value) } + + override fun withLink(link: DatadogSpanLink): DatadogSpanBuilder = apply { + delegate.withLink(DatadogSpanLinkAdapter(link)) + } + + override fun withResourceName(resourceName: String?): DatadogSpanBuilder = apply { + delegate.withResourceName(resourceName) + } + + override fun withParentContext(parentContext: DatadogSpanContext?): DatadogSpanBuilder = apply { + if (parentContext is DatadogSpanContextAdapter) delegate.asChildOf(parentContext.delegate) + } + + override fun withParentSpan(parentSpan: DatadogSpan?) = withParentContext(parentSpan?.context()) +} diff --git a/features/dd-sdk-android-trace/src/main/kotlin/com/datadog/android/trace/internal/DatadogSpanContextAdapter.kt b/features/dd-sdk-android-trace/src/main/kotlin/com/datadog/android/trace/internal/DatadogSpanContextAdapter.kt new file mode 100644 index 0000000000..55ab6584e9 --- /dev/null +++ b/features/dd-sdk-android-trace/src/main/kotlin/com/datadog/android/trace/internal/DatadogSpanContextAdapter.kt @@ -0,0 +1,35 @@ +/* + * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. + * This product includes software developed at Datadog (https://www.datadoghq.com/). + * Copyright 2016-Present Datadog, Inc. + */ +package com.datadog.android.trace.internal + +import com.datadog.android.trace.api.span.DatadogSpanContext +import com.datadog.android.trace.api.trace.DatadogTraceId +import com.datadog.trace.api.sampling.SamplingMechanism +import com.datadog.trace.bootstrap.instrumentation.api.AgentSpan +import com.datadog.trace.core.DDSpanContext +import com.datadog.trace.core.PendingTrace + +internal class DatadogSpanContextAdapter(internal val delegate: AgentSpan.Context) : DatadogSpanContext { + override val spanId: Long get() = delegate.spanId + override val samplingPriority: Int get() = delegate.traceSamplingPriority + override val tags: Map get() = ddSpanContext?.tags.orEmpty() + override val traceId: DatadogTraceId get() = DatadogTraceIdAdapter(delegate.traceId) + + private val ddSpanContext: DDSpanContext? + get() = delegate as? DDSpanContext + + override fun setSamplingPriority(samplingPriority: Int): Boolean { + return ddSpanContext?.setSamplingPriority(samplingPriority, SamplingMechanism.DEFAULT.toInt()) ?: false + } + + override fun setMetric(key: CharSequence?, value: Double) { + ddSpanContext?.setMetric(key, value) + } + + internal fun setTracingSamplingPriorityIfNecessary() { + (delegate.trace as? PendingTrace)?.setSamplingPriorityIfNecessary() + } +} diff --git a/features/dd-sdk-android-trace/src/main/kotlin/com/datadog/android/trace/internal/DatadogSpanIdConverter.kt b/features/dd-sdk-android-trace/src/main/kotlin/com/datadog/android/trace/internal/DatadogSpanIdConverter.kt new file mode 100644 index 0000000000..3040192e99 --- /dev/null +++ b/features/dd-sdk-android-trace/src/main/kotlin/com/datadog/android/trace/internal/DatadogSpanIdConverter.kt @@ -0,0 +1,36 @@ +/* + * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. + * This product includes software developed at Datadog (https://www.datadoghq.com/). + * Copyright 2016-Present Datadog, Inc. + */ +package com.datadog.android.trace.internal + +import com.datadog.android.lint.InternalApi +import com.datadog.trace.api.DDSpanId + +/** + * For library usage only. + * Interface for converting Datadog span IDs between hexadecimal and decimal representations. + */ +@InternalApi +class DatadogSpanIdConverter internal constructor() { + + /** + * Converts a hexadecimal string representation of an spanId into its equivalent long value. + * + * @param spanId The hexadecimal string to convert. + * @return The long value corresponding to the hexadecimal string. + * @throws NumberFormatException if the input string is not a valid hexadecimal representation or is null. + */ + @Throws(NumberFormatException::class) + fun fromHex(spanId: String): Long = DDSpanId.fromHex(spanId) + + /** + * Converts the given long-based span ID into a hexadecimal string representation, + * ensuring that the resulting string has a padded length sufficient for hexadecimal IDs. + * + * @param spanId The span ID to convert, represented as a long value. + * @return A hexadecimal string representation of the given span ID, padded as necessary. + */ + fun toHexStringPadded(spanId: Long): String = DDSpanId.toHexStringPadded(spanId) +} diff --git a/features/dd-sdk-android-trace/src/main/kotlin/com/datadog/android/trace/internal/DatadogSpanLinkAdapter.kt b/features/dd-sdk-android-trace/src/main/kotlin/com/datadog/android/trace/internal/DatadogSpanLinkAdapter.kt new file mode 100644 index 0000000000..cd6da1c127 --- /dev/null +++ b/features/dd-sdk-android-trace/src/main/kotlin/com/datadog/android/trace/internal/DatadogSpanLinkAdapter.kt @@ -0,0 +1,26 @@ +/* + * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. + * This product includes software developed at Datadog (https://www.datadoghq.com/). + * Copyright 2016-Present Datadog, Inc. + */ +package com.datadog.android.trace.internal + +import com.datadog.android.trace.api.span.DatadogSpanLink +import com.datadog.trace.api.DDTraceId +import com.datadog.trace.bootstrap.instrumentation.api.AgentSpanLink +import com.datadog.trace.bootstrap.instrumentation.api.SpanLink +import com.datadog.trace.bootstrap.instrumentation.api.SpanLinkAttributes + +internal class DatadogSpanLinkAdapter(delegate: DatadogSpanLink) : + SpanLink( + /* traceId */ + DDTraceId.fromHex(delegate.traceId.toHexString()), + /* spanId */ + delegate.spanId, + /* traceFlags */ + if (delegate.sampled) AgentSpanLink.SAMPLED_FLAG else AgentSpanLink.DEFAULT_FLAGS, + /* traceState */ + delegate.traceStrace, + /* attributes */ + SpanLinkAttributes.fromMap(delegate.attributes) + ) diff --git a/features/dd-sdk-android-trace/src/main/kotlin/com/datadog/android/trace/internal/DatadogSpanLogger.kt b/features/dd-sdk-android-trace/src/main/kotlin/com/datadog/android/trace/internal/DatadogSpanLogger.kt new file mode 100644 index 0000000000..b3eccacd5a --- /dev/null +++ b/features/dd-sdk-android-trace/src/main/kotlin/com/datadog/android/trace/internal/DatadogSpanLogger.kt @@ -0,0 +1,105 @@ +/* + * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. + * This product includes software developed at Datadog (https://www.datadoghq.com/). + * Copyright 2016-Present Datadog, Inc. + */ +package com.datadog.android.trace.internal + +import android.util.Log +import com.datadog.android.api.InternalLogger +import com.datadog.android.api.feature.Feature +import com.datadog.android.api.feature.FeatureSdkCore +import com.datadog.android.internal.utils.loggableStackTrace +import com.datadog.android.log.LogAttributes +import com.datadog.android.trace.api.DatadogTracingConstants +import com.datadog.android.trace.api.span.DatadogSpan + +internal class DatadogSpanLogger( + private val sdkCore: FeatureSdkCore +) { + + fun log(message: String, span: DatadogSpan) { + val fields = mutableMapOf(DatadogTracingConstants.LogAttributes.EVENT to message) + extractError(fields, span) + sendLogEvent(fields, span) + } + + fun logErrorMessage(message: String, span: DatadogSpan) { + val fields = mutableMapOf( + DatadogTracingConstants.LogAttributes.MESSAGE to message, + DatadogTracingConstants.LogAttributes.STATUS to Log.ERROR + ) + extractError(fields, span) + sendLogEvent(fields, span) + } + + fun log(throwable: Throwable, span: DatadogSpan) { + val fields = mutableMapOf(DatadogTracingConstants.LogAttributes.ERROR_OBJECT to throwable) + extractError(fields, span) + sendLogEvent(fields, span) + } + + fun log(attributes: Map, span: DatadogSpan) { + extractError(attributes.toMutableMap(), span) + sendLogEvent(attributes.toMutableMap(), span) + } + + private fun extractError( + map: MutableMap, + span: DatadogSpan + ) { + val throwable = map.remove(DatadogTracingConstants.LogAttributes.ERROR_OBJECT) as? Throwable + val kind = map.remove(LogAttributes.ERROR_KIND) + val errorType = kind?.toString() ?: throwable?.javaClass?.name + + if (errorType != null) { + val stackField = map.remove(DatadogTracingConstants.LogAttributes.STACK) + val msgField = map[LogAttributes.MESSAGE] + val stack = stackField?.toString() ?: throwable?.loggableStackTrace() + val message = msgField?.toString() ?: throwable?.message + + span.isError = true + span.setTag(DatadogTracingConstants.Tags.KEY_ERROR_TYPE, errorType) + span.setTag(DatadogTracingConstants.Tags.KEY_ERROR_MSG, message) + span.setTag(DatadogTracingConstants.Tags.KEY_ERROR_STACK, stack) + } + } + + private fun sendLogEvent( + fields: MutableMap, + span: DatadogSpan + ) { + val logsFeature = sdkCore.getFeature(Feature.LOGS_FEATURE_NAME) + + if (logsFeature != null && fields.isNotEmpty()) { + val message = fields.remove(DatadogTracingConstants.LogAttributes.MESSAGE) + ?.toString() ?: DEFAULT_EVENT_MESSAGE + val logStatus = fields.remove(DatadogTracingConstants.LogAttributes.STATUS) ?: Log.VERBOSE + fields[LogAttributes.DD_TRACE_ID] = span.context().traceId.toHexString() + fields[LogAttributes.DD_SPAN_ID] = span.context().spanId.toString() + val timestamp = System.currentTimeMillis() + logsFeature.sendEvent( + buildMap { + put("type", "span_log") + put("loggerName", TRACE_LOGGER_NAME) + put("message", message) + put("attributes", fields) + put("timestamp", timestamp) + put("logStatus", logStatus) + } + ) + } else if (logsFeature == null) { + sdkCore.internalLogger.log( + InternalLogger.Level.WARN, + InternalLogger.Target.USER, + { MISSING_LOG_FEATURE_INFO } + ) + } + } + + companion object Companion { + internal const val TRACE_LOGGER_NAME = "trace" + internal const val DEFAULT_EVENT_MESSAGE = "Span event" + internal const val MISSING_LOG_FEATURE_INFO = "Requested to write span log, but Logs feature is not registered." + } +} diff --git a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/opentracing/scopemanager/DDScope.java b/features/dd-sdk-android-trace/src/main/kotlin/com/datadog/android/trace/internal/DatadogSpanWriterWrapper.kt similarity index 51% rename from features/dd-sdk-android-trace-internal/src/main/java/com/datadog/opentracing/scopemanager/DDScope.java rename to features/dd-sdk-android-trace/src/main/kotlin/com/datadog/android/trace/internal/DatadogSpanWriterWrapper.kt index b59aafbe3e..eaac290390 100644 --- a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/opentracing/scopemanager/DDScope.java +++ b/features/dd-sdk-android-trace/src/main/kotlin/com/datadog/android/trace/internal/DatadogSpanWriterWrapper.kt @@ -3,16 +3,9 @@ * This product includes software developed at Datadog (https://www.datadoghq.com/). * Copyright 2016-Present Datadog, Inc. */ +package com.datadog.android.trace.internal -package com.datadog.opentracing.scopemanager; +import com.datadog.android.trace.api.span.DatadogSpanWriter +import com.datadog.trace.common.writer.Writer -import io.opentracing.Scope; -import io.opentracing.Span; - -// Intentionally package private. -interface DDScope extends Scope { - @Override - Span span(); - - int depth(); -} +internal class DatadogSpanWriterWrapper(internal val delegate: Writer) : DatadogSpanWriter diff --git a/features/dd-sdk-android-trace/src/main/kotlin/com/datadog/android/trace/internal/DatadogTraceExt.kt b/features/dd-sdk-android-trace/src/main/kotlin/com/datadog/android/trace/internal/DatadogTraceExt.kt new file mode 100644 index 0000000000..a858d736a1 --- /dev/null +++ b/features/dd-sdk-android-trace/src/main/kotlin/com/datadog/android/trace/internal/DatadogTraceExt.kt @@ -0,0 +1,23 @@ +/* + * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. + * This product includes software developed at Datadog (https://www.datadoghq.com/). + * Copyright 2016-Present Datadog, Inc. + */ +package com.datadog.android.trace.internal + +import com.datadog.android.lint.InternalApi +import com.datadog.android.trace.api.trace.DatadogTraceId +import com.datadog.trace.api.DDTraceId + +/** + * For Datadog internal use only. + * + * Converts a hexadecimal string representation of a trace ID into a [DatadogTraceId] instance. + * + * @param traceId The hexadecimal string representation of the trace ID to be converted. + * @return The corresponding [DatadogTraceId] instance. + */ +@InternalApi +fun DatadogTraceId.Companion.fromHex(traceId: String): DatadogTraceId { + return DatadogTraceIdAdapter(DDTraceId.fromHex(traceId)) +} diff --git a/features/dd-sdk-android-trace/src/main/kotlin/com/datadog/android/trace/internal/DatadogTraceIdAdapter.kt b/features/dd-sdk-android-trace/src/main/kotlin/com/datadog/android/trace/internal/DatadogTraceIdAdapter.kt new file mode 100644 index 0000000000..d82f2a01f0 --- /dev/null +++ b/features/dd-sdk-android-trace/src/main/kotlin/com/datadog/android/trace/internal/DatadogTraceIdAdapter.kt @@ -0,0 +1,17 @@ +/* + * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. + * This product includes software developed at Datadog (https://www.datadoghq.com/). + * Copyright 2016-Present Datadog, Inc. + */ +package com.datadog.android.trace.internal + +import com.datadog.android.trace.api.trace.DatadogTraceId +import com.datadog.trace.api.DDTraceId + +internal data class DatadogTraceIdAdapter(private val delegate: DDTraceId) : DatadogTraceId, DDTraceId() { + override fun toLong(): Long = delegate.toLong() + override fun toString(): String = delegate.toString() + override fun toHexString(): String = delegate.toHexString() + override fun toHighOrderLong(): Long = delegate.toHighOrderLong() + override fun toHexStringPadded(size: Int): String = delegate.toHexStringPadded(size) +} diff --git a/features/dd-sdk-android-trace/src/main/kotlin/com/datadog/android/trace/internal/DatadogTracerAdapter.kt b/features/dd-sdk-android-trace/src/main/kotlin/com/datadog/android/trace/internal/DatadogTracerAdapter.kt new file mode 100644 index 0000000000..ae29454064 --- /dev/null +++ b/features/dd-sdk-android-trace/src/main/kotlin/com/datadog/android/trace/internal/DatadogTracerAdapter.kt @@ -0,0 +1,79 @@ +/* + * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. + * This product includes software developed at Datadog (https://www.datadoghq.com/). + * Copyright 2016-Present Datadog, Inc. + */ +package com.datadog.android.trace.internal + +import com.datadog.android.api.InternalLogger +import com.datadog.android.api.context.DatadogContext +import com.datadog.android.api.feature.Feature +import com.datadog.android.api.feature.FeatureSdkCore +import com.datadog.android.internal.concurrent.CompletableFuture +import com.datadog.android.trace.api.propagation.DatadogPropagation +import com.datadog.android.trace.api.scope.DatadogScope +import com.datadog.android.trace.api.scope.DatadogScopeListener +import com.datadog.android.trace.api.span.DatadogSpan +import com.datadog.android.trace.api.span.DatadogSpanBuilder +import com.datadog.android.trace.api.tracer.DatadogTracer +import com.datadog.trace.bootstrap.instrumentation.api.AgentTracer +import com.datadog.trace.bootstrap.instrumentation.api.ScopeSource + +internal class DatadogTracerAdapter( + internal val sdkCore: FeatureSdkCore, + internal val delegate: AgentTracer.TracerAPI, + internal val bundleWithRumEnabled: Boolean, + private val spanLogger: DatadogSpanLogger +) : DatadogTracer { + + private val internalLogger: InternalLogger + get() = sdkCore.internalLogger + + override fun buildSpan(instrumentationName: String, spanName: CharSequence): DatadogSpanBuilder = wrapSpan( + delegate.buildSpan(instrumentationName, spanName) + ) + + override fun buildSpan(spanName: CharSequence): DatadogSpanBuilder = wrapSpan( + @Suppress("DEPRECATION") + delegate.buildSpan(spanName) + ) + + private fun wrapSpan(span: AgentTracer.SpanBuilder) = + DatadogSpanBuilderAdapter(span, spanLogger) + .withRumContextIfNeeded() + + private fun DatadogSpanBuilder.withRumContextIfNeeded() = apply { + if (bundleWithRumEnabled) { + val rumFeature = sdkCore.getFeature(Feature.RUM_FEATURE_NAME) + if (rumFeature != null) { + val lazyContext = CompletableFuture() + rumFeature.withContext(withFeatureContexts = setOf(Feature.RUM_FEATURE_NAME)) { + lazyContext.complete(it) + } + withTag(SpanAttributes.DATADOG_INITIAL_CONTEXT, lazyContext) + } + } + } + + override fun addScopeListener(scopeListener: DatadogScopeListener) { + delegate.addScopeListener(DatadogScopeListenerAdapter(scopeListener)) + } + + override fun activeSpan(): DatadogSpan? = delegate.activeSpan()?.let { DatadogSpanAdapter(it, spanLogger) } + + override fun activateSpan(span: DatadogSpan): DatadogScope? = (span as? DatadogSpanAdapter)?.let { + DatadogScopeAdapter( + delegate.activateSpan(span.delegate, ScopeSource.INSTRUMENTATION) ?: return null + ) + } + + override fun activateSpan(span: DatadogSpan, asyncPropagating: Boolean): DatadogScope? { + return (span as? DatadogSpanAdapter)?.let { + DatadogScopeAdapter( + delegate.activateSpan(span.delegate, ScopeSource.INSTRUMENTATION, asyncPropagating) ?: return null + ) + } + } + + override fun propagate(): DatadogPropagation = DatadogPropagationAdapter(internalLogger, delegate.propagate()) +} diff --git a/features/dd-sdk-android-trace/src/main/kotlin/com/datadog/android/trace/internal/DatadogTracerBuilderAdapter.kt b/features/dd-sdk-android-trace/src/main/kotlin/com/datadog/android/trace/internal/DatadogTracerBuilderAdapter.kt new file mode 100644 index 0000000000..57b6797009 --- /dev/null +++ b/features/dd-sdk-android-trace/src/main/kotlin/com/datadog/android/trace/internal/DatadogTracerBuilderAdapter.kt @@ -0,0 +1,124 @@ +/* + * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. + * This product includes software developed at Datadog (https://www.datadoghq.com/). + * Copyright 2016-Present Datadog, Inc. + */ +package com.datadog.android.trace.internal + +import androidx.annotation.IntRange +import androidx.annotation.VisibleForTesting +import com.datadog.android.api.feature.FeatureSdkCore +import com.datadog.android.trace.TracingHeaderType +import com.datadog.android.trace.api.DatadogTracingConstants.TracerConfig +import com.datadog.android.trace.api.tracer.DatadogTracer +import com.datadog.android.trace.api.tracer.DatadogTracerBuilder +import com.datadog.trace.api.IdGenerationStrategy +import com.datadog.trace.core.CoreTracer +import java.util.Properties + +internal class DatadogTracerBuilderAdapter( + private val sdkCore: FeatureSdkCore, + private var serviceName: String, + private val delegate: CoreTracer.CoreTracerBuilder +) : DatadogTracerBuilder { + + private var sampleRate: Double? = null + private var bundleWithRumEnabled: Boolean = true + private var sdkV2OTelCompatible: Boolean = false + private var traceRateLimit = Int.MAX_VALUE + private var partialFlushMinSpans = DEFAULT_PARTIAL_MIN_FLUSH + private val globalTags: MutableMap = mutableMapOf() + private var tracingHeadersTypes: Set = setOf( + TracingHeaderType.DATADOG, + TracingHeaderType.TRACECONTEXT + ) + + override fun build(): DatadogTracer { + val coreTracer = delegate.withProperties(properties()).build() + val spanLogger = DatadogSpanLogger(sdkCore) + val datadogTracer = DatadogTracerAdapter(sdkCore, coreTracer, bundleWithRumEnabled, spanLogger) + datadogTracer.addScopeListener(TracePropagationScopeListener(sdkCore, datadogTracer)) + + return datadogTracer + } + + override fun withServiceName(serviceName: String) = apply { + this.serviceName = serviceName + } + + override fun withTracingHeadersTypes(tracingHeadersTypes: Set) = apply { + this.tracingHeadersTypes = tracingHeadersTypes + } + + override fun withSampleRate(sampleRate: Double) = apply { + // In case the sample rate is not set we should not specify it. The agent code under the hood + // will provide different sampler based on this property and also different sampling priorities used + // in the metrics + // -1 MANUAL_DROP User indicated to drop the trace via configuration (sampling rate). + // 0 AUTO_DROP Sampler indicated to drop the trace using a sampling rate provided by the Agent through + // a remote configuration. The Agent API is not used in Android so this `sampling_priority:0` will never + // be used. + // 1 AUTO_KEEP Sampler indicated to keep the trace using a sampling rate from the default configuration + // which right now is 100.0 + // (Default sampling priority value. or in our case no specified sample rate will be considered as 100) + // 2 MANUAL_KEEP User indicated to keep the trace, either manually or via configuration (sampling rate) + + this.sampleRate = sampleRate + } + + override fun withPartialFlushMinSpans(partialFlushMinSpans: Int) = apply { + this.partialFlushMinSpans = partialFlushMinSpans + } + + override fun withTag(key: String, value: String) = apply { + globalTags[key] = value + } + + override fun setBundleWithRumEnabled(enabled: Boolean) = apply { + bundleWithRumEnabled = enabled + } + + override fun setTraceRateLimit(@IntRange(from = 1, to = Int.MAX_VALUE.toLong()) traceRateLimit: Int) = apply { + this.traceRateLimit = traceRateLimit + } + + internal fun setTraceId128BitGenerationEnabled(traceId128BitGenerationEnabled: Boolean) = apply { + delegate.idGenerationStrategy(IdGenerationStrategy.fromName("SECURE_RANDOM", traceId128BitGenerationEnabled)) + } + + internal fun setSdkV2Compatible() { + sdkV2OTelCompatible = true + } + + @VisibleForTesting + internal fun properties(): Properties { + val properties = Properties() + + val propagationStyles = tracingHeadersTypes.joinToString(",") + properties.setProperty(TracerConfig.PROPAGATION_STYLE_EXTRACT, propagationStyles) + properties.setProperty(TracerConfig.PROPAGATION_STYLE_INJECT, propagationStyles) + properties.setProperty(TracerConfig.SERVICE_NAME, serviceName) + properties.setProperty(TracerConfig.TRACE_RATE_LIMIT, traceRateLimit.toString()) + properties.setProperty(TracerConfig.PARTIAL_FLUSH_MIN_SPANS, partialFlushMinSpans.toString()) + properties.setProperty(TracerConfig.URL_AS_RESOURCE_NAME, DEFAULT_URL_AS_RESOURCE_NAME.toString()) + properties.setProperty(TracerConfig.SDK_V2_COMPATIBILITY_FLAG, sdkV2OTelCompatible.toString()) + sampleRate?.let { + properties.setProperty( + TracerConfig.TRACE_SAMPLE_RATE, + (it / DEFAULT_SAMPLE_RATE).toString() + ) + } + properties.setProperty( + TracerConfig.TAGS, + globalTags.map { "${it.key}:${it.value}" }.joinToString(",") + ) + + return properties + } + + companion object { + internal const val DEFAULT_SAMPLE_RATE = 100.0 + internal const val DEFAULT_PARTIAL_MIN_FLUSH = 5 + internal const val DEFAULT_URL_AS_RESOURCE_NAME = false + } +} diff --git a/features/dd-sdk-android-trace/src/main/kotlin/com/datadog/android/trace/internal/DatadogTracingToolkit.kt b/features/dd-sdk-android-trace/src/main/kotlin/com/datadog/android/trace/internal/DatadogTracingToolkit.kt new file mode 100644 index 0000000000..aec0b54314 --- /dev/null +++ b/features/dd-sdk-android-trace/src/main/kotlin/com/datadog/android/trace/internal/DatadogTracingToolkit.kt @@ -0,0 +1,60 @@ +/* + * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. + * This product includes software developed at Datadog (https://www.datadoghq.com/). + * Copyright 2016-Present Datadog, Inc. + */ +package com.datadog.android.trace.internal + +import com.datadog.android.lint.InternalApi +import com.datadog.android.trace.api.span.DatadogSpanContext +import com.datadog.android.trace.api.tracer.DatadogTracerBuilder + +/** + * For library usage only. + * Provides implementation for specific interfaces to dependent modules + */ +@InternalApi +object DatadogTracingToolkit { + /** + * Provides a mechanism for converting Datadog span IDs between decimal and hexadecimal representations. + * + * This converter is utilized to ensure span ID consistency and proper formatting for distributed tracing + * when working with the Datadog SDK. + */ + @JvmField + val spanIdConverter: DatadogSpanIdConverter = DatadogSpanIdConverter() + + /** + * Providing helper function to extract [DatadogSpanContext] from tracing context added by addParentSpan method. + + * This property is intended for internal usage only and should not + * be altered externally. + */ + var propagationHelper: DatadogPropagationHelper = DatadogPropagationHelper() + internal set + + internal var testBuilderProvider: DatadogTracerBuilder? = null + + /** + * Sets the tracing sampling priority if it is necessary. + */ + fun setTracingSamplingPriorityIfNecessary(context: DatadogSpanContext) { + (context as? DatadogSpanContextAdapter)?.setTracingSamplingPriorityIfNecessary() + } + + /** + * Enables 128-bit trace ID generation for the provided Datadog tracer builder. + */ + fun setTraceId128BitGenerationEnabled(builder: DatadogTracerBuilder): DatadogTracerBuilder { + (builder as? DatadogTracerBuilderAdapter)?.setTraceId128BitGenerationEnabled(true) + return builder + } + + /** + * Enables compatibility mode with SDK v2 for sampling factory strategy. + */ + fun setSdkV2Compatible(builder: DatadogTracerBuilder): DatadogTracerBuilder { + (builder as? DatadogTracerBuilderAdapter)?.setSdkV2Compatible() + return builder + } +} diff --git a/features/dd-sdk-android-trace/src/main/kotlin/com/datadog/android/trace/internal/TracePropagationScopeListener.kt b/features/dd-sdk-android-trace/src/main/kotlin/com/datadog/android/trace/internal/TracePropagationScopeListener.kt new file mode 100644 index 0000000000..8a66b3cbce --- /dev/null +++ b/features/dd-sdk-android-trace/src/main/kotlin/com/datadog/android/trace/internal/TracePropagationScopeListener.kt @@ -0,0 +1,28 @@ +/* + * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. + * This product includes software developed at Datadog (https://www.datadoghq.com/). + * Copyright 2016-Present Datadog, Inc. + */ +package com.datadog.android.trace.internal + +import com.datadog.android.api.feature.FeatureSdkCore +import com.datadog.android.trace.api.scope.DatadogScopeListener +import com.datadog.android.trace.api.tracer.DatadogTracer + +internal class TracePropagationScopeListener( + private val sdkCore: FeatureSdkCore, + private val datadogTracer: DatadogTracer +) : DatadogScopeListener { + override fun afterScopeActivated() { + val activeSpanContext = datadogTracer.activeSpan()?.context() + if (activeSpanContext != null) { + val activeSpanId = activeSpanContext.spanId.toString() + val activeTraceId = activeSpanContext.traceId.toHexString() + sdkCore.addActiveTraceToContext(activeTraceId, activeSpanId) + } + } + + override fun afterScopeClosed() { + sdkCore.removeActiveTraceFromContext() + } +} diff --git a/features/dd-sdk-android-trace/src/main/kotlin/com/datadog/android/trace/internal/TracingFeature.kt b/features/dd-sdk-android-trace/src/main/kotlin/com/datadog/android/trace/internal/TracingFeature.kt index 518cdea414..278fa675f7 100644 --- a/features/dd-sdk-android-trace/src/main/kotlin/com/datadog/android/trace/internal/TracingFeature.kt +++ b/features/dd-sdk-android-trace/src/main/kotlin/com/datadog/android/trace/internal/TracingFeature.kt @@ -14,16 +14,13 @@ import com.datadog.android.api.net.RequestFactory import com.datadog.android.api.storage.FeatureStorageConfiguration import com.datadog.android.trace.InternalCoreWriterProvider import com.datadog.android.trace.event.SpanEventMapper -import com.datadog.android.trace.internal.data.NoOpCoreTracerWriter -import com.datadog.android.trace.internal.data.NoOpWriter -import com.datadog.android.trace.internal.data.OtelTraceWriter -import com.datadog.android.trace.internal.data.TraceWriter +import com.datadog.android.trace.internal.data.CoreTraceWriter import com.datadog.android.trace.internal.domain.event.CoreTracerSpanToSpanEventMapper -import com.datadog.android.trace.internal.domain.event.DdSpanToSpanEventMapper import com.datadog.android.trace.internal.domain.event.SpanEventMapperWrapper import com.datadog.android.trace.internal.domain.event.SpanEventSerializer import com.datadog.android.trace.internal.net.TracesRequestFactory -import com.datadog.legacy.trace.common.writer.Writer +import com.datadog.trace.common.writer.NoOpWriter +import com.datadog.trace.common.writer.Writer import java.util.concurrent.atomic.AtomicBoolean /** @@ -36,8 +33,7 @@ internal class TracingFeature( internal val networkInfoEnabled: Boolean ) : InternalCoreWriterProvider, StorageBackedFeature { - internal var legacyTracerWriter: Writer = NoOpWriter() - internal var coreTracerDataWriter: com.datadog.trace.common.writer.Writer = NoOpCoreTracerWriter() + internal var coreTracerDataWriter: Writer = NoOpWriter() internal val initialized = AtomicBoolean(false) // region Feature @@ -45,8 +41,7 @@ internal class TracingFeature( override val name: String = Feature.TRACING_FEATURE_NAME override fun onInitialize(appContext: Context) { - legacyTracerWriter = createDataWriter(sdkCore) - coreTracerDataWriter = createOtelDataWriter(sdkCore) + coreTracerDataWriter = createDataWriter(sdkCore) initialized.set(true) } @@ -61,7 +56,6 @@ internal class TracingFeature( FeatureStorageConfiguration.DEFAULT override fun onStop() { - legacyTracerWriter = NoOpWriter() initialized.set(false) } @@ -69,30 +63,13 @@ internal class TracingFeature( // region InternalCoreWriterProvider - override fun getCoreTracerWriter(): com.datadog.trace.common.writer.Writer { - return coreTracerDataWriter - } + override fun getCoreTracerWriter() = DatadogSpanWriterWrapper(coreTracerDataWriter) // endregion - private fun createDataWriter( - sdkCore: FeatureSdkCore - ): Writer { + private fun createDataWriter(sdkCore: FeatureSdkCore): Writer { val internalLogger = sdkCore.internalLogger - return TraceWriter( - sdkCore, - ddSpanToSpanEventMapper = DdSpanToSpanEventMapper(networkInfoEnabled), - eventMapper = SpanEventMapperWrapper(spanEventMapper, internalLogger), - serializer = SpanEventSerializer(internalLogger), - internalLogger = internalLogger - ) - } - - private fun createOtelDataWriter( - sdkCore: FeatureSdkCore - ): com.datadog.trace.common.writer.Writer { - val internalLogger = sdkCore.internalLogger - return OtelTraceWriter( + return CoreTraceWriter( sdkCore, ddSpanToSpanEventMapper = CoreTracerSpanToSpanEventMapper(networkInfoEnabled), eventMapper = SpanEventMapperWrapper(spanEventMapper, internalLogger), @@ -100,9 +77,4 @@ internal class TracingFeature( internalLogger = internalLogger ) } - - companion object { - internal const val IS_OPENTELEMETRY_ENABLED_CONFIG_KEY = "is_opentelemetry_enabled" - internal const val OPENTELEMETRY_API_VERSION_CONFIG_KEY = "opentelemetry_api_version" - } } diff --git a/features/dd-sdk-android-trace/src/main/kotlin/com/datadog/android/trace/internal/data/OtelTraceWriter.kt b/features/dd-sdk-android-trace/src/main/kotlin/com/datadog/android/trace/internal/data/CoreTraceWriter.kt similarity index 93% rename from features/dd-sdk-android-trace/src/main/kotlin/com/datadog/android/trace/internal/data/OtelTraceWriter.kt rename to features/dd-sdk-android-trace/src/main/kotlin/com/datadog/android/trace/internal/data/CoreTraceWriter.kt index cc66cd09d3..f1f7d8809d 100644 --- a/features/dd-sdk-android-trace/src/main/kotlin/com/datadog/android/trace/internal/data/OtelTraceWriter.kt +++ b/features/dd-sdk-android-trace/src/main/kotlin/com/datadog/android/trace/internal/data/CoreTraceWriter.kt @@ -22,12 +22,12 @@ import com.datadog.android.trace.internal.SpanAttributes import com.datadog.android.trace.internal.domain.event.ContextAwareMapper import com.datadog.android.trace.internal.storage.ContextAwareSerializer import com.datadog.android.trace.model.SpanEvent -import com.datadog.legacy.trace.api.sampling.PrioritySampling +import com.datadog.trace.api.sampling.PrioritySampling import com.datadog.trace.common.writer.Writer import com.datadog.trace.core.DDSpan import java.util.Locale -internal class OtelTraceWriter( +internal class CoreTraceWriter( private val sdkCore: FeatureSdkCore, internal val ddSpanToSpanEventMapper: ContextAwareMapper, internal val eventMapper: EventMapper = NoOpEventMapper(), @@ -46,7 +46,7 @@ internal class OtelTraceWriter( ?.withWriteContext { datadogContext, writeScope -> val writeSpans = trace .filter { - it.samplingPriority() !in DROP_SAMPLING_PRIORITIES + it.getTraceSamplingPriority() !in DROP_SAMPLING_PRIORITIES } .map { it.bundleWithRum() } // TODO RUM-4092 Add the capability in the serializer to handle multiple spans in one payload @@ -132,6 +132,9 @@ internal class OtelTraceWriter( internal const val ERROR_SERIALIZING = "Error serializing %s model" internal const val INITIAL_DATADOG_CONTEXT_NOT_AVAILABLE_ERROR = "Initial span creation Datadog context" + " is not available at the write time." - internal val DROP_SAMPLING_PRIORITIES = setOf(PrioritySampling.SAMPLER_DROP, PrioritySampling.USER_DROP) + internal val DROP_SAMPLING_PRIORITIES = setOf( + PrioritySampling.SAMPLER_DROP.toInt(), + PrioritySampling.USER_DROP.toInt() + ) } } diff --git a/features/dd-sdk-android-trace/src/main/kotlin/com/datadog/android/trace/internal/data/NoOpCoreTracerWriter.kt b/features/dd-sdk-android-trace/src/main/kotlin/com/datadog/android/trace/internal/data/NoOpCoreTracerWriter.kt deleted file mode 100644 index 9bfda94381..0000000000 --- a/features/dd-sdk-android-trace/src/main/kotlin/com/datadog/android/trace/internal/data/NoOpCoreTracerWriter.kt +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. - * This product includes software developed at Datadog (https://www.datadoghq.com/). - * Copyright 2016-Present Datadog, Inc. - */ - -package com.datadog.android.trace.internal.data - -import com.datadog.trace.common.writer.Writer - -internal class NoOpCoreTracerWriter : Writer { - override fun close() { - } - - override fun write(p0: MutableList?) { - } - - override fun start() { - } - - override fun flush(): Boolean { - return true - } - - override fun incrementDropCounts(p0: Int) { - } -} diff --git a/features/dd-sdk-android-trace/src/main/kotlin/com/datadog/android/trace/internal/data/NoOpWriter.kt b/features/dd-sdk-android-trace/src/main/kotlin/com/datadog/android/trace/internal/data/NoOpWriter.kt deleted file mode 100644 index 7e144f8d7e..0000000000 --- a/features/dd-sdk-android-trace/src/main/kotlin/com/datadog/android/trace/internal/data/NoOpWriter.kt +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. - * This product includes software developed at Datadog (https://www.datadoghq.com/). - * Copyright 2016-Present Datadog, Inc. - */ - -package com.datadog.android.trace.internal.data - -import com.datadog.legacy.trace.common.writer.Writer -import com.datadog.opentracing.DDSpan - -internal class NoOpWriter : Writer { - override fun close() { - // no-op - } - - override fun write(trace: MutableList?) { - // no-op - } - - override fun start() { - // no-op - } - - override fun incrementTraceCount() { - // no-op - } -} diff --git a/features/dd-sdk-android-trace/src/main/kotlin/com/datadog/android/trace/internal/data/TraceWriter.kt b/features/dd-sdk-android-trace/src/main/kotlin/com/datadog/android/trace/internal/data/TraceWriter.kt deleted file mode 100644 index 7b1f9a769b..0000000000 --- a/features/dd-sdk-android-trace/src/main/kotlin/com/datadog/android/trace/internal/data/TraceWriter.kt +++ /dev/null @@ -1,135 +0,0 @@ -/* - * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. - * This product includes software developed at Datadog (https://www.datadoghq.com/). - * Copyright 2016-Present Datadog, Inc. - */ - -package com.datadog.android.trace.internal.data - -import androidx.annotation.WorkerThread -import com.datadog.android.api.InternalLogger -import com.datadog.android.api.context.DatadogContext -import com.datadog.android.api.feature.Feature -import com.datadog.android.api.feature.FeatureSdkCore -import com.datadog.android.api.storage.EventBatchWriter -import com.datadog.android.api.storage.EventType -import com.datadog.android.api.storage.RawBatchEvent -import com.datadog.android.event.EventMapper -import com.datadog.android.internal.concurrent.CompletableFuture -import com.datadog.android.log.LogAttributes -import com.datadog.android.trace.AndroidTracer -import com.datadog.android.trace.internal.domain.event.ContextAwareMapper -import com.datadog.android.trace.internal.storage.ContextAwareSerializer -import com.datadog.android.trace.model.SpanEvent -import com.datadog.legacy.trace.api.sampling.PrioritySampling -import com.datadog.legacy.trace.common.writer.Writer -import com.datadog.opentracing.DDSpan -import java.util.Locale - -internal class TraceWriter( - private val sdkCore: FeatureSdkCore, - internal val ddSpanToSpanEventMapper: ContextAwareMapper, - internal val eventMapper: EventMapper, - private val serializer: ContextAwareSerializer, - private val internalLogger: InternalLogger -) : Writer { - - // region Writer - override fun start() { - // NO - OP - } - - override fun write(trace: List?) { - if (trace == null) return - sdkCore.getFeature(Feature.TRACING_FEATURE_NAME) - ?.withWriteContext { datadogContext, writeScope -> - val writeSpans = trace - .filter { - it.samplingPriority == null || it.samplingPriority !in DROP_SAMPLING_PRIORITIES - } - .map { it.bundleWithRum() } - writeScope { - writeSpans - .forEach { span -> - writeSpan(datadogContext, it, span) - } - } - } - } - - override fun close() { - // NO - OP - } - - override fun incrementTraceCount() { - // NO - OP - } - - // endregion - - @Suppress("UNCHECKED_CAST") - private fun DDSpan.bundleWithRum(): DDSpan { - val initialDatadogContext = tags[AndroidTracer.DATADOG_CONTEXT_TAG.key] - as? CompletableFuture - setTag(AndroidTracer.DATADOG_CONTEXT_TAG, null) - if (initialDatadogContext != null) { - if (initialDatadogContext.isComplete()) { - val rumContext = initialDatadogContext.value - .featuresContext[Feature.RUM_FEATURE_NAME] - .orEmpty() - setTag( - LogAttributes.RUM_APPLICATION_ID, - rumContext["application_id"] as? String - ) - setTag(LogAttributes.RUM_SESSION_ID, rumContext["session_id"] as? String) - setTag(LogAttributes.RUM_VIEW_ID, rumContext["view_id"] as? String) - setTag(LogAttributes.RUM_ACTION_ID, rumContext["action_id"] as? String) - } else { - internalLogger.log( - InternalLogger.Level.ERROR, - InternalLogger.Target.USER, - { INITIAL_DATADOG_CONTEXT_NOT_AVAILABLE_ERROR } - ) - } - } - return this - } - - @WorkerThread - private fun writeSpan( - datadogContext: DatadogContext, - writer: EventBatchWriter, - span: DDSpan - ) { - val spanEvent = ddSpanToSpanEventMapper.map(datadogContext, span) - val mapped = eventMapper.map(spanEvent) ?: return - try { - val serialized = serializer - .serialize(datadogContext, mapped) - ?.toByteArray(Charsets.UTF_8) ?: return - synchronized(this) { - writer.write( - event = RawBatchEvent(data = serialized), - batchMetadata = null, - eventType = EventType.DEFAULT - ) - } - } catch (@Suppress("TooGenericExceptionCaught") e: Throwable) { - internalLogger.log( - InternalLogger.Level.ERROR, - listOf(InternalLogger.Target.USER, InternalLogger.Target.TELEMETRY), - { ERROR_SERIALIZING.format(Locale.US, mapped.javaClass.simpleName) }, - e - ) - } - } - - companion object { - internal const val ERROR_SERIALIZING = "Error serializing %s model" - internal const val INITIAL_DATADOG_CONTEXT_NOT_AVAILABLE_ERROR = "Initial span creation Datadog context" + - " is not available at the write time." - internal val DROP_SAMPLING_PRIORITIES = setOf(PrioritySampling.SAMPLER_DROP, PrioritySampling.USER_DROP) - private val KEEP_SAMPLING_PRIORITIES = setOf(PrioritySampling.SAMPLER_KEEP, PrioritySampling.USER_KEEP) - internal val KEEP_AND_UNSET_SAMPLING_PRIORITIES = KEEP_SAMPLING_PRIORITIES + PrioritySampling.UNSET - } -} diff --git a/features/dd-sdk-android-trace/src/main/kotlin/com/datadog/android/trace/internal/domain/event/CoreTracerSpanToSpanEventMapper.kt b/features/dd-sdk-android-trace/src/main/kotlin/com/datadog/android/trace/internal/domain/event/CoreTracerSpanToSpanEventMapper.kt index 2b12d744fc..a8996789a8 100644 --- a/features/dd-sdk-android-trace/src/main/kotlin/com/datadog/android/trace/internal/domain/event/CoreTracerSpanToSpanEventMapper.kt +++ b/features/dd-sdk-android-trace/src/main/kotlin/com/datadog/android/trace/internal/domain/event/CoreTracerSpanToSpanEventMapper.kt @@ -11,6 +11,7 @@ import com.datadog.android.log.LogAttributes import com.datadog.android.trace.model.SpanEvent import com.datadog.trace.api.DDSpanId import com.datadog.trace.api.internal.util.LongStringUtils +import com.datadog.trace.api.sampling.PrioritySampling import com.datadog.trace.bootstrap.instrumentation.api.AgentSpanLink import com.datadog.trace.core.DDSpan import com.datadog.trace.core.DDSpanContext @@ -28,8 +29,6 @@ internal class CoreTracerSpanToSpanEventMapper( val serverOffset = datadogContext.time.serverTimeOffsetNs val metrics = resolveMetrics(model) val metadata = resolveMeta(datadogContext, model) - val deviceInfo = resolveDeviceInfo(datadogContext.deviceInfo) - val osInfo = resolveOsInfo(datadogContext.deviceInfo) val lessSignificantTraceId = LongStringUtils.toHexStringPadded(model.traceId.toLong(), TRACE_ID_HEXA_SIZE) return SpanEvent( traceId = lessSignificantTraceId, @@ -42,9 +41,7 @@ internal class CoreTracerSpanToSpanEventMapper( start = model.startTime + serverOffset, error = model.error.toLong(), meta = metadata, - metrics = metrics, - device = deviceInfo, - os = osInfo + metrics = metrics ) } @@ -61,9 +58,15 @@ internal class CoreTracerSpanToSpanEventMapper( return DDSpanId.toHexStringPadded(model.parentId) } - private fun resolveMetrics(event: DDSpan): SpanEvent.Metrics { + // TODO RUM-10805 - make it back private and re-create objects in tests + internal fun resolveMetrics(event: DDSpan): SpanEvent.Metrics { val metrics = resolveMetricsFromSpanContext(event).apply { - this[DDSpanContext.PRIORITY_SAMPLING_KEY] = event.samplingPriority() + val spanSamplingPriority = event.spanSamplingPriority + if (spanSamplingPriority != PrioritySampling.UNSET.toInt()) { + // This required for backward compatibility with AndroidTracer that + // don't add the sampling priority if it not set for current span. + this[DDSpanContext.PRIORITY_SAMPLING_KEY] = spanSamplingPriority + } } return SpanEvent.Metrics( topLevel = if (event.parentId == 0L) 1 else null, @@ -71,7 +74,10 @@ internal class CoreTracerSpanToSpanEventMapper( ) } - private fun resolveMeta(datadogContext: DatadogContext, event: DDSpan): SpanEvent.Meta { + // TODO RUM-10805 - make it back private and re-create objects in tests + internal fun resolveMeta(datadogContext: DatadogContext, event: DDSpan): SpanEvent.Meta { + val deviceInfo = resolveDeviceInfo(datadogContext.deviceInfo) + val osInfo = resolveOsInfo(datadogContext.deviceInfo) val networkInfoMeta = if (networkInfoEnabled) resolveNetworkInfo(datadogContext.networkInfo) else null val userInfo = datadogContext.userInfo val accountInfo = datadogContext.accountInfo @@ -107,6 +113,8 @@ internal class CoreTracerSpanToSpanEventMapper( usr = usrMeta, account = accountMeta, network = networkInfoMeta, + device = deviceInfo, + os = osInfo, additionalProperties = meta ) } diff --git a/features/dd-sdk-android-trace/src/main/kotlin/com/datadog/android/trace/internal/domain/event/DdSpanToSpanEventMapper.kt b/features/dd-sdk-android-trace/src/main/kotlin/com/datadog/android/trace/internal/domain/event/DdSpanToSpanEventMapper.kt deleted file mode 100644 index 120145dc3b..0000000000 --- a/features/dd-sdk-android-trace/src/main/kotlin/com/datadog/android/trace/internal/domain/event/DdSpanToSpanEventMapper.kt +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. - * This product includes software developed at Datadog (https://www.datadoghq.com/). - * Copyright 2016-Present Datadog, Inc. - */ - -package com.datadog.android.trace.internal.domain.event - -import com.datadog.android.api.context.DatadogContext -import com.datadog.android.internal.utils.toHexString -import com.datadog.android.log.LogAttributes -import com.datadog.android.trace.model.SpanEvent -import com.datadog.opentracing.DDSpan - -internal class DdSpanToSpanEventMapper( - internal val networkInfoEnabled: Boolean, - private val bigIntegerUtils: BigIntegerUtils = BigIntegerUtils -) : BaseSpanEventMapper() { - - // region Mapper - - override fun map(datadogContext: DatadogContext, model: DDSpan): SpanEvent { - val serverOffset = datadogContext.time.serverTimeOffsetNs - val metrics = resolveMetrics(model) - val metadata = resolveMeta(datadogContext, model) - val deviceInfo = resolveDeviceInfo(datadogContext.deviceInfo) - val osInfo = resolveOsInfo(datadogContext.deviceInfo) - val leastSignificantTraceId = bigIntegerUtils.leastSignificant64BitsAsHex(model.traceId) - return SpanEvent( - traceId = leastSignificantTraceId, - spanId = model.spanId.toHexString(), - parentId = model.parentId.toHexString(), - resource = model.resourceName, - name = model.operationName, - service = model.serviceName, - duration = model.durationNano, - start = model.startTime + serverOffset, - error = if (model.isError) 1 else 0, - meta = metadata, - metrics = metrics, - os = osInfo, - device = deviceInfo - ) - } - - // endregion - - // region internal - - private fun resolveMetrics(event: DDSpan) = SpanEvent.Metrics( - topLevel = if (event.parentId.toLong() == 0L) 1 else null, - additionalProperties = event.metrics - ) - - private fun resolveMeta(datadogContext: DatadogContext, event: DDSpan): SpanEvent.Meta { - val networkInfoMeta = if (networkInfoEnabled) resolveNetworkInfo(datadogContext.networkInfo) else null - val userInfo = datadogContext.userInfo - val accountInfo = datadogContext.accountInfo - val mostSignificantTraceId = bigIntegerUtils.mostSignificant64BitsAsHex(event.traceId) - val additionalProperties = mutableMapOf() - additionalProperties[TRACE_ID_META_KEY] = mostSignificantTraceId - additionalProperties[APPLICATION_VARIANT_KEY] = datadogContext.variant - additionalProperties += event.meta - val usrMeta = resolveUserInfo(userInfo) - val accountMeta = accountInfo?.let { resolveAccountInfo(it) } - val dd = SpanEvent.Dd( - source = datadogContext.source, - application = event.tags[LogAttributes.RUM_APPLICATION_ID]?.let { SpanEvent.Application(it as? String) }, - session = event.tags[LogAttributes.RUM_SESSION_ID]?.let { SpanEvent.Session(it as? String) }, - view = event.tags[LogAttributes.RUM_VIEW_ID]?.let { SpanEvent.View(it as? String) } - ) - return SpanEvent.Meta( - version = datadogContext.version, - dd = dd, - span = SpanEvent.Span(), - tracer = SpanEvent.Tracer( - version = datadogContext.sdkVersion - ), - usr = usrMeta, - account = accountMeta, - network = networkInfoMeta, - additionalProperties = additionalProperties - ) - } - - // endregion -} diff --git a/features/dd-sdk-android-trace/src/main/kotlin/com/datadog/android/trace/internal/domain/event/SpanMapperSerializer.kt b/features/dd-sdk-android-trace/src/main/kotlin/com/datadog/android/trace/internal/domain/event/SpanMapperSerializer.kt deleted file mode 100644 index 894f8137d6..0000000000 --- a/features/dd-sdk-android-trace/src/main/kotlin/com/datadog/android/trace/internal/domain/event/SpanMapperSerializer.kt +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. - * This product includes software developed at Datadog (https://www.datadoghq.com/). - * Copyright 2016-Present Datadog, Inc. - */ - -package com.datadog.android.trace.internal.domain.event - -import com.datadog.android.core.persistence.Serializer -import com.datadog.android.event.EventMapper -import com.datadog.android.trace.model.SpanEvent -import com.datadog.opentracing.DDSpan - -internal class SpanMapperSerializer( - private val legacyMapper: Mapper, - internal val spanEventMapper: EventMapper, - private val spanSerializer: Serializer -) : Serializer { - - override fun serialize(model: DDSpan): String? { - val spanEvent = legacyMapper.map(model) - val mappedEvent = spanEventMapper.map(spanEvent) ?: return null - return spanSerializer.serialize(mappedEvent) - } -} diff --git a/features/dd-sdk-android-trace/src/main/kotlin/com/datadog/android/trace/internal/handlers/AndroidSpanLogsHandler.kt b/features/dd-sdk-android-trace/src/main/kotlin/com/datadog/android/trace/internal/handlers/AndroidSpanLogsHandler.kt deleted file mode 100644 index 9871e22c5f..0000000000 --- a/features/dd-sdk-android-trace/src/main/kotlin/com/datadog/android/trace/internal/handlers/AndroidSpanLogsHandler.kt +++ /dev/null @@ -1,127 +0,0 @@ -/* - * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. - * This product includes software developed at Datadog (https://www.datadoghq.com/). - * Copyright 2016-Present Datadog, Inc. - */ - -package com.datadog.android.trace.internal.handlers - -import android.util.Log -import com.datadog.android.api.InternalLogger -import com.datadog.android.api.feature.Feature -import com.datadog.android.api.feature.FeatureSdkCore -import com.datadog.android.internal.utils.loggableStackTrace -import com.datadog.android.log.LogAttributes -import com.datadog.android.trace.AndroidTracer -import com.datadog.android.trace.internal.utils.traceIdAsHexString -import com.datadog.legacy.trace.api.DDTags -import com.datadog.opentracing.DDSpan -import com.datadog.opentracing.LogHandler -import io.opentracing.log.Fields -import java.util.concurrent.TimeUnit - -internal class AndroidSpanLogsHandler( - private val sdkCore: FeatureSdkCore -) : LogHandler { - - // region Span - - override fun log(event: String, span: DDSpan) { - logFields( - span, - mutableMapOf(Fields.EVENT to event), - null - ) - } - - override fun log(timestampMicroseconds: Long, event: String, span: DDSpan) { - logFields( - span, - mutableMapOf(Fields.EVENT to event), - timestampMicroseconds - ) - } - - override fun log(fields: Map, span: DDSpan) { - val mutableMap = fields.toMutableMap() - extractError(mutableMap, span) - logFields(span, mutableMap) - } - - override fun log(timestampMicroseconds: Long, fields: Map, span: DDSpan) { - val mutableMap = fields.toMutableMap() - extractError(mutableMap, span) - logFields(span, mutableMap, timestampMicroseconds) - } - - // endregion - - // region Internal - - private fun toMilliseconds(timestampMicroseconds: Long?): Long? { - return timestampMicroseconds?.let { TimeUnit.MICROSECONDS.toMillis(it) } - } - - private fun logFields( - span: DDSpan, - fields: MutableMap, - timestampMicroseconds: Long? = null - ) { - val logsFeature = sdkCore.getFeature(Feature.LOGS_FEATURE_NAME) - val spanLogStatus = fields.remove(AndroidTracer.LOG_STATUS) as? Int - - if (logsFeature != null && fields.isNotEmpty()) { - val message = fields.remove(Fields.MESSAGE)?.toString() ?: DEFAULT_EVENT_MESSAGE - fields[LogAttributes.DD_TRACE_ID] = span.context().traceIdAsHexString() - fields[LogAttributes.DD_SPAN_ID] = span.context().toSpanId() - val timestamp = toMilliseconds(timestampMicroseconds) ?: System.currentTimeMillis() - logsFeature.sendEvent( - mapOf( - "type" to "span_log", - "loggerName" to TRACE_LOGGER_NAME, - "message" to message, - "attributes" to fields, - "timestamp" to timestamp, - "logStatus" to (spanLogStatus ?: Log.VERBOSE) - ) - ) - } else if (logsFeature == null) { - sdkCore.internalLogger.log( - InternalLogger.Level.WARN, - InternalLogger.Target.USER, - { MISSING_LOG_FEATURE_INFO } - ) - } - } - - private fun extractError( - map: MutableMap, - span: DDSpan - ) { - val throwable = map.remove(Fields.ERROR_OBJECT) as? Throwable - val kind = map.remove(Fields.ERROR_KIND) - val errorType = kind?.toString() ?: throwable?.javaClass?.name - - if (errorType != null) { - val stackField = map.remove(Fields.STACK) - val msgField = map[Fields.MESSAGE] - val stack = stackField?.toString() ?: throwable?.loggableStackTrace() - val message = msgField?.toString() ?: throwable?.message - - span.isError = true - span.setTag(DDTags.ERROR_TYPE, errorType) - span.setTag(DDTags.ERROR_MSG, message) - span.setTag(DDTags.ERROR_STACK, stack) - } - } - - // endregion - - companion object { - internal const val DEFAULT_EVENT_MESSAGE = "Span event" - - internal const val MISSING_LOG_FEATURE_INFO = - "Requested to write span log, but Logs feature is not registered." - internal const val TRACE_LOGGER_NAME = "trace" - } -} diff --git a/features/dd-sdk-android-trace/src/main/kotlin/com/datadog/android/trace/internal/utils/SpanContextExt.kt b/features/dd-sdk-android-trace/src/main/kotlin/com/datadog/android/trace/internal/utils/SpanContextExt.kt deleted file mode 100644 index e32f264bdf..0000000000 --- a/features/dd-sdk-android-trace/src/main/kotlin/com/datadog/android/trace/internal/utils/SpanContextExt.kt +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. - * This product includes software developed at Datadog (https://www.datadoghq.com/). - * Copyright 2016-Present Datadog, Inc. - */ - -package com.datadog.android.trace.internal.utils - -import com.datadog.opentracing.DDSpanContext -import io.opentracing.SpanContext - -private const val TRACE_ID_REQUIRED_LENGTH = 32 -private const val HEX_RADIX = 16 - -internal fun SpanContext.traceIdAsHexString(): String { - return (this as? DDSpanContext)?.traceId?.toString(HEX_RADIX)?.padStart(TRACE_ID_REQUIRED_LENGTH, '0') ?: "" -} diff --git a/features/dd-sdk-android-trace/src/main/kotlin/com/datadog/android/trace/internal/utils/TracerExtensions.kt b/features/dd-sdk-android-trace/src/main/kotlin/com/datadog/android/trace/internal/utils/TracerExtensions.kt deleted file mode 100644 index 0d1411fe6f..0000000000 --- a/features/dd-sdk-android-trace/src/main/kotlin/com/datadog/android/trace/internal/utils/TracerExtensions.kt +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. - * This product includes software developed at Datadog (https://www.datadoghq.com/). - * Copyright 2016-Present Datadog, Inc. - */ - -package com.datadog.android.trace.internal.utils - -import com.datadog.opentracing.DDSpan -import io.opentracing.Span -import io.opentracing.Tracer - -internal fun Tracer.traceId(): String? { - val activeSpan: Span? = activeSpan() - return if (activeSpan is DDSpan) { - activeSpan.traceId.toString() - } else { - null - } -} - -internal fun Tracer.spanId(): String? { - val activeSpan: Span? = activeSpan() - return if (activeSpan is DDSpan) { - activeSpan.spanId.toString() - } else { - null - } -} diff --git a/features/dd-sdk-android-trace/src/main/kotlin/com/datadog/android/trace/sqlite/SqliteDatabaseExt.kt b/features/dd-sdk-android-trace/src/main/kotlin/com/datadog/android/trace/sqlite/SqliteDatabaseExt.kt index 6b519953c2..0d14278941 100644 --- a/features/dd-sdk-android-trace/src/main/kotlin/com/datadog/android/trace/sqlite/SqliteDatabaseExt.kt +++ b/features/dd-sdk-android-trace/src/main/kotlin/com/datadog/android/trace/sqlite/SqliteDatabaseExt.kt @@ -7,9 +7,9 @@ package com.datadog.android.trace.sqlite import android.database.sqlite.SQLiteDatabase +import com.datadog.android.trace.GlobalDatadogTracer +import com.datadog.android.trace.api.span.DatadogSpan import com.datadog.android.trace.withinSpan -import io.opentracing.Span -import io.opentracing.util.GlobalTracer /** * Run [body] in a transaction marking it as successful if it completes without exception. @@ -24,9 +24,9 @@ import io.opentracing.util.GlobalTracer inline fun SQLiteDatabase.transactionTraced( operationName: String, exclusive: Boolean = true, - body: Span.(SQLiteDatabase) -> T + body: DatadogSpan.(SQLiteDatabase) -> T ): T { - val parentSpan = GlobalTracer.get().activeSpan() + val parentSpan = GlobalDatadogTracer.get().activeSpan() withinSpan(operationName, parentSpan, true) { if (exclusive) { @Suppress("UnsafeThirdPartyFunctionCall") // we are in a valid state diff --git a/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/trace/AndroidTracerTest.kt b/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/trace/AndroidTracerTest.kt deleted file mode 100644 index 793178e960..0000000000 --- a/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/trace/AndroidTracerTest.kt +++ /dev/null @@ -1,619 +0,0 @@ -/* - * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. - * This product includes software developed at Datadog (https://www.datadoghq.com/). - * Copyright 2016-Present Datadog, Inc. - */ - -package com.datadog.android.trace - -import android.util.Log -import com.datadog.android.api.InternalLogger -import com.datadog.android.api.context.DatadogContext -import com.datadog.android.api.feature.Feature -import com.datadog.android.api.feature.FeatureScope -import com.datadog.android.api.feature.FeatureSdkCore -import com.datadog.android.internal.concurrent.CompletableFuture -import com.datadog.android.trace.internal.SpanAttributes -import com.datadog.android.trace.internal.TracingFeature -import com.datadog.android.trace.internal.utils.traceIdAsHexString -import com.datadog.android.trace.utils.verifyLog -import com.datadog.android.utils.forge.Configurator -import com.datadog.legacy.trace.api.Config -import com.datadog.legacy.trace.common.writer.Writer -import com.datadog.opentracing.DDSpan -import com.datadog.opentracing.LogHandler -import com.datadog.opentracing.scopemanager.ScopeTestHelper -import fr.xgouchet.elmyr.Forge -import fr.xgouchet.elmyr.annotation.DoubleForgery -import fr.xgouchet.elmyr.annotation.Forgery -import fr.xgouchet.elmyr.annotation.LongForgery -import fr.xgouchet.elmyr.annotation.StringForgery -import fr.xgouchet.elmyr.annotation.StringForgeryType -import fr.xgouchet.elmyr.junit5.ForgeConfiguration -import fr.xgouchet.elmyr.junit5.ForgeExtension -import io.opentracing.Span -import io.opentracing.log.Fields -import io.opentracing.util.GlobalTracer -import org.assertj.core.api.Assertions.assertThat -import org.assertj.core.api.Assertions.offset -import org.junit.jupiter.api.AfterEach -import org.junit.jupiter.api.BeforeEach -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.extension.ExtendWith -import org.junit.jupiter.api.extension.Extensions -import org.mockito.Mock -import org.mockito.junit.jupiter.MockitoExtension -import org.mockito.junit.jupiter.MockitoSettings -import org.mockito.kotlin.any -import org.mockito.kotlin.argumentCaptor -import org.mockito.kotlin.doAnswer -import org.mockito.kotlin.doReturn -import org.mockito.kotlin.eq -import org.mockito.kotlin.inOrder -import org.mockito.kotlin.mock -import org.mockito.kotlin.verify -import org.mockito.kotlin.whenever -import org.mockito.quality.Strictness -import java.math.BigInteger -import java.util.Random -import java.util.UUID -import java.util.concurrent.ConcurrentHashMap -import java.util.concurrent.CountDownLatch -import java.util.concurrent.TimeUnit - -@Extensions( - ExtendWith(MockitoExtension::class), - ExtendWith(ForgeExtension::class) -) -@MockitoSettings(strictness = Strictness.LENIENT) -@ForgeConfiguration(Configurator::class) -internal class AndroidTracerTest { - - lateinit var testedTracerBuilder: AndroidTracer.Builder - - lateinit var fakeToken: String - lateinit var fakeEnvName: String - lateinit var fakeServiceName: String - - @Mock - lateinit var mockLogsHandler: LogHandler - - @Mock - lateinit var mockTracingFeatureScope: FeatureScope - - @Mock - lateinit var mockTracingFeature: TracingFeature - - @Mock - lateinit var mockTraceWriter: Writer - - @Mock - lateinit var mockSdkCore: FeatureSdkCore - - @Mock - lateinit var mockInternalLogger: InternalLogger - - private var fakeRumApplicationId: String? = null - private var fakeRumSessionId: String? = null - private var fakeRumViewId: String? = null - private var fakeRumActionId: String? = null - - @BeforeEach - fun `set up`(forge: Forge) { - fakeServiceName = forge.anAlphabeticalString() - fakeEnvName = forge.anAlphabeticalString() - fakeToken = forge.anHexadecimalString() - - fakeRumApplicationId = forge.aNullable { getForgery().toString() } - fakeRumSessionId = forge.aNullable { getForgery().toString() } - fakeRumViewId = forge.aNullable { getForgery().toString() } - fakeRumActionId = forge.aNullable { getForgery().toString() } - - whenever(mockSdkCore.getFeatureContext(Feature.RUM_FEATURE_NAME)) doReturn mapOf( - "application_id" to fakeRumApplicationId, - "session_id" to fakeRumSessionId, - "view_id" to fakeRumViewId, - "action_id" to fakeRumActionId - ) - whenever( - mockSdkCore.getFeature(Feature.TRACING_FEATURE_NAME) - ) doReturn mockTracingFeatureScope - whenever(mockTracingFeatureScope.unwrap()) doReturn mockTracingFeature - whenever(mockSdkCore.getFeature(Feature.RUM_FEATURE_NAME)) doReturn mock() - whenever(mockSdkCore.service) doReturn fakeServiceName - whenever(mockSdkCore.internalLogger) doReturn mockInternalLogger - - whenever(mockTracingFeature.legacyTracerWriter) doReturn mockTraceWriter - - testedTracerBuilder = AndroidTracer.Builder(mockSdkCore, mockLogsHandler) - } - - @AfterEach - fun `tear down`() { - val tracer = GlobalTracer.get() - val activeSpan = tracer?.activeSpan() - - @Suppress("DEPRECATION") - val activeScope = tracer?.scopeManager()?.active() - activeSpan?.finish() - activeScope?.close() - - ScopeTestHelper.removeThreadLocalScope() - } - - // region Tracer - - @Test - fun `M log a developer error W buildTracer { TracingFeature not enabled }`() { - // GIVEN - whenever(mockSdkCore.getFeature(Feature.TRACING_FEATURE_NAME)) doReturn null - - // WHEN - testedTracerBuilder.build() - - // THEN - mockInternalLogger.verifyLog( - InternalLogger.Level.ERROR, - InternalLogger.Target.USER, - AndroidTracer.TRACING_NOT_ENABLED_ERROR_MESSAGE - ) - } - - @Test - fun `M log a developer error W buildTracer { default service name not available }`() { - // GIVEN - whenever(mockSdkCore.service) doReturn "" - - // WHEN - testedTracerBuilder.build() - - // THEN - mockInternalLogger.verifyLog( - InternalLogger.Level.ERROR, - InternalLogger.Target.USER, - AndroidTracer.DEFAULT_SERVICE_NAME_IS_MISSING_ERROR_MESSAGE - ) - } - - @Test - fun `buildSpan will inject a parent context`( - @StringForgery(type = StringForgeryType.ALPHA_NUMERICAL) operationName: String - ) { - val tracer = testedTracerBuilder - .build() - - val span = tracer.buildSpan(operationName).start() as DDSpan - - assertThat(span.traceId) - .isGreaterThan(BigInteger.valueOf(0L)) - } - - @Test - fun `buildSpan will generate a random Span id`( - @StringForgery(type = StringForgeryType.ALPHA_NUMERICAL) operationName: String, - @LongForgery seed: Long - ) { - val expectedSpanId = BigInteger(AndroidTracer.SPAN_ID_BIT_SIZE, Random(seed)) - val tracer = testedTracerBuilder - .withRandom(Random(seed)) - .build() - - val span = tracer.buildSpan(operationName).start() as DDSpan - - assertThat(span.spanId) - .isEqualTo(expectedSpanId) - } - - @Test - fun `buildSpan will not inject a parent context if one exists`( - @StringForgery(type = StringForgeryType.ALPHA_NUMERICAL) operationName: String - ) { - val tracer = testedTracerBuilder - .build() - - val span = tracer.buildSpan(operationName).start() as DDSpan - tracer.activateSpan(span) - val subSpan = tracer.buildSpan(operationName).start() as DDSpan - - val traceId = subSpan.traceId - assertThat(traceId) - .isEqualTo(span.traceId) - } - - // region bundleWithRum - - @Suppress("UNCHECKED_CAST") - @Test - fun `M inject lazy Datadog context W buildSpan { bundleWithRum enabled }`( - @Forgery fakeDatadogContext: DatadogContext, - @StringForgery(type = StringForgeryType.ALPHA_NUMERICAL) fakeOperationName: String - ) { - // Given - val mockRumFeatureScope = mock() - whenever(mockSdkCore.getFeature(Feature.RUM_FEATURE_NAME)) doReturn mockRumFeatureScope - whenever(mockRumFeatureScope.withContext(eq(setOf(Feature.RUM_FEATURE_NAME)), any())) doAnswer { - it.getArgument<(DatadogContext) -> Unit>(it.arguments.lastIndex).invoke(fakeDatadogContext) - } - val tracer = AndroidTracer.Builder(mockSdkCore) - .build() - - // When - val span = tracer.buildSpan(fakeOperationName).start() as DDSpan - - // Then - val lazyContext = span.tags[SpanAttributes.DATADOG_INITIAL_CONTEXT] as CompletableFuture - assertThat(lazyContext.value).isEqualTo(fakeDatadogContext) - } - - @Test - fun `M not inject lazy Datadog context W buildSpan { bundleWithRum = false }`( - @StringForgery(type = StringForgeryType.ALPHA_NUMERICAL) fakeOperationName: String - ) { - // Given - val tracer = AndroidTracer.Builder(mockSdkCore) - .setBundleWithRumEnabled(false) - .build() - - // When - val span = tracer.buildSpan(fakeOperationName).start() as DDSpan - - // Then - assertThat(span.tags).doesNotContainKey(SpanAttributes.DATADOG_INITIAL_CONTEXT) - } - - @Test - fun `M not inject lazy Datadog context W buildSpan { bundleWithRum = true, RUM not initialized }`( - @StringForgery(type = StringForgeryType.ALPHA_NUMERICAL) fakeOperationName: String - ) { - // Given - whenever(mockSdkCore.getFeature(Feature.RUM_FEATURE_NAME)) doReturn null - val tracer = AndroidTracer.Builder(mockSdkCore) - .build() - - // When - val span = tracer.buildSpan(fakeOperationName).start() as DDSpan - - // Then - assertThat(span.tags).doesNotContainKey(SpanAttributes.DATADOG_INITIAL_CONTEXT) - } - - // endregion - - @Test - fun `it will build a valid Tracer`( - @StringForgery serviceName: String, - forge: Forge - ) { - // Given - val threshold = forge.anInt(max = 100) - // When - val tracer = testedTracerBuilder - .setService(serviceName) - .setPartialFlushThreshold(threshold) - .build() - val properties = testedTracerBuilder.properties() - - // Then - assertThat(tracer).isNotNull() - val span = tracer.buildSpan(forge.anAlphabeticalString()).start() as DDSpan - assertThat(span.serviceName).isEqualTo(serviceName) - assertThat(properties.getProperty(Config.PARTIAL_FLUSH_MIN_SPANS).toInt()) - .isEqualTo(threshold) - } - - @Test - fun `it will build a valid Tracer with sample rate`( - @DoubleForgery(0.0, 1.0) sampleRate: Double - ) { - // Given - - // When - val tracer = testedTracerBuilder - .setSampleRate(sampleRate * 100.0) - .build() - val properties = testedTracerBuilder.properties() - - // Then - assertThat(tracer).isNotNull() - assertThat(properties.getProperty(Config.TRACE_SAMPLE_RATE).toDouble()) - .isCloseTo(sampleRate, offset(0.005)) - } - - @Test - fun `it will build a valid Tracer with DATADOG and TRACECONTEXT propagation by default`() { - // Given - - // When - val tracer = testedTracerBuilder - .build() - val properties = testedTracerBuilder.properties() - - // Then - assertThat(tracer).isNotNull() - - val injectionStyles = - properties.getProperty(Config.PROPAGATION_STYLE_INJECT).toString().split(",").toSet() - val extractionStyles = - properties.getProperty(Config.PROPAGATION_STYLE_EXTRACT).toString().split(",").toSet() - - val expectedPropagationStyles = - listOf(TracingHeaderType.DATADOG, TracingHeaderType.TRACECONTEXT) - .map { it.headerType } - .toSet() - assertThat(injectionStyles).isEqualTo(expectedPropagationStyles) - assertThat(extractionStyles).isEqualTo(expectedPropagationStyles) - } - - @Test - fun `it will build a valid Tracer with global tags { addGlobalTag }`( - @StringForgery operation: String, - @StringForgery key: String, - @StringForgery(type = StringForgeryType.HEXADECIMAL) value: String, - @StringForgery serviceName: String - ) { - // When - @Suppress("DEPRECATION") - val tracer = testedTracerBuilder - .setService(serviceName) - .addGlobalTag(key, value) - .build() - - // Then - assertThat(tracer).isNotNull() - val span = tracer.buildSpan(operation).start() as DDSpan - assertThat(span.serviceName).isEqualTo(serviceName) - assertThat(span.tags).containsEntry(key, value) - } - - @Test - fun `it will build a valid Tracer with global tags { addTag }`( - @StringForgery operation: String, - @StringForgery key: String, - @StringForgery(type = StringForgeryType.HEXADECIMAL) value: String, - @StringForgery serviceName: String - ) { - // When - val tracer = testedTracerBuilder - .setService(serviceName) - .addTag(key, value) - .build() - - // Then - assertThat(tracer).isNotNull() - val span = tracer.buildSpan(operation).start() as DDSpan - assertThat(span.serviceName).isEqualTo(serviceName) - assertThat(span.tags).containsEntry(key, value) - } - - @Test - fun `it will build a valid Tracer with default values if not provided`(forge: Forge) { - // When - val tracer = testedTracerBuilder.build() - - // Then - val properties = testedTracerBuilder.properties() - assertThat(tracer).isNotNull() - val span = tracer.buildSpan(forge.anAlphabeticalString()).start() as DDSpan - assertThat(span.serviceName).isEqualTo(fakeServiceName) - assertThat(properties.getProperty(Config.PARTIAL_FLUSH_MIN_SPANS).toInt()) - .isEqualTo(AndroidTracer.DEFAULT_PARTIAL_MIN_FLUSH) - } - - @Test - fun `it will delegate all the span log action to the logsHandler`(forge: Forge) { - // Given - val tracer = testedTracerBuilder.build() - val logEvent = forge.anAlphabeticalString() - val logMaps = forge.aMap { - forge.anAlphabeticalString() to forge.anAlphabeticalString() - } - val logTimestamp = forge.aLong() - val span = tracer.buildSpan(logEvent).start() as DDSpan - - // When - span.log(logEvent) - span.log(logTimestamp, logEvent) - span.log(logMaps) - span.log(logTimestamp, logMaps) - - // Then - val inOrder = inOrder(mockLogsHandler) - inOrder.verify(mockLogsHandler).log(logEvent, span) - inOrder.verify(mockLogsHandler).log(logTimestamp, logEvent, span) - inOrder.verify(mockLogsHandler).log(logMaps, span) - inOrder.verify(mockLogsHandler).log(logTimestamp, logMaps, span) - } - - // endregion - - // region Helpers - - @Test - fun `it will delegate to the right fields when logging a throwable for a span`( - @Forgery throwable: Throwable - ) { - // Given - val mockSpan: Span = mock() - - // When - AndroidTracer.logThrowable(mockSpan, throwable) - - // Then - argumentCaptor>().apply { - verify(mockSpan).log(capture()) - assertThat(firstValue) - .containsEntry(Fields.ERROR_OBJECT, throwable) - .containsOnlyKeys(Fields.ERROR_OBJECT) - } - } - - @Test - fun `it will delegate to the right fields when logging an error message for a span`( - forge: Forge - ) { - // Given - val anErrorMessage: String = forge.aString() - val mockSpan: Span = mock() - - // When - AndroidTracer.logErrorMessage(mockSpan, anErrorMessage) - - // Then - argumentCaptor>().apply { - verify(mockSpan).log(capture()) - assertThat(firstValue) - .containsEntry(Fields.MESSAGE, anErrorMessage) - .containsEntry(AndroidTracer.LOG_STATUS, Log.ERROR) - .containsOnlyKeys(Fields.MESSAGE, AndroidTracer.LOG_STATUS) - } - } - - @Test - fun `M generate a different trace id W new tracer is created`(forge: Forge) { - // Given - val countDownLatch = CountDownLatch(2) - val tracer1: AndroidTracer - val tracer2: AndroidTracer - val span1: DDSpan - val span2: DDSpan - - // When - Thread().run { - tracer1 = AndroidTracer.Builder(mockSdkCore).build() - span1 = tracer1.buildSpan(forge.anAlphabeticalString()).start() as DDSpan - countDownLatch.countDown() - } - Thread().run { - tracer2 = AndroidTracer.Builder(mockSdkCore).build() - span2 = tracer2.buildSpan(forge.anAlphabeticalString()).start() as DDSpan - countDownLatch.countDown() - } - countDownLatch.await(10, TimeUnit.SECONDS) - - // Then - val traceIdSpan1 = span1.traceId - val traceIdSpan2 = span2.traceId - assertThat(traceIdSpan1).isNotEqualTo(traceIdSpan2) - } - - @Test - fun `M set correct propagating style W setting tracing header types`( - @StringForgery(type = StringForgeryType.ALPHA_NUMERICAL) serviceName: String, - forge: Forge - ) { - // Given - val tracingHeaderStyles = forge.aList { aValueFrom(TracingHeaderType::class.java) }.toSet() - // When - val tracer = testedTracerBuilder - .setService(serviceName) - .setTracingHeaderTypes(tracingHeaderStyles) - .build() - val properties = testedTracerBuilder.properties() - - // Then - assertThat(tracer).isNotNull() - - val injectionStyles = - properties.getProperty(Config.PROPAGATION_STYLE_INJECT).toString().split(",").toSet() - val extractionStyles = - properties.getProperty(Config.PROPAGATION_STYLE_EXTRACT).toString().split(",").toSet() - - assertThat(injectionStyles).isEqualTo(tracingHeaderStyles.map { it.headerType }.toSet()) - assertThat(extractionStyles).isEqualTo(tracingHeaderStyles.map { it.headerType }.toSet()) - } - - // endregion - - @Test - fun `M report active span context for the thread W build`( - @StringForgery(type = StringForgeryType.ALPHA_NUMERICAL) serviceName: String, - forge: Forge - ) { - // Given - val tracer = testedTracerBuilder - .setService(serviceName) - .build() - val tracingContext = ConcurrentHashMap() - whenever( - mockSdkCore.updateFeatureContext(eq(Feature.TRACING_FEATURE_NAME), eq(true), any()) - ) doAnswer { - val callback = it.getArgument<(context: MutableMap) -> Unit>(it.arguments.lastIndex) - callback.invoke(tracingContext) - } - val errorCollector = mutableListOf() - - // When+Then - val threads = forge.aList(forge.anInt(min = 2, max = 5)) { - Thread { - val threadName = Thread.currentThread().name - - val parentSpan = tracer.buildSpan(forge.anAlphabeticalString()).start() - val parentActiveScope = tracer.activateSpan(parentSpan) - - with(checkNotNull(tracingContext.activeContext(threadName))) { - assertThat(this["span_id"]).isEqualTo(parentSpan.context().toSpanId()) - assertThat(this["trace_id"]).isEqualTo(parentSpan.context().traceIdAsHexString()) - } - - // should update the context for the child span - val childActiveSpan = tracer.buildSpan(forge.anAlphabeticalString()) - .asChildOf(parentSpan).start() - val childActiveScope = tracer.activateSpan(childActiveSpan) - - with(checkNotNull(tracingContext.activeContext(threadName))) { - assertThat(this["span_id"]).isEqualTo(childActiveSpan.context().toSpanId()) - assertThat(this["trace_id"]).isEqualTo(childActiveSpan.context().traceIdAsHexString()) - } - - // should not update the context for the child non-active span - val childNonActiveSpan = tracer.buildSpan(forge.anAlphabeticalString()) - .asChildOf(parentSpan).start() - - with(checkNotNull(tracingContext.activeContext(threadName))) { - assertThat(this["span_id"]).isEqualTo(childActiveSpan.context().toSpanId()) - assertThat(this["trace_id"]).isEqualTo(childActiveSpan.context().traceIdAsHexString()) - } - - childNonActiveSpan.finish() - - with(checkNotNull(tracingContext.activeContext(threadName))) { - assertThat(this["span_id"]).isEqualTo(childActiveSpan.context().toSpanId()) - assertThat(this["trace_id"]).isEqualTo(childActiveSpan.context().traceIdAsHexString()) - } - - // should restore context of parent span - childActiveSpan.finish() - childActiveScope.close() - - with(checkNotNull(tracingContext.activeContext(threadName))) { - assertThat(this["span_id"]).isEqualTo(parentSpan.context().toSpanId()) - assertThat(this["trace_id"]).isEqualTo(parentSpan.context().traceIdAsHexString()) - } - - // should clean everything - parentSpan.finish() - parentActiveScope.close() - - assertThat(tracingContext.activeContext(threadName)).isNull() - }.apply { - setUncaughtExceptionHandler { _, e -> - synchronized(errorCollector) { - errorCollector += e - } - } - } - } - - threads.forEach { it.start() } - threads.forEach { it.join() } - - if (errorCollector.isNotEmpty()) { - // if there are multiple, we need only one to start debugging - throw errorCollector[0] - } - } - - @Suppress("UNCHECKED_CAST") - private fun Map.activeContext(threadName: String) = - this["context@$threadName"] as? Map -} diff --git a/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/trace/DatadogTracingTest.kt b/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/trace/DatadogTracingTest.kt new file mode 100644 index 0000000000..37f6f3497d --- /dev/null +++ b/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/trace/DatadogTracingTest.kt @@ -0,0 +1,160 @@ +/* + * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. + * This product includes software developed at Datadog (https://www.datadoghq.com/). + * Copyright 2016-Present Datadog, Inc. + */ +package com.datadog.android.trace + +import com.datadog.android.api.InternalLogger +import com.datadog.android.api.feature.Feature +import com.datadog.android.api.feature.FeatureScope +import com.datadog.android.api.feature.FeatureSdkCore +import com.datadog.android.trace.DatadogTracing.ErrorMessages.DEFAULT_SERVICE_NAME_IS_MISSING_ERROR_MESSAGE +import com.datadog.android.trace.DatadogTracing.ErrorMessages.TRACING_NOT_ENABLED_ERROR_MESSAGE +import com.datadog.android.trace.DatadogTracing.ErrorMessages.WRITER_PROVIDER_INTERFACE_NOT_IMPLEMENTED_ERROR_MESSAGE +import com.datadog.android.trace.DatadogTracing.ErrorMessages.buildWrongWrapperMessage +import com.datadog.android.trace.api.span.DatadogSpanWriter +import com.datadog.android.trace.internal.DatadogSpanWriterWrapper +import com.datadog.android.trace.internal.DatadogTracerAdapter +import com.datadog.android.trace.utils.verifyLog +import com.datadog.android.utils.forge.Configurator +import com.datadog.tools.unit.getFieldValue +import com.datadog.trace.common.writer.NoOpWriter +import com.datadog.trace.common.writer.Writer +import com.datadog.trace.core.CoreTracer +import fr.xgouchet.elmyr.Forge +import fr.xgouchet.elmyr.junit5.ForgeConfiguration +import fr.xgouchet.elmyr.junit5.ForgeExtension +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith +import org.junit.jupiter.api.extension.Extensions +import org.mockito.Mock +import org.mockito.junit.jupiter.MockitoExtension +import org.mockito.junit.jupiter.MockitoSettings +import org.mockito.kotlin.doReturn +import org.mockito.kotlin.mock +import org.mockito.kotlin.whenever +import org.mockito.quality.Strictness + +@Extensions( + ExtendWith(MockitoExtension::class), + ExtendWith(ForgeExtension::class) +) +@MockitoSettings(strictness = Strictness.LENIENT) +@ForgeConfiguration(Configurator::class) +class DatadogTracingTest { + + @Mock + lateinit var mockSdkCore: FeatureSdkCore + + @Mock + lateinit var mockInternalLogger: InternalLogger + + interface StubTracingFeature : Feature, InternalCoreWriterProvider + + @Mock + lateinit var mockTracingFeature: FeatureScope + + @Mock + lateinit var mockTracingFeatureScope: StubTracingFeature + + lateinit var fakeServiceName: String + + @BeforeEach + fun `set up`(forge: Forge) { + fakeServiceName = forge.anAlphabeticalString() + val writerWrapperMock = mock { + on { delegate } doReturn mock() + } + whenever(mockSdkCore.service) doReturn fakeServiceName + whenever(mockSdkCore.internalLogger) doReturn mockInternalLogger + whenever(mockTracingFeature.unwrap()) doReturn mockTracingFeatureScope + whenever(mockSdkCore.getFeature(Feature.TRACING_FEATURE_NAME)) doReturn mockTracingFeature + whenever(mockTracingFeatureScope.getCoreTracerWriter()) doReturn writerWrapperMock + } + + @Test + fun `M use a NoOpCoreTracerWriter W build { TracingFeature not enabled }`() { + // Given + whenever(mockSdkCore.getFeature(Feature.TRACING_FEATURE_NAME)) doReturn null + + // When + val tracer = DatadogTracing.newTracerBuilder(mockSdkCore).build() as DatadogTracerAdapter + + // Then + val coreTracer = tracer.delegate as CoreTracer + val writer: Writer = coreTracer.getFieldValue("writer") + assertThat(writer).isInstanceOf(NoOpWriter::class.java) + } + + @Test + fun `M log a maintainer error W build { TracingFeature not implementing InternalCoreTracerWriterProvider }`() { + // Given + whenever(mockTracingFeature.unwrap()) doReturn mock() + + // When + val tracer = DatadogTracing.newTracerBuilder(mockSdkCore).build() + + // Then + assertThat(tracer).isNotNull + mockInternalLogger.verifyLog( + InternalLogger.Level.ERROR, + InternalLogger.Target.MAINTAINER, + WRITER_PROVIDER_INTERFACE_NOT_IMPLEMENTED_ERROR_MESSAGE + ) + } + + @Test + fun `M log a user error W build { default service name not available }`() { + // Given + whenever(mockSdkCore.service) doReturn "" + + // When + DatadogTracing.newTracerBuilder(mockSdkCore).build() + + // Then + mockInternalLogger.verifyLog( + InternalLogger.Level.ERROR, + InternalLogger.Target.USER, + DEFAULT_SERVICE_NAME_IS_MISSING_ERROR_MESSAGE + ) + } + + @Test + fun `M log a user error W build { writer is null }`() { + // Given + class CustomWrapper : DatadogSpanWriter + + val customWrapperInstance = CustomWrapper() + whenever(mockTracingFeatureScope.getCoreTracerWriter()) doReturn customWrapperInstance + + // When + DatadogTracing.newTracerBuilder(mockSdkCore).build() + + // Then + mockInternalLogger.verifyLog( + InternalLogger.Level.ERROR, + InternalLogger.Target.USER, + buildWrongWrapperMessage(customWrapperInstance.javaClass) + ) + } + + @Test + fun `M log a user error W build { TracingFeature not enabled }`() { + // Given + whenever(mockSdkCore.getFeature(Feature.TRACING_FEATURE_NAME)) doReturn null + + // When + val tracer = DatadogTracing.newTracerBuilder(mockSdkCore).build() + + // Then + assertThat(tracer).isNotNull + mockInternalLogger.verifyLog( + InternalLogger.Level.ERROR, + InternalLogger.Target.USER, + TRACING_NOT_ENABLED_ERROR_MESSAGE + ) + } +} diff --git a/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/trace/SpanExtTest.kt b/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/trace/SpanExtTest.kt index 5feba40aba..b456080562 100644 --- a/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/trace/SpanExtTest.kt +++ b/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/trace/SpanExtTest.kt @@ -6,19 +6,16 @@ package com.datadog.android.trace -import android.util.Log +import com.datadog.android.trace.api.scope.DatadogScope +import com.datadog.android.trace.api.span.DatadogSpan +import com.datadog.android.trace.api.span.DatadogSpanBuilder +import com.datadog.android.trace.api.tracer.DatadogTracer import com.datadog.tools.unit.forge.BaseConfigurator -import com.datadog.tools.unit.setStaticValue import fr.xgouchet.elmyr.annotation.Forgery import fr.xgouchet.elmyr.annotation.LongForgery import fr.xgouchet.elmyr.annotation.StringForgery import fr.xgouchet.elmyr.junit5.ForgeConfiguration import fr.xgouchet.elmyr.junit5.ForgeExtension -import io.opentracing.Scope -import io.opentracing.Span -import io.opentracing.Tracer -import io.opentracing.log.Fields -import io.opentracing.util.GlobalTracer import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.BeforeEach @@ -29,10 +26,8 @@ import org.junit.jupiter.api.extension.Extensions import org.mockito.Mock import org.mockito.junit.jupiter.MockitoExtension import org.mockito.junit.jupiter.MockitoSettings -import org.mockito.kotlin.argumentCaptor import org.mockito.kotlin.doReturn import org.mockito.kotlin.inOrder -import org.mockito.kotlin.mock import org.mockito.kotlin.never import org.mockito.kotlin.verify import org.mockito.kotlin.whenever @@ -47,68 +42,35 @@ import org.mockito.quality.Strictness class SpanExtTest { @Mock - lateinit var mockTracer: Tracer + lateinit var mockTracer: DatadogTracer @Mock - lateinit var mockSpanBuilder: Tracer.SpanBuilder + lateinit var mockSpanBuilder: DatadogSpanBuilder @Mock - lateinit var mockSpan: Span + lateinit var mockSpan: DatadogSpan @Mock - lateinit var mockParentSpan: Span + lateinit var mockParentSpan: DatadogSpan @Mock - lateinit var mockScope: Scope + lateinit var mockScope: DatadogScope @StringForgery lateinit var fakeOperationName: String @BeforeEach fun `set up`() { - GlobalTracer.registerIfAbsent(mockTracer) + GlobalDatadogTracer.registerIfAbsent(mockTracer) whenever(mockTracer.buildSpan(fakeOperationName)) doReturn mockSpanBuilder whenever(mockTracer.activateSpan(mockSpan)) doReturn mockScope - whenever(mockSpanBuilder.asChildOf(mockParentSpan)) doReturn mockSpanBuilder + whenever(mockSpanBuilder.withParentSpan(mockParentSpan)) doReturn mockSpanBuilder whenever(mockSpanBuilder.start()) doReturn mockSpan } @AfterEach fun `tear down`() { - GlobalTracer::class.java.setStaticValue("isRegistered", false) - } - - @Test - fun `M log throwable W setError(Throwable)`( - @Forgery throwable: Throwable - ) { - val mockSpan: Span = mock() - - mockSpan.setError(throwable) - - argumentCaptor>().apply { - verify(mockSpan).log(capture()) - assertThat(firstValue) - .containsEntry(Fields.ERROR_OBJECT, throwable) - .containsOnlyKeys(Fields.ERROR_OBJECT) - } - } - - @Test - fun `M log error message W setError(String)`( - @StringForgery message: String - ) { - val mockSpan: Span = mock() - - mockSpan.setError(message) - - argumentCaptor>().apply { - verify(mockSpan).log(capture()) - assertThat(firstValue) - .containsEntry(Fields.MESSAGE, message) - .containsEntry(AndroidTracer.LOG_STATUS, Log.ERROR) - .containsOnlyKeys(Fields.MESSAGE, AndroidTracer.LOG_STATUS) - } + GlobalDatadogTracer.clear() } @Test @@ -116,7 +78,7 @@ class SpanExtTest { @LongForgery result: Long ) { var lambdaCalled = false - whenever(mockSpanBuilder.asChildOf(null as Span?)) doReturn mockSpanBuilder + whenever(mockSpanBuilder.withParentSpan(null as DatadogSpan?)) doReturn mockSpanBuilder val callResult = withinSpan(fakeOperationName) { lambdaCalled = true @@ -125,7 +87,7 @@ class SpanExtTest { assertThat(lambdaCalled).isTrue() assertThat(callResult).isEqualTo(result) - verify(mockSpanBuilder).asChildOf(null as Span?) + verify(mockSpanBuilder).withParentSpan(null as DatadogSpan?) inOrder(mockSpan, mockScope) { verify(mockSpan).finish() verify(mockScope).close() @@ -145,7 +107,7 @@ class SpanExtTest { assertThat(lambdaCalled).isTrue() assertThat(callResult).isEqualTo(result) - verify(mockSpanBuilder).asChildOf(mockParentSpan) + verify(mockSpanBuilder).withParentSpan(mockParentSpan) inOrder(mockSpan, mockScope) { verify(mockSpan).finish() verify(mockScope).close() @@ -167,15 +129,9 @@ class SpanExtTest { assertThat(thrown).isEqualTo(throwable) assertThat(lambdaCalled).isTrue() - verify(mockSpanBuilder).asChildOf(mockParentSpan) + verify(mockSpanBuilder).withParentSpan(mockParentSpan) inOrder(mockSpan, mockScope) { - argumentCaptor>().apply { - verify(mockSpan).log(capture()) - assertThat(firstValue) - .containsEntry(Fields.ERROR_OBJECT, throwable) - .containsOnlyKeys(Fields.ERROR_OBJECT) - } - + verify(mockSpan).logThrowable(throwable) verify(mockSpan).finish() verify(mockScope).close() } @@ -194,7 +150,7 @@ class SpanExtTest { assertThat(lambdaCalled).isTrue() assertThat(callResult).isEqualTo(result) - verify(mockSpanBuilder).asChildOf(mockParentSpan) + verify(mockSpanBuilder).withParentSpan(mockParentSpan) inOrder(mockSpan) { verify(mockSpan).finish() } diff --git a/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/trace/assertj/SpanEventAssert.kt b/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/trace/assertj/SpanEventAssert.kt index 0ddcd3576e..9813c5a406 100644 --- a/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/trace/assertj/SpanEventAssert.kt +++ b/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/trace/assertj/SpanEventAssert.kt @@ -359,64 +359,64 @@ internal class SpanEventAssert(actual: SpanEvent) : DeviceType.GAMING_CONSOLE -> SpanEvent.Type.GAMING_CONSOLE DeviceType.BOT -> SpanEvent.Type.BOT } - assertThat(actual.device.type) + assertThat(actual.meta.device.type) .overridingErrorMessage( "Expected SpanEvent to have device type: " + "$expectedType but " + - "instead was: ${actual.device.type}" + "instead was: ${actual.meta.device.type}" ) .isEqualTo(expectedType) - assertThat(actual.device.name) + assertThat(actual.meta.device.name) .overridingErrorMessage( "Expected SpanEvent to have device name: " + "${deviceInfo.deviceName} but " + - "instead was: ${actual.device.name}" + "instead was: ${actual.meta.device.name}" ) .isEqualTo(deviceInfo.deviceName) - assertThat(actual.device.model) + assertThat(actual.meta.device.model) .overridingErrorMessage( "Expected SpanEvent to have device model: " + "${deviceInfo.deviceModel} but " + - "instead was: ${actual.device.model}" + "instead was: ${actual.meta.device.model}" ) .isEqualTo(deviceInfo.deviceModel) - assertThat(actual.device.brand) + assertThat(actual.meta.device.brand) .overridingErrorMessage( "Expected SpanEvent to have device brand: " + "${deviceInfo.deviceBrand} but " + - "instead was: ${actual.device.brand}" + "instead was: ${actual.meta.device.brand}" ) .isEqualTo(deviceInfo.deviceBrand) - assertThat(actual.device.architecture) + assertThat(actual.meta.device.architecture) .overridingErrorMessage( "Expected SpanEvent to have device architecture: " + "${deviceInfo.architecture} but " + - "instead was: ${actual.device.architecture}" + "instead was: ${actual.meta.device.architecture}" ) .isEqualTo(deviceInfo.architecture) return this } fun hasOsInfo(deviceInfo: DeviceInfo): SpanEventAssert { - assertThat(actual.os.name) + assertThat(actual.meta.os.name) .overridingErrorMessage( "Expected SpanEvent to have os name: " + "${deviceInfo.osName} but " + - "instead was: ${actual.os.name}" + "instead was: ${actual.meta.os.name}" ) .isEqualTo(deviceInfo.osName) - assertThat(actual.os.versionMajor) + assertThat(actual.meta.os.versionMajor) .overridingErrorMessage( "Expected SpanEvent to have os major version: " + "${deviceInfo.osMajorVersion} but " + - "instead was: ${actual.os.versionMajor}" + "instead was: ${actual.meta.os.versionMajor}" ) .isEqualTo(deviceInfo.osMajorVersion) - assertThat(actual.os.version) + assertThat(actual.meta.os.version) .overridingErrorMessage( "Expected SpanEvent to have os version: " + "${deviceInfo.osVersion} but " + - "instead was: ${actual.os.version}" + "instead was: ${actual.meta.os.version}" ) .isEqualTo(deviceInfo.osVersion) return this diff --git a/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/trace/assertj/TracerConfigAssert.kt b/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/trace/assertj/TracerConfigAssert.kt deleted file mode 100644 index b1caeffecf..0000000000 --- a/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/trace/assertj/TracerConfigAssert.kt +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. - * This product includes software developed at Datadog (https://www.datadoghq.com/). - * Copyright 2016-Present Datadog, Inc. - */ - -package com.datadog.android.trace.assertj - -import com.datadog.legacy.trace.api.Config -import org.assertj.core.api.AbstractObjectAssert -import org.assertj.core.api.Assertions.assertThat - -internal class TracerConfigAssert(actual: Config) : AbstractObjectAssert - (actual, TracerConfigAssert::class.java) { - - fun hasServiceName(expected: String): TracerConfigAssert { - assertThat(actual.serviceName) - .overridingErrorMessage( - "Expected config to have serviceName $expected but was ${actual.serviceName}" - ) - .isEqualTo(expected) - - return this - } - - companion object { - fun assertThat(actual: Config): TracerConfigAssert { - return TracerConfigAssert(actual) - } - } -} diff --git a/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/trace/internal/DatadogPropagationAdapterTest.kt b/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/trace/internal/DatadogPropagationAdapterTest.kt new file mode 100644 index 0000000000..102b1d8f8b --- /dev/null +++ b/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/trace/internal/DatadogPropagationAdapterTest.kt @@ -0,0 +1,126 @@ +/* + * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. + * This product includes software developed at Datadog (https://www.datadoghq.com/). + * Copyright 2016-Present Datadog, Inc. + */ +package com.datadog.android.trace.internal + +import com.datadog.android.api.InternalLogger +import com.datadog.android.trace.api.span.DatadogSpanContext +import com.datadog.android.trace.api.trace.DatadogTraceId +import com.datadog.android.trace.utils.verifyLog +import com.datadog.trace.bootstrap.instrumentation.api.AgentPropagation +import com.datadog.trace.bootstrap.instrumentation.api.AgentSpan +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith +import org.junit.jupiter.api.extension.Extensions +import org.mockito.Mock +import org.mockito.junit.jupiter.MockitoExtension +import org.mockito.kotlin.any +import org.mockito.kotlin.doReturn +import org.mockito.kotlin.eq +import org.mockito.kotlin.mock +import org.mockito.kotlin.verify +import org.mockito.kotlin.whenever + +@Extensions( + ExtendWith(MockitoExtension::class) +) +internal class DatadogPropagationAdapterTest { + + private lateinit var testedPropagation: DatadogPropagationAdapter + + @Mock + lateinit var mockAgentPropagation: AgentPropagation + + @Mock + lateinit var mockInternalLogger: InternalLogger + + @Mock + lateinit var mockAgentSpanContext: AgentSpan.Context + + @BeforeEach + fun `set up`() { + testedPropagation = DatadogPropagationAdapter( + mockInternalLogger, + mockAgentPropagation + ) + } + + @Test + fun `M report an error W inject { unsupported DatadogSpanContext implementation provided }`() { + // Given + val carrier = Any() + + // When + testedPropagation.inject(UnsupportedDatadogSpanContextImplementation(), carrier) { _, _, _ -> } + + // Then + mockInternalLogger.verifyLog( + InternalLogger.Level.ERROR, + InternalLogger.Target.USER, + "DatadogPropagationAdapter supports only DatadogSpanContextAdapter instancies for injection " + + "but UnsupportedDatadogSpanContextImplementation is given" + ) + } + + @Test + fun `M delegate to AgentPropagation W inject `() { + // Given + val carrier = Any() + val mockContext: DatadogSpanContextAdapter = mock { + on { mock.delegate } doReturn mockAgentSpanContext + } + + // When + val setter: (carrier: Any, key: String, value: String) -> Unit = { _, _, _ -> } + testedPropagation.inject(mockContext, carrier, setter) + + // Then + verify(mockAgentPropagation).inject(eq(mockAgentSpanContext), any(), any()) + } + + @Test + fun `M delegate to AgentPropagation W extract `() { + // Given + val carrier = Any() + val getter = { _: Any, _: (String, String) -> Boolean -> } + + // When + testedPropagation.extract(carrier, getter) + + // Then + verify(mockAgentPropagation).extract(eq(carrier), any()) + } + + @Test + fun `M return DatadogSpanContextAdapter W extract `() { + // Given + val carrier = Any() + val expectedContext = mock() + val getter = { _: Any, _: (String, String) -> Boolean -> } + whenever(mockAgentPropagation.extract(eq(carrier), any())).doReturn(expectedContext) + + // When + val actual = testedPropagation.extract(carrier, getter) as DatadogSpanContextAdapter + + // Then + assertThat(actual.delegate).isEqualTo(expectedContext) + } + + private class UnsupportedDatadogSpanContextImplementation : DatadogSpanContext { + override val traceId: DatadogTraceId + get() = TODO("Not yet implemented") + override val spanId: Long + get() = TODO("Not yet implemented") + override val samplingPriority: Int + get() = TODO("Not yet implemented") + override val tags: Map + get() = TODO("Not yet implemented") + + override fun setSamplingPriority(samplingPriority: Int): Boolean = TODO("Not yet implemented") + override fun setMetric(key: CharSequence?, value: Double): Unit = TODO("Not yet implemented") + } +} diff --git a/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/trace/internal/DatadogScopeAdapterTest.kt b/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/trace/internal/DatadogScopeAdapterTest.kt new file mode 100644 index 0000000000..68e730a73b --- /dev/null +++ b/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/trace/internal/DatadogScopeAdapterTest.kt @@ -0,0 +1,34 @@ +/* + * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. + * This product includes software developed at Datadog (https://www.datadoghq.com/). + * Copyright 2016-Present Datadog, Inc. + */ +package com.datadog.android.trace.internal + +import com.datadog.trace.bootstrap.instrumentation.api.AgentScope +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith +import org.junit.jupiter.api.extension.Extensions +import org.mockito.Mock +import org.mockito.junit.jupiter.MockitoExtension +import org.mockito.kotlin.verify + +@Extensions( + ExtendWith(MockitoExtension::class) +) +class DatadogScopeAdapterTest { + @Mock + lateinit var delegate: AgentScope + + @Test + fun `M delegate close W close is called`() { + // Given + val adapter = DatadogScopeAdapter(delegate) + + // When + adapter.close() + + // Then + verify(delegate).close() + } +} diff --git a/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/trace/internal/DatadogScopeListenerAdapterTest.kt b/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/trace/internal/DatadogScopeListenerAdapterTest.kt new file mode 100644 index 0000000000..a872998de6 --- /dev/null +++ b/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/trace/internal/DatadogScopeListenerAdapterTest.kt @@ -0,0 +1,47 @@ +/* + * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. + * This product includes software developed at Datadog (https://www.datadoghq.com/). + * Copyright 2016-Present Datadog, Inc. + */ +package com.datadog.android.trace.internal + +import com.datadog.android.trace.api.scope.DatadogScopeListener +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith +import org.junit.jupiter.api.extension.Extensions +import org.mockito.Mock +import org.mockito.junit.jupiter.MockitoExtension +import org.mockito.kotlin.verify + +@Extensions( + ExtendWith(MockitoExtension::class) +) +class DatadogScopeListenerAdapterTest { + + @Mock + lateinit var delegate: DatadogScopeListener + + @Test + fun `M delegate afterScopeClosed W afterScopeClosed is called`() { + // Given + val adapter = DatadogScopeListenerAdapter(delegate) + + // When + adapter.afterScopeClosed() + + // Then + verify(delegate).afterScopeClosed() + } + + @Test + fun `M delegate afterScopeActivated W afterScopeActivated is called`() { + // Then + val adapter = DatadogScopeListenerAdapter(delegate) + + // When + adapter.afterScopeActivated() + + // Then + verify(delegate).afterScopeActivated() + } +} diff --git a/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/trace/internal/DatadogSpanAdapterTest.kt b/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/trace/internal/DatadogSpanAdapterTest.kt new file mode 100644 index 0000000000..b85c3908a4 --- /dev/null +++ b/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/trace/internal/DatadogSpanAdapterTest.kt @@ -0,0 +1,301 @@ +/* + * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. + * This product includes software developed at Datadog (https://www.datadoghq.com/). + * Copyright 2016-Present Datadog, Inc. + */ +package com.datadog.android.trace.internal + +import com.datadog.android.trace.api.span.DatadogSpan +import com.datadog.android.trace.api.span.DatadogSpanContext +import com.datadog.android.trace.api.trace.DatadogTraceId +import com.datadog.android.utils.forge.Configurator +import com.datadog.trace.api.DDTraceId +import com.datadog.trace.bootstrap.instrumentation.api.AgentSpan +import com.datadog.trace.core.DDSpan +import fr.xgouchet.elmyr.annotation.BoolForgery +import fr.xgouchet.elmyr.annotation.Forgery +import fr.xgouchet.elmyr.annotation.IntForgery +import fr.xgouchet.elmyr.annotation.LongForgery +import fr.xgouchet.elmyr.annotation.StringForgery +import fr.xgouchet.elmyr.junit5.ForgeConfiguration +import fr.xgouchet.elmyr.junit5.ForgeExtension +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith +import org.junit.jupiter.api.extension.Extensions +import org.mockito.Mock +import org.mockito.junit.jupiter.MockitoExtension +import org.mockito.junit.jupiter.MockitoSettings +import org.mockito.kotlin.doReturn +import org.mockito.kotlin.mock +import org.mockito.kotlin.verify +import org.mockito.kotlin.whenever +import org.mockito.quality.Strictness + +@Extensions( + ExtendWith(MockitoExtension::class), + ExtendWith(ForgeExtension::class) +) +@MockitoSettings(strictness = Strictness.LENIENT) +@ForgeConfiguration(Configurator::class) +internal class DatadogSpanAdapterTest { + + @Mock + lateinit var mockAgentSpan: AgentSpan + + @Mock + lateinit var mockSpanLogger: DatadogSpanLogger + + @Mock + lateinit var mockDDTraceId: DDTraceId + + @Mock + lateinit var mockDDSpan: DDSpan + + @Mock + lateinit var mockSpanContext: AgentSpan.Context + + @IntForgery + private var fakeInt: Int = 0 + + @BoolForgery + private var fakeBool: Boolean = false + + @StringForgery + lateinit var fakeString: String + + @LongForgery + private var fakeLong: Long = 0L + + @Forgery + lateinit var fakeThrowable: Throwable + + private lateinit var testedSpanAdapter: DatadogSpanAdapter + private lateinit var testedSpanAdapterWithDDSpanDelegate: DatadogSpanAdapter + + @BeforeEach + fun `set up`() { + whenever(mockAgentSpan.context()).thenReturn(mockSpanContext) + whenever(mockAgentSpan.traceId).thenReturn(mockDDTraceId) + testedSpanAdapter = DatadogSpanAdapter(mockAgentSpan, mockSpanLogger) + testedSpanAdapterWithDDSpanDelegate = DatadogSpanAdapter(mockDDSpan, mockSpanLogger) + } + + @Test + fun `M delegate drop W drop is called`() { + // When + testedSpanAdapter.drop() + + // Then + verify(mockAgentSpan).drop() + } + + @Test + fun `M delegate finish W finish() is called`() { + // When + testedSpanAdapter.finish() + + // Then + verify(mockAgentSpan).finish() + } + + @Test + fun `M delegate finish W finish(millis) is called`() { + // When + testedSpanAdapter.finish(fakeLong) + + // Then + verify(mockAgentSpan).finish(fakeLong) + } + + @Test + fun `M delegate setTag(String, Number) W drop is called`() { + // When + testedSpanAdapter.setTag(fakeString, fakeLong) + + // Then + verify(mockAgentSpan).setTag(fakeString, fakeLong as? Number) + } + + @Test + fun `M delegate setTag(String, String) W drop is called`() { + // When + testedSpanAdapter.setTag(fakeString, fakeString) + + // Then + verify(mockAgentSpan).setTag(fakeString, fakeString) + } + + @Test + fun `M delegate setTag(String, Object) W drop is called`() { + // Given + val value = Any() + + // When + testedSpanAdapter.setTag(fakeString, value) + + // Then + verify(mockAgentSpan).setTag(fakeString, value) + } + + @Test + fun `M delegate setMetric(String, Int) W drop is called`() { + // When + testedSpanAdapter.setMetric(fakeString, fakeInt) + + // Then + verify(mockAgentSpan).setMetric(fakeString, fakeInt) + } + + @Test + fun `M delegate setErrorMessage(String) W drop is called`() { + // When + testedSpanAdapter.setErrorMessage(fakeString) + + // Then + verify(mockAgentSpan).setErrorMessage(fakeString) + } + + @Test + fun `M delegate addThrowable(Throwable) W drop is called`() { + // When + testedSpanAdapter.addThrowable(fakeThrowable) + + // Then + verify(mockAgentSpan).addThrowable(fakeThrowable) + } + + @Test + fun `M delegate addThrowable(Throwable, Byte) W drop is called`() { + // Given + val errorPriority = fakeInt.toByte() + + // When + testedSpanAdapter.addThrowable(fakeThrowable, errorPriority) + + // Then + verify(mockAgentSpan).addThrowable(fakeThrowable, errorPriority) + } + + @Test + fun `M return delegate#isError W isError is set`() { + // When + testedSpanAdapter.isError = fakeBool + + // Then + verify(mockAgentSpan).setError(fakeBool) + } + + @Test + fun `M return delegate#isError W isError is get`() { + // Given + whenever(mockAgentSpan.isError) doReturn fakeBool + + // When + val error = testedSpanAdapter.isError + + // Then + assertThat(error).isEqualTo(fakeBool) + } + + @Test + fun `M return false W isRootSpan {delegate is not DDSpan}`() { + // When + val rootSpan = testedSpanAdapter.isRootSpan + + // Then + assertThat(rootSpan).isFalse() + } + + @Test + fun `M return return delegate#isRootSpan W isRootSpan {delegate is DDSpan}`() { + // Given + whenever(testedSpanAdapterWithDDSpanDelegate.isRootSpan).thenReturn(fakeBool) + + // When + val actual = testedSpanAdapterWithDDSpanDelegate.isRootSpan + + // Then + assertThat(actual).isEqualTo(fakeBool) + } + + @Test + fun `M return DatadogContext instance W context() is called`() { + // When + val context = testedSpanAdapter.context() + + // Then + assertThat(context).isInstanceOf(DatadogSpanContext::class.java) + } + + @Test + fun `M return DatadogTraceId instance W traceId is called`() { + // When + val traceId = testedSpanAdapter.traceId + + // Then + assertThat(traceId).isInstanceOf(DatadogTraceId::class.java) + } + + @Test + fun `M return delegate#parentSpanId W parentSpanId is called`() { + // Given + whenever(mockDDSpan.parentId).thenReturn(fakeLong) + + // When + val actual = testedSpanAdapterWithDDSpanDelegate.parentSpanId + + // Then + assertThat(actual).isEqualTo(fakeLong) + } + + @Test + fun `M return delegate#samplingPriority W samplingPriority is called`() { + // Given + whenever(mockAgentSpan.traceSamplingPriority).thenReturn(fakeInt) + + // When + val actual = testedSpanAdapter.samplingPriority + + // Then + assertThat(actual).isEqualTo(fakeInt) + } + + @Test + fun `M return delegate#durationNano W durationNano is called`() { + // Given + whenever(mockAgentSpan.durationNano).thenReturn(fakeLong) + + // When + val actual = testedSpanAdapter.durationNano + + // Then + assertThat(actual).isEqualTo(fakeLong) + } + + @Test + fun `M return delegate#startTimeNano W startTimeNano is called`() { + // Given + whenever(mockAgentSpan.startTime).thenReturn(fakeLong) + + // When + val startTimeNano = testedSpanAdapter.startTimeNanos + + // Then + assertThat(startTimeNano).isEqualTo(fakeLong) + } + + @Test + fun `M return DatadogContext instance W localRootSpan is called`() { + // Given + val expected = mock() + whenever(mockAgentSpan.localRootSpan).thenReturn(expected) + + // When + val actual = testedSpanAdapter.localRootSpan + + // Then + assertThat(actual).isInstanceOf(DatadogSpan::class.java) + } +} diff --git a/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/trace/internal/DatadogSpanBuilderAdapterTest.kt b/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/trace/internal/DatadogSpanBuilderAdapterTest.kt new file mode 100644 index 0000000000..f4a23e29f7 --- /dev/null +++ b/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/trace/internal/DatadogSpanBuilderAdapterTest.kt @@ -0,0 +1,187 @@ +/* + * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. + * This product includes software developed at Datadog (https://www.datadoghq.com/). + * Copyright 2016-Present Datadog, Inc. + */ +package com.datadog.android.trace.internal + +import com.datadog.android.trace.api.span.DatadogSpan +import com.datadog.android.trace.api.span.DatadogSpanLink +import com.datadog.android.utils.forge.Configurator +import com.datadog.trace.bootstrap.instrumentation.api.AgentSpan +import com.datadog.trace.bootstrap.instrumentation.api.AgentSpanLink +import com.datadog.trace.bootstrap.instrumentation.api.AgentTracer +import fr.xgouchet.elmyr.annotation.DoubleForgery +import fr.xgouchet.elmyr.annotation.Forgery +import fr.xgouchet.elmyr.annotation.LongForgery +import fr.xgouchet.elmyr.annotation.StringForgery +import fr.xgouchet.elmyr.junit5.ForgeConfiguration +import fr.xgouchet.elmyr.junit5.ForgeExtension +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith +import org.junit.jupiter.api.extension.Extensions +import org.mockito.Mock +import org.mockito.junit.jupiter.MockitoExtension +import org.mockito.junit.jupiter.MockitoSettings +import org.mockito.kotlin.argumentCaptor +import org.mockito.kotlin.doReturn +import org.mockito.kotlin.mock +import org.mockito.kotlin.verify +import org.mockito.kotlin.whenever +import org.mockito.quality.Strictness + +@Extensions( + ExtendWith(MockitoExtension::class), + ExtendWith(ForgeExtension::class) +) +@MockitoSettings(strictness = Strictness.LENIENT) +@ForgeConfiguration(Configurator::class) +class DatadogSpanBuilderAdapterTest { + + @StringForgery + lateinit var fakeString: String + + @LongForgery + private var fakeLong: Long = 0 + + @DoubleForgery + private var fakeDouble: Double = 0.0 + + @Forgery + lateinit var fakeDatadogSpanLink: DatadogSpanLink + + @Mock + private lateinit var mockAgentSpanBuilderAdapter: AgentTracer.SpanBuilder + + @Mock + private lateinit var mockAgentSpanContext: AgentSpan.Context + + @Mock + private lateinit var mockSpanLogger: DatadogSpanLogger + + private lateinit var testedBuilderAdapter: DatadogSpanBuilderAdapter + + @BeforeEach + fun `set up`() { + testedBuilderAdapter = DatadogSpanBuilderAdapter(mockAgentSpanBuilderAdapter, mockSpanLogger) + } + + @Test + fun `M delegate ignoreActiveSpan W ignoreActiveSpan is called`() { + // When + testedBuilderAdapter.ignoreActiveSpan() + + // Then + verify(mockAgentSpanBuilderAdapter).ignoreActiveSpan() + } + + @Test + fun `M return DatadogSpan W start() is called`() { + // Given + whenever(mockAgentSpanBuilderAdapter.start()).thenReturn(mock()) + + // When + val actual = testedBuilderAdapter.start() + + // Then + assertThat(actual).isInstanceOf(DatadogSpan::class.java) + } + + @Test + fun `M delegate withOrigin W withOrigin is called`() { + // When + val actual = testedBuilderAdapter.withOrigin(fakeString) + + // Then + assertThat(actual).isEqualTo(testedBuilderAdapter) + verify(mockAgentSpanBuilderAdapter).withOrigin(fakeString) + } + + @Test + fun `M delegate withStartTimestamp W withStartTimestamp is called`() { + // When + val actual = testedBuilderAdapter.withStartTimestamp(fakeLong) + + // Then + assertThat(actual).isEqualTo(testedBuilderAdapter) + verify(mockAgentSpanBuilderAdapter).withStartTimestamp(fakeLong) + } + + @Test + fun `M delegate withTag W withTag(String, Long) is called`() { + // When + testedBuilderAdapter.withTag(fakeString, fakeLong) + + // Then + verify(mockAgentSpanBuilderAdapter).withTag(fakeString, fakeLong) + } + + @Test + fun `M delegate withTag W withTag(String, Any) is called`() { + // Given + val value = Any() + + // When + testedBuilderAdapter.withTag(fakeString, value) + + // Then + verify(mockAgentSpanBuilderAdapter).withTag(fakeString, value) + } + + @Test + fun `M delegate withTag W withTag(String, Double) is called`() { + // When + testedBuilderAdapter.withTag(fakeString, fakeDouble) + + // Then + verify(mockAgentSpanBuilderAdapter).withTag(fakeString, fakeDouble) + } + + @Test + fun `M delegate withTag W withLink(DatadogSpanLink) is called`() { + // When + testedBuilderAdapter.withLink(fakeDatadogSpanLink) + + // Then + argumentCaptor { + verify(mockAgentSpanBuilderAdapter).withLink(capture()) + assertThat(firstValue.spanId()).isEqualTo(fakeDatadogSpanLink.spanId) + assertThat(firstValue.traceId().toString()).isEqualTo(fakeDatadogSpanLink.traceId.toString()) + assertThat(firstValue.attributes().asMap()).isEqualTo(fakeDatadogSpanLink.attributes) + } + } + + @Test + fun `M delegate withResourceName W withResourceName(String) is called`() { + // When + testedBuilderAdapter.withResourceName(fakeString) + + // Then + verify(mockAgentSpanBuilderAdapter).withResourceName(fakeString) + } + + @Test + fun `M delegate asChildOf W withParentContext(DatadogSpanContextAdapter) is called`() { + // When + testedBuilderAdapter.withParentContext(DatadogSpanContextAdapter(mockAgentSpanContext)) + + // Then + verify(mockAgentSpanBuilderAdapter).asChildOf(mockAgentSpanContext) + } + + @Test + fun `M delegate asChildOf W withParentSpan(DatadogSpanContextAdapter) is called`() { + // Given + val mockSpan = mock { + on { context() } doReturn DatadogSpanContextAdapter(mockAgentSpanContext) + } + + // When + testedBuilderAdapter.withParentSpan(mockSpan) + + // Then + verify(mockAgentSpanBuilderAdapter).asChildOf(mockAgentSpanContext) + } +} diff --git a/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/trace/internal/DatadogSpanContextAdapterTest.kt b/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/trace/internal/DatadogSpanContextAdapterTest.kt new file mode 100644 index 0000000000..61a83ea0f4 --- /dev/null +++ b/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/trace/internal/DatadogSpanContextAdapterTest.kt @@ -0,0 +1,105 @@ +/* + * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. + * This product includes software developed at Datadog (https://www.datadoghq.com/). + * Copyright 2016-Present Datadog, Inc. + */ +package com.datadog.android.trace.internal + +import com.datadog.android.utils.forge.Configurator +import com.datadog.trace.api.DDTraceId +import com.datadog.trace.bootstrap.instrumentation.api.AgentSpan +import com.datadog.trace.core.DDSpanContext +import fr.xgouchet.elmyr.Forge +import fr.xgouchet.elmyr.annotation.IntForgery +import fr.xgouchet.elmyr.annotation.LongForgery +import fr.xgouchet.elmyr.junit5.ForgeConfiguration +import fr.xgouchet.elmyr.junit5.ForgeExtension +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith +import org.junit.jupiter.api.extension.Extensions +import org.mockito.Mock +import org.mockito.junit.jupiter.MockitoExtension +import org.mockito.junit.jupiter.MockitoSettings +import org.mockito.kotlin.whenever +import org.mockito.quality.Strictness + +@Extensions( + ExtendWith(MockitoExtension::class), + ExtendWith(ForgeExtension::class) +) +@MockitoSettings(strictness = Strictness.LENIENT) +@ForgeConfiguration(Configurator::class) +class DatadogSpanContextAdapterTest { + + @LongForgery + private var fakeLong: Long = 0L + + @IntForgery + private var fakeInt: Int = 0 + + @Mock + lateinit var mockAgentSpanContext: AgentSpan.Context + + @Mock + lateinit var mockDDSpanContext: DDSpanContext + + private lateinit var testedAgentSpanContextAdapter: DatadogSpanContextAdapter + private lateinit var testedDDSpanContextContextAdapter: DatadogSpanContextAdapter + + @BeforeEach + fun `set up`() { + testedAgentSpanContextAdapter = DatadogSpanContextAdapter(mockAgentSpanContext) + testedDDSpanContextContextAdapter = DatadogSpanContextAdapter(mockDDSpanContext) + } + + @Test + fun `M return delegate#spanId W spanId is called`() { + // Given + whenever(mockAgentSpanContext.spanId).thenReturn(fakeLong) + + // When + val actual = testedAgentSpanContextAdapter.spanId + + // Then + assertThat(actual).isEqualTo(fakeLong) + } + + @Test + fun `M return delegate#samplingPriority W samplingPriority is called`() { + // Given + whenever(mockAgentSpanContext.traceSamplingPriority).thenReturn(fakeInt) + + // When + val actual = testedAgentSpanContextAdapter.samplingPriority + + // Then + assertThat(actual).isEqualTo(fakeInt) + } + + @Test + fun `M return delegate#tags W tags is called`(forge: Forge) { + // Given + val expectedMap = forge.aMap { aString() to aString() } + whenever(mockDDSpanContext.tags).thenReturn(expectedMap) + + // When + val actual = testedDDSpanContextContextAdapter.tags + + // Then + assertThat(actual).isEqualTo(expectedMap) + } + + @Test + fun `M return delegate#traceId W traceId is called`(forge: Forge) { + // Given + val ddTraceId = forge.getForgery() + + // When + whenever(mockAgentSpanContext.traceId).thenReturn(ddTraceId) + + // Then + assertThat(testedAgentSpanContextAdapter.traceId).isEqualTo(DatadogTraceIdAdapter(ddTraceId)) + } +} diff --git a/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/trace/internal/DatadogSpanIdConverterAdapterTest.kt b/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/trace/internal/DatadogSpanIdConverterAdapterTest.kt new file mode 100644 index 0000000000..c61e93f93b --- /dev/null +++ b/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/trace/internal/DatadogSpanIdConverterAdapterTest.kt @@ -0,0 +1,29 @@ +/* + * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. + * This product includes software developed at Datadog (https://www.datadoghq.com/). + * Copyright 2016-Present Datadog, Inc. + */ +package com.datadog.android.trace.internal + +import com.datadog.trace.api.DDSpanId.toHexStringPadded +import fr.xgouchet.elmyr.annotation.LongForgery +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +class DatadogSpanIdConverterAdapterTest { + + @LongForgery + private var fakeLong: Long = 0L + + @Test + fun `M serialize to String and deserialize valid W fromHex(toHexStringPadded(Long))`() { + // Given + val converter = DatadogSpanIdConverter() + + // When + val actual = converter.fromHex(toHexStringPadded(fakeLong)) + + // Then + assertThat(actual).isEqualTo(fakeLong) + } +} diff --git a/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/trace/internal/DatadogSpanLoggerTest.kt b/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/trace/internal/DatadogSpanLoggerTest.kt new file mode 100644 index 0000000000..bb9bba0d65 --- /dev/null +++ b/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/trace/internal/DatadogSpanLoggerTest.kt @@ -0,0 +1,150 @@ +/* + * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. + * This product includes software developed at Datadog (https://www.datadoghq.com/). + * Copyright 2016-Present Datadog, Inc. + */ +package com.datadog.android.trace.internal + +import android.util.Log +import com.datadog.android.api.feature.Feature +import com.datadog.android.api.feature.FeatureScope +import com.datadog.android.api.feature.FeatureSdkCore +import com.datadog.android.internal.utils.loggableStackTrace +import com.datadog.android.log.LogAttributes +import com.datadog.android.trace.api.DatadogTracingConstants +import com.datadog.android.trace.api.span.DatadogSpan +import com.datadog.android.trace.internal.DatadogSpanLogger.Companion.DEFAULT_EVENT_MESSAGE +import com.datadog.android.trace.internal.DatadogSpanLogger.Companion.TRACE_LOGGER_NAME +import com.datadog.android.utils.forge.Configurator +import fr.xgouchet.elmyr.Forge +import fr.xgouchet.elmyr.annotation.Forgery +import fr.xgouchet.elmyr.annotation.StringForgery +import fr.xgouchet.elmyr.junit5.ForgeConfiguration +import fr.xgouchet.elmyr.junit5.ForgeExtension +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith +import org.junit.jupiter.api.extension.Extensions +import org.mockito.Mock +import org.mockito.junit.jupiter.MockitoExtension +import org.mockito.junit.jupiter.MockitoSettings +import org.mockito.kotlin.argumentCaptor +import org.mockito.kotlin.doReturn +import org.mockito.kotlin.mock +import org.mockito.kotlin.verify +import org.mockito.quality.Strictness + +@Extensions( + ExtendWith(MockitoExtension::class), + ExtendWith(ForgeExtension::class) +) +@MockitoSettings(strictness = Strictness.LENIENT) +@ForgeConfiguration(Configurator::class) +class DatadogSpanLoggerTest { + + private lateinit var mockSdkCore: FeatureSdkCore + + @StringForgery + lateinit var fakeString: String + + @Forgery + lateinit var fakeThrowable: Throwable + + @Mock + lateinit var mockSpan: DatadogSpan + + @Forgery + lateinit var fakeSpan: DatadogSpan + + @Mock + lateinit var mockLogFeatureScope: FeatureScope + + private lateinit var testedLogger: DatadogSpanLogger + + @BeforeEach + fun `set up`() { + mockSdkCore = mock { + on { getFeature(Feature.LOGS_FEATURE_NAME) } doReturn mockLogFeatureScope + } + + testedLogger = DatadogSpanLogger(mockSdkCore) + } + + @Test + fun `M send expected event W log(String)`() { + // When + testedLogger.log(fakeString, fakeSpan) + + // Then + argumentCaptor> { + verify(mockLogFeatureScope).sendEvent(capture()) + + assertThat(firstValue["type"]).isEqualTo("span_log") + assertThat(firstValue["loggerName"]).isEqualTo(TRACE_LOGGER_NAME) + assertThat(firstValue["message"]).isEqualTo(DEFAULT_EVENT_MESSAGE) + assertThat(firstValue["logStatus"]).isEqualTo(Log.VERBOSE) + + val attributes = firstValue["attributes"] as Map<*, *> + assertThat(attributes[DatadogTracingConstants.LogAttributes.EVENT]).isEqualTo(fakeString) + assertThat(attributes[LogAttributes.DD_SPAN_ID]).isEqualTo(fakeSpan.context().spanId.toString()) + assertThat(attributes[LogAttributes.DD_TRACE_ID]).isEqualTo(fakeSpan.context().traceId.toHexString()) + } + } + + @Test + fun `M send expected event W logErrorMessage(String)`() { + // When + testedLogger.logErrorMessage(fakeString, fakeSpan) + + // Then + argumentCaptor> { + verify(mockLogFeatureScope).sendEvent(capture()) + + assertThat(firstValue["type"]).isEqualTo("span_log") + assertThat(firstValue["loggerName"]).isEqualTo(TRACE_LOGGER_NAME) + assertThat(firstValue["message"]).isEqualTo(fakeString) + assertThat(firstValue["logStatus"]).isEqualTo(Log.ERROR) + + val attributes = firstValue["attributes"] as Map<*, *> + assertThat(attributes[LogAttributes.DD_SPAN_ID]).isEqualTo(fakeSpan.context().spanId.toString()) + assertThat(attributes[LogAttributes.DD_TRACE_ID]).isEqualTo(fakeSpan.context().traceId.toHexString()) + } + } + + @Test + fun `M set expected tags W log(fakeThrowable)`() { + // When + testedLogger.log(fakeThrowable, mockSpan) + + // Then + verify(mockSpan).isError = true + verify(mockSpan).setTag(DatadogTracingConstants.Tags.KEY_ERROR_TYPE, fakeThrowable.javaClass.name) + verify(mockSpan).setTag(DatadogTracingConstants.Tags.KEY_ERROR_MSG, fakeThrowable.message) + verify(mockSpan).setTag(DatadogTracingConstants.Tags.KEY_ERROR_STACK, fakeThrowable.loggableStackTrace()) + } + + @Test + fun `M send expected event W logErrorMessage(Map)`(forge: Forge) { + // Given + val fakeAttributes = forge.aMap { aString() to aString() } + + // When + testedLogger.log(fakeAttributes, fakeSpan) + + // Then + argumentCaptor> { + verify(mockLogFeatureScope).sendEvent(capture()) + + assertThat(firstValue["type"]).isEqualTo("span_log") + assertThat(firstValue["loggerName"]).isEqualTo(TRACE_LOGGER_NAME) + assertThat(firstValue["message"]).isEqualTo(DEFAULT_EVENT_MESSAGE) + assertThat(firstValue["logStatus"]).isEqualTo(Log.VERBOSE) + + val attributes = (firstValue["attributes"] as Map<*, *>).toMutableMap() + assertThat(attributes[LogAttributes.DD_SPAN_ID]).isEqualTo(fakeSpan.context().spanId.toString()) + assertThat(attributes[LogAttributes.DD_TRACE_ID]).isEqualTo(fakeSpan.context().traceId.toHexString()) + assertThat(attributes).containsAllEntriesOf(fakeAttributes) + } + } +} diff --git a/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/trace/internal/DatadogTraceIdAdapterTest.kt b/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/trace/internal/DatadogTraceIdAdapterTest.kt new file mode 100644 index 0000000000..c340b6516d --- /dev/null +++ b/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/trace/internal/DatadogTraceIdAdapterTest.kt @@ -0,0 +1,136 @@ +/* + * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. + * This product includes software developed at Datadog (https://www.datadoghq.com/). + * Copyright 2016-Present Datadog, Inc. + */ +package com.datadog.android.trace.internal + +import com.datadog.android.trace.api.trace.DatadogTraceId +import com.datadog.android.utils.forge.Configurator +import com.datadog.trace.api.DDTraceId +import fr.xgouchet.elmyr.annotation.IntForgery +import fr.xgouchet.elmyr.annotation.LongForgery +import fr.xgouchet.elmyr.junit5.ForgeConfiguration +import fr.xgouchet.elmyr.junit5.ForgeExtension +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith +import org.junit.jupiter.api.extension.Extensions +import org.mockito.Mock +import org.mockito.junit.jupiter.MockitoExtension +import org.mockito.junit.jupiter.MockitoSettings +import org.mockito.kotlin.any +import org.mockito.kotlin.verify +import org.mockito.kotlin.whenever +import org.mockito.quality.Strictness + +@Extensions( + ExtendWith(MockitoExtension::class), + ExtendWith(ForgeExtension::class) +) +@MockitoSettings(strictness = Strictness.LENIENT) +@ForgeConfiguration(Configurator::class) +class DatadogTraceIdAdapterTest { + + private lateinit var testedAdapter: DatadogTraceIdAdapter + + @Mock + lateinit var mockDDTraceId: DDTraceId + + @IntForgery + private var fakeInt = 0 + + @LongForgery(min = 0) + private var fakeLong = 0L + + @BeforeEach + fun `set up`() { + testedAdapter = DatadogTraceIdAdapter(mockDDTraceId) + whenever(mockDDTraceId.toHexString()).thenReturn(fakeInt.toString(16)) + whenever(mockDDTraceId.toHexStringPadded(any())).thenReturn(fakeInt.toString(16)) + whenever(mockDDTraceId.toString()).thenReturn(fakeInt.toString()) + } + + @Test + fun `M delegate to DDTraceId#toLong W toLong is called`() { + // When + testedAdapter.toLong() + + // Then + verify(mockDDTraceId).toLong() + } + + @Test + fun `M delegate to DDTraceId#toString W toString is called`() { + // When + val actual = testedAdapter.toString() + + // Then + assertThat(actual).isEqualTo(fakeInt.toString()) + } + + @Test + fun `M delegate to DDTraceId#toHexString W toHexString is called`() { + // When + testedAdapter.toHexString() + + // Then + verify(mockDDTraceId).toHexString() + } + + @Test + fun `M delegate to DDTraceId#toHighOrderLong W toHighOrderLong is called`() { + // When + testedAdapter.toHighOrderLong() + + // Then + verify(mockDDTraceId).toHighOrderLong() + } + + @Test + fun `M delegate to DDTraceId#toHexStringPadded W toHexStringPadded is called`() { + // When + testedAdapter.toHexStringPadded(fakeInt) + + // Then + verify(mockDDTraceId).toHexStringPadded(fakeInt) + } + + @Test + fun `M return expected W fromHex(String)`() { + // Given + val stringTraceId = fakeLong.toString() + val expected = DatadogTraceIdAdapter(DDTraceId.fromHex(stringTraceId)) + + // When + val actual = DatadogTraceId.fromHex(stringTraceId) + + // Then + assertThat(actual).isEqualTo(expected) + } + + @Test + fun `M return expected W toHexString()`() { + // Given + val ddTraceId = DDTraceId.from(fakeLong) + + // When + val actual = DatadogTraceIdAdapter(ddTraceId).toHexString() + + // Then + assertThat(actual).isEqualTo(ddTraceId.toHexString()) + } + + @Test + fun `M return expected W toLong()`() { + // Given + val traceId = DatadogTraceIdAdapter(DDTraceId.from(fakeLong)) + + // When + val actual = traceId.toLong() + + // Then + assertThat(actual).isEqualTo(fakeLong) + } +} diff --git a/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/trace/internal/DatadogTracerAdapterTest.kt b/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/trace/internal/DatadogTracerAdapterTest.kt new file mode 100644 index 0000000000..901108cf5e --- /dev/null +++ b/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/trace/internal/DatadogTracerAdapterTest.kt @@ -0,0 +1,160 @@ +/* + * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. + * This product includes software developed at Datadog (https://www.datadoghq.com/). + * Copyright 2016-Present Datadog, Inc. + */ +package com.datadog.android.trace.internal + +import com.datadog.android.api.feature.FeatureSdkCore +import com.datadog.android.trace.api.scope.DatadogScopeListener +import com.datadog.android.trace.api.span.DatadogSpanBuilder +import com.datadog.android.utils.forge.Configurator +import com.datadog.trace.bootstrap.instrumentation.api.AgentScope +import com.datadog.trace.bootstrap.instrumentation.api.AgentSpan +import com.datadog.trace.bootstrap.instrumentation.api.AgentTracer +import com.datadog.trace.bootstrap.instrumentation.api.ScopeSource +import fr.xgouchet.elmyr.annotation.BoolForgery +import fr.xgouchet.elmyr.annotation.StringForgery +import fr.xgouchet.elmyr.junit5.ForgeConfiguration +import fr.xgouchet.elmyr.junit5.ForgeExtension +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith +import org.junit.jupiter.api.extension.Extensions +import org.mockito.Mock +import org.mockito.junit.jupiter.MockitoExtension +import org.mockito.junit.jupiter.MockitoSettings +import org.mockito.kotlin.any +import org.mockito.kotlin.isA +import org.mockito.kotlin.mock +import org.mockito.kotlin.verify +import org.mockito.kotlin.whenever +import org.mockito.quality.Strictness + +@Extensions( + ExtendWith(MockitoExtension::class), + ExtendWith(ForgeExtension::class) +) +@MockitoSettings(strictness = Strictness.LENIENT) +@ForgeConfiguration(Configurator::class) +internal class DatadogTracerAdapterTest { + + private lateinit var testedTracer: DatadogTracerAdapter + + @Mock + lateinit var mockSdk: FeatureSdkCore + + @Mock + lateinit var mockTracer: AgentTracer.TracerAPI + + @Mock + lateinit var mockSpan: AgentSpan + + @Mock + lateinit var mockSpanLogger: DatadogSpanLogger + + @Mock + lateinit var mockDatadogSpan: DatadogSpanAdapter + + @Mock + lateinit var mockScope: AgentScope + + @Mock + lateinit var mockSpanBuilder: AgentTracer.SpanBuilder + + @StringForgery + lateinit var fakeString: String + + @BoolForgery + private var fakeBool: Boolean = false + + @BeforeEach + fun `set up`() { + whenever(mockSdk.internalLogger).thenReturn(mock()) + testedTracer = DatadogTracerAdapter(mockSdk, mockTracer, true, mockSpanLogger) + whenever(mockDatadogSpan.delegate).thenReturn(mockSpan) + whenever(mockTracer.propagate()).thenReturn(mock()) + @Suppress("DEPRECATION") + whenever(mockTracer.buildSpan(any())).thenReturn(mockSpanBuilder) + whenever(mockTracer.buildSpan(any(), any())).thenReturn(mockSpanBuilder) + } + + @Test + fun `M return span W buildSpan(String)`() { + // When + val builder = testedTracer.buildSpan(fakeString) + + // Then + assertThat(builder).isInstanceOf(DatadogSpanBuilder::class.java) + @Suppress("DEPRECATION") + verify(mockTracer).buildSpan(fakeString) + } + + @Test + fun `M return span W buildSpan(String, String) `() { + // When + val builder = testedTracer.buildSpan(fakeString, fakeString) + + // Then + assertThat(builder).isInstanceOf(DatadogSpanBuilder::class.java) + verify(mockTracer).buildSpan(fakeString, fakeString) + } + + @Test + fun `M delegate SpanBuilder#addScopeListener W addScopeListener`() { + // Given + val mockListener = mock() + + // When + testedTracer.addScopeListener(mockListener) + + // Then + verify(mockTracer).addScopeListener(isA()) + } + + @Test + fun `M return wrapped span W activeSpan`() { + // Given + whenever(mockTracer.activeSpan()).thenReturn(mockSpan) + + // When + val span = testedTracer.activeSpan() as DatadogSpanAdapter + + // Then + assertThat(span.delegate).isEqualTo(mockSpan) + } + + @Test + fun `M return wrapped scope W activateSpan(DatadogSpan)`() { + // Given + whenever(mockTracer.activateSpan(mockSpan, ScopeSource.INSTRUMENTATION)).thenReturn(mockScope) + + // When + val scope = testedTracer.activateSpan(mockDatadogSpan) as DatadogScopeAdapter + + // Then + assertThat(scope.delegate).isEqualTo(mockScope) + } + + @Test + fun `M return wrapped scope W activateSpan(DatadogSpan, Boolean)`() { + // Given + whenever(mockTracer.activateSpan(mockSpan, ScopeSource.INSTRUMENTATION, fakeBool)).thenReturn(mockScope) + + // When + val scope = testedTracer.activateSpan(mockDatadogSpan, fakeBool) as DatadogScopeAdapter + + // Then + assertThat(scope.delegate).isEqualTo(mockScope) + } + + @Test + fun `M return propagate W propagate()`() { + // When + val actual = testedTracer.propagate() + + // Then + assertThat(actual).isInstanceOf(DatadogPropagationAdapter::class.java) + } +} diff --git a/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/trace/internal/DatadogTracerBuilderAdapterTest.kt b/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/trace/internal/DatadogTracerBuilderAdapterTest.kt new file mode 100644 index 0000000000..a429c716f3 --- /dev/null +++ b/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/trace/internal/DatadogTracerBuilderAdapterTest.kt @@ -0,0 +1,175 @@ +/* + * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. + * This product includes software developed at Datadog (https://www.datadoghq.com/). + * Copyright 2016-Present Datadog, Inc. + */ +package com.datadog.android.trace.internal + +import com.datadog.android.api.feature.FeatureSdkCore +import com.datadog.android.trace.TracingHeaderType +import com.datadog.android.trace.api.DatadogTracingConstants.TracerConfig +import com.datadog.android.trace.internal.DatadogTracerBuilderAdapter.Companion.DEFAULT_URL_AS_RESOURCE_NAME +import com.datadog.android.utils.forge.Configurator +import com.datadog.trace.api.DD128bTraceId +import com.datadog.trace.api.DD64bTraceId +import com.datadog.trace.api.IdGenerationStrategy +import com.datadog.trace.core.CoreTracer +import fr.xgouchet.elmyr.Forge +import fr.xgouchet.elmyr.annotation.BoolForgery +import fr.xgouchet.elmyr.annotation.StringForgery +import fr.xgouchet.elmyr.junit5.ForgeConfiguration +import fr.xgouchet.elmyr.junit5.ForgeExtension +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith +import org.junit.jupiter.api.extension.Extensions +import org.mockito.Mock +import org.mockito.junit.jupiter.MockitoExtension +import org.mockito.junit.jupiter.MockitoSettings +import org.mockito.kotlin.any +import org.mockito.kotlin.argumentCaptor +import org.mockito.kotlin.verify +import org.mockito.kotlin.whenever +import org.mockito.quality.Strictness +import java.util.Properties + +@Extensions( + ExtendWith(MockitoExtension::class), + ExtendWith(ForgeExtension::class) +) +@MockitoSettings(strictness = Strictness.LENIENT) +@ForgeConfiguration(Configurator::class) +class DatadogTracerBuilderAdapterTest { + private lateinit var testedBuilder: DatadogTracerBuilderAdapter + + @Mock + lateinit var mockSdkCore: FeatureSdkCore + + @Mock + lateinit var mockBuilder: CoreTracer.CoreTracerBuilder + + @Mock + lateinit var mockTracerDelegate: CoreTracer + + @StringForgery + lateinit var fakeServiceName: String + + @BoolForgery + private var fakeBoolean: Boolean = false + + @BeforeEach + fun `set up`() { + whenever(mockBuilder.build()).thenReturn(mockTracerDelegate) + whenever(mockBuilder.withProperties(any())).thenReturn(mockBuilder) + testedBuilder = DatadogTracerBuilderAdapter(mockSdkCore, fakeServiceName, mockBuilder) + } + + @Test + fun `M return default properties W properties() {no method called}`() { + // Given + val expected = Properties() + expected.setProperty(TracerConfig.PROPAGATION_STYLE_EXTRACT, EXPECTED_DEFAULT_PROPAGATION) + expected.setProperty(TracerConfig.PROPAGATION_STYLE_INJECT, EXPECTED_DEFAULT_PROPAGATION) + expected.setProperty(TracerConfig.SERVICE_NAME, fakeServiceName) + expected.setProperty(TracerConfig.TRACE_RATE_LIMIT, Int.MAX_VALUE.toString()) + expected.setProperty( + TracerConfig.PARTIAL_FLUSH_MIN_SPANS, + DatadogTracerBuilderAdapter.DEFAULT_PARTIAL_MIN_FLUSH.toString() + ) + expected.setProperty(TracerConfig.URL_AS_RESOURCE_NAME, DEFAULT_URL_AS_RESOURCE_NAME.toString()) + expected.setProperty(TracerConfig.SDK_V2_COMPATIBILITY_FLAG, false.toString()) + expected.setProperty(TracerConfig.TAGS, "") + + // When + val actual = testedBuilder.properties() + + // Then + assertThat(actual).isEqualTo(expected) + } + + @Test + fun `M return valid properties W {setters called}`(forge: Forge) { + // Given + val fakeServiceName = forge.aString() + val fakeTagKey = forge.aString() + val fakeTagValue = forge.aString() + val fakeSampleRate = forge.aDouble(min = 0.0, max = 100.0) + val fakeTraceLimit = forge.anInt() + val fakeV2Compatible = forge.aBool() + val fakePartialFlushMinSpans = forge.anInt() + val fakeHeaderType = forge.anElementFrom( + TracingHeaderType.B3, + TracingHeaderType.DATADOG, + TracingHeaderType.TRACECONTEXT, + TracingHeaderType.B3MULTI + ) + + val expected = Properties().apply { + setProperty(TracerConfig.PROPAGATION_STYLE_EXTRACT, fakeHeaderType.toString()) + setProperty(TracerConfig.PROPAGATION_STYLE_INJECT, fakeHeaderType.toString()) + setProperty(TracerConfig.SERVICE_NAME, fakeServiceName) + setProperty(TracerConfig.TRACE_RATE_LIMIT, fakeTraceLimit.toString()) + setProperty(TracerConfig.TRACE_SAMPLE_RATE, (fakeSampleRate / 100.0).toString()) + setProperty(TracerConfig.PARTIAL_FLUSH_MIN_SPANS, fakePartialFlushMinSpans.toString()) + setProperty(TracerConfig.URL_AS_RESOURCE_NAME, DEFAULT_URL_AS_RESOURCE_NAME.toString()) + setProperty(TracerConfig.SDK_V2_COMPATIBILITY_FLAG, fakeV2Compatible.toString()) + setProperty(TracerConfig.TAGS, "$fakeTagKey:$fakeTagValue") + } + + // When + val actual = testedBuilder + .withTag(fakeTagKey, fakeTagValue) + .withSampleRate(fakeSampleRate) + .setTraceRateLimit(fakeTraceLimit) + .withServiceName(fakeServiceName) + .withTracingHeadersTypes(setOf(fakeHeaderType)) + .withPartialFlushMinSpans(fakePartialFlushMinSpans) + .also { if (fakeV2Compatible) it.setSdkV2Compatible() } + .properties() + + // Then + assertThat(actual).isEqualTo(expected) + } + + @Test + fun `M delegate CoreTracerBuilder#idGenerationStrategy W idGenerationStrategy`() { + // Given + val expectedTraceIdClass = if (fakeBoolean) { + DD128bTraceId::class.java + } else { + DD64bTraceId::class.java + } + + // When + testedBuilder.setTraceId128BitGenerationEnabled(fakeBoolean) + + // Then + argumentCaptor { + verify(mockBuilder).idGenerationStrategy(capture()) + assertThat(firstValue::class.simpleName).isEqualTo("SRandom") + assertThat(firstValue.generateTraceId()).isInstanceOf(expectedTraceIdClass) + } + } + + @Test + fun `M return tracer with expected parameters W build`() { + // When + val tracer = testedBuilder + .setBundleWithRumEnabled(fakeBoolean) + .build() as DatadogTracerAdapter + + // Then + assertThat(tracer.delegate).isEqualTo(mockTracerDelegate) + assertThat(tracer.sdkCore).isEqualTo(mockSdkCore) + assertThat(tracer.bundleWithRumEnabled).isEqualTo(fakeBoolean) + argumentCaptor { + verify(mockTracerDelegate).addScopeListener(capture()) + assertThat(firstValue.delegate).isInstanceOf(TracePropagationScopeListener::class.java) + } + } + + companion object { + private const val EXPECTED_DEFAULT_PROPAGATION = "DATADOG,TRACECONTEXT" + } +} diff --git a/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/trace/internal/TracePropagationScopeListenerTest.kt b/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/trace/internal/TracePropagationScopeListenerTest.kt new file mode 100644 index 0000000000..26f3c92dfb --- /dev/null +++ b/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/trace/internal/TracePropagationScopeListenerTest.kt @@ -0,0 +1,115 @@ +/* + * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. + * This product includes software developed at Datadog (https://www.datadoghq.com/). + * Copyright 2016-Present Datadog, Inc. + */ +package com.datadog.android.trace.internal + +import com.datadog.android.api.feature.Feature +import com.datadog.android.api.feature.FeatureSdkCore +import com.datadog.android.trace.api.span.DatadogSpan +import com.datadog.android.trace.api.span.DatadogSpanContext +import com.datadog.android.trace.api.tracer.DatadogTracer +import com.datadog.android.utils.forge.Configurator +import fr.xgouchet.elmyr.annotation.Forgery +import fr.xgouchet.elmyr.junit5.ForgeConfiguration +import fr.xgouchet.elmyr.junit5.ForgeExtension +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith +import org.junit.jupiter.api.extension.Extensions +import org.mockito.Mock +import org.mockito.junit.jupiter.MockitoExtension +import org.mockito.junit.jupiter.MockitoSettings +import org.mockito.kotlin.argumentCaptor +import org.mockito.kotlin.eq +import org.mockito.kotlin.verify +import org.mockito.kotlin.verifyNoInteractions +import org.mockito.kotlin.whenever +import org.mockito.quality.Strictness + +@Extensions( + ExtendWith(MockitoExtension::class), + ExtendWith(ForgeExtension::class) +) +@MockitoSettings(strictness = Strictness.LENIENT) +@ForgeConfiguration(Configurator::class) +class TracePropagationScopeListenerTest { + + @Mock + lateinit var mockSdkCore: FeatureSdkCore + + @Mock + lateinit var mockTracer: DatadogTracer + + @Mock + lateinit var mockSpan: DatadogSpan + + @Forgery + lateinit var fakeSpanContext: DatadogSpanContext + + private val contextName = "context@${Thread.currentThread().name}" + + private lateinit var testedListener: TracePropagationScopeListener + + @BeforeEach + fun `set up`() { + whenever(mockSpan.context()).thenReturn(fakeSpanContext) + testedListener = TracePropagationScopeListener(mockSdkCore, mockTracer) + } + + @Test + fun `M add context tag W afterScopeActivated {context present}`() { + // Given + whenever(mockTracer.activeSpan()).thenReturn(mockSpan) + + // When + testedListener.afterScopeActivated() + + // Then + val mapWithContext = assertCallbackCalled(mutableMapOf()) + assertThat(mapWithContext[contextName]).isEqualTo( + mapOf( + "span_id" to fakeSpanContext.spanId.toString(), + "trace_id" to fakeSpanContext.traceId.toHexString() + ) + ) + } + + @Test + fun `M context tag not added W afterScopeActivated {context absent}`() { + // Given + whenever(mockTracer.activeSpan()).thenReturn(null) + + // When + testedListener.afterScopeActivated() + + // Then + verifyNoInteractions(mockSdkCore) + } + + @Test + fun `M remove context tag W afterScopeClosed`() { + // Given + testedListener.afterScopeClosed() + + // When + val mapWithContext = assertCallbackCalled(mutableMapOf(contextName to Any())) + + // Then + assertThat(mapWithContext).isEmpty() + } + + private fun assertCallbackCalled(mapWithContext: MutableMap): MutableMap { + val callbackCaptor = argumentCaptor<(MutableMap) -> Unit>() + verify(mockSdkCore).updateFeatureContext( + eq(Feature.TRACING_FEATURE_NAME), + eq(true), + callbackCaptor.capture() + ) + + callbackCaptor.firstValue.invoke(mapWithContext) + return mapWithContext + } +} diff --git a/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/trace/internal/TracingFeatureTest.kt b/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/trace/internal/TracingFeatureTest.kt index 6b9fc2951a..4f177f1fa4 100644 --- a/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/trace/internal/TracingFeatureTest.kt +++ b/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/trace/internal/TracingFeatureTest.kt @@ -11,10 +11,8 @@ import com.datadog.android.api.feature.Feature import com.datadog.android.api.feature.FeatureSdkCore import com.datadog.android.api.storage.FeatureStorageConfiguration import com.datadog.android.trace.event.SpanEventMapper -import com.datadog.android.trace.internal.data.OtelTraceWriter -import com.datadog.android.trace.internal.data.TraceWriter +import com.datadog.android.trace.internal.data.CoreTraceWriter import com.datadog.android.trace.internal.domain.event.CoreTracerSpanToSpanEventMapper -import com.datadog.android.trace.internal.domain.event.DdSpanToSpanEventMapper import com.datadog.android.trace.internal.domain.event.SpanEventMapperWrapper import com.datadog.android.trace.internal.net.TracesRequestFactory import com.datadog.android.utils.forge.Configurator @@ -73,29 +71,12 @@ internal class TracingFeatureTest { } @Test - fun `M initialize opentracing writer W initialize()`() { + fun `M initialize core writer W initialize()`() { // When testedFeature.onInitialize(mock()) // Then - assertThat(testedFeature.legacyTracerWriter) - .isInstanceOf(TraceWriter::class.java) - val traceWriter = testedFeature.legacyTracerWriter as TraceWriter - val ddSpanToSpanEventMapper = traceWriter.ddSpanToSpanEventMapper - assertThat(ddSpanToSpanEventMapper).isInstanceOf(DdSpanToSpanEventMapper::class.java) - assertThat((ddSpanToSpanEventMapper as DdSpanToSpanEventMapper).networkInfoEnabled) - .isEqualTo(fakeNetworkInfoEnabled) - } - - @Test - fun `M initialize otel writer W initialize()`() { - // When - testedFeature.onInitialize(mock()) - - // Then - assertThat(testedFeature.legacyTracerWriter) - .isInstanceOf(TraceWriter::class.java) - val traceWriter = testedFeature.coreTracerDataWriter as OtelTraceWriter + val traceWriter = testedFeature.coreTracerDataWriter as CoreTraceWriter val ddSpanToSpanEventMapper = traceWriter.ddSpanToSpanEventMapper assertThat(ddSpanToSpanEventMapper).isInstanceOf(CoreTracerSpanToSpanEventMapper::class.java) assertThat((ddSpanToSpanEventMapper as CoreTracerSpanToSpanEventMapper).networkInfoEnabled) @@ -103,24 +84,12 @@ internal class TracingFeatureTest { } @Test - fun `M use the eventMapper for opentracing writer W initialize()`() { - // When - testedFeature.onInitialize(mock()) - - // Then - val dataWriter = testedFeature.legacyTracerWriter as? TraceWriter - val spanEventMapperWrapper = dataWriter?.eventMapper as? SpanEventMapperWrapper - val spanEventMapper = spanEventMapperWrapper?.wrappedEventMapper - assertThat(spanEventMapper).isSameAs(mockSpanEventMapper) - } - - @Test - fun `M use the eventMapper for otel writer W initialize()`() { + fun `M use the eventMapper for core writer W initialize()`() { // When testedFeature.onInitialize(mock()) // Then - val dataWriter = testedFeature.coreTracerDataWriter as? OtelTraceWriter + val dataWriter = testedFeature.coreTracerDataWriter as? CoreTraceWriter val spanEventMapperWrapper = dataWriter?.eventMapper as? SpanEventMapperWrapper val spanEventMapper = spanEventMapperWrapper?.wrappedEventMapper assertThat(spanEventMapper).isSameAs(mockSpanEventMapper) diff --git a/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/trace/internal/data/OtelTraceWriterTest.kt b/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/trace/internal/data/CoreTraceWriterTest.kt similarity index 97% rename from features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/trace/internal/data/OtelTraceWriterTest.kt rename to features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/trace/internal/data/CoreTraceWriterTest.kt index e7bb968848..67c7946a3c 100644 --- a/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/trace/internal/data/OtelTraceWriterTest.kt +++ b/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/trace/internal/data/CoreTraceWriterTest.kt @@ -60,9 +60,9 @@ import java.util.UUID ) @MockitoSettings(strictness = Strictness.LENIENT) @ForgeConfiguration(Configurator::class) -internal class OtelTraceWriterTest { +internal class CoreTraceWriterTest { - private lateinit var testedWriter: OtelTraceWriter + private lateinit var testedWriter: CoreTraceWriter @Mock lateinit var mockSdkCore: FeatureSdkCore @@ -110,7 +110,7 @@ internal class OtelTraceWriterTest { whenever(mockEventMapper.map(any())) doAnswer { it.getArgument(0) } - testedWriter = OtelTraceWriter( + testedWriter = CoreTraceWriter( sdkCore = mockSdkCore, ddSpanToSpanEventMapper = mockLegacyMapper, eventMapper = mockEventMapper, @@ -282,7 +282,7 @@ internal class OtelTraceWriterTest { mockInternalLogger.verifyLog( InternalLogger.Level.ERROR, InternalLogger.Target.USER, - TraceWriter.INITIAL_DATADOG_CONTEXT_NOT_AVAILABLE_ERROR, + CoreTraceWriter.INITIAL_DATADOG_CONTEXT_NOT_AVAILABLE_ERROR, mode = times(ddSpans.size) ) @@ -480,7 +480,7 @@ internal class OtelTraceWriterTest { mockInternalLogger.verifyLog( InternalLogger.Level.ERROR, listOf(InternalLogger.Target.USER, InternalLogger.Target.TELEMETRY), - TraceWriter.ERROR_SERIALIZING.format(Locale.US, SpanEvent::class.java.simpleName), + CoreTraceWriter.ERROR_SERIALIZING.format(Locale.US, SpanEvent::class.java.simpleName), fakeThrowable ) } @@ -505,9 +505,9 @@ internal class OtelTraceWriterTest { private fun createNonEmptyDdSpans(forge: Forge, includeDropSamplingPriority: Boolean): List { val predicate: (DDSpan) -> Boolean = if (includeDropSamplingPriority) { - { it.samplingPriority() in OtelTraceWriter.DROP_SAMPLING_PRIORITIES } + { it.getTraceSamplingPriority() in CoreTraceWriter.DROP_SAMPLING_PRIORITIES } } else { - { it.samplingPriority() !in OtelTraceWriter.DROP_SAMPLING_PRIORITIES } + { it.getTraceSamplingPriority() !in CoreTraceWriter.DROP_SAMPLING_PRIORITIES } } var spans = emptyList() diff --git a/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/trace/internal/data/TraceWriterTest.kt b/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/trace/internal/data/TraceWriterTest.kt deleted file mode 100644 index 649aef109e..0000000000 --- a/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/trace/internal/data/TraceWriterTest.kt +++ /dev/null @@ -1,526 +0,0 @@ -/* - * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. - * This product includes software developed at Datadog (https://www.datadoghq.com/). - * Copyright 2016-Present Datadog, Inc. - */ - -package com.datadog.android.trace.internal.data - -import com.datadog.android.api.InternalLogger -import com.datadog.android.api.context.DatadogContext -import com.datadog.android.api.feature.EventWriteScope -import com.datadog.android.api.feature.Feature -import com.datadog.android.api.feature.FeatureScope -import com.datadog.android.api.feature.FeatureSdkCore -import com.datadog.android.api.storage.EventBatchWriter -import com.datadog.android.api.storage.EventType -import com.datadog.android.api.storage.RawBatchEvent -import com.datadog.android.event.EventMapper -import com.datadog.android.internal.concurrent.CompletableFuture -import com.datadog.android.log.LogAttributes -import com.datadog.android.trace.AndroidTracer -import com.datadog.android.trace.internal.domain.event.ContextAwareMapper -import com.datadog.android.trace.internal.storage.ContextAwareSerializer -import com.datadog.android.trace.model.SpanEvent -import com.datadog.android.trace.utils.verifyLog -import com.datadog.android.utils.forge.Configurator -import com.datadog.android.utils.forge.SpanForgeryFactory -import com.datadog.opentracing.DDSpan -import com.datadog.tools.unit.forge.aThrowable -import fr.xgouchet.elmyr.Forge -import fr.xgouchet.elmyr.annotation.Forgery -import fr.xgouchet.elmyr.junit5.ForgeConfiguration -import fr.xgouchet.elmyr.junit5.ForgeExtension -import org.assertj.core.api.Assertions.assertThat -import org.junit.jupiter.api.BeforeEach -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.extension.ExtendWith -import org.junit.jupiter.api.extension.Extensions -import org.mockito.Mock -import org.mockito.junit.jupiter.MockitoExtension -import org.mockito.junit.jupiter.MockitoSettings -import org.mockito.kotlin.any -import org.mockito.kotlin.argumentCaptor -import org.mockito.kotlin.doAnswer -import org.mockito.kotlin.doReturn -import org.mockito.kotlin.doThrow -import org.mockito.kotlin.eq -import org.mockito.kotlin.times -import org.mockito.kotlin.verify -import org.mockito.kotlin.verifyNoInteractions -import org.mockito.kotlin.verifyNoMoreInteractions -import org.mockito.kotlin.whenever -import org.mockito.quality.Strictness -import java.util.Locale -import java.util.UUID - -@Extensions( - ExtendWith(MockitoExtension::class), - ExtendWith(ForgeExtension::class) -) -@MockitoSettings(strictness = Strictness.LENIENT) -@ForgeConfiguration(Configurator::class) -internal class TraceWriterTest { - - private lateinit var testedWriter: TraceWriter - - @Mock - lateinit var mockSdkCore: FeatureSdkCore - - @Mock - lateinit var mockLegacyMapper: ContextAwareMapper - - @Mock - lateinit var mockEventMapper: EventMapper - - @Mock - lateinit var mockSerializer: ContextAwareSerializer - - @Mock - lateinit var mockInternalLogger: InternalLogger - - @Mock - lateinit var mockTracingFeatureScope: FeatureScope - - @Mock - lateinit var mockEventBatchWriter: EventBatchWriter - - @Mock - lateinit var mockEventWriteScope: EventWriteScope - - @Forgery - lateinit var fakeDatadogContext: DatadogContext - - // region Unit Tests - - @BeforeEach - fun `set up`() { - whenever( - mockSdkCore.getFeature(Feature.TRACING_FEATURE_NAME) - ) doReturn mockTracingFeatureScope - - whenever(mockEventWriteScope.invoke(any())) doAnswer { - val callback = it.getArgument<(EventBatchWriter) -> Unit>(0) - callback.invoke(mockEventBatchWriter) - } - whenever(mockTracingFeatureScope.withWriteContext(eq(emptySet()), any())) doAnswer { - val callback = it.getArgument<(DatadogContext, EventWriteScope) -> Unit>(it.arguments.lastIndex) - callback.invoke(fakeDatadogContext, mockEventWriteScope) - } - - whenever(mockEventMapper.map(any())) doAnswer { it.getArgument(0) } - - testedWriter = TraceWriter( - sdkCore = mockSdkCore, - ddSpanToSpanEventMapper = mockLegacyMapper, - eventMapper = mockEventMapper, - serializer = mockSerializer, - internalLogger = mockInternalLogger - ) - } - - @Test - fun `M write spans W write()`(forge: Forge) { - // GIVEN - val ddSpans = generateSpanListWithPriorities(forge, TraceWriter.KEEP_AND_UNSET_SAMPLING_PRIORITIES) - - val spanEvents = ddSpans.map { forge.getForgery() } - val serializedSpans = ddSpans.map { forge.aString() } - - ddSpans.forEachIndexed { index, ddSpan -> - whenever(mockLegacyMapper.map(fakeDatadogContext, ddSpan)) doReturn spanEvents[index] - } - - spanEvents.forEachIndexed { index, spanEvent -> - whenever( - mockSerializer.serialize(fakeDatadogContext, spanEvent) - ) doReturn serializedSpans[index] - } - - // WHEN - testedWriter.write(ddSpans) - - // THEN - serializedSpans.forEach { - verify(mockEventBatchWriter).write( - event = RawBatchEvent(data = it.toByteArray()), - batchMetadata = null, - eventType = EventType.DEFAULT - ) - } - verifyNoMoreInteractions(mockEventBatchWriter) - - ddSpans.forEach { - it.finish() - } - } - - // region RUM context - - @Test - fun `M write spans W write() { with RUM context }`( - @Forgery fakeApplicationId: UUID, - @Forgery fakeSessionId: UUID, - @Forgery fakeViewId: UUID, - @Forgery fakeActionId: UUID, - forge: Forge - ) { - // GIVEN - val fakeInitialDatadogContext = forge.getForgery().let { - it.copy( - featuresContext = it.featuresContext + mapOf( - Feature.RUM_FEATURE_NAME to mapOf( - "application_id" to fakeApplicationId.toString(), - "session_id" to fakeSessionId.toString(), - "view_id" to fakeViewId.toString(), - "action_id" to fakeActionId.toString() - ) - ) - ) - } - val fakeLazyContext = CompletableFuture().apply { complete(fakeInitialDatadogContext) } - val ddSpans = generateSpanListWithPriorities(forge, TraceWriter.KEEP_AND_UNSET_SAMPLING_PRIORITIES) - .map { - it.apply { setTag(AndroidTracer.DATADOG_CONTEXT_TAG, fakeLazyContext) } - } - - whenever(mockLegacyMapper.map(eq(fakeDatadogContext), any())) doReturn forge.getForgery() - whenever(mockSerializer.serialize(eq(fakeDatadogContext), any())) doReturn forge.aString() - - // WHEN - testedWriter.write(ddSpans) - - // THEN - argumentCaptor { - verify(mockLegacyMapper, times(ddSpans.size)).map(eq(fakeDatadogContext), capture()) - allValues.forEach { - assertThat(it.tags[LogAttributes.RUM_APPLICATION_ID]).isEqualTo(fakeApplicationId.toString()) - assertThat(it.tags[LogAttributes.RUM_SESSION_ID]).isEqualTo(fakeSessionId.toString()) - assertThat(it.tags[LogAttributes.RUM_VIEW_ID]).isEqualTo(fakeViewId.toString()) - assertThat(it.tags[LogAttributes.RUM_ACTION_ID]).isEqualTo(fakeActionId.toString()) - assertThat(it.tags).doesNotContainKey(AndroidTracer.DATADOG_CONTEXT_TAG.key) - } - } - - ddSpans.forEach { - it.finish() - } - } - - @Test - fun `M write spans W write() { without RUM context when empty }`( - forge: Forge - ) { - // GIVEN - val fakeInitialDatadogContext = forge.getForgery().let { - it.copy( - featuresContext = it.featuresContext + mapOf( - Feature.RUM_FEATURE_NAME to emptyMap() - ) - ) - } - val fakeLazyContext = CompletableFuture().apply { complete(fakeInitialDatadogContext) } - val ddSpans = generateSpanListWithPriorities(forge, TraceWriter.KEEP_AND_UNSET_SAMPLING_PRIORITIES) - .map { - it.apply { setTag(AndroidTracer.DATADOG_CONTEXT_TAG, fakeLazyContext) } - } - - whenever(mockLegacyMapper.map(eq(fakeDatadogContext), any())) doReturn forge.getForgery() - whenever(mockSerializer.serialize(eq(fakeDatadogContext), any())) doReturn forge.aString() - - // WHEN - testedWriter.write(ddSpans) - - // THEN - argumentCaptor { - verify(mockLegacyMapper, times(ddSpans.size)).map(eq(fakeDatadogContext), capture()) - allValues.forEach { - assertThat(it.tags).doesNotContainKey(LogAttributes.RUM_APPLICATION_ID) - assertThat(it.tags).doesNotContainKey(LogAttributes.RUM_SESSION_ID) - assertThat(it.tags).doesNotContainKey(LogAttributes.RUM_VIEW_ID) - assertThat(it.tags).doesNotContainKey(LogAttributes.RUM_ACTION_ID) - assertThat(it.tags).doesNotContainKey(AndroidTracer.DATADOG_CONTEXT_TAG.key) - } - assertThat(allValues).isEqualTo(ddSpans) - } - - ddSpans.forEach { - it.finish() - } - } - - @Test - fun `M write spans W write() { without RUM context, lazy Datadog context is not complete }`( - forge: Forge - ) { - // GIVEN - val fakeLazyContext = CompletableFuture() - val ddSpans = generateSpanListWithPriorities(forge, TraceWriter.KEEP_AND_UNSET_SAMPLING_PRIORITIES) - .map { - it.apply { setTag(AndroidTracer.DATADOG_CONTEXT_TAG, fakeLazyContext) } - } - - whenever(mockLegacyMapper.map(eq(fakeDatadogContext), any())) doReturn forge.getForgery() - whenever(mockSerializer.serialize(eq(fakeDatadogContext), any())) doReturn forge.aString() - - // WHEN - testedWriter.write(ddSpans) - - // THEN - argumentCaptor { - verify(mockLegacyMapper, times(ddSpans.size)).map(eq(fakeDatadogContext), capture()) - allValues.forEach { - assertThat(it.tags).doesNotContainKey(LogAttributes.RUM_APPLICATION_ID) - assertThat(it.tags).doesNotContainKey(LogAttributes.RUM_SESSION_ID) - assertThat(it.tags).doesNotContainKey(LogAttributes.RUM_VIEW_ID) - assertThat(it.tags).doesNotContainKey(LogAttributes.RUM_ACTION_ID) - assertThat(it.tags).doesNotContainKey(AndroidTracer.DATADOG_CONTEXT_TAG.key) - } - assertThat(allValues).isEqualTo(ddSpans) - } - mockInternalLogger.verifyLog( - InternalLogger.Level.ERROR, - InternalLogger.Target.USER, - TraceWriter.INITIAL_DATADOG_CONTEXT_NOT_AVAILABLE_ERROR, - mode = times(ddSpans.size) - ) - - ddSpans.forEach { - it.finish() - } - } - - @Test - fun `M write spans W write() { without RUM context, lazy Datadog context is of wrong type }`( - forge: Forge - ) { - // GIVEN - val ddSpans = generateSpanListWithPriorities(forge, TraceWriter.KEEP_AND_UNSET_SAMPLING_PRIORITIES) - .map { - it.apply { setTag(AndroidTracer.DATADOG_CONTEXT_TAG.key, forge.aString()) } - } - - whenever(mockLegacyMapper.map(eq(fakeDatadogContext), any())) doReturn forge.getForgery() - whenever(mockSerializer.serialize(eq(fakeDatadogContext), any())) doReturn forge.aString() - - // WHEN - testedWriter.write(ddSpans) - - // THEN - argumentCaptor { - verify(mockLegacyMapper, times(ddSpans.size)).map(eq(fakeDatadogContext), capture()) - allValues.forEach { - assertThat(it.tags).doesNotContainKey(LogAttributes.RUM_APPLICATION_ID) - assertThat(it.tags).doesNotContainKey(LogAttributes.RUM_SESSION_ID) - assertThat(it.tags).doesNotContainKey(LogAttributes.RUM_VIEW_ID) - assertThat(it.tags).doesNotContainKey(LogAttributes.RUM_ACTION_ID) - assertThat(it.tags).doesNotContainKey(AndroidTracer.DATADOG_CONTEXT_TAG.key) - } - assertThat(allValues).isEqualTo(ddSpans) - } - verifyNoInteractions(mockInternalLogger) - - ddSpans.forEach { - it.finish() - } - } - - // endregion - - @Test - fun `M not write spans with drop sampling priority W write() { drop sampling decision }`(forge: Forge) { - // GIVEN - val ddSpans = generateSpanListWithPriorities(forge, TraceWriter.DROP_SAMPLING_PRIORITIES) - - // WHEN - testedWriter.write(ddSpans) - - // THEN - verifyNoInteractions(mockEventBatchWriter) - - ddSpans.forEach { - it.finish() - } - } - - @Test - fun `M not write non-mapped spans W write()`(forge: Forge) { - // GIVEN - val ddSpans = generateSpanListWithPriorities(forge, TraceWriter.KEEP_AND_UNSET_SAMPLING_PRIORITIES) - - val spanEvents = ddSpans - .map { forge.getForgery() } - val mappedEvents = spanEvents.map { forge.aNullable { it } } - - val serializedSpans = mappedEvents.filterNotNull().map { forge.aString() } - - ddSpans.forEachIndexed { index, ddSpan -> - whenever(mockLegacyMapper.map(fakeDatadogContext, ddSpan)) doReturn spanEvents[index] - } - - spanEvents.forEachIndexed { index, event -> - whenever(mockEventMapper.map(event)) doReturn mappedEvents[index] - } - - mappedEvents.filterNotNull().forEachIndexed { index, spanEvent -> - whenever( - mockSerializer.serialize(fakeDatadogContext, spanEvent) - ) doReturn serializedSpans[index] - } - - // WHEN - testedWriter.write(ddSpans) - - // THEN - serializedSpans.forEach { - verify(mockEventBatchWriter).write( - event = RawBatchEvent(data = it.toByteArray()), - batchMetadata = null, - eventType = EventType.DEFAULT - ) - } - verifyNoMoreInteractions(mockEventBatchWriter) - - ddSpans.forEach { - it.finish() - } - } - - @Test - fun `M not write non-serialized spans W write()`(forge: Forge) { - // GIVEN - val ddSpans = generateSpanListWithPriorities(forge, TraceWriter.KEEP_AND_UNSET_SAMPLING_PRIORITIES) - - val spanEvents = ddSpans.map { forge.getForgery() } - - val serializedSpans = spanEvents.map { forge.aNullable { aString() } } - - ddSpans.forEachIndexed { index, ddSpan -> - whenever(mockLegacyMapper.map(fakeDatadogContext, ddSpan)) doReturn spanEvents[index] - } - - spanEvents.forEachIndexed { index, spanEvent -> - whenever( - mockSerializer.serialize(fakeDatadogContext, spanEvent) - ) doReturn serializedSpans[index] - } - - // WHEN - testedWriter.write(ddSpans) - - // THEN - serializedSpans.filterNotNull().forEach { - verify(mockEventBatchWriter).write( - event = RawBatchEvent(data = it.toByteArray()), - batchMetadata = null, - eventType = EventType.DEFAULT - ) - } - verifyNoMoreInteractions(mockEventBatchWriter) - - ddSpans.forEach { - it.finish() - } - } - - @Test - fun `M do nothing W write() { null trace }`() { - // WHEN - testedWriter.write(null) - - // THEN - verifyNoInteractions( - mockEventBatchWriter, - mockEventMapper, - mockSerializer, - mockSdkCore, - mockLegacyMapper, - mockInternalLogger - ) - } - - @Test - fun `M log error and proceed W write() { serialization failed }`(forge: Forge) { - // GIVEN - val ddSpans = generateSpanListWithPriorities(forge, TraceWriter.KEEP_AND_UNSET_SAMPLING_PRIORITIES) - - val spanEvents = ddSpans.map { forge.getForgery() } - val serializedSpans = ddSpans.map { forge.aString() } - - ddSpans.forEachIndexed { index, ddSpan -> - whenever(mockLegacyMapper.map(fakeDatadogContext, ddSpan)) doReturn spanEvents[index] - } - - val faultySpanIndex = forge.anInt(min = 0, max = spanEvents.size) - val fakeThrowable = forge.aThrowable() - spanEvents.forEachIndexed { index, spanEvent -> - if (index == faultySpanIndex) { - whenever( - mockSerializer.serialize( - fakeDatadogContext, - spanEvent - ) - ) doThrow fakeThrowable - } else { - whenever( - mockSerializer.serialize(fakeDatadogContext, spanEvent) - ) doReturn serializedSpans[index] - } - } - - // WHEN - testedWriter.write(ddSpans) - - // THEN - serializedSpans.forEachIndexed { index, serializedSpan -> - if (index != faultySpanIndex) { - verify(mockEventBatchWriter).write( - event = RawBatchEvent(data = serializedSpan.toByteArray()), - batchMetadata = null, - eventType = EventType.DEFAULT - ) - } - } - verifyNoMoreInteractions(mockEventBatchWriter) - - mockInternalLogger.verifyLog( - InternalLogger.Level.ERROR, - listOf(InternalLogger.Target.USER, InternalLogger.Target.TELEMETRY), - TraceWriter.ERROR_SERIALIZING.format(Locale.US, SpanEvent::class.java.simpleName), - fakeThrowable - ) - - ddSpans.forEach { - it.finish() - } - } - - @Test - fun `M request event write context once W write()`(forge: Forge) { - // GIVEN - val ddSpans = forge.aList { getForgery() } - - // WHEN - testedWriter.write(ddSpans) - - // THEN - verify(mockSdkCore).getFeature(Feature.TRACING_FEATURE_NAME) - verify(mockTracingFeatureScope).withWriteContext(any(), any()) - - verifyNoMoreInteractions(mockSdkCore, mockTracingFeatureScope) - - ddSpans.forEach { - it.finish() - } - } - - // endregion - - private fun generateSpanListWithPriorities(forge: Forge, priorities: Set): List { - return forge.aList { - SpanForgeryFactory { frg, span -> - span.samplingPriority = frg.anElementFrom(priorities) - }.getForgery(forge) - } - } -} diff --git a/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/trace/internal/domain/event/CoreTracerSpanToSpanEventMapperTest.kt b/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/trace/internal/domain/event/CoreTracerSpanToSpanEventMapperTest.kt index 13a495d9a1..20e12490dc 100644 --- a/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/trace/internal/domain/event/CoreTracerSpanToSpanEventMapperTest.kt +++ b/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/trace/internal/domain/event/CoreTracerSpanToSpanEventMapperTest.kt @@ -194,7 +194,7 @@ internal class CoreTracerSpanToSpanEventMapperTest { private fun DDSpan.expectedMetrics(): Map { return tags.filterValues { it is Number }.mapValues { it.value as Number }.toMutableMap().apply { - this[DDSpanContext.PRIORITY_SAMPLING_KEY] = samplingPriority() + this[DDSpanContext.PRIORITY_SAMPLING_KEY] = spanSamplingPriority } } diff --git a/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/trace/internal/domain/event/DdSpanToSpanEventMapperTest.kt b/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/trace/internal/domain/event/DdSpanToSpanEventMapperTest.kt deleted file mode 100644 index 2603474551..0000000000 --- a/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/trace/internal/domain/event/DdSpanToSpanEventMapperTest.kt +++ /dev/null @@ -1,185 +0,0 @@ -/* - * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. - * This product includes software developed at Datadog (https://www.datadoghq.com/). - * Copyright 2016-Present Datadog, Inc. - */ - -package com.datadog.android.trace.internal.domain.event - -import com.datadog.android.api.context.DatadogContext -import com.datadog.android.internal.utils.toHexString -import com.datadog.android.log.LogAttributes -import com.datadog.android.trace.assertj.SpanEventAssert.Companion.assertThat -import com.datadog.android.utils.forge.Configurator -import com.datadog.opentracing.DDSpan -import com.datadog.tools.unit.setFieldValue -import fr.xgouchet.elmyr.Forge -import fr.xgouchet.elmyr.annotation.BoolForgery -import fr.xgouchet.elmyr.annotation.Forgery -import fr.xgouchet.elmyr.annotation.StringForgery -import fr.xgouchet.elmyr.junit5.ForgeConfiguration -import fr.xgouchet.elmyr.junit5.ForgeExtension -import org.junit.jupiter.api.BeforeEach -import org.junit.jupiter.api.RepeatedTest -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.extension.ExtendWith -import org.junit.jupiter.api.extension.Extensions -import org.mockito.Mock -import org.mockito.junit.jupiter.MockitoExtension -import org.mockito.junit.jupiter.MockitoSettings -import org.mockito.kotlin.whenever -import org.mockito.quality.Strictness -import java.math.BigInteger - -@Extensions( - ExtendWith(MockitoExtension::class), - ExtendWith(ForgeExtension::class) -) -@MockitoSettings(strictness = Strictness.STRICT_STUBS) -@ForgeConfiguration(Configurator::class) -internal class DdSpanToSpanEventMapperTest { - - lateinit var testedMapper: DdSpanToSpanEventMapper - - @Forgery - lateinit var fakeDatadogContext: DatadogContext - - @BoolForgery - var fakeNetworkInfoEnabled: Boolean = false - - @Mock - lateinit var mockBigIntegerUtils: BigIntegerUtils - - @StringForgery(regex = "[a-f0-9]{16}") - lateinit var fakeLeastSignificantTraceId: String - - @StringForgery(regex = "[a-f0-9]{16}") - lateinit var fakeMostSignificantTraceId: String - - @Forgery - lateinit var fakeSpan: DDSpan - - @BeforeEach - fun `set up`() { - testedMapper = DdSpanToSpanEventMapper(fakeNetworkInfoEnabled, mockBigIntegerUtils) - whenever(mockBigIntegerUtils.leastSignificant64BitsAsHex(fakeSpan.traceId)) - .thenReturn(fakeLeastSignificantTraceId) - whenever(mockBigIntegerUtils.mostSignificant64BitsAsHex(fakeSpan.traceId)) - .thenReturn(fakeMostSignificantTraceId) - } - - @RepeatedTest(4) - fun `M map a DdSpan to a SpanEvent W map()`() { - // When - val event = testedMapper.map(fakeDatadogContext, fakeSpan) - - // Then - assertThat(event) - .hasSpanId(fakeSpan.spanId.toHexString()) - .hasLeastSignificant64BitsTraceId(fakeLeastSignificantTraceId) - .hasMostSignificant64BitsTraceId(fakeMostSignificantTraceId) - .hasParentId(fakeSpan.parentId.toHexString()) - .hasServiceName(fakeSpan.serviceName) - .hasOperationName(fakeSpan.operationName) - .hasResourceName(fakeSpan.resourceName) - .hasSpanType("custom") - .hasSpanSource(fakeDatadogContext.source) - .hasApplicationId(null) - .hasSessionId(null) - .hasViewId(null) - .hasErrorFlag(fakeSpan.error.toLong()) - .hasSpanStartTime(fakeSpan.startTime + fakeDatadogContext.time.serverTimeOffsetNs) - .hasSpanDuration(fakeSpan.durationNano) - .hasTracerVersion(fakeDatadogContext.sdkVersion) - .hasVariant(fakeDatadogContext.variant) - .hasClientPackageVersion(fakeDatadogContext.version) - .apply { - if (fakeNetworkInfoEnabled) { - hasNetworkInfo(fakeDatadogContext.networkInfo) - } else { - doesntHaveNetworkInfo() - } - } - .hasUserInfo(fakeDatadogContext.userInfo) - .hasAccountInfo(fakeDatadogContext.accountInfo) - .hasMeta(fakeSpan.meta) - .hasDeviceInfo(fakeDatadogContext.deviceInfo) - .hasOsInfo(fakeDatadogContext.deviceInfo) - .hasMetrics(fakeSpan.metrics) - } - - @RepeatedTest(4) - fun `M map a DdSpan to a SpanEvent with RUM info W map() {RUM info present}`( - @StringForgery fakeApplicationId: String, - @StringForgery fakeSessionId: String, - @StringForgery fakeViewId: String - ) { - // Given - fakeSpan.setTag(LogAttributes.RUM_APPLICATION_ID, fakeApplicationId) - fakeSpan.setTag(LogAttributes.RUM_SESSION_ID, fakeSessionId) - fakeSpan.setTag(LogAttributes.RUM_VIEW_ID, fakeViewId) - - // When - val event = testedMapper.map(fakeDatadogContext, fakeSpan) - - // Then - assertThat(event) - .hasSpanId(fakeSpan.spanId.toHexString()) - .hasLeastSignificant64BitsTraceId(fakeLeastSignificantTraceId) - .hasMostSignificant64BitsTraceId(fakeMostSignificantTraceId) - .hasParentId(fakeSpan.parentId.toHexString()) - .hasServiceName(fakeSpan.serviceName) - .hasOperationName(fakeSpan.operationName) - .hasResourceName(fakeSpan.resourceName) - .hasSpanType("custom") - .hasSpanSource(fakeDatadogContext.source) - .hasApplicationId(fakeApplicationId) - .hasSessionId(fakeSessionId) - .hasViewId(fakeViewId) - .hasErrorFlag(fakeSpan.error.toLong()) - .hasSpanStartTime(fakeSpan.startTime + fakeDatadogContext.time.serverTimeOffsetNs) - .hasSpanDuration(fakeSpan.durationNano) - .hasTracerVersion(fakeDatadogContext.sdkVersion) - .hasVariant(fakeDatadogContext.variant) - .hasClientPackageVersion(fakeDatadogContext.version) - .apply { - if (fakeNetworkInfoEnabled) { - hasNetworkInfo(fakeDatadogContext.networkInfo) - } else { - doesntHaveNetworkInfo() - } - } - .hasUserInfo(fakeDatadogContext.userInfo) - .hasAccountInfo(fakeDatadogContext.accountInfo) - .hasMeta(fakeSpan.meta) - .hasDeviceInfo(fakeDatadogContext.deviceInfo) - .hasOsInfo(fakeDatadogContext.deviceInfo) - .hasMetrics(fakeSpan.metrics) - } - - @Test - fun `M mark the SpanEvent as top span W map() { parentId is 0 }`() { - // Given - fakeSpan.setFieldValue("parentId", 0) - - // When - val event = testedMapper.map(fakeDatadogContext, fakeSpan) - - // Then - assertThat(event) - .isTopSpan() - } - - @Test - fun `M not mark the SpanEvent as top span W map() { parentId is different than 0 }`(forge: Forge) { - // Given - fakeSpan.context().setFieldValue("parentId", BigInteger.valueOf(forge.aLong(min = 1))) - - // When - val event = testedMapper.map(fakeDatadogContext, fakeSpan) - - // Then - assertThat(event) - .isNotTopSpan() - } -} diff --git a/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/trace/internal/domain/event/SpanMapperSerializerTest.kt b/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/trace/internal/domain/event/SpanMapperSerializerTest.kt deleted file mode 100644 index f77983d3b9..0000000000 --- a/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/trace/internal/domain/event/SpanMapperSerializerTest.kt +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. - * This product includes software developed at Datadog (https://www.datadoghq.com/). - * Copyright 2016-Present Datadog, Inc. - */ - -package com.datadog.android.trace.internal.domain.event - -import com.datadog.android.core.persistence.Serializer -import com.datadog.android.event.EventMapper -import com.datadog.android.trace.model.SpanEvent -import com.datadog.android.utils.forge.Configurator -import com.datadog.opentracing.DDSpan -import fr.xgouchet.elmyr.annotation.Forgery -import fr.xgouchet.elmyr.annotation.StringForgery -import fr.xgouchet.elmyr.junit5.ForgeConfiguration -import fr.xgouchet.elmyr.junit5.ForgeExtension -import org.assertj.core.api.Assertions.assertThat -import org.junit.jupiter.api.BeforeEach -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.extension.ExtendWith -import org.junit.jupiter.api.extension.Extensions -import org.mockito.Mock -import org.mockito.junit.jupiter.MockitoExtension -import org.mockito.junit.jupiter.MockitoSettings -import org.mockito.kotlin.whenever -import org.mockito.quality.Strictness - -@Extensions( - ExtendWith(MockitoExtension::class), - ExtendWith(ForgeExtension::class) -) -@MockitoSettings(strictness = Strictness.LENIENT) -@ForgeConfiguration(Configurator::class) -internal class SpanMapperSerializerTest { - lateinit var testedSerializer: SpanMapperSerializer - - @Mock - lateinit var mockSpanEventMapper: Mapper - - @Mock - lateinit var mockExposedEventMapper: EventMapper - - @Mock - lateinit var mockSerializer: Serializer - - @Forgery - lateinit var fakeDdSpan: DDSpan - - @Mock - lateinit var mockSpanEvent: SpanEvent - - @StringForgery - lateinit var fakeSerializedSpanEvent: String - - @BeforeEach - fun `set up`() { - whenever(mockSpanEventMapper.map(fakeDdSpan)).thenReturn(mockSpanEvent) - whenever(mockSerializer.serialize(mockSpanEvent)).thenReturn(fakeSerializedSpanEvent) - testedSerializer = SpanMapperSerializer( - mockSpanEventMapper, - mockExposedEventMapper, - mockSerializer - ) - } - - @Test - fun `M return the serialized equivalent SpanEvent W serialize`() { - // GIVEN - whenever(mockExposedEventMapper.map(mockSpanEvent)).thenReturn(mockSpanEvent) - - // WHEN - val serializedEvent = testedSerializer.serialize(fakeDdSpan) - - // THEN - assertThat(serializedEvent).isEqualTo(fakeSerializedSpanEvent) - } - - @Test - fun `M return null W serialize { event dropped from exposedEventMapper }`() { - assertThat(testedSerializer.serialize(fakeDdSpan)).isNull() - } -} diff --git a/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/trace/internal/handlers/AndroidSpanLogsHandlerTest.kt b/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/trace/internal/handlers/AndroidSpanLogsHandlerTest.kt deleted file mode 100644 index 3209d4c75d..0000000000 --- a/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/trace/internal/handlers/AndroidSpanLogsHandlerTest.kt +++ /dev/null @@ -1,472 +0,0 @@ -/* - * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. - * This product includes software developed at Datadog (https://www.datadoghq.com/). - * Copyright 2016-Present Datadog, Inc. - */ - -package com.datadog.android.trace.internal.handlers - -import android.util.Log -import com.datadog.android.api.InternalLogger -import com.datadog.android.api.feature.Feature -import com.datadog.android.api.feature.FeatureScope -import com.datadog.android.api.feature.FeatureSdkCore -import com.datadog.android.internal.utils.loggableStackTrace -import com.datadog.android.internal.utils.toHexString -import com.datadog.android.log.LogAttributes -import com.datadog.android.trace.utils.verifyLog -import com.datadog.android.utils.forge.Configurator -import com.datadog.legacy.trace.api.DDTags -import com.datadog.opentracing.DDSpan -import com.datadog.opentracing.DDSpanContext -import fr.xgouchet.elmyr.Forge -import fr.xgouchet.elmyr.annotation.Forgery -import fr.xgouchet.elmyr.annotation.LongForgery -import fr.xgouchet.elmyr.annotation.StringForgery -import fr.xgouchet.elmyr.junit5.ForgeConfiguration -import fr.xgouchet.elmyr.junit5.ForgeExtension -import io.opentracing.log.Fields -import org.assertj.core.api.Assertions.assertThat -import org.junit.jupiter.api.BeforeEach -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.extension.ExtendWith -import org.junit.jupiter.api.extension.Extensions -import org.mockito.Mock -import org.mockito.junit.jupiter.MockitoExtension -import org.mockito.junit.jupiter.MockitoSettings -import org.mockito.kotlin.argumentCaptor -import org.mockito.kotlin.doReturn -import org.mockito.kotlin.verify -import org.mockito.kotlin.whenever -import org.mockito.quality.Strictness -import java.math.BigInteger -import java.util.concurrent.TimeUnit - -@Extensions( - ExtendWith(MockitoExtension::class), - ExtendWith(ForgeExtension::class) -) -@MockitoSettings(strictness = Strictness.LENIENT) -@ForgeConfiguration(Configurator::class) -internal class AndroidSpanLogsHandlerTest { - - lateinit var testedLogHandler: AndroidSpanLogsHandler - - @Mock - lateinit var mockSdkCore: FeatureSdkCore - - @Mock - lateinit var mockInternalLogger: InternalLogger - - @Mock - lateinit var mockLogsFeatureScope: FeatureScope - - @Mock - lateinit var mockSpan: DDSpan - - @Mock - lateinit var mockSpanContext: DDSpanContext - - @LongForgery(min = 1) - var fakeTraceId: Long = 0L - - @LongForgery - var fakeSpanId: Long = 0L - - @BeforeEach - fun `set up`() { - whenever(mockSpan.context()) doReturn mockSpanContext - whenever(mockSpan.traceId) doReturn BigInteger.valueOf(fakeTraceId) - whenever(mockSpan.spanId) doReturn BigInteger.valueOf(fakeSpanId) - whenever(mockSpanContext.traceId) doReturn BigInteger.valueOf(fakeTraceId) - whenever(mockSpanContext.toSpanId()) doReturn fakeSpanId.toString() - - whenever( - mockSdkCore.getFeature(Feature.LOGS_FEATURE_NAME) - ) doReturn mockLogsFeatureScope - - whenever(mockSdkCore.internalLogger) doReturn mockInternalLogger - - testedLogHandler = AndroidSpanLogsHandler(mockSdkCore) - } - - @Test - fun `log event`( - @StringForgery event: String - ) { - // When - testedLogHandler.log(event, mockSpan) - - // Then - argumentCaptor> { - verify(mockLogsFeatureScope) - .sendEvent(capture()) - - val spanLogEvent = firstValue.toMutableMap() - val timestamp = spanLogEvent.remove("timestamp") - assertThat(spanLogEvent).isEqualTo( - mapOf( - "type" to "span_log", - "loggerName" to AndroidSpanLogsHandler.TRACE_LOGGER_NAME, - "message" to AndroidSpanLogsHandler.DEFAULT_EVENT_MESSAGE, - "attributes" to mapOf( - Fields.EVENT to event, - LogAttributes.DD_TRACE_ID to fakeTraceId.toHexString().padStart(32, '0'), - LogAttributes.DD_SPAN_ID to fakeSpanId.toString() - ), - "logStatus" to Log.VERBOSE - ) - ) - - val timestampAge = System.currentTimeMillis() - timestamp as Long - assertThat(timestampAge).isBetween(0, 150) - } - } - - @Test - fun `log event with timestamp`( - @StringForgery event: String, - @LongForgery timestampMicros: Long - ) { - // When - testedLogHandler.log(timestampMicros, event, mockSpan) - - // Then - verify(mockLogsFeatureScope) - .sendEvent( - mapOf( - "type" to "span_log", - "loggerName" to AndroidSpanLogsHandler.TRACE_LOGGER_NAME, - "message" to AndroidSpanLogsHandler.DEFAULT_EVENT_MESSAGE, - "attributes" to mapOf( - Fields.EVENT to event, - LogAttributes.DD_TRACE_ID to fakeTraceId.toHexString().padStart(32, '0'), - LogAttributes.DD_SPAN_ID to fakeSpanId.toString() - ), - "timestamp" to TimeUnit.MICROSECONDS.toMillis(timestampMicros), - "logStatus" to Log.VERBOSE - ) - ) - } - - @Test - fun `log map`( - forge: Forge - ) { - // Given - val fields = forge.aMap { anAlphabeticalString() to anAsciiString() } - val logAttributes = fields.toMutableMap() - .apply { - put(LogAttributes.DD_TRACE_ID, fakeTraceId.toHexString().padStart(32, '0')) - put(LogAttributes.DD_SPAN_ID, fakeSpanId.toString()) - } - - // When - testedLogHandler.log(fields, mockSpan) - - // Then - argumentCaptor> { - verify(mockLogsFeatureScope) - .sendEvent(capture()) - - val spanLogEvent = firstValue.toMutableMap() - val timestamp = spanLogEvent.remove("timestamp") - assertThat(spanLogEvent).isEqualTo( - mapOf( - "type" to "span_log", - "loggerName" to AndroidSpanLogsHandler.TRACE_LOGGER_NAME, - "message" to AndroidSpanLogsHandler.DEFAULT_EVENT_MESSAGE, - "attributes" to logAttributes, - "logStatus" to Log.VERBOSE - ) - ) - - val timestampAge = System.currentTimeMillis() - timestamp as Long - assertThat(timestampAge).isBetween(0, 150) - } - } - - @Test - fun `log map with timestamp`( - forge: Forge, - @LongForgery timestampMicros: Long - ) { - // Given - val fields = forge.aMap { anAlphabeticalString() to anAsciiString() } - val logAttributes = fields.toMutableMap() - .apply { - put(LogAttributes.DD_TRACE_ID, fakeTraceId.toHexString().padStart(32, '0')) - put(LogAttributes.DD_SPAN_ID, fakeSpanId.toString()) - } - - // When - testedLogHandler.log(timestampMicros, fields, mockSpan) - - // Then - verify(mockLogsFeatureScope) - .sendEvent( - mapOf( - "type" to "span_log", - "loggerName" to AndroidSpanLogsHandler.TRACE_LOGGER_NAME, - "message" to AndroidSpanLogsHandler.DEFAULT_EVENT_MESSAGE, - "attributes" to logAttributes, - "timestamp" to TimeUnit.MICROSECONDS.toMillis(timestampMicros), - "logStatus" to Log.VERBOSE - ) - ) - } - - @Test - fun `log map with throwable`( - forge: Forge, - @Forgery throwable: Throwable - ) { - // Given - val fields = forge.aMap { aNumericalString() to anAsciiString() } - val fieldsWithError = fields.toMutableMap() - .apply { put(Fields.ERROR_OBJECT, throwable) } - - val logAttributes = fields.toMutableMap() - .apply { - put(LogAttributes.DD_TRACE_ID, fakeTraceId.toHexString().padStart(32, '0')) - put(LogAttributes.DD_SPAN_ID, fakeSpanId.toString()) - } - - // When - testedLogHandler.log(fieldsWithError, mockSpan) - - // Then - verify(mockSpan).setError(true) - verify(mockSpan).setTag(DDTags.ERROR_MSG, throwable.message) - verify(mockSpan).setTag(DDTags.ERROR_TYPE, throwable.javaClass.name) - verify(mockSpan).setTag(DDTags.ERROR_STACK, throwable.loggableStackTrace()) - - argumentCaptor> { - verify(mockLogsFeatureScope) - .sendEvent(capture()) - - val spanLogEvent = firstValue.toMutableMap() - val timestamp = spanLogEvent.remove("timestamp") - assertThat(spanLogEvent).isEqualTo( - mapOf( - "type" to "span_log", - "loggerName" to AndroidSpanLogsHandler.TRACE_LOGGER_NAME, - "message" to AndroidSpanLogsHandler.DEFAULT_EVENT_MESSAGE, - "attributes" to logAttributes, - "logStatus" to Log.VERBOSE - ) - ) - - val timestampAge = System.currentTimeMillis() - timestamp as Long - assertThat(timestampAge).isBetween(0, 150) - } - } - - @Test - fun `log map with throwable and timestamp`( - forge: Forge, - @Forgery throwable: Throwable, - @LongForgery timestampMicros: Long - ) { - // Given - val fields = forge.aMap { aNumericalString() to anAsciiString() } - val fieldsWithError = fields.toMutableMap() - .apply { put(Fields.ERROR_OBJECT, throwable) } - - val logAttributes = fields.toMutableMap() - .apply { - put(LogAttributes.DD_TRACE_ID, fakeTraceId.toHexString().padStart(32, '0')) - put(LogAttributes.DD_SPAN_ID, fakeSpanId.toString()) - } - - // When - testedLogHandler.log(timestampMicros, fieldsWithError, mockSpan) - - // Then - verify(mockSpan).setError(true) - verify(mockSpan).setTag(DDTags.ERROR_MSG, throwable.message) - verify(mockSpan).setTag(DDTags.ERROR_TYPE, throwable.javaClass.name) - verify(mockSpan).setTag(DDTags.ERROR_STACK, throwable.loggableStackTrace()) - verify(mockLogsFeatureScope) - .sendEvent( - mapOf( - "type" to "span_log", - "loggerName" to AndroidSpanLogsHandler.TRACE_LOGGER_NAME, - "message" to AndroidSpanLogsHandler.DEFAULT_EVENT_MESSAGE, - "attributes" to logAttributes, - "timestamp" to TimeUnit.MICROSECONDS.toMillis(timestampMicros), - "logStatus" to Log.VERBOSE - ) - ) - } - - @Test - fun `log map with throwable and overridden error fields`( - forge: Forge, - @Forgery throwable: Throwable, - @StringForgery message: String, - @StringForgery kind: String - ) { - // Given - val fields = forge.aMap { aNumericalString() to anAsciiString() } - val fieldsWithError = fields.toMutableMap() - .apply { - put(Fields.ERROR_OBJECT, throwable) - put(Fields.ERROR_KIND, kind) - put(Fields.MESSAGE, message) - } - - val logAttributes = fields.toMutableMap() - .apply { - put(LogAttributes.DD_TRACE_ID, fakeTraceId.toHexString().padStart(32, '0')) - put(LogAttributes.DD_SPAN_ID, fakeSpanId.toString()) - } - - // When - testedLogHandler.log(fieldsWithError, mockSpan) - - // Then - verify(mockSpan).setError(true) - verify(mockSpan).setTag(DDTags.ERROR_MSG, message) - verify(mockSpan).setTag(DDTags.ERROR_TYPE, kind) - verify(mockSpan).setTag(DDTags.ERROR_STACK, throwable.loggableStackTrace()) - - argumentCaptor> { - verify(mockLogsFeatureScope) - .sendEvent(capture()) - - val spanLogEvent = firstValue.toMutableMap() - val timestamp = spanLogEvent.remove("timestamp") - assertThat(spanLogEvent).isEqualTo( - mapOf( - "type" to "span_log", - "loggerName" to AndroidSpanLogsHandler.TRACE_LOGGER_NAME, - "message" to message, - "attributes" to logAttributes, - "logStatus" to Log.VERBOSE - ) - ) - - val timestampAge = System.currentTimeMillis() - timestamp as Long - assertThat(timestampAge).isBetween(0, 150) - } - } - - @Test - fun `log map with throwable and stack trace`( - forge: Forge, - @Forgery throwable: Throwable, - @StringForgery stack: String - ) { - // Given - val fields = forge.aMap { aNumericalString() to anAsciiString() } - val fieldsWithError = fields.toMutableMap() - .apply { - put(Fields.ERROR_OBJECT, throwable) - put(Fields.STACK, stack) - } - - val logAttributes = fields.toMutableMap() - .apply { - put(LogAttributes.DD_TRACE_ID, fakeTraceId.toHexString().padStart(32, '0')) - put(LogAttributes.DD_SPAN_ID, fakeSpanId.toString()) - } - - // When - testedLogHandler.log(fieldsWithError, mockSpan) - - // Then - verify(mockSpan).setError(true) - verify(mockSpan).setTag(DDTags.ERROR_MSG, throwable.message) - verify(mockSpan).setTag(DDTags.ERROR_TYPE, throwable.javaClass.name) - verify(mockSpan).setTag(DDTags.ERROR_STACK, stack) - - argumentCaptor> { - verify(mockLogsFeatureScope) - .sendEvent(capture()) - - val spanLogEvent = firstValue.toMutableMap() - val timestamp = spanLogEvent.remove("timestamp") - assertThat(spanLogEvent).isEqualTo( - mapOf( - "type" to "span_log", - "loggerName" to AndroidSpanLogsHandler.TRACE_LOGGER_NAME, - "message" to AndroidSpanLogsHandler.DEFAULT_EVENT_MESSAGE, - "attributes" to logAttributes, - "logStatus" to Log.VERBOSE - ) - ) - - val timestampAge = System.currentTimeMillis() - timestamp as Long - assertThat(timestampAge).isBetween(0, 150) - } - } - - @Test - fun `log map with error fields`( - forge: Forge, - @StringForgery stack: String, - @StringForgery message: String, - @StringForgery kind: String - ) { - // Given - val fields = forge.aMap { aNumericalString() to anAsciiString() } - val fieldsWithError = fields.toMutableMap() - .apply { - put(Fields.STACK, stack) - put(Fields.ERROR_KIND, kind) - put(Fields.MESSAGE, message) - } - - val logAttributes = fields.toMutableMap() - .apply { - put(LogAttributes.DD_TRACE_ID, fakeTraceId.toHexString().padStart(32, '0')) - put(LogAttributes.DD_SPAN_ID, fakeSpanId.toString()) - } - - // When - testedLogHandler.log(fieldsWithError, mockSpan) - - // Then - verify(mockSpan).setError(true) - verify(mockSpan).setTag(DDTags.ERROR_MSG, message) - verify(mockSpan).setTag(DDTags.ERROR_TYPE, kind) - verify(mockSpan).setTag(DDTags.ERROR_STACK, stack) - - argumentCaptor> { - verify(mockLogsFeatureScope) - .sendEvent(capture()) - - val spanLogEvent = firstValue.toMutableMap() - val timestamp = spanLogEvent.remove("timestamp") - assertThat(spanLogEvent).isEqualTo( - mapOf( - "type" to "span_log", - "loggerName" to AndroidSpanLogsHandler.TRACE_LOGGER_NAME, - "message" to message, - "attributes" to logAttributes, - "logStatus" to Log.VERBOSE - ) - ) - - val timestampAge = System.currentTimeMillis() - timestamp as Long - assertThat(timestampAge).isBetween(0, 150) - } - } - - @Test - fun `M log info W log() { Logs feature is not registered }`( - @StringForgery event: String - ) { - // When - whenever(mockSdkCore.getFeature(Feature.LOGS_FEATURE_NAME)) doReturn null - testedLogHandler.log(event, mockSpan) - - // Then - mockInternalLogger.verifyLog( - InternalLogger.Level.WARN, - InternalLogger.Target.USER, - AndroidSpanLogsHandler.MISSING_LOG_FEATURE_INFO - ) - } -} diff --git a/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/trace/internal/utils/SpanContextExtTest.kt b/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/trace/internal/utils/SpanContextExtTest.kt deleted file mode 100644 index a12580fa1b..0000000000 --- a/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/trace/internal/utils/SpanContextExtTest.kt +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. - * This product includes software developed at Datadog (https://www.datadoghq.com/). - * Copyright 2016-Present Datadog, Inc. - */ - -package com.datadog.android.trace.internal.utils - -import com.datadog.android.utils.forge.Configurator -import com.datadog.opentracing.DDSpanContext -import fr.xgouchet.elmyr.annotation.StringForgery -import fr.xgouchet.elmyr.junit5.ForgeConfiguration -import fr.xgouchet.elmyr.junit5.ForgeExtension -import io.opentracing.Span -import io.opentracing.SpanContext -import org.assertj.core.api.Assertions.assertThat -import org.junit.jupiter.api.BeforeEach -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.extension.ExtendWith -import org.junit.jupiter.api.extension.Extensions -import org.mockito.Mock -import org.mockito.junit.jupiter.MockitoExtension -import org.mockito.junit.jupiter.MockitoSettings -import org.mockito.kotlin.mock -import org.mockito.kotlin.whenever -import org.mockito.quality.Strictness -import java.math.BigInteger - -@Extensions( - ExtendWith(MockitoExtension::class), - ExtendWith(ForgeExtension::class) -) -@MockitoSettings(strictness = Strictness.LENIENT) -@ForgeConfiguration(Configurator::class) -internal class SpanContextExtTest { - - @Mock - lateinit var mockSpan: Span - - @Mock - lateinit var mockDDSpanContext: DDSpanContext - - @StringForgery(regex = "([a-f0-9]{32})") - lateinit var expectedFakeTraceId: String - - private lateinit var fakeTraceIdAsBigInteger: BigInteger - - @BeforeEach - fun `set up`() { - fakeTraceIdAsBigInteger = BigInteger(expectedFakeTraceId, 16) - } - - @Test - fun `M return empty string W traceIdAsHexString() { spanContext is not DDSpanContext }`() { - // Given - whenever(mockSpan.context()).thenReturn(mock()) - - // When - val result = mockSpan.context().traceIdAsHexString() - - // Then - assertThat(result).isEmpty() - } - - @Test - fun `M return empty string W traceIdAsHexString() { ddSpanContext id is null}`() { - // Given - whenever(mockSpan.context()).thenReturn(mockDDSpanContext) - whenever(mockDDSpanContext.traceId).thenReturn(null) - - // When - val result = mockSpan.context().traceIdAsHexString() - - // Then - assertThat(result).isEmpty() - } - - @Test - fun `M return the expected hex padded string W traceIdAsHexString()`() { - // Given - whenever(mockSpan.context()).thenReturn(mockDDSpanContext) - whenever(mockDDSpanContext.traceId).thenReturn(fakeTraceIdAsBigInteger) - - // When - val result = mockSpan.context().traceIdAsHexString() - - // Then - assertThat(result).isEqualTo(expectedFakeTraceId) - } -} diff --git a/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/trace/internal/utils/TracerExtensionsTest.kt b/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/trace/internal/utils/TracerExtensionsTest.kt deleted file mode 100644 index 7a050d68e0..0000000000 --- a/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/trace/internal/utils/TracerExtensionsTest.kt +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. - * This product includes software developed at Datadog (https://www.datadoghq.com/). - * Copyright 2016-Present Datadog, Inc. - */ - -package com.datadog.android.trace.internal.utils - -import com.datadog.android.api.feature.FeatureSdkCore -import com.datadog.android.trace.AndroidTracer -import com.datadog.android.utils.forge.Configurator -import com.datadog.opentracing.DDSpan -import com.datadog.opentracing.scopemanager.ScopeTestHelper -import fr.xgouchet.elmyr.Forge -import fr.xgouchet.elmyr.junit5.ForgeConfiguration -import fr.xgouchet.elmyr.junit5.ForgeExtension -import org.assertj.core.api.Assertions.assertThat -import org.junit.jupiter.api.AfterEach -import org.junit.jupiter.api.BeforeEach -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.extension.ExtendWith -import org.junit.jupiter.api.extension.Extensions -import org.mockito.Mock -import org.mockito.junit.jupiter.MockitoExtension -import org.mockito.junit.jupiter.MockitoSettings -import org.mockito.kotlin.doReturn -import org.mockito.kotlin.mock -import org.mockito.kotlin.whenever -import org.mockito.quality.Strictness - -@Extensions( - ExtendWith(MockitoExtension::class), - ExtendWith(ForgeExtension::class) -) -@MockitoSettings(strictness = Strictness.LENIENT) -@ForgeConfiguration(Configurator::class) -internal class TracerExtensionsTest { - - lateinit var tracer: AndroidTracer - - @Mock - lateinit var mockSdkCore: FeatureSdkCore - - @BeforeEach - fun `set up`(forge: Forge) { - whenever(mockSdkCore.internalLogger) doReturn mock() - - tracer = AndroidTracer.Builder(mockSdkCore) - .setService(forge.anAlphaNumericalString()) - .build() - } - - @AfterEach - fun `tear down`() { - val activeSpan = tracer.activeSpan() - - @Suppress("DEPRECATION") - val activeScope = tracer.scopeManager().active() - activeSpan?.finish() - activeScope?.close() - - ScopeTestHelper.removeThreadLocalScope() - } - - @Test - fun `it will return the trace id and span id if there is an active span`(forge: Forge) { - // When - val span = tracer.buildSpan(forge.anAlphabeticalString()).start() as DDSpan - tracer.activateSpan(span) - - // Then - assertThat(tracer.traceId()).isEqualTo(span.traceId.toString()) - assertThat(tracer.spanId()).isEqualTo(span.spanId.toString()) - } - - @Test - fun `it will return null for trace and span id if there is no active span`() { - // Then - assertThat(tracer.traceId()).isNull() - assertThat(tracer.spanId()).isNull() - } -} diff --git a/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/trace/sqlite/SqliteDatabaseExtTest.kt b/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/trace/sqlite/SqliteDatabaseExtTest.kt index 4e990f271c..bb7378bc5f 100644 --- a/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/trace/sqlite/SqliteDatabaseExtTest.kt +++ b/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/trace/sqlite/SqliteDatabaseExtTest.kt @@ -8,17 +8,17 @@ package com.datadog.android.trace.sqlite import android.content.ContentValues import android.database.sqlite.SQLiteDatabase +import com.datadog.android.trace.GlobalDatadogTracer +import com.datadog.android.trace.api.scope.DatadogScope +import com.datadog.android.trace.api.span.DatadogSpan +import com.datadog.android.trace.api.span.DatadogSpanBuilder +import com.datadog.android.trace.api.tracer.DatadogTracer import com.datadog.tools.unit.forge.BaseConfigurator -import com.datadog.tools.unit.setStaticValue import fr.xgouchet.elmyr.Forge import fr.xgouchet.elmyr.annotation.Forgery import fr.xgouchet.elmyr.annotation.StringForgery import fr.xgouchet.elmyr.junit5.ForgeConfiguration import fr.xgouchet.elmyr.junit5.ForgeExtension -import io.opentracing.Scope -import io.opentracing.Span -import io.opentracing.Tracer -import io.opentracing.util.GlobalTracer import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.catchThrowable import org.junit.jupiter.api.AfterEach @@ -44,19 +44,19 @@ import org.mockito.quality.Strictness class SqliteDatabaseExtTest { @Mock - lateinit var mockTracer: Tracer + lateinit var mockTracer: DatadogTracer @Mock - lateinit var mockSpanBuilder: Tracer.SpanBuilder + lateinit var mockSpanBuilder: DatadogSpanBuilder @Mock - lateinit var mockSpan: Span + lateinit var mockSpan: DatadogSpan @Mock - lateinit var mockParentSpan: Span + lateinit var mockParentSpan: DatadogSpan @Mock - lateinit var mockScope: Scope + lateinit var mockScope: DatadogScope @StringForgery lateinit var fakeOperationName: String @@ -69,7 +69,7 @@ class SqliteDatabaseExtTest { @BeforeEach fun `set up`() { - GlobalTracer.registerIfAbsent(mockTracer) + GlobalDatadogTracer.registerIfAbsent(mockTracer) whenever(mockTracer.buildSpan(fakeOperationName)) doReturn mockSpanBuilder whenever(mockTracer.activateSpan(mockSpan)) doReturn mockScope whenever(mockSpanBuilder.start()) doReturn mockSpan @@ -77,14 +77,14 @@ class SqliteDatabaseExtTest { @AfterEach fun `tear down`() { - GlobalTracer::class.java.setStaticValue("isRegistered", false) + GlobalDatadogTracer.clear() } @Test fun `M create Span around transaction W transactionTraced() {exclusive = true}`() { // GIVEN whenever(mockTracer.activeSpan()) doReturn mockParentSpan - whenever(mockSpanBuilder.asChildOf(mockParentSpan)) doReturn mockSpanBuilder + whenever(mockSpanBuilder.withParentSpan(mockParentSpan)) doReturn mockSpanBuilder // WHEN val transactionExecuted: Boolean = mockDatabase.transactionTraced( @@ -96,7 +96,7 @@ class SqliteDatabaseExtTest { // THEN assertThat(transactionExecuted).isTrue() - verify(mockSpanBuilder).asChildOf(mockParentSpan) + verify(mockSpanBuilder).withParentSpan(mockParentSpan) inOrder(mockSpan, mockScope) { verify(mockSpan).finish() verify(mockScope).close() @@ -112,7 +112,7 @@ class SqliteDatabaseExtTest { fun `M create Span around transaction W transactionTraced() {exclusive = false}`() { // GIVEN whenever(mockTracer.activeSpan()) doReturn mockParentSpan - whenever(mockSpanBuilder.asChildOf(mockParentSpan)) doReturn mockSpanBuilder + whenever(mockSpanBuilder.withParentSpan(mockParentSpan)) doReturn mockSpanBuilder // WHEN val transactionExecuted: Boolean = mockDatabase.transactionTraced( @@ -124,7 +124,7 @@ class SqliteDatabaseExtTest { // THEN assertThat(transactionExecuted).isTrue() - verify(mockSpanBuilder).asChildOf(mockParentSpan) + verify(mockSpanBuilder).withParentSpan(mockParentSpan) inOrder(mockSpan, mockScope) { verify(mockSpan).finish() verify(mockScope).close() @@ -140,7 +140,7 @@ class SqliteDatabaseExtTest { fun `M create Span around transaction W transactionTraced() without parents`() { // GIVEN whenever(mockTracer.activeSpan()) doReturn null - whenever(mockSpanBuilder.asChildOf(null as Span?)) doReturn mockSpanBuilder + whenever(mockSpanBuilder.withParentSpan(null as DatadogSpan?)) doReturn mockSpanBuilder // WHEN val transactionExecuted = mockDatabase.transactionTraced( @@ -152,7 +152,7 @@ class SqliteDatabaseExtTest { // THEN assertThat(transactionExecuted).isTrue() - verify(mockSpanBuilder).asChildOf(null as Span?) + verify(mockSpanBuilder).withParentSpan(null as DatadogSpan?) inOrder(mockSpan, mockScope) { verify(mockSpan).finish() verify(mockScope).close() @@ -168,7 +168,7 @@ class SqliteDatabaseExtTest { fun `M close the Span around transaction W transactionTraced() throws exception`() { // GIVEN whenever(mockTracer.activeSpan()) doReturn null - whenever(mockSpanBuilder.asChildOf(null as Span?)) doReturn mockSpanBuilder + whenever(mockSpanBuilder.withParentSpan(null as DatadogSpan?)) doReturn mockSpanBuilder // WHEN @@ -181,7 +181,7 @@ class SqliteDatabaseExtTest { ).isEqualTo(fakeException) // THEN - verify(mockSpanBuilder).asChildOf(null as Span?) + verify(mockSpanBuilder).withParentSpan(null as DatadogSpan?) inOrder(mockSpan, mockScope) { verify(mockSpan).finish() verify(mockScope).close() @@ -198,7 +198,7 @@ class SqliteDatabaseExtTest { val fakeTagKey = forge.anAlphabeticalString() val fakeTagValue = forge.anAlphabeticalString() whenever(mockTracer.activeSpan()) doReturn mockParentSpan - whenever(mockSpanBuilder.asChildOf(mockParentSpan)) doReturn mockSpanBuilder + whenever(mockSpanBuilder.withParentSpan(mockParentSpan)) doReturn mockSpanBuilder // WHEN val transactionExecuted = mockDatabase.transactionTraced( @@ -220,7 +220,7 @@ class SqliteDatabaseExtTest { val fakeTable = forge.anAlphabeticalString() val contentValues = ContentValues() whenever(mockTracer.activeSpan()) doReturn mockParentSpan - whenever(mockSpanBuilder.asChildOf(mockParentSpan)) doReturn mockSpanBuilder + whenever(mockSpanBuilder.withParentSpan(mockParentSpan)) doReturn mockSpanBuilder // WHEN val transactionExecuted = mockDatabase.transactionTraced( diff --git a/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/utils/forge/Configurator.kt b/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/utils/forge/Configurator.kt index e6bd1730c0..d7afe67d22 100644 --- a/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/utils/forge/Configurator.kt +++ b/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/utils/forge/Configurator.kt @@ -19,10 +19,14 @@ internal class Configurator : BaseConfigurator() { forge.useCoreFactories() // APM - forge.addFactory(SpanForgeryFactory()) forge.addFactory(SpanEventForgeryFactory()) forge.addFactory(TraceConfigurationForgeryFactory()) forge.addFactory(CoreDDSpanForgeryFactory()) forge.addFactory(AgentSpanLinkForgeryFactory()) + forge.addFactory(DatadogSpanLinkForgery()) + forge.addFactory(DDTraceIdForgeryFactory()) + forge.addFactory(DatadogSpanForgeryFactory()) + forge.addFactory(DatadogTraceIdForgeryFactory()) + forge.addFactory(DatadogSpanContextForgeryFactory()) } } diff --git a/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/utils/forge/CoreDDSpanForgeryFactory.kt b/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/utils/forge/CoreDDSpanForgeryFactory.kt index 30e6541e95..3bc70fd08d 100644 --- a/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/utils/forge/CoreDDSpanForgeryFactory.kt +++ b/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/utils/forge/CoreDDSpanForgeryFactory.kt @@ -60,7 +60,7 @@ internal class CoreDDSpanForgeryFactory : ForgeryFactory { whenever(it.parentId).thenReturn(parentId) whenever(it.baggage).thenReturn(baggageItems) whenever(it.tags).thenReturn(tagsAndMetrics) - whenever(it.samplingPriority()).thenReturn(samplingPriority) + whenever(it.traceSamplingPriority).thenReturn(samplingPriority) whenever(it.links).thenReturn(spanLinks) } return mockDDSpan diff --git a/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/utils/forge/DDTraceIdForgeryFactory.kt b/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/utils/forge/DDTraceIdForgeryFactory.kt new file mode 100644 index 0000000000..b8563e542d --- /dev/null +++ b/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/utils/forge/DDTraceIdForgeryFactory.kt @@ -0,0 +1,16 @@ +/* + * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. + * This product includes software developed at Datadog (https://www.datadoghq.com/). + * Copyright 2016-Present Datadog, Inc. + */ +package com.datadog.android.utils.forge + +import com.datadog.trace.api.DDTraceId +import fr.xgouchet.elmyr.Forge +import fr.xgouchet.elmyr.ForgeryFactory + +internal class DDTraceIdForgeryFactory : ForgeryFactory { + override fun getForgery(forge: Forge): DDTraceId { + return DDTraceId.from(forge.aLong()) + } +} diff --git a/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/utils/forge/DatadogSpanContextForgeryFactory.kt b/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/utils/forge/DatadogSpanContextForgeryFactory.kt new file mode 100644 index 0000000000..fe4f730167 --- /dev/null +++ b/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/utils/forge/DatadogSpanContextForgeryFactory.kt @@ -0,0 +1,21 @@ +/* + * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. + * This product includes software developed at Datadog (https://www.datadoghq.com/). + * Copyright 2016-Present Datadog, Inc. + */ +package com.datadog.android.utils.forge + +import com.datadog.android.trace.api.span.DatadogSpanContext +import fr.xgouchet.elmyr.Forge +import fr.xgouchet.elmyr.ForgeryFactory +import org.mockito.kotlin.doReturn +import org.mockito.kotlin.mock + +class DatadogSpanContextForgeryFactory : ForgeryFactory { + override fun getForgery(forge: Forge): DatadogSpanContext = mock { + on { traceId } doReturn forge.getForgery() + on { spanId } doReturn forge.aLong() + on { samplingPriority } doReturn forge.anInt() + on { tags } doReturn forge.aMap { aString() to aString() } + } +} diff --git a/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/utils/forge/DatadogSpanForgeryFactory.kt b/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/utils/forge/DatadogSpanForgeryFactory.kt new file mode 100644 index 0000000000..77213579bc --- /dev/null +++ b/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/utils/forge/DatadogSpanForgeryFactory.kt @@ -0,0 +1,38 @@ +/* + * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. + * This product includes software developed at Datadog (https://www.datadoghq.com/). + * Copyright 2016-Present Datadog, Inc. + */ +package com.datadog.android.utils.forge + +import com.datadog.android.trace.api.span.DatadogSpan +import com.datadog.android.trace.api.span.DatadogSpanContext +import com.datadog.android.trace.internal.DatadogTraceIdAdapter +import com.datadog.trace.api.DDTraceId +import fr.xgouchet.elmyr.Forge +import fr.xgouchet.elmyr.ForgeryFactory +import org.mockito.kotlin.doReturn +import org.mockito.kotlin.mock + +internal class DatadogSpanForgeryFactory : ForgeryFactory { + + override fun getForgery(forge: Forge): DatadogSpan { + val rootSpan = forge.createSpan(context = forge.getForgery()) + return forge.createSpan(context = forge.getForgery(), localSpan = rootSpan) + } + + private fun Forge.createSpan(context: DatadogSpanContext, localSpan: DatadogSpan? = null) = mock { + on { isRootSpan } doReturn aBool() + on { isError } doReturn aBool() + on { resourceName } doReturn aString() + on { serviceName } doReturn aString() + on { operationName } doReturn aString() + on { traceId } doReturn DatadogTraceIdAdapter(getForgery()) + on { parentSpanId } doReturn aLong() + on { samplingPriority } doReturn anInt() + on { durationNano } doReturn aLong() + on { startTimeNanos } doReturn aLong() + on { localRootSpan } doReturn localSpan + on { context() } doReturn context + } +} diff --git a/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/utils/forge/DatadogSpanLinkForgery.kt b/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/utils/forge/DatadogSpanLinkForgery.kt new file mode 100644 index 0000000000..df33d68732 --- /dev/null +++ b/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/utils/forge/DatadogSpanLinkForgery.kt @@ -0,0 +1,21 @@ +/* + * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. + * This product includes software developed at Datadog (https://www.datadoghq.com/). + * Copyright 2016-Present Datadog, Inc. + */ +package com.datadog.android.utils.forge + +import com.datadog.android.trace.api.span.DatadogSpanLink +import com.datadog.android.trace.api.trace.DatadogTraceId +import fr.xgouchet.elmyr.Forge +import fr.xgouchet.elmyr.ForgeryFactory + +internal class DatadogSpanLinkForgery : ForgeryFactory { + override fun getForgery(forge: Forge): DatadogSpanLink = object : DatadogSpanLink { + override val spanId: Long = forge.aLong() + override val sampled: Boolean = forge.aBool() + override val traceStrace: String = forge.aString() + override val attributes: Map = forge.aMap { aString() to aString() } + override val traceId: DatadogTraceId = forge.getForgery() + } +} diff --git a/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/utils/forge/DatadogTraceIdForgeryFactory.kt b/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/utils/forge/DatadogTraceIdForgeryFactory.kt new file mode 100644 index 0000000000..e849019e56 --- /dev/null +++ b/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/utils/forge/DatadogTraceIdForgeryFactory.kt @@ -0,0 +1,17 @@ +/* + * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. + * This product includes software developed at Datadog (https://www.datadoghq.com/). + * Copyright 2016-Present Datadog, Inc. + */ +package com.datadog.android.utils.forge + +import com.datadog.android.trace.api.trace.DatadogTraceId +import com.datadog.android.trace.internal.DatadogTraceIdAdapter +import fr.xgouchet.elmyr.Forge +import fr.xgouchet.elmyr.ForgeryFactory + +class DatadogTraceIdForgeryFactory : ForgeryFactory { + override fun getForgery(forge: Forge): DatadogTraceId { + return DatadogTraceIdAdapter(forge.getForgery()) + } +} diff --git a/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/utils/forge/SpanEventForgeryFactory.kt b/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/utils/forge/SpanEventForgeryFactory.kt index 26f0d884dc..211fd6b8ab 100644 --- a/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/utils/forge/SpanEventForgeryFactory.kt +++ b/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/utils/forge/SpanEventForgeryFactory.kt @@ -47,18 +47,6 @@ internal class SpanEventForgeryFactory : ForgeryFactory { error = errorFlag, duration = duration, start = startTime, - device = SpanEvent.Device( - type = resolveDeviceType(deviceInfo.deviceType), - name = deviceInfo.deviceName, - model = deviceInfo.deviceModel, - brand = deviceInfo.deviceBrand, - architecture = deviceInfo.architecture - ), - os = SpanEvent.Os( - name = deviceInfo.osName, - version = deviceInfo.osVersion, - versionMajor = deviceInfo.osMajorVersion - ), meta = SpanEvent.Meta( version = appPackageVersion, dd = SpanEvent.Dd(source = forge.aNullable { anAlphabeticalString() }), @@ -85,6 +73,18 @@ internal class SpanEventForgeryFactory : ForgeryFactory { connectivity = networkInfo?.connectivity?.toString().orEmpty() ) ), + device = SpanEvent.Device( + type = resolveDeviceType(deviceInfo.deviceType), + name = deviceInfo.deviceName, + model = deviceInfo.deviceModel, + brand = deviceInfo.deviceBrand, + architecture = deviceInfo.architecture + ), + os = SpanEvent.Os( + name = deviceInfo.osName, + version = deviceInfo.osVersion, + versionMajor = deviceInfo.osMajorVersion + ), additionalProperties = meta ), metrics = SpanEvent.Metrics(topLevel = isTopLevel, additionalProperties = metrics) diff --git a/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/utils/forge/SpanForgeryFactory.kt b/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/utils/forge/SpanForgeryFactory.kt deleted file mode 100644 index 5cc7c45db9..0000000000 --- a/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/utils/forge/SpanForgeryFactory.kt +++ /dev/null @@ -1,127 +0,0 @@ -/* - * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. - * This product includes software developed at Datadog (https://www.datadoghq.com/). - * Copyright 2016-Present Datadog, Inc. - */ - -package com.datadog.android.utils.forge - -import com.datadog.legacy.trace.api.Config -import com.datadog.legacy.trace.api.sampling.PrioritySampling -import com.datadog.opentracing.DDSpan -import com.datadog.opentracing.DDTracer -import com.datadog.opentracing.DDTracer.DDSpanBuilder -import fr.xgouchet.elmyr.Forge -import fr.xgouchet.elmyr.ForgeryFactory -import org.mockito.kotlin.mock -import java.security.SecureRandom - -internal class SpanForgeryFactory( - private val forgeSamplingPriority: (forge: Forge, span: DDSpan) -> Unit = ::forgeSamplingPriorityDefault -) : ForgeryFactory { - - override fun getForgery(forge: Forge): DDSpan { - val operationName = forge.anAlphabeticalString() - val resourceName = forge.anAlphabeticalString() - val serviceName = forge.anAlphabeticalString() - val spanType = forge.anAlphabeticalString() - val isWithErrorFlag = forge.aBool() - val tags = forge.exhaustiveTraceTags() - val metrics = forge.exhaustiveMetrics() - val meta = forge.exhaustiveMeta() - val span = generateSpanBuilder( - operationName, - spanType, - resourceName, - serviceName, - isWithErrorFlag, - tags - ).start() as DDSpan - - forgeSamplingPriority(forge, span) - - metrics.forEach { - span.context().setMetric(it.key, it.value) - } - meta.forEach { - span.context().baggageItems.putIfAbsent(it.key, it.value) - } - return span - } - - fun Forge.exhaustiveTraceTags(): Map { - return listOf( - aBool(), - anInt(), - aLong(), - aFloat(), - aDouble(), - anAsciiString() - ).map { anAlphabeticalString() to it } - .toMap() - } - - fun Forge.exhaustiveMetrics(): Map { - return listOf( - aLong(), - anInt(), - aFloat(), - aDouble() - ).map { anAlphabeticalString() to it as Number } - .toMap() - } - - fun Forge.exhaustiveMeta(): Map { - return listOf( - aString() - ).map { anAlphabeticalString() to it } - .toMap() - } - - companion object { - // TODO remove service call once Tracer gets default value - val TEST_TRACER = object : DDTracer(Config.get(), mock(), SecureRandom()) { - } - - fun generateSpanBuilder( - operationName: String, - spanType: String, - resourceName: String, - serviceName: String, - isWithErrorFlag: Boolean, - tags: Map - ): DDTracer.DDSpanBuilder { - val spanBuilder = (TEST_TRACER.buildSpan(operationName) as DDSpanBuilder) - .withSpanType(spanType) - .withResourceName(resourceName) - .withServiceName(serviceName) - - if (isWithErrorFlag) { - spanBuilder.withErrorFlag() - } - - tags.forEach { - val mapValue = it.value - when (mapValue) { - is String -> spanBuilder.withTag(it.key, mapValue) - is Number -> spanBuilder.withTag(it.key, mapValue) - is Boolean -> spanBuilder.withTag(it.key, mapValue) - } - } - - return spanBuilder - } - } -} - -private fun forgeSamplingPriorityDefault(forge: Forge, span: DDSpan) { - if (forge.aBool()) { - span.samplingPriority = forge.anElementFrom( - PrioritySampling.UNSET, - PrioritySampling.SAMPLER_DROP, - PrioritySampling.USER_DROP, - PrioritySampling.SAMPLER_KEEP, - PrioritySampling.USER_KEEP - ) - } -} diff --git a/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/opentracing/scopemanager/ScopeTestHelper.kt b/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/opentracing/scopemanager/ScopeTestHelper.kt deleted file mode 100644 index 52347f9fdb..0000000000 --- a/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/opentracing/scopemanager/ScopeTestHelper.kt +++ /dev/null @@ -1,14 +0,0 @@ -/* - * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. - * This product includes software developed at Datadog (https://www.datadoghq.com/). - * Copyright 2016-Present Datadog, Inc. - */ - -package com.datadog.opentracing.scopemanager - -object ScopeTestHelper { - - fun removeThreadLocalScope() { - ContextualScopeManager.tlsScope.remove() - } -} diff --git a/features/dd-sdk-android-trace/src/testFixtures/kotlin/com/datadog/android/trace/api/DatadogTracingTestExt.kt b/features/dd-sdk-android-trace/src/testFixtures/kotlin/com/datadog/android/trace/api/DatadogTracingTestExt.kt new file mode 100644 index 0000000000..35fad3d8f4 --- /dev/null +++ b/features/dd-sdk-android-trace/src/testFixtures/kotlin/com/datadog/android/trace/api/DatadogTracingTestExt.kt @@ -0,0 +1,95 @@ +/* + * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. + * This product includes software developed at Datadog (https://www.datadoghq.com/). + * Copyright 2016-Present Datadog, Inc. + */ + +package com.datadog.android.trace.api + +import com.datadog.android.api.context.DatadogContext +import com.datadog.android.trace.api.span.DatadogSpan +import com.datadog.android.trace.api.span.DatadogSpanContext +import com.datadog.android.trace.api.trace.DatadogTraceId +import com.datadog.android.trace.api.tracer.DatadogTracer +import com.datadog.android.trace.api.tracer.DatadogTracerBuilder +import com.datadog.android.trace.internal.DatadogPropagationHelper +import com.datadog.android.trace.internal.DatadogSpanAdapter +import com.datadog.android.trace.internal.DatadogSpanContextAdapter +import com.datadog.android.trace.internal.DatadogTraceIdAdapter +import com.datadog.android.trace.internal.DatadogTracerAdapter +import com.datadog.android.trace.internal.DatadogTracingToolkit +import com.datadog.android.trace.internal.domain.event.CoreTracerSpanToSpanEventMapper +import com.datadog.trace.api.DDTraceId +import com.datadog.trace.core.CoreTracer +import com.datadog.trace.core.DDSpan +import com.datadog.trace.core.DDSpanContext +import com.google.gson.JsonElement + +val DatadogTracer.partialFlushMinSpans: Int? + get() = coreTracer?.partialFlushMinSpans + +val DatadogSpanContext.resourceName: String? + get() = ddSpanContext?.resourceName?.toString() + +val DatadogSpanContext.serviceName: String? + get() = ddSpanContext?.serviceName?.toString() + +val DatadogTraceId.Companion.ZERO: DatadogTraceId + get() = DatadogTraceIdAdapter(DDTraceId.ZERO) + +fun DatadogTraceId.Companion.from(traceId: Long): DatadogTraceId { + return DatadogTraceIdAdapter(DDTraceId.from(traceId)) +} + +fun DatadogTraceId.Companion.from(traceId: String): DatadogTraceId { + return DatadogTraceIdAdapter(DDTraceId.from(traceId)) +} + +fun DatadogSpan.resolveMeta(datadogContext: DatadogContext): JsonElement { + val mapper = CoreTracerSpanToSpanEventMapper(false) + val ddSpan = (this as DatadogSpanAdapter).delegate as DDSpan + return mapper.resolveMeta(datadogContext, ddSpan).toJson() +} + +fun DatadogSpan.resolveMetrics(): JsonElement { + val mapper = CoreTracerSpanToSpanEventMapper(false) + val ddSpan = (this as DatadogSpanAdapter).delegate as DDSpan + return mapper.resolveMetrics(ddSpan).toJson() +} + +fun DatadogSpan.forceSamplingDecision() { + (this as DatadogSpanAdapter).delegate.forceSamplingDecision() +} + +fun DatadogTracingToolkit.setTracingAdapterBuilderMock(mock: DatadogTracerBuilder?) { + testBuilderProvider = mock +} + +fun DatadogTracingToolkit.clear() { + setTracingAdapterBuilderMock(null) +} + +fun DatadogTracingToolkit.withMockPropagationHelper( + mockHelper: DatadogPropagationHelper, + block: DatadogTracingToolkit.() -> Unit +) { + val helper = propagationHelper + try { + propagationHelper = mockHelper + block() + } finally { + propagationHelper = helper + } +} + +private val DatadogSpanContext.ddSpanContext: DDSpanContext? + get() { + val spanContextAdapter = this as? DatadogSpanContextAdapter + return spanContextAdapter?.delegate as? DDSpanContext + } + +private val DatadogTracer.coreTracer: CoreTracer? + get() { + val tracerAdapter = this as? DatadogTracerAdapter + return tracerAdapter?.delegate as? CoreTracer + } diff --git a/features/dd-sdk-android-trace/transitiveDependencies b/features/dd-sdk-android-trace/transitiveDependencies index 1a6159fdd1..998404520a 100644 --- a/features/dd-sdk-android-trace/transitiveDependencies +++ b/features/dd-sdk-android-trace/transitiveDependencies @@ -3,9 +3,6 @@ Dependencies List androidx.annotation:annotation-jvm:1.9.1 : 59 Kb com.google.code.gson:gson:2.10.1 : 276 Kb com.google.re2j:re2j:1.7 : 111 Kb -io.opentracing:opentracing-api:0.32.0 : 18 Kb -io.opentracing:opentracing-noop:0.32.0 : 10 Kb -io.opentracing:opentracing-util:0.32.0 : 10 Kb org.jctools:jctools-core:3.3.0 : 328 Kb org.jetbrains.kotlin:kotlin-stdlib:2.0.21 : 1706 Kb org.jetbrains:annotations:13.0 : 17 Kb diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index e65c1aec58..f33f84e63a 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -33,9 +33,6 @@ googleAccompanist = "0.20.2" googleMaterial = "1.3.0" dagger = "2.56.2" -# DD-TRACE-OT -openTracing = "0.32.0" - # JUnit jUnit4 = "4.13.2" jUnitJupiter = "5.9.3" @@ -101,9 +98,7 @@ ktorServer = "3.0.0-rc-1" # Otel jctools = "3.3.0" -openTelemetry = "1.4.0" -# Only for internal benchmark use -openTelemetryBenchmark = "1.40.0" +openTelemetry = "1.40.0" re2j = "1.7" material3Android = "1.1.2" @@ -177,11 +172,6 @@ androidXComposeUiTooling = { module = "androidx.compose.ui:ui-tooling" } androidXComposeMaterial = { module = "androidx.compose.material:material" } material3Android = { group = "androidx.compose.material3", name = "material3", version.ref = "material3Android" } -# DD-TRACE-OT -openTracingApi = { module = "io.opentracing:opentracing-api", version.ref = "openTracing" } -openTracingNoOp = { module = "io.opentracing:opentracing-noop", version.ref = "openTracing" } -openTracingUtil = { module = "io.opentracing:opentracing-util", version.ref = "openTracing" } - # Junit 4 (for buildSrc) jUnit4 = { module = "junit:junit", version.ref = "jUnit4" } @@ -277,8 +267,7 @@ ktorClientMock = { module = "io.ktor:ktor-client-mock", version.ref = "ktor" } # Otel jctools = { module = "org.jctools:jctools-core", version.ref = "jctools" } openTelemetryApi = { module = "io.opentelemetry:opentelemetry-api", version.ref = "openTelemetry" } -openTelemetryApiBenchmark = { module = "io.opentelemetry:opentelemetry-api", version.ref = "openTelemetryBenchmark" } -openTelemetrySdkBenchmark = { module = "io.opentelemetry:opentelemetry-sdk", version.ref = "openTelemetryBenchmark" } +openTelemetrySdk = { module = "io.opentelemetry:opentelemetry-sdk", version.ref = "openTelemetry" } re2j = { module = "com.google.re2j:re2j", version.ref = "re2j" } [bundles] @@ -319,12 +308,6 @@ androidXCompose = [ "androidXComposeMaterial", ] -openTracing = [ - "openTracingApi", - "openTracingNoOp", - "openTracingUtil" -] - integrationTests = [ "androidXTestCore", "androidXTestRunner", diff --git a/instrumented/integration/build.gradle.kts b/instrumented/integration/build.gradle.kts index 0955dc6226..f535ce5bd3 100644 --- a/instrumented/integration/build.gradle.kts +++ b/instrumented/integration/build.gradle.kts @@ -115,7 +115,8 @@ dependencies { androidTestImplementation(libs.assertJ) androidTestImplementation(libs.bundles.integrationTests) androidTestImplementation(libs.okHttpMock) - + androidTestImplementation(project(":features:dd-sdk-android-trace-internal")) + androidTestImplementation(testFixtures(project(":features:dd-sdk-android-trace"))) if (project.hasProperty(com.datadog.gradle.Properties.USE_API21_JAVA_BACKPORT)) { // this is needed to make AssertJ working on APIs <24 androidTestImplementation(project(":tools:javabackport")) diff --git a/instrumented/integration/proguard-rules.pro b/instrumented/integration/proguard-rules.pro index cfd2255066..2eb8d7727f 100644 --- a/instrumented/integration/proguard-rules.pro +++ b/instrumented/integration/proguard-rules.pro @@ -11,9 +11,10 @@ *; } -# Required because we need access to GlobalTracer isRegistered property to reset it through reflection --keepnames class io.opentracing.util.GlobalTracer { - private boolean isRegistered; +# Required because we need access to GlobalDatadogTracer getOrNull method to reset it through reflection +-keepnames class com.datadog.android.trace.GlobalDatadogTracer { + public com.datadog.android.trace.api.tracer.DatadogTracer getOrNull(); + public static com.datadog.android.trace.GlobalDatadogTracer INSTANCE; } # Required because we need access to GlobalRumMonitor reset method to reset it through reflection diff --git a/instrumented/integration/src/androidTest/kotlin/com/datadog/android/sdk/integration/cross/CrossFeatureTest.kt b/instrumented/integration/src/androidTest/kotlin/com/datadog/android/sdk/integration/cross/CrossFeatureTest.kt index f4f23a3630..a2c3bf1006 100644 --- a/instrumented/integration/src/androidTest/kotlin/com/datadog/android/sdk/integration/cross/CrossFeatureTest.kt +++ b/instrumented/integration/src/androidTest/kotlin/com/datadog/android/sdk/integration/cross/CrossFeatureTest.kt @@ -30,11 +30,12 @@ import com.datadog.android.sdk.utils.isLogsUrl import com.datadog.android.sdk.utils.isRumUrl import com.datadog.android.sdk.utils.isTracesUrl import com.datadog.android.sdk.utils.overrideProcessImportance -import com.datadog.android.trace.AndroidTracer +import com.datadog.android.trace.DatadogTracing +import com.datadog.android.trace.GlobalDatadogTracer import com.datadog.android.trace.Trace import com.datadog.android.trace.TraceConfiguration +import com.datadog.android.trace.api.tracer.DatadogTracer import com.datadog.android.trace.model.SpanEvent -import com.datadog.android.trace.withinSpan import com.datadog.tools.unit.ConditionWatcher import com.google.gson.JsonNull import com.google.gson.JsonObject @@ -59,7 +60,7 @@ class CrossFeatureTest { private val logEvents = mutableListOf() private val spanEvents = mutableListOf() private lateinit var logger: Logger - private lateinit var openTracingTracer: AndroidTracer + private lateinit var openTracingTracer: DatadogTracer private val forge = Forge() @Before @@ -105,13 +106,11 @@ class CrossFeatureTest { .useCustomEndpoint(mockWebServer.url("/traces").toString()) .build() Trace.enable(traceConfiguration) - openTracingTracer = AndroidTracer.Builder() + openTracingTracer = DatadogTracing.newTracerBuilder() + .withPartialFlushMinSpans(1) .setBundleWithRumEnabled(true) .build() - // don't register GlobalTracer, because call to unregister it - // GlobalTracer::class.java.setStaticValue("isRegistered", false) will fail on API 21, - // it is impossible to change static final field there - // TODO RUM-0000 missing Otel tracer + GlobalDatadogTracer.registerIfAbsent(openTracingTracer) val logsConfiguration = LogsConfiguration.Builder() .useCustomEndpoint(mockWebServer.url("/logs").toString()) .build() @@ -131,6 +130,7 @@ class CrossFeatureTest { .getInstrumentation() .targetContext .cacheDir.deleteRecursively() + GlobalDatadogTracer.clear() } @Test @@ -334,7 +334,7 @@ class CrossFeatureTest { }.doWait(TimeUnit.SECONDS.toMillis(30)) } - private fun AndroidTracer.withinSpan(operationName: String, block: () -> Unit) { + private fun DatadogTracer.withinSpan(operationName: String, block: () -> Unit) { val span = buildSpan(operationName).start() activateSpan(span).use { block() diff --git a/instrumented/integration/src/androidTest/kotlin/com/datadog/android/sdk/integration/security/EncryptionTest.kt b/instrumented/integration/src/androidTest/kotlin/com/datadog/android/sdk/integration/security/EncryptionTest.kt index ed71418dbe..9c9fb233b6 100644 --- a/instrumented/integration/src/androidTest/kotlin/com/datadog/android/sdk/integration/security/EncryptionTest.kt +++ b/instrumented/integration/src/androidTest/kotlin/com/datadog/android/sdk/integration/security/EncryptionTest.kt @@ -25,13 +25,12 @@ import com.datadog.android.rum.RumResourceMethod import com.datadog.android.security.Encryption import com.datadog.android.sessionreplay.SessionReplay import com.datadog.android.sessionreplay.SessionReplayConfiguration -import com.datadog.android.trace.AndroidTracer +import com.datadog.android.trace.DatadogTracing +import com.datadog.android.trace.GlobalDatadogTracer import com.datadog.android.trace.Trace import com.datadog.android.trace.TraceConfiguration -import com.datadog.tools.unit.setStaticValue +import com.datadog.android.trace.api.tracer.DatadogTracer import fr.xgouchet.elmyr.junit4.ForgeRule -import io.opentracing.Tracer -import io.opentracing.util.GlobalTracer import org.assertj.core.api.Assertions.assertThat import org.junit.After import org.junit.Before @@ -80,8 +79,8 @@ internal class EncryptionTest { ) featureActivations.shuffled(Random(forge.seed)).forEach { it() } - val tracer = AndroidTracer.Builder(sdkCore).setBundleWithRumEnabled(true).build() - GlobalTracer.registerIfAbsent(tracer) + val tracer = DatadogTracing.newTracerBuilder(sdkCore).setBundleWithRumEnabled(true).build() + GlobalDatadogTracer.registerIfAbsent(tracer) val logger = Logger.Builder(sdkCore) .setBundleWithRumEnabled(true) @@ -159,7 +158,7 @@ internal class EncryptionTest { .build() } - private fun sendEventsForAllFeatures(rumMonitor: RumMonitor, logger: Logger, tracer: Tracer) { + private fun sendEventsForAllFeatures(rumMonitor: RumMonitor, logger: Logger, tracer: DatadogTracer) { val viewName = "rumView-${forge.aString()}" rumMonitor.startView(viewName, viewName) @@ -202,7 +201,7 @@ internal class EncryptionTest { private fun stopSdk() { Datadog.stopInstance() - GlobalTracer::class.java.setStaticValue("isRegistered", false) + GlobalDatadogTracer.clear() } private fun flushAndShutdownExecutors() { diff --git a/instrumented/integration/src/androidTest/kotlin/com/datadog/android/sdk/integration/trace/ConsentGrantedTracesTest.kt b/instrumented/integration/src/androidTest/kotlin/com/datadog/android/sdk/integration/trace/ConsentGrantedTracesTest.kt index 885e408f55..4592fa2070 100644 --- a/instrumented/integration/src/androidTest/kotlin/com/datadog/android/sdk/integration/trace/ConsentGrantedTracesTest.kt +++ b/instrumented/integration/src/androidTest/kotlin/com/datadog/android/sdk/integration/trace/ConsentGrantedTracesTest.kt @@ -37,7 +37,8 @@ internal class ConsentGrantedTracesTest : TracesTest() { ConditionWatcher { // Check sent requests val handledRequests = mockServerRule.getRequests() - verifyExpectedSpans(handledRequests, mockServerRule.activity.getSentSpans()) + val datadogContext = mockServerRule.activity.getDatadogContext()!! + verifyExpectedSpans(datadogContext, handledRequests, mockServerRule.activity.getSentSpans()) verifyExpectedLogs(handledRequests, mockServerRule.activity.getSentLogs()) true }.doWait(timeoutMs = INITIAL_WAIT_MS) diff --git a/instrumented/integration/src/androidTest/kotlin/com/datadog/android/sdk/integration/trace/ConsentPendingGrantedTracesTest.kt b/instrumented/integration/src/androidTest/kotlin/com/datadog/android/sdk/integration/trace/ConsentPendingGrantedTracesTest.kt index 317c0eb4e7..139355282a 100644 --- a/instrumented/integration/src/androidTest/kotlin/com/datadog/android/sdk/integration/trace/ConsentPendingGrantedTracesTest.kt +++ b/instrumented/integration/src/androidTest/kotlin/com/datadog/android/sdk/integration/trace/ConsentPendingGrantedTracesTest.kt @@ -41,7 +41,8 @@ internal class ConsentPendingGrantedTracesTest : TracesTest() { ConditionWatcher { // Check sent requests val handledRequests = mockServerRule.getRequests() - verifyExpectedSpans(handledRequests, mockServerRule.activity.getSentSpans()) + val context = mockServerRule.activity.getDatadogContext()!! + verifyExpectedSpans(context, handledRequests, mockServerRule.activity.getSentSpans()) verifyExpectedLogs(handledRequests, mockServerRule.activity.getSentLogs()) true }.doWait(timeoutMs = INITIAL_WAIT_MS) diff --git a/instrumented/integration/src/androidTest/kotlin/com/datadog/android/sdk/integration/trace/SpanExt.kt b/instrumented/integration/src/androidTest/kotlin/com/datadog/android/sdk/integration/trace/SpanExt.kt index 6c88b46880..7db8f1a812 100644 --- a/instrumented/integration/src/androidTest/kotlin/com/datadog/android/sdk/integration/trace/SpanExt.kt +++ b/instrumented/integration/src/androidTest/kotlin/com/datadog/android/sdk/integration/trace/SpanExt.kt @@ -6,30 +6,26 @@ package com.datadog.android.sdk.integration.trace -import com.datadog.android.internal.utils.toHexString -import com.datadog.opentracing.DDSpan -import io.opentracing.Span -import io.opentracing.SpanContext +import com.datadog.android.trace.api.span.DatadogSpan +import com.datadog.android.trace.internal.DatadogTracingToolkit /** * Returns the span's least significant trace id in hex format (the last 64 bits from the 128 bits trace id) */ -fun Span.leastSignificant64BitsTraceId(): String { - return (this as? DDSpan)?.traceId?.toString(16)?.padStart(32, '0')?.takeLast(16) ?: "" +fun DatadogSpan.leastSignificant64BitsTraceId(): String { + return traceId.toHexString().padStart(32, '0').takeLast(16) } /** * Returns the span's most significant trace id in hex format (the first 64 bits from the 128 bits trace id) */ -fun Span.mostSignificant64BitsTraceId(): String { - return (this as? DDSpan)?.traceId?.toString(16)?.padStart(32, '0')?.take(16) ?: "" +fun DatadogSpan.mostSignificant64BitsTraceId(): String { + return traceId.toHexString().padStart(32, '0').take(16) } /** * Returns the span's spanId in hex format. - * The [SpanContext.toSpanId] method returns a string in decimal format, - * which doesn't match what we send in our events */ -fun Span.spanIdAsHexString(): String { - return context().toSpanId().toLong().toHexString() +fun DatadogSpan.spanIdAsHexString(): String { + return DatadogTracingToolkit.spanIdConverter.toHexStringPadded(context().spanId) } diff --git a/instrumented/integration/src/androidTest/kotlin/com/datadog/android/sdk/integration/trace/TracesTest.kt b/instrumented/integration/src/androidTest/kotlin/com/datadog/android/sdk/integration/trace/TracesTest.kt index f1208457b3..273aee5b87 100644 --- a/instrumented/integration/src/androidTest/kotlin/com/datadog/android/sdk/integration/trace/TracesTest.kt +++ b/instrumented/integration/src/androidTest/kotlin/com/datadog/android/sdk/integration/trace/TracesTest.kt @@ -8,7 +8,7 @@ package com.datadog.android.sdk.integration.trace import android.util.Log import androidx.test.platform.app.InstrumentationRegistry -import com.datadog.android.internal.utils.toHexString +import com.datadog.android.api.context.DatadogContext import com.datadog.android.sdk.assertj.HeadersAssert import com.datadog.android.sdk.assertj.HeadersAssert.Companion.assertThat import com.datadog.android.sdk.integration.RuntimeConfig @@ -16,7 +16,10 @@ import com.datadog.android.sdk.rules.HandledRequest import com.datadog.android.sdk.rules.MockServerActivityTestRule import com.datadog.android.sdk.utils.isLogsUrl import com.datadog.android.sdk.utils.isTracesUrl -import com.datadog.opentracing.DDSpan +import com.datadog.android.trace.api.resolveMeta +import com.datadog.android.trace.api.resolveMetrics +import com.datadog.android.trace.api.span.DatadogSpan +import com.datadog.android.trace.internal.DatadogTracingToolkit import com.datadog.tools.unit.assertj.JsonObjectAssert.Companion.assertThat import com.google.gson.JsonElement import com.google.gson.JsonObject @@ -47,8 +50,9 @@ internal abstract class TracesTest { } protected fun verifyExpectedSpans( + context: DatadogContext, handledRequests: List, - expectedSpans: List + expectedSpans: List ) { val sentSpansObjects = mutableListOf() handledRequests @@ -57,23 +61,31 @@ internal abstract class TracesTest { assertThat(request.headers) .isNotNull .hasHeader(HeadersAssert.HEADER_CT, RuntimeConfig.CONTENT_TYPE_TEXT) - val sentSpans = tracesPayloadToJsonArray(request.textBody.orEmpty()) - sentSpans.forEach { - Log.i("EndToEndTraceTest", "adding span $it") - sentSpansObjects.add(it.asJsonObject) - } + + tracesPayloadToJsonArray(request.textBody.orEmpty()) + .forEach { + Log.i("EndToEndTraceTest", "adding span $it") + sentSpansObjects.add(it.asJsonObject) + } } - assertThat(expectedSpans.size).isEqualTo(sentSpansObjects.size) + assertThat(expectedSpans.size) + .withFailMessage { + "Expected spans doesn't equal to sent spans. Expected=$expectedSpans, sent=$sentSpansObjects" + } + .isEqualTo(sentSpansObjects.size) + expectedSpans.forEach { span -> - val json = sentSpansObjects.first { - val leastSignificantTraceId = it.get(TRACE_ID_KEY).asString - val mostSignificantTraceId = it.getAsJsonObject("meta") + val json = sentSpansObjects.first { spanJson -> + val leastSignificantTraceId = spanJson.get(TRACE_ID_KEY).asString + val mostSignificantTraceId = spanJson + .getAsJsonObject("meta") .getAsJsonPrimitive(MOST_SIGNIFICANT_64_BITS_TRACE_ID_KEY).asString + leastSignificantTraceId == span.leastSignificant64BitsTraceId() && mostSignificantTraceId == span.mostSignificant64BitsTraceId() && - it.get(SPAN_ID_KEY).asString == span.spanIdAsHexString() + spanJson.get(SPAN_ID_KEY).asString == span.spanIdAsHexString() } - assertMatches(json, span) + assertMatches(json, span, context) } } @@ -106,22 +118,30 @@ internal abstract class TracesTest { } } - private fun assertMatches(jsonObject: JsonObject, span: DDSpan) { + private fun assertMatches(jsonObject: JsonObject, span: DatadogSpan, context: DatadogContext) { + val meta = span.resolveMeta(context) + val metrics = span.resolveMetrics() assertThat(jsonObject) .hasField(SERVICE_NAME_KEY, span.serviceName) .hasField(TRACE_ID_KEY, span.leastSignificant64BitsTraceId()) .hasField(SPAN_ID_KEY, span.spanIdAsHexString()) - .hasField(PARENT_ID_KEY, span.parentId.toLong().toHexString()) + .hasField( + PARENT_ID_KEY, + span.parentSpanId?.let { + DatadogTracingToolkit.spanIdConverter.toHexStringPadded(it) + } + ?: throw AssertionError("No parentId provided from $span") + ) .hasField( START_TIMESTAMP_KEY, - span.startTime, + span.startTimeNanos, Offset.offset(TimeUnit.MINUTES.toNanos(1)) ) .hasField(DURATION_KEY, span.durationNano) - .hasField(RESOURCE_KEY, span.resourceName) + .hasField(RESOURCE_KEY, span.resourceName.orEmpty()) .hasField(OPERATION_NAME_KEY, span.operationName) - .hasField(META_KEY, span.meta) - .hasField(METRICS_KEY, span.metrics) + .hasField(META_KEY, meta) + .hasField(METRICS_KEY, metrics) val metaObject = jsonObject.getAsJsonObject(META_KEY) assertThat(metaObject) .hasField(MOST_SIGNIFICANT_64_BITS_TRACE_ID_KEY, span.mostSignificant64BitsTraceId()) diff --git a/instrumented/integration/src/main/kotlin/com/datadog/android/sdk/integration/RuntimeConfig.kt b/instrumented/integration/src/main/kotlin/com/datadog/android/sdk/integration/RuntimeConfig.kt index 80d44b96ee..2b7338261d 100644 --- a/instrumented/integration/src/main/kotlin/com/datadog/android/sdk/integration/RuntimeConfig.kt +++ b/instrumented/integration/src/main/kotlin/com/datadog/android/sdk/integration/RuntimeConfig.kt @@ -15,8 +15,9 @@ import com.datadog.android.log.Logger import com.datadog.android.log.LogsConfiguration import com.datadog.android.rum.RumConfiguration import com.datadog.android.sessionreplay.SessionReplayConfiguration -import com.datadog.android.trace.AndroidTracer +import com.datadog.android.trace.DatadogTracing import com.datadog.android.trace.TraceConfiguration +import com.datadog.android.trace.api.tracer.DatadogTracer import java.util.UUID internal object RuntimeConfig { @@ -28,6 +29,8 @@ internal object RuntimeConfig { const val CONTENT_TYPE_JSON = "application/json" const val CONTENT_TYPE_TEXT = "text/plain;charset=UTF-8" private const val LOCALHOST = "http://localhost" + private const val SAMPLE_ALL = 100.0 + private const val FLUSH_ON_EACH_SPAN_THRESHOLD = 1 var logsEndpointUrl: String = "$LOCALHOST/logs" var tracesEndpointUrl: String = "$LOCALHOST/traces" @@ -53,9 +56,10 @@ internal object RuntimeConfig { return logger } - fun tracer(sdkCore: SdkCore): AndroidTracer { - return AndroidTracer.Builder(sdkCore).build() - } + fun tracer(sdkCore: SdkCore): DatadogTracer = DatadogTracing.newTracerBuilder(sdkCore) + .withSampleRate(SAMPLE_ALL) + .withPartialFlushMinSpans(FLUSH_ON_EACH_SPAN_THRESHOLD) + .build() fun configBuilder(): Configuration.Builder { return Configuration.Builder( diff --git a/instrumented/integration/src/main/kotlin/com/datadog/android/sdk/integration/trace/ActivityLifecycleTrace.kt b/instrumented/integration/src/main/kotlin/com/datadog/android/sdk/integration/trace/ActivityLifecycleTrace.kt index 3ffe23b86e..8a42460b3d 100644 --- a/instrumented/integration/src/main/kotlin/com/datadog/android/sdk/integration/trace/ActivityLifecycleTrace.kt +++ b/instrumented/integration/src/main/kotlin/com/datadog/android/sdk/integration/trace/ActivityLifecycleTrace.kt @@ -10,16 +10,17 @@ import android.os.Bundle import android.util.Log import androidx.appcompat.app.AppCompatActivity import com.datadog.android.Datadog +import com.datadog.android.api.context.DatadogContext +import com.datadog.android.core.InternalSdkCore import com.datadog.android.log.Logs import com.datadog.android.sdk.integration.R import com.datadog.android.sdk.integration.RuntimeConfig import com.datadog.android.sdk.utils.getForgeSeed import com.datadog.android.sdk.utils.getTrackingConsent -import com.datadog.android.trace.AndroidTracer import com.datadog.android.trace.Trace -import com.datadog.opentracing.DDSpan +import com.datadog.android.trace.api.span.DatadogSpan +import com.datadog.android.trace.api.tracer.DatadogTracer import fr.xgouchet.elmyr.Forge -import io.opentracing.Scope import java.util.LinkedList import java.util.Random @@ -27,11 +28,11 @@ internal class ActivityLifecycleTrace : AppCompatActivity() { private val forge by lazy { Forge().apply { seed = intent.getForgeSeed() } } - lateinit var tracer: AndroidTracer - private val sentSpans = LinkedList() + private lateinit var tracer: DatadogTracer + private val sentSpans = LinkedList() private val sentLogs = LinkedList>() - lateinit var activityStartScope: Scope - lateinit var activityResumeScope: Scope + private lateinit var activityStartSpan: DatadogSpan + private lateinit var activityResumeSpan: DatadogSpan // region Activity @@ -43,9 +44,11 @@ internal class ActivityLifecycleTrace : AppCompatActivity() { val trackingConsent = intent.getTrackingConsent() Datadog.setVerbosity(Log.VERBOSE) - val sdkCore = Datadog.initialize(this, config, trackingConsent) - checkNotNull(sdkCore) - mutableListOf( + val sdkCore = checkNotNull( + Datadog.initialize(this, config, trackingConsent) + ) + + listOf( { Logs.enable(RuntimeConfig.logsConfigBuilder().build(), sdkCore) }, { Trace.enable(RuntimeConfig.tracesConfigBuilder().build(), sdkCore) } ) @@ -58,29 +61,29 @@ internal class ActivityLifecycleTrace : AppCompatActivity() { override fun onStart() { super.onStart() - activityStartScope = buildSpan(forge.anAlphabeticalString()) + activityStartSpan = buildSpan(forge.anAlphabeticalString()) } override fun onResume() { super.onResume() - activityResumeScope = buildSpan(forge.anAlphabeticalString()) + activityResumeSpan = buildSpan(forge.anAlphabeticalString()) } override fun onPause() { super.onPause() - activityResumeScope.close() + activityResumeSpan.finish() } override fun onStop() { super.onStop() - activityStartScope.close() + activityStartSpan.finish() } // endregion // region Tests - fun getSentSpans(): LinkedList { + fun getSentSpans(): LinkedList { return sentSpans } @@ -88,17 +91,22 @@ internal class ActivityLifecycleTrace : AppCompatActivity() { return sentLogs } + fun getDatadogContext(): DatadogContext? { + return (Datadog.getInstance() as InternalSdkCore).getDatadogContext() + } + // endregion // region Internal - private fun buildSpan(title: String): Scope { - val scope = tracer.buildSpan(title).startActive(true) - val ddSpan = tracer.activeSpan() as DDSpan - ddSpan.log(title) + private fun buildSpan(title: String): DatadogSpan { + val span = tracer.buildSpan(title).start() + checkNotNull(tracer.activateSpan(span)) { "Span activation failed" } + val ddSpan = tracer.activeSpan() as DatadogSpan + ddSpan.logMessage(title) sentLogs.add(Log.VERBOSE to title) sentSpans.add(ddSpan) - return scope + return ddSpan } // endregion diff --git a/integrations/dd-sdk-android-glide/transitiveDependencies b/integrations/dd-sdk-android-glide/transitiveDependencies index e91cb76d10..35c5e4cbda 100644 --- a/integrations/dd-sdk-android-glide/transitiveDependencies +++ b/integrations/dd-sdk-android-glide/transitiveDependencies @@ -42,7 +42,6 @@ com.github.bumptech.glide:glide:4.11.0 : 614 Kb com.github.bumptech.glide:okhttp3-integration:4.11.0 : 8 Kb com.squareup.okhttp3:okhttp:4.12.0 : 771 Kb com.squareup.okio:okio-jvm:3.6.0 : 351 Kb -io.opentracing:opentracing-api:0.32.0 : 18 Kb org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.9.10 : 959 b org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.9.10 : 965 b org.jetbrains.kotlin:kotlin-stdlib:2.0.21 : 1706 Kb diff --git a/integrations/dd-sdk-android-okhttp-otel/build.gradle.kts b/integrations/dd-sdk-android-okhttp-otel/build.gradle.kts index 2fc7ad3e1a..1e040271fa 100644 --- a/integrations/dd-sdk-android-okhttp-otel/build.gradle.kts +++ b/integrations/dd-sdk-android-okhttp-otel/build.gradle.kts @@ -42,11 +42,16 @@ android { } dependencies { - implementation(project(":integrations:dd-sdk-android-okhttp")) - implementation(project(":features:dd-sdk-android-trace-otel")) implementation(libs.okHttp) implementation(libs.kotlin) + implementation(project(":features:dd-sdk-android-trace")) + implementation(project(":integrations:dd-sdk-android-okhttp")) + implementation(project(":features:dd-sdk-android-trace-otel")) + + testImplementation(libs.okHttpMock) + testImplementation(libs.bundles.jUnit5) + testImplementation(libs.bundles.testTools) testImplementation(project(":tools:unit")) { attributes { attribute( @@ -55,9 +60,6 @@ dependencies { ) } } - testImplementation(libs.bundles.jUnit5) - testImplementation(libs.bundles.testTools) - testImplementation(libs.okHttpMock) } kotlinConfig(jvmBytecodeTarget = JvmTarget.JVM_11) diff --git a/integrations/dd-sdk-android-okhttp-otel/src/main/kotlin/com/datadog/android/okhttp/otel/OkHttpExt.kt b/integrations/dd-sdk-android-okhttp-otel/src/main/kotlin/com/datadog/android/okhttp/otel/OkHttpExt.kt index 8eb2d547b1..894504a23f 100644 --- a/integrations/dd-sdk-android-okhttp-otel/src/main/kotlin/com/datadog/android/okhttp/otel/OkHttpExt.kt +++ b/integrations/dd-sdk-android-okhttp-otel/src/main/kotlin/com/datadog/android/okhttp/otel/OkHttpExt.kt @@ -7,9 +7,9 @@ package com.datadog.android.okhttp.otel import com.datadog.android.okhttp.TraceContext -import com.datadog.legacy.trace.api.sampling.PrioritySampling +import com.datadog.android.trace.api.DatadogTracingConstants +import com.datadog.android.trace.internal.DatadogTracingToolkit import com.datadog.opentelemetry.trace.OtelSpan -import com.datadog.trace.core.DDSpanContext import io.opentelemetry.api.trace.Span import okhttp3.Request @@ -18,24 +18,27 @@ import okhttp3.Request * @param span the parent span to add to the request. * @return the modified Request.Builder instance */ -fun Request.Builder.addParentSpan(span: Span): Request.Builder { +fun Request.Builder.addParentSpan(span: Span): Request.Builder = apply { // very fragile and assumes that Datadog Tracer is used // we need to trigger sampling decision at this point, because we are doing context propagation out of OpenTelemetry - if (span is OtelSpan) { - val agentSpanContext = span.agentSpanContext - if (agentSpanContext is DDSpanContext) { - agentSpanContext.trace.setSamplingPriorityIfNecessary() - } - @Suppress("UnsafeThirdPartyFunctionCall") // the context will always be a TraceContext - tag( - TraceContext::class.java, - TraceContext(span.spanContext.traceId, span.spanContext.spanId, agentSpanContext.samplingPriority) + val tracingContext = if (span is OtelSpan) { + DatadogTracingToolkit.setTracingSamplingPriorityIfNecessary(span.datadogSpanContext) + TraceContext( + span.spanContext.traceId, + span.spanContext.spanId, + span.datadogSpanContext.samplingPriority ) } else { - val context = span.spanContext - val prioritySampling = if (context.isSampled) PrioritySampling.USER_KEEP else PrioritySampling.UNSET - @Suppress("UnsafeThirdPartyFunctionCall") // the context will always be a TraceContext - tag(TraceContext::class.java, TraceContext(context.traceId, context.spanId, prioritySampling)) + TraceContext( + span.spanContext.traceId, + span.spanContext.spanId, + if (span.spanContext.isSampled) { + DatadogTracingConstants.PrioritySampling.USER_KEEP + } else { + DatadogTracingConstants.PrioritySampling.UNSET + } + ) } - return this + @Suppress("UnsafeThirdPartyFunctionCall") // the context will always be a TraceContext + tag(TraceContext::class.java, tracingContext) } diff --git a/integrations/dd-sdk-android-okhttp-otel/src/test/kotlin/com/datadog/android/okhttp/otel/OkHttpExtTest.kt b/integrations/dd-sdk-android-okhttp-otel/src/test/kotlin/com/datadog/android/okhttp/otel/OkHttpExtTest.kt index 082cea3a9e..b686d4d9b0 100644 --- a/integrations/dd-sdk-android-okhttp-otel/src/test/kotlin/com/datadog/android/okhttp/otel/OkHttpExtTest.kt +++ b/integrations/dd-sdk-android-okhttp-otel/src/test/kotlin/com/datadog/android/okhttp/otel/OkHttpExtTest.kt @@ -7,7 +7,7 @@ package com.datadog.android.okhttp.otel import com.datadog.android.okhttp.TraceContext -import com.datadog.legacy.trace.api.sampling.PrioritySampling +import com.datadog.android.trace.api.DatadogTracingConstants import com.datadog.tools.unit.forge.BaseConfigurator import fr.xgouchet.elmyr.annotation.BoolForgery import fr.xgouchet.elmyr.annotation.StringForgery @@ -60,8 +60,11 @@ internal class OkHttpExtTest { on { traceId }.thenReturn(fakeTraceId) on { isSampled }.thenReturn(fakeIsSampled) } - expectedPrioritySampling = - if (fakeIsSampled) PrioritySampling.USER_KEEP else PrioritySampling.UNSET + expectedPrioritySampling = if (fakeIsSampled) { + DatadogTracingConstants.PrioritySampling.USER_KEEP + } else { + DatadogTracingConstants.PrioritySampling.UNSET + } whenever(mockSpan.spanContext).thenReturn(spanContext) } @@ -71,8 +74,7 @@ internal class OkHttpExtTest { val request = Request.Builder().url(fakeUrl).addParentSpan(mockSpan).build() // Then - val taggedContext = request.tag(TraceContext::class.java) - checkNotNull(taggedContext) + val taggedContext = checkNotNull(request.tag(TraceContext::class.java)) assertThat(taggedContext.spanId).isEqualTo(fakeSpanId) assertThat(taggedContext.traceId).isEqualTo(fakeTraceId) assertThat(taggedContext.samplingPriority).isEqualTo(expectedPrioritySampling) diff --git a/integrations/dd-sdk-android-okhttp-otel/transitiveDependencies b/integrations/dd-sdk-android-okhttp-otel/transitiveDependencies index 03a89d67a8..127e86c4fa 100644 --- a/integrations/dd-sdk-android-okhttp-otel/transitiveDependencies +++ b/integrations/dd-sdk-android-okhttp-otel/transitiveDependencies @@ -2,11 +2,8 @@ Dependencies List com.squareup.okhttp3:okhttp:4.12.0 : 771 Kb com.squareup.okio:okio-jvm:3.6.0 : 351 Kb -io.opentelemetry:opentelemetry-api:1.4.0 : 78 Kb -io.opentelemetry:opentelemetry-context:1.4.0 : 42 Kb -io.opentracing:opentracing-api:0.32.0 : 18 Kb -io.opentracing:opentracing-noop:0.32.0 : 10 Kb -io.opentracing:opentracing-util:0.32.0 : 10 Kb +io.opentelemetry:opentelemetry-api:1.40.0 : 138 Kb +io.opentelemetry:opentelemetry-context:1.40.0 : 46 Kb org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.9.10 : 959 b org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.9.10 : 965 b org.jetbrains.kotlin:kotlin-stdlib:2.0.21 : 1706 Kb diff --git a/integrations/dd-sdk-android-okhttp/api/apiSurface b/integrations/dd-sdk-android-okhttp/api/apiSurface index 656a6f559f..81ca94efc9 100644 --- a/integrations/dd-sdk-android-okhttp/api/apiSurface +++ b/integrations/dd-sdk-android-okhttp/api/apiSurface @@ -17,7 +17,7 @@ class com.datadog.android.okhttp.DatadogEventListener : okhttp3.EventListener override fun create(okhttp3.Call): okhttp3.EventListener open class com.datadog.android.okhttp.DatadogInterceptor : com.datadog.android.okhttp.trace.TracingInterceptor override fun intercept(okhttp3.Interceptor.Chain): okhttp3.Response - override fun onRequestIntercepted(com.datadog.android.api.feature.FeatureSdkCore, okhttp3.Request, io.opentracing.Span?, okhttp3.Response?, Throwable?) + override fun onRequestIntercepted(com.datadog.android.api.feature.FeatureSdkCore, okhttp3.Request, com.datadog.android.trace.api.span.DatadogSpan?, okhttp3.Response?, Throwable?) override fun canSendSpan(): Boolean override fun onSdkInstanceReady(com.datadog.android.core.InternalSdkCore) class Builder : BaseBuilder @@ -30,16 +30,16 @@ data class com.datadog.android.okhttp.TraceContext enum com.datadog.android.okhttp.TraceContextInjection - ALL - SAMPLED -open class com.datadog.android.okhttp.trace.DeterministicTraceSampler : com.datadog.android.core.sampling.DeterministicSampler +open class com.datadog.android.okhttp.trace.DeterministicTraceSampler : com.datadog.android.core.sampling.DeterministicSampler constructor(() -> Float) constructor(Float) constructor(Double) -fun okhttp3.Request.Builder.parentSpan(io.opentracing.Span): okhttp3.Request.Builder +fun okhttp3.Request.Builder.parentSpan(com.datadog.android.trace.api.span.DatadogSpan): okhttp3.Request.Builder interface com.datadog.android.okhttp.trace.TracedRequestListener - fun onRequestIntercepted(okhttp3.Request, io.opentracing.Span, okhttp3.Response?, Throwable?) + fun onRequestIntercepted(okhttp3.Request, com.datadog.android.trace.api.span.DatadogSpan, okhttp3.Response?, Throwable?) open class com.datadog.android.okhttp.trace.TracingInterceptor : okhttp3.Interceptor override fun intercept(okhttp3.Interceptor.Chain): okhttp3.Response - protected open fun onRequestIntercepted(com.datadog.android.api.feature.FeatureSdkCore, okhttp3.Request, io.opentracing.Span?, okhttp3.Response?, Throwable?) + protected open fun onRequestIntercepted(com.datadog.android.api.feature.FeatureSdkCore, okhttp3.Request, com.datadog.android.trace.api.span.DatadogSpan?, okhttp3.Response?, Throwable?) class Builder : BaseBuilder constructor(Map>) constructor(List) @@ -50,7 +50,7 @@ open class com.datadog.android.okhttp.trace.TracingInterceptor : okhttp3.Interce fun setSdkInstanceName(String): R fun setTracedRequestListener(TracedRequestListener): R fun setTraceSampleRate(Float): R - fun setTraceSampler(com.datadog.android.core.sampling.Sampler): R + fun setTraceSampler(com.datadog.android.core.sampling.Sampler): R fun setTraceContextInjection(com.datadog.android.okhttp.TraceContextInjection): R fun set404ResourcesRedacted(Boolean): R abstract fun build(): T diff --git a/integrations/dd-sdk-android-okhttp/api/dd-sdk-android-okhttp.api b/integrations/dd-sdk-android-okhttp/api/dd-sdk-android-okhttp.api index 37984187bd..47c143475e 100644 --- a/integrations/dd-sdk-android-okhttp/api/dd-sdk-android-okhttp.api +++ b/integrations/dd-sdk-android-okhttp/api/dd-sdk-android-okhttp.api @@ -23,7 +23,7 @@ public final class com/datadog/android/okhttp/DatadogEventListener$Factory : okh public class com/datadog/android/okhttp/DatadogInterceptor : com/datadog/android/okhttp/trace/TracingInterceptor { public fun intercept (Lokhttp3/Interceptor$Chain;)Lokhttp3/Response; - protected fun onRequestIntercepted (Lcom/datadog/android/api/feature/FeatureSdkCore;Lokhttp3/Request;Lio/opentracing/Span;Lokhttp3/Response;Ljava/lang/Throwable;)V + protected fun onRequestIntercepted (Lcom/datadog/android/api/feature/FeatureSdkCore;Lokhttp3/Request;Lcom/datadog/android/trace/api/span/DatadogSpan;Lokhttp3/Response;Ljava/lang/Throwable;)V } public final class com/datadog/android/okhttp/DatadogInterceptor$Builder : com/datadog/android/okhttp/trace/TracingInterceptor$BaseBuilder { @@ -64,16 +64,16 @@ public class com/datadog/android/okhttp/trace/DeterministicTraceSampler : com/da } public final class com/datadog/android/okhttp/trace/OkHttpRequestExtKt { - public static final fun parentSpan (Lokhttp3/Request$Builder;Lio/opentracing/Span;)Lokhttp3/Request$Builder; + public static final fun parentSpan (Lokhttp3/Request$Builder;Lcom/datadog/android/trace/api/span/DatadogSpan;)Lokhttp3/Request$Builder; } public abstract interface class com/datadog/android/okhttp/trace/TracedRequestListener { - public abstract fun onRequestIntercepted (Lokhttp3/Request;Lio/opentracing/Span;Lokhttp3/Response;Ljava/lang/Throwable;)V + public abstract fun onRequestIntercepted (Lokhttp3/Request;Lcom/datadog/android/trace/api/span/DatadogSpan;Lokhttp3/Response;Ljava/lang/Throwable;)V } public class com/datadog/android/okhttp/trace/TracingInterceptor : okhttp3/Interceptor { public fun intercept (Lokhttp3/Interceptor$Chain;)Lokhttp3/Response; - protected fun onRequestIntercepted (Lcom/datadog/android/api/feature/FeatureSdkCore;Lokhttp3/Request;Lio/opentracing/Span;Lokhttp3/Response;Ljava/lang/Throwable;)V + protected fun onRequestIntercepted (Lcom/datadog/android/api/feature/FeatureSdkCore;Lokhttp3/Request;Lcom/datadog/android/trace/api/span/DatadogSpan;Lokhttp3/Response;Ljava/lang/Throwable;)V } public abstract class com/datadog/android/okhttp/trace/TracingInterceptor$BaseBuilder { diff --git a/integrations/dd-sdk-android-okhttp/build.gradle.kts b/integrations/dd-sdk-android-okhttp/build.gradle.kts index 4194b65bcc..63d57f47b7 100644 --- a/integrations/dd-sdk-android-okhttp/build.gradle.kts +++ b/integrations/dd-sdk-android-okhttp/build.gradle.kts @@ -46,14 +46,12 @@ android { } dependencies { - implementation(project(":features:dd-sdk-android-trace")) - implementation(project(":features:dd-sdk-android-rum")) - implementation(project(":dd-sdk-android-internal")) implementation(libs.kotlin) implementation(libs.okHttp) implementation(libs.androidXAnnotation) - api(libs.openTracingApi) - + implementation(project(":dd-sdk-android-internal")) + implementation(project(":features:dd-sdk-android-rum")) + implementation(project(":features:dd-sdk-android-trace")) // Generate NoOp implementations ksp(project(":tools:noopfactory")) @@ -67,6 +65,7 @@ dependencies { } testImplementation(testFixtures(project(":dd-sdk-android-core"))) testImplementation(testFixtures(project(":dd-sdk-android-internal"))) + testImplementation(testFixtures(project(":features:dd-sdk-android-trace"))) testImplementation(libs.bundles.jUnit5) testImplementation(libs.bundles.testTools) testImplementation(libs.okHttpMock) diff --git a/integrations/dd-sdk-android-okhttp/src/main/kotlin/com/datadog/android/okhttp/DatadogInterceptor.kt b/integrations/dd-sdk-android-okhttp/src/main/kotlin/com/datadog/android/okhttp/DatadogInterceptor.kt index fe5ed1780d..cf7aa51118 100644 --- a/integrations/dd-sdk-android-okhttp/src/main/kotlin/com/datadog/android/okhttp/DatadogInterceptor.kt +++ b/integrations/dd-sdk-android-okhttp/src/main/kotlin/com/datadog/android/okhttp/DatadogInterceptor.kt @@ -15,7 +15,6 @@ import com.datadog.android.core.configuration.Configuration import com.datadog.android.core.sampling.Sampler import com.datadog.android.okhttp.internal.rum.NoOpRumResourceAttributesProvider import com.datadog.android.okhttp.internal.rum.buildResourceId -import com.datadog.android.okhttp.internal.utils.traceIdAsHexString import com.datadog.android.okhttp.trace.TracedRequestListener import com.datadog.android.okhttp.trace.TracingInterceptor import com.datadog.android.rum.GlobalRumMonitor @@ -27,10 +26,9 @@ import com.datadog.android.rum.RumResourceKind import com.datadog.android.rum.RumResourceMethod import com.datadog.android.rum.internal.monitor.AdvancedNetworkRumMonitor import com.datadog.android.rum.tracking.ViewTrackingStrategy -import com.datadog.android.trace.AndroidTracer import com.datadog.android.trace.TracingHeaderType -import io.opentracing.Span -import io.opentracing.Tracer +import com.datadog.android.trace.api.span.DatadogSpan +import com.datadog.android.trace.api.tracer.DatadogTracer import okhttp3.Interceptor import okhttp3.OkHttpClient import okhttp3.Request @@ -75,10 +73,11 @@ open class DatadogInterceptor internal constructor( tracedHosts: Map>, tracedRequestListener: TracedRequestListener, internal val rumResourceAttributesProvider: RumResourceAttributesProvider, - traceSampler: Sampler, + traceSampler: Sampler, traceContextInjection: TraceContextInjection, redacted404ResourceName: Boolean, - localTracerFactory: (SdkCore, Set) -> Tracer + localTracerFactory: (SdkCore, Set) -> DatadogTracer, + globalTracerProvider: () -> DatadogTracer? ) : TracingInterceptor( sdkInstanceName, tracedHosts, @@ -87,7 +86,8 @@ open class DatadogInterceptor internal constructor( traceSampler, traceContextInjection, redacted404ResourceName, - localTracerFactory + localTracerFactory, + globalTracerProvider ) { // region Interceptor @@ -126,7 +126,7 @@ open class DatadogInterceptor internal constructor( override fun onRequestIntercepted( sdkCore: FeatureSdkCore, request: Request, - span: Span?, + span: DatadogSpan?, response: Response?, throwable: Throwable? ) { @@ -165,7 +165,7 @@ open class DatadogInterceptor internal constructor( sdkCore: FeatureSdkCore, request: Request, response: Response, - span: Span?, + span: DatadogSpan?, isSampled: Boolean ) { val requestId = request.buildResourceId(generateUuid = false) @@ -178,8 +178,8 @@ open class DatadogInterceptor internal constructor( emptyMap() } else { mapOf( - RumAttributes.TRACE_ID to span.context().traceIdAsHexString(), - RumAttributes.SPAN_ID to span.context().toSpanId(), + RumAttributes.TRACE_ID to span.context().traceId.toHexString(), + RumAttributes.SPAN_ID to span.context().spanId.toString(), RumAttributes.RULE_PSR to (traceSampler.getSampleRate() ?: ZERO_SAMPLE_RATE) / ALL_IN_SAMPLE_RATE ) } @@ -290,8 +290,8 @@ open class DatadogInterceptor internal constructor( /** * A Builder for the [DatadogInterceptor]. * @param tracedHostsWithHeaderType a list of all the hosts and header types that you want to - * be automatically tracked by this interceptor. If registering a [io.opentracing.util.GlobalTracer], - * the tracer must be configured with [AndroidTracer.Builder.setTracingHeaderTypes] containing all the necessary + * be automatically tracked by this interceptor. If registering a [com.datadog.android.trace.GlobalDatadogTracer], + * the tracer must be configured with [com.datadog.android.trace.api.tracer.DatadogTracerBuilder.withTracingHeadersTypes] containing all the necessary * header types configured for OkHttp tracking. * If no hosts are provided (via this argument or global configuration * [Configuration.Builder.setFirstPartyHosts] or [Configuration.Builder.setFirstPartyHostsWithHeaderType] ) @@ -327,7 +327,8 @@ open class DatadogInterceptor internal constructor( traceSampler, traceContextInjection, redacted404ResourceName, - localTracerFactory + localTracerFactory, + globalTracerProvider ) } diff --git a/integrations/dd-sdk-android-okhttp/src/main/kotlin/com/datadog/android/okhttp/internal/otel/TraceContextExt.kt b/integrations/dd-sdk-android-okhttp/src/main/kotlin/com/datadog/android/okhttp/internal/otel/TraceContextExt.kt deleted file mode 100644 index 1cb11be041..0000000000 --- a/integrations/dd-sdk-android-okhttp/src/main/kotlin/com/datadog/android/okhttp/internal/otel/TraceContextExt.kt +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. - * This product includes software developed at Datadog (https://www.datadoghq.com/). - * Copyright 2016-Present Datadog, Inc. - */ - -package com.datadog.android.okhttp.internal.otel - -import com.datadog.android.okhttp.TraceContext -import com.datadog.opentracing.propagation.ExtractedContext -import io.opentracing.SpanContext -import java.math.BigInteger - -private const val BASE_16_RADIX = 16 - -internal fun TraceContext.toOpenTracingContext(): SpanContext { - val traceIdAsBigInteger = parseToBigInteger(traceId) - val spanIdAsBigInteger = parseToBigInteger(spanId) - return ExtractedContext( - traceIdAsBigInteger, - spanIdAsBigInteger, - samplingPriority, - null, - emptyMap(), - emptyMap() - ) -} - -@Suppress("SwallowedException") -private fun parseToBigInteger(value: String): BigInteger { - // just in case but theoretically it should never happen as we are controlling the way the ID is - // generated in the CoreTracer. - return try { - BigInteger(value, BASE_16_RADIX) - } catch (e: NumberFormatException) { - BigInteger.ZERO - } catch (e: ArithmeticException) { - BigInteger.ZERO - } -} diff --git a/integrations/dd-sdk-android-okhttp/src/main/kotlin/com/datadog/android/okhttp/internal/utils/SpanContextExt.kt b/integrations/dd-sdk-android-okhttp/src/main/kotlin/com/datadog/android/okhttp/internal/utils/SpanContextExt.kt deleted file mode 100644 index 50f7191d43..0000000000 --- a/integrations/dd-sdk-android-okhttp/src/main/kotlin/com/datadog/android/okhttp/internal/utils/SpanContextExt.kt +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. - * This product includes software developed at Datadog (https://www.datadoghq.com/). - * Copyright 2016-Present Datadog, Inc. - */ - -package com.datadog.android.okhttp.internal.utils - -import com.datadog.opentracing.DDSpanContext -import io.opentracing.SpanContext - -private const val TRACE_ID_REQUIRED_LENGTH = 32 -internal const val HEX_RADIX = 16 - -internal fun SpanContext.traceIdAsHexString(): String { - return (this as? DDSpanContext)?.traceId?.toString(HEX_RADIX)?.padStart(TRACE_ID_REQUIRED_LENGTH, '0') ?: "" -} diff --git a/integrations/dd-sdk-android-okhttp/src/main/kotlin/com/datadog/android/okhttp/internal/utils/SpanSamplingIdProvider.kt b/integrations/dd-sdk-android-okhttp/src/main/kotlin/com/datadog/android/okhttp/internal/utils/SpanSamplingIdProvider.kt index 303edf7e85..375236a403 100644 --- a/integrations/dd-sdk-android-okhttp/src/main/kotlin/com/datadog/android/okhttp/internal/utils/SpanSamplingIdProvider.kt +++ b/integrations/dd-sdk-android-okhttp/src/main/kotlin/com/datadog/android/okhttp/internal/utils/SpanSamplingIdProvider.kt @@ -7,23 +7,23 @@ package com.datadog.android.okhttp.internal.utils import com.datadog.android.log.LogAttributes -import com.datadog.opentracing.DDSpanContext -import io.opentracing.Span +import com.datadog.android.trace.api.span.DatadogSpan + +private const val HEX_RADIX = 16 internal object SpanSamplingIdProvider { - fun provideId(span: Span): ULong { + fun provideId(span: DatadogSpan): ULong { val context = span.context() - val sessionId = (context as? DDSpanContext)?.tags?.get(LogAttributes.RUM_SESSION_ID) as? String + val sessionId = context.tags[LogAttributes.RUM_SESSION_ID] as? String // for a UUID with value aaaaaaaa-bbbb-Mccc-Nddd-1234567890ab // we use as the input id the last part : 0x1234567890ab - val sessionIdToken = sessionId?.split('-')?.lastOrNull()?.toLongOrNull(HEX_RADIX)?.toULong() + val sessionIdToken = sessionId?.split('-') + ?.lastOrNull() + ?.toLongOrNull(HEX_RADIX) + ?.toULong() - return if (sessionIdToken != null) { - sessionIdToken - } else { - context.toTraceId().toBigIntegerOrNull()?.toLong()?.toULong() ?: 0u - } + return sessionIdToken ?: context.traceId.toLong().toULong() } } diff --git a/integrations/dd-sdk-android-okhttp/src/main/kotlin/com/datadog/android/okhttp/trace/DeterministicTraceSampler.kt b/integrations/dd-sdk-android-okhttp/src/main/kotlin/com/datadog/android/okhttp/trace/DeterministicTraceSampler.kt index ab91864761..661601baa4 100644 --- a/integrations/dd-sdk-android-okhttp/src/main/kotlin/com/datadog/android/okhttp/trace/DeterministicTraceSampler.kt +++ b/integrations/dd-sdk-android-okhttp/src/main/kotlin/com/datadog/android/okhttp/trace/DeterministicTraceSampler.kt @@ -9,7 +9,7 @@ package com.datadog.android.okhttp.trace import androidx.annotation.FloatRange import com.datadog.android.core.sampling.DeterministicSampler import com.datadog.android.okhttp.internal.utils.SpanSamplingIdProvider -import io.opentracing.Span +import com.datadog.android.trace.api.span.DatadogSpan /** * A [DeterministicSampler] using the TraceID of a Span to compute the sampling decision. @@ -19,7 +19,7 @@ import io.opentracing.Span */ open class DeterministicTraceSampler( sampleRateProvider: () -> Float -) : DeterministicSampler( +) : DeterministicSampler( SpanSamplingIdProvider::provideId, sampleRateProvider ) { diff --git a/integrations/dd-sdk-android-okhttp/src/main/kotlin/com/datadog/android/okhttp/trace/OkHttpRequestExt.kt b/integrations/dd-sdk-android-okhttp/src/main/kotlin/com/datadog/android/okhttp/trace/OkHttpRequestExt.kt index 4abd1f14c2..a633d48c4f 100644 --- a/integrations/dd-sdk-android-okhttp/src/main/kotlin/com/datadog/android/okhttp/trace/OkHttpRequestExt.kt +++ b/integrations/dd-sdk-android-okhttp/src/main/kotlin/com/datadog/android/okhttp/trace/OkHttpRequestExt.kt @@ -6,15 +6,15 @@ package com.datadog.android.okhttp.trace -import io.opentracing.Span +import com.datadog.android.trace.api.span.DatadogSpan import okhttp3.Request /** - * Set the parent for the [Span] created around this OkHttp [Request]. - * @param span the parent [Span] + * Set the parent for the [DatadogSpan] created around this OkHttp [Request]. + * @param span the parent [DatadogSpan] */ -fun Request.Builder.parentSpan(span: Span): Request.Builder { +fun Request.Builder.parentSpan(span: DatadogSpan): Request.Builder { @Suppress("UnsafeThirdPartyFunctionCall") // Span can't be null - tag(Span::class.java, span) + tag(DatadogSpan::class.java, span) return this } diff --git a/integrations/dd-sdk-android-okhttp/src/main/kotlin/com/datadog/android/okhttp/trace/TracedRequestListener.kt b/integrations/dd-sdk-android-okhttp/src/main/kotlin/com/datadog/android/okhttp/trace/TracedRequestListener.kt index eaf8817125..4248f461ff 100644 --- a/integrations/dd-sdk-android-okhttp/src/main/kotlin/com/datadog/android/okhttp/trace/TracedRequestListener.kt +++ b/integrations/dd-sdk-android-okhttp/src/main/kotlin/com/datadog/android/okhttp/trace/TracedRequestListener.kt @@ -6,8 +6,8 @@ package com.datadog.android.okhttp.trace +import com.datadog.android.trace.api.span.DatadogSpan import com.datadog.tools.annotation.NoOpImplementation -import io.opentracing.Span import okhttp3.Request import okhttp3.Response @@ -29,7 +29,7 @@ interface TracedRequestListener { */ fun onRequestIntercepted( request: Request, - span: Span, + span: DatadogSpan, response: Response?, throwable: Throwable? ) diff --git a/integrations/dd-sdk-android-okhttp/src/main/kotlin/com/datadog/android/okhttp/trace/TracingInterceptor.kt b/integrations/dd-sdk-android-okhttp/src/main/kotlin/com/datadog/android/okhttp/trace/TracingInterceptor.kt index 6e440a9d2a..102bacefa0 100644 --- a/integrations/dd-sdk-android-okhttp/src/main/kotlin/com/datadog/android/okhttp/trace/TracingInterceptor.kt +++ b/integrations/dd-sdk-android-okhttp/src/main/kotlin/com/datadog/android/okhttp/trace/TracingInterceptor.kt @@ -21,26 +21,16 @@ import com.datadog.android.internal.telemetry.TracingHeaderTypesSet import com.datadog.android.internal.utils.loggableStackTrace import com.datadog.android.okhttp.TraceContext import com.datadog.android.okhttp.TraceContextInjection -import com.datadog.android.okhttp.internal.otel.toOpenTracingContext import com.datadog.android.okhttp.internal.trace.toInternalTracingHeaderType -import com.datadog.android.okhttp.internal.utils.traceIdAsHexString -import com.datadog.android.trace.AndroidTracer +import com.datadog.android.trace.DatadogTracing +import com.datadog.android.trace.GlobalDatadogTracer import com.datadog.android.trace.TracingHeaderType -import com.datadog.legacy.trace.api.DDTags -import com.datadog.legacy.trace.api.interceptor.MutableSpan -import com.datadog.legacy.trace.api.sampling.PrioritySampling -import com.datadog.opentracing.DDSpan -import com.datadog.opentracing.DDSpanContext -import com.datadog.opentracing.DDTracer -import com.datadog.opentracing.propagation.ExtractedContext -import io.opentracing.Span -import io.opentracing.SpanContext -import io.opentracing.Tracer -import io.opentracing.propagation.Format -import io.opentracing.propagation.TextMapExtractAdapter -import io.opentracing.propagation.TextMapInject -import io.opentracing.tag.Tags -import io.opentracing.util.GlobalTracer +import com.datadog.android.trace.api.DatadogTracingConstants.PrioritySampling +import com.datadog.android.trace.api.DatadogTracingConstants.Tags +import com.datadog.android.trace.api.span.DatadogSpan +import com.datadog.android.trace.api.span.DatadogSpanContext +import com.datadog.android.trace.api.tracer.DatadogTracer +import com.datadog.android.trace.internal.DatadogTracingToolkit import okhttp3.Interceptor import okhttp3.OkHttpClient import okhttp3.Request @@ -84,13 +74,14 @@ internal constructor( internal val tracedHosts: Map>, internal val tracedRequestListener: TracedRequestListener, internal val traceOrigin: String?, - internal val traceSampler: Sampler, + internal val traceSampler: Sampler, internal val traceContextInjection: TraceContextInjection, internal val redacted404ResourceName: Boolean, - internal val localTracerFactory: (SdkCore, Set) -> Tracer + internal val localTracerFactory: (SdkCore, Set) -> DatadogTracer, + private val globalTracerProvider: () -> DatadogTracer? ) : Interceptor { - private val localTracerReference: AtomicReference = AtomicReference() + private val localTracerReference: AtomicReference = AtomicReference() private val sanitizedHosts = HostsSanitizer().sanitizeHosts( tracedHosts.keys.toList(), NETWORK_REQUESTS_TRACKING_FEATURE_NAME @@ -110,8 +101,11 @@ internal constructor( // update meta for the configuration telemetry reporting, can be done directly from this thread sdkCore?.updateFeatureContext(Feature.TRACING_FEATURE_NAME, useContextThread = false) { it[OKHTTP_INTERCEPTOR_SAMPLE_RATE] = traceSampler.getSampleRate() - it[OKHTTP_INTERCEPTOR_HEADER_TYPES] = - TracingHeaderTypesSet(tracedHosts.values.flatten().map { it.toInternalTracingHeaderType() }.toSet()) + it[OKHTTP_INTERCEPTOR_HEADER_TYPES] = TracingHeaderTypesSet( + tracedHosts.values.flatten() + .map(TracingHeaderType::toInternalTracingHeaderType) + .toSet() + ) } } @@ -155,18 +149,18 @@ internal constructor( /** * Called whenever a span was successfully created around an OkHttp [Request]. - * The given [Span] can be updated (e.g.: add custom tags / baggage items) before it is + * The given [AgentSpan] can be updated (e.g.: add custom tags / baggage items) before it is * finalized. * @param sdkCore SDK instance to use. * @param request the intercepted [Request] - * @param span the [Span] created around the [Request] (or null if request is not traced) + * @param span the [AgentSpan] created around the [Request] (or null if request is not traced) * @param response the [Request] response (or null if an error occurred) * @param throwable the error which occurred during the [Request] (or null) */ protected open fun onRequestIntercepted( sdkCore: FeatureSdkCore, request: Request, - span: Span?, + span: DatadogSpan?, response: Response?, throwable: Throwable? ) { @@ -210,13 +204,18 @@ internal constructor( sdkCore: InternalSdkCore, chain: Interceptor.Chain, request: Request, - tracer: Tracer + tracer: DatadogTracer ): Response { - val span = buildSpan(tracer, request) - val isSampled = span.sample(tracer, request) + val span: DatadogSpan = buildSpan(tracer, request) + val isSampled = span.sample(request) + + if (span.isRootSpan) { + val samplingPriority = if (isSampled) { + PrioritySampling.SAMPLER_KEEP + } else { + PrioritySampling.SAMPLER_DROP + } - if (span is DDSpan && span.isRootSpan) { - val samplingPriority = if (isSampled) PrioritySampling.SAMPLER_KEEP else PrioritySampling.SAMPLER_DROP val spanContext = span.context() if (spanContext.setSamplingPriority(samplingPriority)) { spanContext.setMetric( @@ -265,8 +264,9 @@ internal constructor( } @Synchronized - private fun resolveTracer(sdkCore: InternalSdkCore): Tracer? { + private fun resolveTracer(sdkCore: InternalSdkCore): DatadogTracer? { val tracingFeature = sdkCore.getFeature(Feature.TRACING_FEATURE_NAME) + val globalTracerInstance = globalTracerProvider.invoke() return if (tracingFeature == null) { sdkCore.internalLogger.log( InternalLogger.Level.WARN, @@ -275,17 +275,17 @@ internal constructor( onlyOnce = true ) null - } else if (GlobalTracer.isRegistered()) { + } else if (globalTracerInstance != null) { // clear the localTracer reference if any localTracerReference.set(null) - GlobalTracer.get() + globalTracerInstance } else { // we check if we already have a local tracer if not we instantiate one resolveLocalTracer(sdkCore) } } - private fun resolveLocalTracer(sdkCore: InternalSdkCore): Tracer { + private fun resolveLocalTracer(sdkCore: InternalSdkCore): DatadogTracer { // only register once if (localTracerReference.get() == null) { @Suppress("UnsafeThirdPartyFunctionCall") // internal safe call @@ -302,50 +302,37 @@ internal constructor( return localTracerReference.get() } - private fun buildSpan(tracer: Tracer, request: Request): Span { + private fun buildSpan(tracer: DatadogTracer, request: Request): DatadogSpan { val parentContext = extractParentContext(tracer, request) val url = request.url.toString() - val spanBuilder = tracer.buildSpan(SPAN_NAME) - (spanBuilder as? DDTracer.DDSpanBuilder)?.withOrigin(traceOrigin) - val span = spanBuilder - .asChildOf(parentContext) + val span = tracer.buildSpan(SPAN_NAME) + .withOrigin(traceOrigin) + .withParentContext(parentContext) .start() - (span as? MutableSpan)?.resourceName = url.substringBefore(URL_QUERY_PARAMS_BLOCK_SEPARATOR) - span.setTag(Tags.HTTP_URL.key, url) - span.setTag(Tags.HTTP_METHOD.key, request.method) - span.setTag(Tags.SPAN_KIND, Tags.SPAN_KIND_CLIENT) + span.resourceName = url.substringBefore(URL_QUERY_PARAMS_BLOCK_SEPARATOR) + span.setTag(Tags.KEY_HTTP_URL, url) + span.setTag(Tags.KEY_HTTP_METHOD, request.method) + span.setTag(Tags.KEY_SPAN_KIND, Tags.VALUE_SPAN_KIND_CLIENT) return span } - @Suppress("ReturnCount") - private fun extractSamplingDecision(tracer: Tracer, request: Request): Boolean? { + private fun extractSamplingDecision(request: Request): Boolean? { val headerSamplingPriority = extractSamplingDecisionFromHeader(request) - if (headerSamplingPriority != null) return headerSamplingPriority - - // if parent context is propagated via headers, sampling decision will be there (see DDTracer#inject), but if - // it is tagged span, we need to trigger sampling decision manually, it is not yet made probably. - val openTracingSpan = request.tag(Span::class.java) - if (openTracingSpan != null && openTracingSpan.context() is DDSpanContext) { - // very hacky, this is to trigger sampling decision and avoid - // making DDTracer#setSamplingPriorityIfNecessary public - tracer.inject(openTracingSpan.context(), Format.Builtin.TEXT_MAP_INJECT, TextMapInject { _, _ -> }) - return (openTracingSpan.context() as? DDSpanContext) - ?.let { if (it.samplingPriority == PrioritySampling.UNSET) null else it.samplingPriority > 0 } - } - - val openTelemetrySpan = request.tag(TraceContext::class.java) - if (openTelemetrySpan != null) { - return if (openTelemetrySpan.samplingPriority == PrioritySampling.UNSET) { - null - } else { - openTelemetrySpan.samplingPriority > 0 + val datadogSpan = request.tag(DatadogSpan::class.java) + val openTelemetrySpanSamplingPriority = request.tag(TraceContext::class.java)?.samplingPriority + + return when { + headerSamplingPriority != null -> headerSamplingPriority + datadogSpan != null -> { + DatadogTracingToolkit.setTracingSamplingPriorityIfNecessary(datadogSpan.context()) + datadogSpan.context().samplingPriority > 0 } + openTelemetrySpanSamplingPriority == PrioritySampling.UNSET -> null + else -> openTelemetrySpanSamplingPriority?.let { samplingPriority -> samplingPriority > 0 } } - - return null } @Suppress("ReturnCount") @@ -396,38 +383,39 @@ internal constructor( return null } - private fun extractParentContext(tracer: Tracer, request: Request): SpanContext? { - val tagContext = request.tag(Span::class.java)?.context() - ?: request.tag(TraceContext::class.java)?.toOpenTracingContext() - // need this, because TagContext#toSpanId returns empty string even if there is non-empty context - val hasTag = request.tag(Span::class.java) - ?: request.tag(TraceContext::class.java) != null - - val headerContext = tracer.extract( - Format.Builtin.TEXT_MAP_EXTRACT, - TextMapExtractAdapter( - request.headers.toMultimap() - .map { it.key to it.value.joinToString(";") } - .toMap() - ) - ) + private fun extractParentContext(tracer: DatadogTracer, request: Request): DatadogSpanContext? { + val tagContext = request.tag(DatadogSpan::class.java)?.context() ?: extractTraceContext(request) - // Tracer.extract will return empty object, not null, if nothing was extracted. ExtractedContext will be - // returned only if there is real tracing info. - return if (headerContext is ExtractedContext) { + val headerContext: DatadogSpanContext? = tracer.propagate().extract(request) { carrier, classifier -> + val headers = carrier.headers.toMultimap() + .map { it.key to it.value.joinToString(";") } + .toMap() + + // there is no actual classification here, values just got cached + for ((key, value) in headers) classifier(key, value) + } + + return if (headerContext != null && DatadogTracingToolkit.propagationHelper.isExtractedContext(headerContext)) { headerContext - } else if (hasTag) { - tagContext } else { - null + tagContext } } + private fun extractTraceContext(request: Request): DatadogSpanContext? = + request.tag(TraceContext::class.java)?.let { + DatadogTracingToolkit.propagationHelper.createExtractedContext( + it.traceId, + it.spanId, + it.samplingPriority + ) + } + private fun setSampledOutHeaders( requestBuilder: Request.Builder, tracingHeaderTypes: Set, - span: Span, - tracer: Tracer + span: DatadogSpan, + tracer: DatadogTracer ) { for (headerType in tracingHeaderTypes) { when (headerType) { @@ -454,21 +442,24 @@ internal constructor( } } - private fun handleDatadogSampledOutHeaders(requestBuilder: Request.Builder, span: Span, tracer: Tracer) { + private fun handleDatadogSampledOutHeaders( + requestBuilder: Request.Builder, + span: DatadogSpan, + tracer: DatadogTracer + ) { if (traceContextInjection == TraceContextInjection.ALL) { - tracer.inject( + tracer.propagate().inject( span.context(), - Format.Builtin.TEXT_MAP_INJECT, - TextMapInject { key, value -> - requestBuilder.removeHeader(key) - when (key) { - DATADOG_LEAST_SIGNIFICANT_64_BITS_TRACE_ID_HEADER, - DATADOG_TAGS_HEADER, - DATADOG_SPAN_ID_HEADER, - DATADOG_ORIGIN_HEADER -> requestBuilder.addHeader(key, value) - } + requestBuilder + ) { carrier, key, value -> + carrier.removeHeader(key) + when (key) { + DATADOG_LEAST_SIGNIFICANT_64_BITS_TRACE_ID_HEADER, + DATADOG_TAGS_HEADER, + DATADOG_SPAN_ID_HEADER, + DATADOG_ORIGIN_HEADER -> carrier.addHeader(key, value) } - ) + } requestBuilder.addHeader( DATADOG_SAMPLING_PRIORITY_HEADER, DATADOG_DROP_SAMPLING_DECISION @@ -488,20 +479,20 @@ internal constructor( } } - private fun handleW3CNotSampledHeaders(span: Span, requestBuilder: Request.Builder) { + private fun handleW3CNotSampledHeaders(span: DatadogSpan, requestBuilder: Request.Builder) { if (traceContextInjection == TraceContextInjection.ALL) { - val traceId = span.context().traceIdAsHexString() - val spanId = span.context().toSpanId() + val traceId = span.context().traceId.toHexString() + val spanId = span.context().spanId.toString() requestBuilder.addHeader( W3C_TRACEPARENT_KEY, - @Suppress("UnsafeThirdPartyFunctionCall") // Format string is static + @Suppress("UnsafeThirdPartyFunctionCall", "InvalidStringFormat") // Format string is static W3C_TRACEPARENT_DROP_SAMPLING_DECISION.format( traceId.padStart(length = W3C_TRACE_ID_LENGTH, padChar = '0'), spanId.padStart(length = W3C_PARENT_ID_LENGTH, padChar = '0') ) ) // TODO RUM-2121 3rd party vendor information will be erased - @Suppress("UnsafeThirdPartyFunctionCall") // Format string is static + @Suppress("UnsafeThirdPartyFunctionCall", "InvalidStringFormat") // Format string is static var traceStateHeader = W3C_TRACESTATE_DROP_SAMPLING_DECISION .format(spanId.padStart(length = W3C_PARENT_ID_LENGTH, padChar = '0')) if (traceOrigin != null) { @@ -541,8 +532,8 @@ internal constructor( private fun updateRequest( sdkCore: InternalSdkCore, request: Request, - tracer: Tracer, - span: Span, + tracer: DatadogTracer, + span: DatadogSpan, isSampled: Boolean ): Request.Builder { val tracedRequestBuilder = request.newBuilder() @@ -555,44 +546,41 @@ internal constructor( if (!isSampled) { setSampledOutHeaders(tracedRequestBuilder, tracingHeaderTypes, span, tracer) } else { - tracer.inject( + tracer.propagate().inject( span.context(), - Format.Builtin.TEXT_MAP_INJECT, - TextMapInject { key, value -> - // By default the `addHeader` method adds a value and doesn't replace it - // We need to remove the old trace/span info to use the one for the current span - tracedRequestBuilder.removeHeader(key) - when (key) { - DATADOG_SAMPLING_PRIORITY_HEADER, - DATADOG_LEAST_SIGNIFICANT_64_BITS_TRACE_ID_HEADER, - DATADOG_TAGS_HEADER, - DATADOG_SPAN_ID_HEADER, - DATADOG_ORIGIN_HEADER -> if (tracingHeaderTypes.contains(TracingHeaderType.DATADOG)) { - tracedRequestBuilder.addHeader(key, value) - } - - B3_HEADER_KEY -> if (tracingHeaderTypes.contains(TracingHeaderType.B3)) { - tracedRequestBuilder.addHeader(key, value) - } - - B3M_SPAN_ID_KEY, - B3M_TRACE_ID_KEY, - B3M_SAMPLING_PRIORITY_KEY -> if (tracingHeaderTypes.contains( - TracingHeaderType.B3MULTI - ) - ) { - tracedRequestBuilder.addHeader(key, value) - } - - W3C_TRACEPARENT_KEY, - W3C_TRACESTATE_KEY -> if (tracingHeaderTypes.contains(TracingHeaderType.TRACECONTEXT)) { - tracedRequestBuilder.addHeader(key, value) - } - - else -> tracedRequestBuilder.addHeader(key, value) + tracedRequestBuilder + ) { carrier: Request.Builder, key: String, value: String -> + carrier.removeHeader(key) + when (key) { + DATADOG_SAMPLING_PRIORITY_HEADER, + DATADOG_LEAST_SIGNIFICANT_64_BITS_TRACE_ID_HEADER, + DATADOG_TAGS_HEADER, + DATADOG_SPAN_ID_HEADER, + DATADOG_ORIGIN_HEADER -> if (tracingHeaderTypes.contains(TracingHeaderType.DATADOG)) { + carrier.addHeader(key, value) } + + B3_HEADER_KEY -> if (tracingHeaderTypes.contains(TracingHeaderType.B3)) { + carrier.addHeader(key, value) + } + + B3M_SPAN_ID_KEY, + B3M_TRACE_ID_KEY, + B3M_SAMPLING_PRIORITY_KEY -> if (tracingHeaderTypes.contains( + TracingHeaderType.B3MULTI + ) + ) { + carrier.addHeader(key, value) + } + + W3C_TRACEPARENT_KEY, + W3C_TRACESTATE_KEY -> if (tracingHeaderTypes.contains(TracingHeaderType.TRACECONTEXT)) { + carrier.addHeader(key, value) + } + + else -> carrier.addHeader(key, value) } - ) + } } return tracedRequestBuilder @@ -602,19 +590,19 @@ internal constructor( sdkCore: FeatureSdkCore, request: Request, response: Response, - span: Span, + span: DatadogSpan, isSampled: Boolean ) { if (!isSampled) { onRequestIntercepted(sdkCore, request, null, response, null) } else { val statusCode = response.code - span.setTag(Tags.HTTP_STATUS.key, statusCode) + span.setTag(Tags.KEY_HTTP_STATUS, statusCode) if (statusCode in HttpURLConnection.HTTP_BAD_REQUEST until HttpURLConnection.HTTP_INTERNAL_ERROR) { - (span as? MutableSpan)?.isError = true + span.isError = true } if (statusCode == HttpURLConnection.HTTP_NOT_FOUND && redacted404ResourceName) { - (span as? MutableSpan)?.resourceName = RESOURCE_NAME_404 + span.resourceName = RESOURCE_NAME_404 } onRequestIntercepted(sdkCore, request, span, response, null) } @@ -625,22 +613,22 @@ internal constructor( sdkCore: FeatureSdkCore, request: Request, throwable: Throwable, - span: Span, + span: DatadogSpan, isSampled: Boolean ) { if (!isSampled) { onRequestIntercepted(sdkCore, request, null, null, throwable) } else { - (span as? MutableSpan)?.isError = true - span.setTag(DDTags.ERROR_MSG, throwable.message) - span.setTag(DDTags.ERROR_TYPE, throwable.javaClass.name) - span.setTag(DDTags.ERROR_STACK, throwable.loggableStackTrace()) + span.isError = true + span.setTag(Tags.KEY_ERROR_MSG, throwable.message) + span.setTag(Tags.KEY_ERROR_TYPE, throwable.javaClass.name) + span.setTag(Tags.KEY_ERROR_STACK, throwable.loggableStackTrace()) onRequestIntercepted(sdkCore, request, span, null, throwable) } span.finishRumAware(isSampled) } - private fun Span.finishRumAware(isSampled: Boolean) { + private fun DatadogSpan.finishRumAware(isSampled: Boolean) { if (canSendSpan()) { if (isSampled) finish() else drop() } else { @@ -648,13 +636,12 @@ internal constructor( } } - private fun Span.drop() = (this as? MutableSpan)?.drop() - - private fun Span.sample(tracer: Tracer, request: Request): Boolean { - return if (this is DDSpan && samplingPriority != null) { + private fun DatadogSpan.sample(request: Request): Boolean { + val samplingPriority = samplingPriority + return if (samplingPriority != null) { samplingPriority > 0 } else { - extractSamplingDecision(tracer, request) ?: traceSampler.sample(this) + extractSamplingDecision(request) ?: traceSampler.sample(this) } } @@ -665,8 +652,8 @@ internal constructor( /** * A Builder class for the [TracingInterceptor]. * @param tracedHostsWithHeaderType a list of all the hosts and header types that you want to - * be automatically tracked by this interceptor. If registering a [GlobalTracer], the tracer must be - * configured with [AndroidTracer.Builder.setTracingHeaderTypes] containing all the necessary + * be automatically tracked by this interceptor. If registering a [GlobalDatadogTracer], the tracer must be + * configured with [com.datadog.android.trace.api.tracer.DatadogTracerBuilder.withTracingHeadersTypes] containing all the necessary * header types configured for OkHttp tracking. * If no hosts are provided (via this argument or global configuration * [Configuration.Builder.setFirstPartyHosts] or [Configuration.Builder.setFirstPartyHostsWithHeaderType] ) @@ -706,7 +693,8 @@ internal constructor( traceSampler, traceContextInjection, redacted404ResourceName, - localTracerFactory + localTracerFactory, + globalTracerProvider ) } } @@ -720,8 +708,9 @@ internal constructor( internal var sdkInstanceName: String? = null internal var tracedRequestListener: TracedRequestListener = NoOpTracedRequestListener() internal var traceOrigin: String? = null - internal var traceSampler: Sampler = DeterministicTraceSampler(DEFAULT_TRACE_SAMPLE_RATE) + internal var traceSampler: Sampler = DeterministicTraceSampler(DEFAULT_TRACE_SAMPLE_RATE) internal var localTracerFactory = DEFAULT_LOCAL_TRACER_FACTORY + internal var globalTracerProvider: () -> DatadogTracer? = { GlobalDatadogTracer.getOrNull() } internal var traceContextInjection = TraceContextInjection.SAMPLED internal var redacted404ResourceName = true @@ -737,8 +726,8 @@ internal constructor( } /** - * Set the listener for automatically created [Span]s. - * @param tracedRequestListener a listener for automatically created [Span]s + * Set the listener for automatically created [AgentSpan]s. + * @param tracedRequestListener a listener for automatically created [AgentSpan]s */ fun setTracedRequestListener(tracedRequestListener: TracedRequestListener): R { this.tracedRequestListener = tracedRequestListener @@ -763,7 +752,7 @@ internal constructor( * @param traceSampler the trace sampler controlling the sampling of APM traces. * By default it is a sampler accepting 100% of the traces. */ - fun setTraceSampler(traceSampler: Sampler): R { + fun setTraceSampler(traceSampler: Sampler): R { this.traceSampler = traceSampler return getThis() } @@ -795,11 +784,16 @@ internal constructor( return getThis() } - internal fun setLocalTracerFactory(factory: (SdkCore, Set) -> Tracer): R { + internal fun setLocalTracerFactory(factory: (SdkCore, Set) -> DatadogTracer): R { this.localTracerFactory = factory return getThis() } + internal fun setGlobalTracerProvider(globalTracerProvider: () -> DatadogTracer?): R { + this.globalTracerProvider = globalTracerProvider + return getThis() + } + internal abstract fun getThis(): R /** @@ -830,7 +824,7 @@ internal constructor( "Your requests won't be traced." internal const val WARNING_DEFAULT_TRACER = "You added a TracingInterceptor to your OkHttpClient, " + - "but you didn't register any Tracer. " + + "but you didn't register any AgentTracer.TracerAPI. " + "We automatically created a local tracer for you." internal const val NETWORK_REQUESTS_TRACKING_FEATURE_NAME = "Network Requests" @@ -872,12 +866,12 @@ internal constructor( internal const val OKHTTP_INTERCEPTOR_HEADER_TYPES = "okhttp_interceptor_header_types" private const val AGENT_PSR_ATTRIBUTE = "_dd.agent_psr" - private val DEFAULT_LOCAL_TRACER_FACTORY: (SdkCore, Set) -> Tracer = - { sdkCore, tracingHeaderTypes -> - AndroidTracer.Builder(sdkCore) - // set sample rate to 100 to avoid sampling in the tracer, we are going to sample in the interceptor - .setSampleRate(ALL_IN_SAMPLE_RATE) - .setTracingHeaderTypes(tracingHeaderTypes) + + private val DEFAULT_LOCAL_TRACER_FACTORY: (SdkCore, Set) -> DatadogTracer = + { sdkCore, tracingHeaderTypes: Set -> + DatadogTracing.newTracerBuilder(sdkCore) + .withTracingHeadersTypes(tracingHeaderTypes) + .withSampleRate(ALL_IN_SAMPLE_RATE) .build() } } diff --git a/integrations/dd-sdk-android-okhttp/src/test/kotlin/com/datadog/android/okhttp/DatadogInterceptorBuilderTest.kt b/integrations/dd-sdk-android-okhttp/src/test/kotlin/com/datadog/android/okhttp/DatadogInterceptorBuilderTest.kt index 5d5037843a..dda4b50121 100644 --- a/integrations/dd-sdk-android-okhttp/src/test/kotlin/com/datadog/android/okhttp/DatadogInterceptorBuilderTest.kt +++ b/integrations/dd-sdk-android-okhttp/src/test/kotlin/com/datadog/android/okhttp/DatadogInterceptorBuilderTest.kt @@ -13,6 +13,7 @@ import com.datadog.android.okhttp.trace.NoOpTracedRequestListener import com.datadog.android.okhttp.trace.TracedRequestListener import com.datadog.android.rum.RumResourceAttributesProvider import com.datadog.android.trace.TracingHeaderType +import com.datadog.android.trace.api.span.DatadogSpan import com.datadog.tools.unit.extensions.TestConfigurationExtension import com.datadog.tools.unit.forge.BaseConfigurator import fr.xgouchet.elmyr.Forge @@ -20,7 +21,6 @@ import fr.xgouchet.elmyr.annotation.FloatForgery import fr.xgouchet.elmyr.annotation.StringForgery import fr.xgouchet.elmyr.junit5.ForgeConfiguration import fr.xgouchet.elmyr.junit5.ForgeExtension -import io.opentracing.Span import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test @@ -56,7 +56,7 @@ internal class DatadogInterceptorBuilderTest { private lateinit var fakeTracedHosts: List @Mock - lateinit var mockSampler: Sampler + lateinit var mockSampler: Sampler @BeforeEach fun `set up`(forge: Forge) { diff --git a/integrations/dd-sdk-android-okhttp/src/test/kotlin/com/datadog/android/okhttp/DatadogInterceptorTest.kt b/integrations/dd-sdk-android-okhttp/src/test/kotlin/com/datadog/android/okhttp/DatadogInterceptorTest.kt index 7f7b322a15..a14ae4bd1b 100644 --- a/integrations/dd-sdk-android-okhttp/src/test/kotlin/com/datadog/android/okhttp/DatadogInterceptorTest.kt +++ b/integrations/dd-sdk-android-okhttp/src/test/kotlin/com/datadog/android/okhttp/DatadogInterceptorTest.kt @@ -22,6 +22,7 @@ import com.datadog.android.rum.RumResourceKind import com.datadog.android.rum.RumResourceMethod import com.datadog.android.rum.resource.ResourceId import com.datadog.android.trace.TracingHeaderType +import com.datadog.android.trace.api.tracer.DatadogTracer import com.datadog.tools.unit.extensions.TestConfigurationExtension import com.datadog.tools.unit.forge.BaseConfigurator import com.datadog.tools.unit.forge.exhaustiveAttributes @@ -32,7 +33,6 @@ import fr.xgouchet.elmyr.annotation.IntForgery import fr.xgouchet.elmyr.annotation.StringForgery import fr.xgouchet.elmyr.junit5.ForgeConfiguration import fr.xgouchet.elmyr.junit5.ForgeExtension -import io.opentracing.Tracer import okhttp3.MediaType import okhttp3.MediaType.Companion.toMediaType import okhttp3.Protocol @@ -84,7 +84,8 @@ internal class DatadogInterceptorTest : TracingInterceptorNotSendingSpanTest() { override fun instantiateTestedInterceptor( tracedHosts: Map>, - factory: (SdkCore, Set) -> Tracer + globalTracerProvider: () -> DatadogTracer?, + localTracerFactory: (SdkCore, Set) -> DatadogTracer ): TracingInterceptor { whenever(rumMonitor.mockSdkCore.getFeature(Feature.RUM_FEATURE_NAME)) doReturn mock() whenever(rumMonitor.mockSdkCore.firstPartyHostResolver) doReturn mockResolver @@ -96,7 +97,8 @@ internal class DatadogInterceptorTest : TracingInterceptorNotSendingSpanTest() { traceSampler = mockTraceSampler, traceContextInjection = TraceContextInjection.ALL, redacted404ResourceName = fakeRedacted404Resources, - localTracerFactory = factory + localTracerFactory = localTracerFactory, + globalTracerProvider = globalTracerProvider ) } @@ -188,7 +190,7 @@ internal class DatadogInterceptorTest : TracingInterceptorNotSendingSpanTest() { val expectedStartAttrs = emptyMap() val expectedStopAttrs = mapOf( RumAttributes.TRACE_ID to fakeTraceIdAsString, - RumAttributes.SPAN_ID to fakeSpanId, + RumAttributes.SPAN_ID to fakeSpanId.toString(), RumAttributes.RULE_PSR to fakeTracingSampleRate / 100 ) + fakeAttributes val mimeType = fakeMediaType?.type @@ -231,7 +233,7 @@ internal class DatadogInterceptorTest : TracingInterceptorNotSendingSpanTest() { val expectedStartAttrs = emptyMap() val expectedStopAttrs = mapOf( RumAttributes.TRACE_ID to fakeTraceIdAsString, - RumAttributes.SPAN_ID to fakeSpanId, + RumAttributes.SPAN_ID to fakeSpanId.toString(), RumAttributes.RULE_PSR to fakeTracingSampleRate / 100 ) + fakeAttributes val mimeType = fakeMediaType?.type @@ -276,7 +278,7 @@ internal class DatadogInterceptorTest : TracingInterceptorNotSendingSpanTest() { val expectedStartAttrs = emptyMap() val expectedStopAttrs = mapOf( RumAttributes.TRACE_ID to fakeTraceIdAsString, - RumAttributes.SPAN_ID to fakeSpanId, + RumAttributes.SPAN_ID to fakeSpanId.toString(), RumAttributes.RULE_PSR to fakeTracingSampleRate / 100 ) + fakeAttributes val kind = RumResourceKind.NATIVE @@ -317,7 +319,7 @@ internal class DatadogInterceptorTest : TracingInterceptorNotSendingSpanTest() { val expectedStartAttrs = emptyMap() val expectedStopAttrs = mapOf( RumAttributes.TRACE_ID to fakeTraceIdAsString, - RumAttributes.SPAN_ID to fakeSpanId, + RumAttributes.SPAN_ID to fakeSpanId.toString(), RumAttributes.RULE_PSR to fakeTracingSampleRate / 100 ) + fakeAttributes val mimeType = fakeMediaType?.type @@ -364,7 +366,7 @@ internal class DatadogInterceptorTest : TracingInterceptorNotSendingSpanTest() { val expectedStartAttrs = emptyMap() val expectedStopAttrs = mapOf( RumAttributes.TRACE_ID to fakeTraceIdAsString, - RumAttributes.SPAN_ID to fakeSpanId, + RumAttributes.SPAN_ID to fakeSpanId.toString(), RumAttributes.RULE_PSR to fakeTracingSampleRate / 100 ) + fakeAttributes val mimeType = fakeMediaType?.type @@ -460,7 +462,7 @@ internal class DatadogInterceptorTest : TracingInterceptorNotSendingSpanTest() { } val expectedStopAttrs = mapOf( RumAttributes.TRACE_ID to fakeTraceIdAsString, - RumAttributes.SPAN_ID to fakeSpanId, + RumAttributes.SPAN_ID to fakeSpanId.toString(), RumAttributes.RULE_PSR to fakeTracingSampleRate / 100 ) + fakeAttributes val mimeType = fakeMediaType?.type @@ -509,7 +511,7 @@ internal class DatadogInterceptorTest : TracingInterceptorNotSendingSpanTest() { } val expectedStopAttrs = mapOf( RumAttributes.TRACE_ID to fakeTraceIdAsString, - RumAttributes.SPAN_ID to fakeSpanId, + RumAttributes.SPAN_ID to fakeSpanId.toString(), RumAttributes.RULE_PSR to fakeTracingSampleRate / 100 ) + fakeAttributes val mimeType = fakeMediaType?.type @@ -666,7 +668,7 @@ internal class DatadogInterceptorTest : TracingInterceptorNotSendingSpanTest() { val expectedStartAttrs = emptyMap() val expectedStopAttrs = mapOf( RumAttributes.TRACE_ID to fakeTraceIdAsString, - RumAttributes.SPAN_ID to fakeSpanId, + RumAttributes.SPAN_ID to fakeSpanId.toString(), RumAttributes.RULE_PSR to fakeTracingSampleRate / 100 ) + fakeAttributes val mimeType = fakeMediaType?.type @@ -768,7 +770,7 @@ internal class DatadogInterceptorTest : TracingInterceptorNotSendingSpanTest() { val expectedStartAttrs = emptyMap() val expectedStopAttrs = mapOf( RumAttributes.TRACE_ID to fakeTraceIdAsString, - RumAttributes.SPAN_ID to fakeSpanId, + RumAttributes.SPAN_ID to fakeSpanId.toString(), RumAttributes.RULE_PSR to fakeTracingSampleRate / 100 ) + fakeAttributes val mimeType = fakeMediaType?.type diff --git a/integrations/dd-sdk-android-okhttp/src/test/kotlin/com/datadog/android/okhttp/DatadogInterceptorWithoutRumTest.kt b/integrations/dd-sdk-android-okhttp/src/test/kotlin/com/datadog/android/okhttp/DatadogInterceptorWithoutRumTest.kt index 9617f391a3..d2332642e8 100644 --- a/integrations/dd-sdk-android-okhttp/src/test/kotlin/com/datadog/android/okhttp/DatadogInterceptorWithoutRumTest.kt +++ b/integrations/dd-sdk-android-okhttp/src/test/kotlin/com/datadog/android/okhttp/DatadogInterceptorWithoutRumTest.kt @@ -14,12 +14,12 @@ import com.datadog.android.okhttp.trace.TracingInterceptorTest import com.datadog.android.okhttp.utils.verifyLog import com.datadog.android.rum.RumResourceAttributesProvider import com.datadog.android.trace.TracingHeaderType +import com.datadog.android.trace.api.tracer.DatadogTracer import com.datadog.tools.unit.extensions.TestConfigurationExtension import fr.xgouchet.elmyr.annotation.Forgery import fr.xgouchet.elmyr.annotation.IntForgery import fr.xgouchet.elmyr.junit5.ForgeConfiguration import fr.xgouchet.elmyr.junit5.ForgeExtension -import io.opentracing.Tracer import org.junit.jupiter.api.Test import org.junit.jupiter.api.assertThrows import org.junit.jupiter.api.extension.ExtendWith @@ -51,7 +51,8 @@ internal class DatadogInterceptorWithoutRumTest : TracingInterceptorTest() { override fun instantiateTestedInterceptor( tracedHosts: Map>, - factory: (SdkCore, Set) -> Tracer + globalTracerProvider: () -> DatadogTracer?, + localTracerFactory: (SdkCore, Set) -> DatadogTracer ): TracingInterceptor { return DatadogInterceptor( sdkInstanceName = null, @@ -61,7 +62,8 @@ internal class DatadogInterceptorWithoutRumTest : TracingInterceptorTest() { traceSampler = mockTraceSampler, traceContextInjection = TraceContextInjection.ALL, redacted404ResourceName = fakeRedacted404Resources, - localTracerFactory = factory + localTracerFactory = localTracerFactory, + globalTracerProvider = globalTracerProvider ) } diff --git a/integrations/dd-sdk-android-okhttp/src/test/kotlin/com/datadog/android/okhttp/DatadogInterceptorWithoutTracesTest.kt b/integrations/dd-sdk-android-okhttp/src/test/kotlin/com/datadog/android/okhttp/DatadogInterceptorWithoutTracesTest.kt index 1391452d94..857ac24fdd 100644 --- a/integrations/dd-sdk-android-okhttp/src/test/kotlin/com/datadog/android/okhttp/DatadogInterceptorWithoutTracesTest.kt +++ b/integrations/dd-sdk-android-okhttp/src/test/kotlin/com/datadog/android/okhttp/DatadogInterceptorWithoutTracesTest.kt @@ -13,6 +13,12 @@ import com.datadog.android.core.sampling.Sampler import com.datadog.android.okhttp.trace.TracedRequestListener import com.datadog.android.okhttp.trace.TracingInterceptor import com.datadog.android.okhttp.trace.TracingInterceptorTest +import com.datadog.android.okhttp.trace.aDatadogTraceId +import com.datadog.android.okhttp.trace.newAgentPropagationMock +import com.datadog.android.okhttp.trace.newSpanBuilderMock +import com.datadog.android.okhttp.trace.newSpanContextMock +import com.datadog.android.okhttp.trace.newSpanMock +import com.datadog.android.okhttp.trace.newTracerMock import com.datadog.android.okhttp.utils.config.DatadogSingletonTestConfiguration import com.datadog.android.okhttp.utils.config.GlobalRumMonitorTestConfiguration import com.datadog.android.okhttp.utils.verifyLog @@ -21,10 +27,12 @@ import com.datadog.android.rum.RumResourceAttributesProvider import com.datadog.android.rum.RumResourceKind import com.datadog.android.rum.RumResourceMethod import com.datadog.android.rum.resource.ResourceId -import com.datadog.legacy.trace.api.interceptor.MutableSpan -import com.datadog.opentracing.DDSpan -import com.datadog.opentracing.DDSpanContext -import com.datadog.opentracing.DDTracer +import com.datadog.android.trace.api.propagation.DatadogPropagation +import com.datadog.android.trace.api.span.DatadogSpan +import com.datadog.android.trace.api.span.DatadogSpanBuilder +import com.datadog.android.trace.api.span.DatadogSpanContext +import com.datadog.android.trace.api.trace.DatadogTraceId +import com.datadog.android.trace.api.tracer.DatadogTracer import com.datadog.tools.unit.annotations.TestConfigurationsProvider import com.datadog.tools.unit.extensions.TestConfigurationExtension import com.datadog.tools.unit.extensions.config.TestConfiguration @@ -34,13 +42,11 @@ import fr.xgouchet.elmyr.Forge import fr.xgouchet.elmyr.annotation.BoolForgery import fr.xgouchet.elmyr.annotation.Forgery import fr.xgouchet.elmyr.annotation.IntForgery +import fr.xgouchet.elmyr.annotation.LongForgery import fr.xgouchet.elmyr.annotation.StringForgery import fr.xgouchet.elmyr.annotation.StringForgeryType import fr.xgouchet.elmyr.junit5.ForgeConfiguration import fr.xgouchet.elmyr.junit5.ForgeExtension -import io.opentracing.Span -import io.opentracing.SpanContext -import io.opentracing.Tracer import okhttp3.HttpUrl.Companion.toHttpUrl import okhttp3.Interceptor import okhttp3.MediaType @@ -78,15 +84,22 @@ import java.util.Locale ExtendWith(TestConfigurationExtension::class) ) @MockitoSettings(strictness = Strictness.LENIENT) -@ForgeConfiguration(BaseConfigurator::class) +@ForgeConfiguration(value = BaseConfigurator::class) internal class DatadogInterceptorWithoutTracesTest { lateinit var testedInterceptor: TracingInterceptor // region Mocks - @Mock - lateinit var mockLocalTracer: Tracer + private lateinit var mockLocalTracer: DatadogTracer + + private lateinit var mockSpanBuilder: DatadogSpanBuilder + + private lateinit var mockSpanContext: DatadogSpanContext + + private lateinit var mockPropagation: DatadogPropagation + + private lateinit var fakeSpan: DatadogSpan @Mock lateinit var mockChain: Interceptor.Chain @@ -101,16 +114,7 @@ internal class DatadogInterceptorWithoutTracesTest { lateinit var mockResolver: DefaultFirstPartyHostHeaderTypeResolver @Mock - lateinit var mockSpanBuilder: DDTracer.DDSpanBuilder - - @Mock - lateinit var mockSpanContext: DDSpanContext - - @Mock - lateinit var mockSpan: DDSpan - - @Mock - lateinit var mockTraceSampler: Sampler + lateinit var mockTraceSampler: Sampler @Mock lateinit var mockInternalLogger: InternalLogger @@ -133,11 +137,13 @@ internal class DatadogInterceptorWithoutTracesTest { lateinit var fakeResourceAttributes: Map - @StringForgery(type = StringForgeryType.HEXADECIMAL) - lateinit var fakeSpanId: String + @LongForgery + var fakeSpanId: Long = 0L + + @StringForgery(regex = "[a-f][0-9]{31}") + lateinit var fakeTraceIdString: String - @StringForgery(type = StringForgeryType.HEXADECIMAL) - lateinit var fakeTraceId: String + lateinit var fakeTraceId: DatadogTraceId @BoolForgery var fakeRedacted404Resources: Boolean = true @@ -145,14 +151,14 @@ internal class DatadogInterceptorWithoutTracesTest { @BeforeEach fun `set up`(forge: Forge) { - whenever(mockLocalTracer.buildSpan(TracingInterceptor.SPAN_NAME)) doReturn mockSpanBuilder - whenever(mockSpanBuilder.withOrigin(DatadogInterceptor.ORIGIN_RUM)) doReturn mockSpanBuilder - whenever(mockSpanBuilder.asChildOf(null as SpanContext?)) doReturn mockSpanBuilder - whenever(mockSpanBuilder.start()) doReturn mockSpan - whenever(mockSpan.samplingPriority) doReturn null - whenever(mockSpan.context()) doReturn mockSpanContext - whenever(mockSpanContext.toSpanId()) doReturn fakeSpanId - whenever(mockSpanContext.toTraceId()) doReturn fakeTraceId + fakeTraceId = forge.aDatadogTraceId(fakeTraceIdString) + mockSpanContext = forge.newSpanContextMock(fakeTraceId, fakeSpanId) + fakeSpan = forge.newSpanMock(mockSpanContext) + mockSpanBuilder = forge.newSpanBuilderMock(fakeSpan, mockSpanContext) + mockPropagation = newAgentPropagationMock() + mockLocalTracer = forge.newTracerMock(mockSpanBuilder, mockPropagation) + + whenever(fakeSpan.samplingPriority) doReturn null whenever(mockTraceSampler.sample(any())) doReturn true whenever(rumMonitor.mockSdkCore.firstPartyHostResolver) doReturn mockResolver @@ -167,8 +173,10 @@ internal class DatadogInterceptorWithoutTracesTest { rumResourceAttributesProvider = mockRumAttributesProvider, traceSampler = mockTraceSampler, redacted404ResourceName = fakeRedacted404Resources, - traceContextInjection = TraceContextInjection.ALL - ) { _, _ -> mockLocalTracer } + traceContextInjection = TraceContextInjection.ALL, + localTracerFactory = { _, _ -> mockLocalTracer }, + globalTracerProvider = { null } + ) whenever(rumMonitor.mockSdkCore.getFeature(Feature.TRACING_FEATURE_NAME)) doReturn mock() whenever(rumMonitor.mockSdkCore.getFeature(Feature.RUM_FEATURE_NAME)) doReturn mock() whenever(rumMonitor.mockSdkCore.internalLogger) doReturn mockInternalLogger @@ -356,7 +364,7 @@ internal class DatadogInterceptorWithoutTracesTest { val response = testedInterceptor.intercept(mockChain) verify(mockSpanBuilder).withOrigin(DatadogInterceptor.ORIGIN_RUM) - verify(mockSpan).drop() + verify(fakeSpan).drop() assertThat(response).isSameAs(fakeResponse) } @@ -370,9 +378,9 @@ internal class DatadogInterceptorWithoutTracesTest { val response = testedInterceptor.intercept(mockChain) verify(mockSpanBuilder).withOrigin(DatadogInterceptor.ORIGIN_RUM) - verify(mockSpan as MutableSpan).setResourceName(fakeUrl.lowercase(Locale.US)) - verify(mockSpan as MutableSpan).setError(true) - verify(mockSpan).drop() + verify(fakeSpan).resourceName = fakeUrl.lowercase(Locale.US) + verify(fakeSpan).isError = true + verify(fakeSpan).drop() assertThat(response).isSameAs(fakeResponse) } diff --git a/integrations/dd-sdk-android-okhttp/src/test/kotlin/com/datadog/android/okhttp/TracingInterceptorBuilderTest.kt b/integrations/dd-sdk-android-okhttp/src/test/kotlin/com/datadog/android/okhttp/TracingInterceptorBuilderTest.kt index 06e15f22b7..3fb360666a 100644 --- a/integrations/dd-sdk-android-okhttp/src/test/kotlin/com/datadog/android/okhttp/TracingInterceptorBuilderTest.kt +++ b/integrations/dd-sdk-android-okhttp/src/test/kotlin/com/datadog/android/okhttp/TracingInterceptorBuilderTest.kt @@ -12,6 +12,7 @@ import com.datadog.android.okhttp.trace.NoOpTracedRequestListener import com.datadog.android.okhttp.trace.TracedRequestListener import com.datadog.android.okhttp.trace.TracingInterceptor import com.datadog.android.trace.TracingHeaderType +import com.datadog.android.trace.api.span.DatadogSpan import com.datadog.tools.unit.extensions.TestConfigurationExtension import com.datadog.tools.unit.forge.BaseConfigurator import fr.xgouchet.elmyr.Forge @@ -20,7 +21,6 @@ import fr.xgouchet.elmyr.annotation.FloatForgery import fr.xgouchet.elmyr.annotation.StringForgery import fr.xgouchet.elmyr.junit5.ForgeConfiguration import fr.xgouchet.elmyr.junit5.ForgeExtension -import io.opentracing.Span import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test @@ -41,7 +41,7 @@ import org.mockito.quality.Strictness internal class TracingInterceptorBuilderTest { @StringForgery - lateinit var fakeSdkInstaceName: String + lateinit var fakeSdkInstanceName: String private lateinit var fakeTraceContextInjection: TraceContextInjection @@ -51,7 +51,7 @@ internal class TracingInterceptorBuilderTest { lateinit var mockTracedRequestListener: TracedRequestListener @Mock - lateinit var mockSampler: Sampler + lateinit var mockSampler: Sampler @StringForgery lateinit var fakeOrigin: String @@ -115,13 +115,13 @@ internal class TracingInterceptorBuilderTest { fun `M set sdkInstanceName W build { setSdkInstanceName }`() { // When val interceptor = TracingInterceptor.Builder(fakeTracedHostsWithHeaderType) - .setSdkInstanceName(fakeSdkInstaceName) + .setSdkInstanceName(fakeSdkInstanceName) .build() // Then assertThat(interceptor.tracedHosts).isEqualTo(fakeTracedHostsWithHeaderType) assertThat(interceptor.traceContextInjection).isEqualTo(TraceContextInjection.SAMPLED) - assertThat(interceptor.sdkInstanceName).isEqualTo(fakeSdkInstaceName) + assertThat(interceptor.sdkInstanceName).isEqualTo(fakeSdkInstanceName) assertThat(interceptor.tracedRequestListener).isInstanceOf(NoOpTracedRequestListener::class.java) assertThat(interceptor.traceSampler).isInstanceOf(DeterministicTraceSampler::class.java) assertThat(interceptor.traceSampler.getSampleRate()).isEqualTo(100f) diff --git a/integrations/dd-sdk-android-okhttp/src/test/kotlin/com/datadog/android/okhttp/internal/otel/TraceContextExtTest.kt b/integrations/dd-sdk-android-okhttp/src/test/kotlin/com/datadog/android/okhttp/internal/otel/TraceContextExtTest.kt deleted file mode 100644 index c8a7453871..0000000000 --- a/integrations/dd-sdk-android-okhttp/src/test/kotlin/com/datadog/android/okhttp/internal/otel/TraceContextExtTest.kt +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. - * This product includes software developed at Datadog (https://www.datadoghq.com/). - * Copyright 2016-Present Datadog, Inc. - */ - -package com.datadog.android.okhttp.internal.otel - -import com.datadog.android.okhttp.TraceContext -import com.datadog.android.okhttp.internal.utils.forge.OkHttpConfigurator -import com.datadog.opentracing.propagation.ExtractedContext -import fr.xgouchet.elmyr.Forge -import fr.xgouchet.elmyr.annotation.Forgery -import fr.xgouchet.elmyr.junit5.ForgeConfiguration -import fr.xgouchet.elmyr.junit5.ForgeExtension -import org.assertj.core.api.Assertions.assertThat -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.extension.ExtendWith -import org.junit.jupiter.api.extension.Extensions -import org.mockito.junit.jupiter.MockitoExtension -import org.mockito.junit.jupiter.MockitoSettings -import org.mockito.quality.Strictness -import java.math.BigInteger - -@Extensions( - ExtendWith(MockitoExtension::class), - ExtendWith(ForgeExtension::class) -) -@MockitoSettings(strictness = Strictness.LENIENT) -@ForgeConfiguration(OkHttpConfigurator::class) -internal class TraceContextExtTest { - - @Forgery - lateinit var fakeTestedContext: TraceContext - - @Test - fun `M extract the context W toOpenTracingContext`() { - // When - val spanContext = fakeTestedContext.toOpenTracingContext() - - // Then - val extractedContext = spanContext as ExtractedContext - assertThat(extractedContext.traceId).isEqualTo(BigInteger(fakeTestedContext.traceId, 16)) - assertThat(extractedContext.spanId).isEqualTo(BigInteger(fakeTestedContext.spanId, 16)) - assertThat(extractedContext.samplingPriority).isEqualTo(fakeTestedContext.samplingPriority) - } - - @Test - fun `M extract the context W toOpenTracingContext {broken trace id format}`(forge: Forge) { - // When - val fakeBrokenTraceId = forge.aNonHexadecimalString() - fakeTestedContext = fakeTestedContext.copy(traceId = fakeBrokenTraceId) - val spanContext = fakeTestedContext.toOpenTracingContext() - - // Then - val extractedContext = spanContext as ExtractedContext - assertThat(extractedContext.traceId).isEqualTo(BigInteger.ZERO) - assertThat(extractedContext.spanId).isEqualTo(BigInteger(fakeTestedContext.spanId, 16)) - assertThat(extractedContext.samplingPriority).isEqualTo(fakeTestedContext.samplingPriority) - } - - @Test - fun `M extract the context W toOpenTracingContext {broken span id format}`(forge: Forge) { - // When - val fakeBrokenSpanId = forge.aNonHexadecimalString() - fakeTestedContext = fakeTestedContext.copy(spanId = fakeBrokenSpanId) - val spanContext = fakeTestedContext.toOpenTracingContext() - - // Then - val extractedContext = spanContext as ExtractedContext - assertThat(extractedContext.traceId).isEqualTo(BigInteger(fakeTestedContext.traceId, 16)) - assertThat(extractedContext.spanId).isEqualTo(BigInteger.ZERO) - assertThat(extractedContext.samplingPriority).isEqualTo(fakeTestedContext.samplingPriority) - } - - private fun Forge.aNonHexadecimalString(): String { - return anAlphabeticalString() + aStringMatching("[^0-9a-fA-F]") - } -} diff --git a/integrations/dd-sdk-android-okhttp/src/test/kotlin/com/datadog/android/okhttp/internal/utils/SpanContextExtTest.kt b/integrations/dd-sdk-android-okhttp/src/test/kotlin/com/datadog/android/okhttp/internal/utils/SpanContextExtTest.kt deleted file mode 100644 index 27cbca72eb..0000000000 --- a/integrations/dd-sdk-android-okhttp/src/test/kotlin/com/datadog/android/okhttp/internal/utils/SpanContextExtTest.kt +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. - * This product includes software developed at Datadog (https://www.datadoghq.com/). - * Copyright 2016-Present Datadog, Inc. - */ - -package com.datadog.android.okhttp.internal.utils - -import com.datadog.opentracing.DDSpanContext -import com.datadog.tools.unit.forge.BaseConfigurator -import fr.xgouchet.elmyr.annotation.StringForgery -import fr.xgouchet.elmyr.junit5.ForgeConfiguration -import fr.xgouchet.elmyr.junit5.ForgeExtension -import io.opentracing.Span -import io.opentracing.SpanContext -import org.assertj.core.api.Assertions.assertThat -import org.junit.jupiter.api.BeforeEach -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.extension.ExtendWith -import org.junit.jupiter.api.extension.Extensions -import org.mockito.Mock -import org.mockito.junit.jupiter.MockitoExtension -import org.mockito.junit.jupiter.MockitoSettings -import org.mockito.kotlin.mock -import org.mockito.kotlin.whenever -import org.mockito.quality.Strictness -import java.math.BigInteger - -@Extensions( - ExtendWith(MockitoExtension::class), - ExtendWith(ForgeExtension::class) -) -@MockitoSettings(strictness = Strictness.LENIENT) -@ForgeConfiguration(BaseConfigurator::class) -internal class SpanContextExtTest { - - @Mock - lateinit var mockSpan: Span - - @Mock - lateinit var mockDDSpanContext: DDSpanContext - - @StringForgery(regex = "([a-f0-9]{32})") - lateinit var expectedFakeTraceId: String - - private lateinit var fakeTraceIdAsBigInteger: BigInteger - - @BeforeEach - fun `set up`() { - fakeTraceIdAsBigInteger = BigInteger(expectedFakeTraceId, 16) - } - - @Test - fun `M return empty string W traceIdAsHexString() { spanContext is not DDSpanContext }`() { - // When - whenever(mockSpan.context()).thenReturn(mock()) - - // When - val result = mockSpan.context().traceIdAsHexString() - - // Then - assertThat(result).isEmpty() - } - - @Test - fun `M return empty string W traceIdAsHexString() { ddSpanContext id is null}`() { - // When - whenever(mockSpan.context()).thenReturn(mockDDSpanContext) - whenever(mockDDSpanContext.traceId).thenReturn(null) - - // When - val result = mockSpan.context().traceIdAsHexString() - - // Then - assertThat(result).isEmpty() - } - - @Test - fun `M return the expected hexa padded string W traceIdAsHexString()`() { - // When - whenever(mockSpan.context()).thenReturn(mockDDSpanContext) - whenever(mockDDSpanContext.traceId).thenReturn(fakeTraceIdAsBigInteger) - - // When - val result = mockSpan.context().traceIdAsHexString() - - // Then - assertThat(result).isEqualTo(expectedFakeTraceId) - } -} diff --git a/integrations/dd-sdk-android-okhttp/src/test/kotlin/com/datadog/android/okhttp/internal/utils/SpanSamplingIdProviderTest.kt b/integrations/dd-sdk-android-okhttp/src/test/kotlin/com/datadog/android/okhttp/internal/utils/SpanSamplingIdProviderTest.kt index 363f0a07e1..49f3d6ac95 100644 --- a/integrations/dd-sdk-android-okhttp/src/test/kotlin/com/datadog/android/okhttp/internal/utils/SpanSamplingIdProviderTest.kt +++ b/integrations/dd-sdk-android-okhttp/src/test/kotlin/com/datadog/android/okhttp/internal/utils/SpanSamplingIdProviderTest.kt @@ -8,14 +8,17 @@ package com.datadog.android.okhttp.internal.utils import com.datadog.android.internal.utils.toHexString import com.datadog.android.log.LogAttributes -import com.datadog.opentracing.DDSpanContext +import com.datadog.android.okhttp.trace.newSpanMock +import com.datadog.android.trace.api.ZERO +import com.datadog.android.trace.api.from +import com.datadog.android.trace.api.span.DatadogSpan +import com.datadog.android.trace.api.span.DatadogSpanContext +import com.datadog.android.trace.api.trace.DatadogTraceId import com.datadog.tools.unit.forge.BaseConfigurator import fr.xgouchet.elmyr.Forge import fr.xgouchet.elmyr.annotation.LongForgery -import fr.xgouchet.elmyr.annotation.StringForgery import fr.xgouchet.elmyr.junit5.ForgeConfiguration import fr.xgouchet.elmyr.junit5.ForgeExtension -import io.opentracing.Span import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test @@ -37,18 +40,18 @@ import org.mockito.quality.Strictness internal class SpanSamplingIdProviderTest { @Mock - lateinit var mockSpan: Span + lateinit var mockSpan: DatadogSpan @Mock - lateinit var mockSpanContext: DDSpanContext + lateinit var mockSpanContext: DatadogSpanContext - lateinit var fakeTags: Map + private lateinit var fakeTags: Map @BeforeEach fun `set up`(forge: Forge) { fakeTags = forge.aMap { anAlphabeticalString() to aString() } - whenever(mockSpan.context()) doReturn mockSpanContext - whenever(mockSpanContext.tags) doReturn forge.aNullable { fakeTags } + whenever(mockSpanContext.tags) doReturn fakeTags + mockSpan = forge.newSpanMock(mockSpanContext) } @Test @@ -78,7 +81,7 @@ internal class SpanSamplingIdProviderTest { ) { // Given val expectedId = traceId.toULong() - whenever(mockSpanContext.toTraceId()) doReturn traceId.toString() + whenever(mockSpanContext.traceId) doReturn DatadogTraceId.from(traceId) // When val result = SpanSamplingIdProvider.provideId(mockSpan) @@ -88,12 +91,10 @@ internal class SpanSamplingIdProviderTest { } @Test - fun `M return 0u W provideId() {no rum session, invalid traceId}`( - @StringForgery(regex = "([g-z][\\w\\d])+") fakeString: String - ) { + fun `M return 0u W provideId() {no rum session, invalid traceId}`() { // Given val expectedId: ULong = 0u - whenever(mockSpanContext.toTraceId()) doReturn fakeString + whenever(mockSpanContext.traceId) doReturn DatadogTraceId.ZERO // When val result = SpanSamplingIdProvider.provideId(mockSpan) @@ -106,7 +107,7 @@ internal class SpanSamplingIdProviderTest { fun `M return 0u W provideId() {no rum session, empty traceId}`() { // Given val expectedId: ULong = 0u - whenever(mockSpanContext.toTraceId()) doReturn "" + whenever(mockSpanContext.traceId) doReturn DatadogTraceId.ZERO // When val result = SpanSamplingIdProvider.provideId(mockSpan) diff --git a/integrations/dd-sdk-android-okhttp/src/test/kotlin/com/datadog/android/okhttp/internal/utils/forge/TraceContextFactory.kt b/integrations/dd-sdk-android-okhttp/src/test/kotlin/com/datadog/android/okhttp/internal/utils/forge/TraceContextFactory.kt index 30f36639c2..06132f8b62 100644 --- a/integrations/dd-sdk-android-okhttp/src/test/kotlin/com/datadog/android/okhttp/internal/utils/forge/TraceContextFactory.kt +++ b/integrations/dd-sdk-android-okhttp/src/test/kotlin/com/datadog/android/okhttp/internal/utils/forge/TraceContextFactory.kt @@ -13,7 +13,7 @@ import fr.xgouchet.elmyr.ForgeryFactory internal class TraceContextFactory : ForgeryFactory { override fun getForgery(forge: Forge): TraceContext { - val fakeTraceId = forge.aStringMatching("[0-9a-f]{32}") + val fakeTraceId = forge.aStringMatching("[0-9a-f]{31}") val fakeSpanId = forge.aStringMatching("[0-9a-f]{16}") val fakeSamplingPriority = forge.anInt() return TraceContext( diff --git a/integrations/dd-sdk-android-okhttp/src/test/kotlin/com/datadog/android/okhttp/trace/OkHttpRequestExtTest.kt b/integrations/dd-sdk-android-okhttp/src/test/kotlin/com/datadog/android/okhttp/trace/OkHttpRequestExtTest.kt index 5efd9f6c91..b508760bdc 100644 --- a/integrations/dd-sdk-android-okhttp/src/test/kotlin/com/datadog/android/okhttp/trace/OkHttpRequestExtTest.kt +++ b/integrations/dd-sdk-android-okhttp/src/test/kotlin/com/datadog/android/okhttp/trace/OkHttpRequestExtTest.kt @@ -6,11 +6,11 @@ package com.datadog.android.okhttp.trace +import com.datadog.android.trace.api.span.DatadogSpan import com.datadog.tools.unit.forge.BaseConfigurator import fr.xgouchet.elmyr.annotation.StringForgery import fr.xgouchet.elmyr.junit5.ForgeConfiguration import fr.xgouchet.elmyr.junit5.ForgeExtension -import io.opentracing.Span import okhttp3.Request import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -33,9 +33,9 @@ class OkHttpRequestExtTest { fun `set the parentSpan through the Request builder`( @StringForgery(regex = "http://[a-z0-9_]{8}\\.[a-z]{3}/") fakeUrl: String ) { - val parentSpan: Span = mock() + val parentSpan: DatadogSpan = mock() val request = Request.Builder().url(fakeUrl).parentSpan(parentSpan).build() - assertThat(request.tag(Span::class.java)).isEqualTo(parentSpan) + assertThat(request.tag(DatadogSpan::class.java)).isEqualTo(parentSpan) } } diff --git a/integrations/dd-sdk-android-okhttp/src/test/kotlin/com/datadog/android/okhttp/trace/TracingInterceptorContextInjectionSampledTest.kt b/integrations/dd-sdk-android-okhttp/src/test/kotlin/com/datadog/android/okhttp/trace/TracingInterceptorContextInjectionSampledTest.kt index ca8459f1da..69d373bb74 100644 --- a/integrations/dd-sdk-android-okhttp/src/test/kotlin/com/datadog/android/okhttp/trace/TracingInterceptorContextInjectionSampledTest.kt +++ b/integrations/dd-sdk-android-okhttp/src/test/kotlin/com/datadog/android/okhttp/trace/TracingInterceptorContextInjectionSampledTest.kt @@ -3,7 +3,6 @@ * This product includes software developed at Datadog (https://www.datadoghq.com/). * Copyright 2016-Present Datadog, Inc. */ - package com.datadog.android.okhttp.trace import com.datadog.android.api.InternalLogger @@ -19,30 +18,29 @@ import com.datadog.android.okhttp.utils.config.DatadogSingletonTestConfiguration import com.datadog.android.okhttp.utils.config.GlobalRumMonitorTestConfiguration import com.datadog.android.okhttp.utils.verifyLog import com.datadog.android.trace.TracingHeaderType -import com.datadog.legacy.trace.api.interceptor.MutableSpan -import com.datadog.legacy.trace.api.sampling.PrioritySampling -import com.datadog.opentracing.DDSpanContext -import com.datadog.opentracing.DDTracer -import com.datadog.opentracing.propagation.ExtractedContext +import com.datadog.android.trace.api.DatadogTracingConstants +import com.datadog.android.trace.api.propagation.DatadogPropagation +import com.datadog.android.trace.api.span.DatadogSpan +import com.datadog.android.trace.api.span.DatadogSpanBuilder +import com.datadog.android.trace.api.span.DatadogSpanContext +import com.datadog.android.trace.api.trace.DatadogTraceId +import com.datadog.android.trace.api.tracer.DatadogTracer +import com.datadog.android.trace.api.withMockPropagationHelper +import com.datadog.android.trace.internal.DatadogPropagationHelper +import com.datadog.android.trace.internal.DatadogTracingToolkit +import com.datadog.android.trace.internal.fromHex import com.datadog.tools.unit.annotations.TestConfigurationsProvider import com.datadog.tools.unit.extensions.TestConfigurationExtension import com.datadog.tools.unit.extensions.config.TestConfiguration -import com.datadog.tools.unit.setStaticValue import fr.xgouchet.elmyr.Forge import fr.xgouchet.elmyr.annotation.BoolForgery import fr.xgouchet.elmyr.annotation.Forgery import fr.xgouchet.elmyr.annotation.IntForgery +import fr.xgouchet.elmyr.annotation.LongForgery import fr.xgouchet.elmyr.annotation.StringForgery import fr.xgouchet.elmyr.annotation.StringForgeryType import fr.xgouchet.elmyr.junit5.ForgeConfiguration import fr.xgouchet.elmyr.junit5.ForgeExtension -import io.opentracing.Span -import io.opentracing.SpanContext -import io.opentracing.Tracer -import io.opentracing.propagation.TextMapExtract -import io.opentracing.propagation.TextMapInject -import io.opentracing.tag.Tags -import io.opentracing.util.GlobalTracer import okhttp3.HttpUrl import okhttp3.HttpUrl.Companion.toHttpUrl import okhttp3.Interceptor @@ -54,7 +52,6 @@ import okhttp3.RequestBody.Companion.toRequestBody import okhttp3.Response import okhttp3.ResponseBody.Companion.toResponseBody import org.assertj.core.api.Assertions.assertThat -import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test import org.junit.jupiter.api.assertThrows @@ -76,7 +73,6 @@ import org.mockito.kotlin.verify import org.mockito.kotlin.verifyNoInteractions import org.mockito.kotlin.whenever import org.mockito.quality.Strictness -import java.math.BigInteger import java.util.Locale import java.util.concurrent.CountDownLatch import java.util.concurrent.TimeUnit @@ -98,32 +94,31 @@ internal class TracingInterceptorContextInjectionSampledTest { // region Mocks - @Mock - lateinit var mockTracer: Tracer + private lateinit var mockTracer: DatadogTracer - @Mock - lateinit var mockLocalTracer: Tracer + private lateinit var mockPropagation: DatadogPropagation - @Mock - lateinit var mockSpanBuilder: DDTracer.DDSpanBuilder + private lateinit var mockLocalTracer: DatadogTracer - @Mock - lateinit var mockSpanContext: DDSpanContext + private lateinit var mockSpanBuilder: DatadogSpanBuilder - @Mock(extraInterfaces = [MutableSpan::class]) - lateinit var mockSpan: Span + private lateinit var mockSpanContext: DatadogSpanContext + + private lateinit var mockSpan: DatadogSpan @Mock lateinit var mockChain: Interceptor.Chain + @Mock + lateinit var mockPropagationHelper: DatadogPropagationHelper + @Mock lateinit var mockRequestListener: TracedRequestListener @Mock lateinit var mockResolver: DefaultFirstPartyHostHeaderTypeResolver - @Mock - lateinit var mockTraceSampler: Sampler + lateinit var mockTraceSampler: Sampler @Mock lateinit var mockInternalLogger: InternalLogger @@ -135,9 +130,9 @@ internal class TracingInterceptorContextInjectionSampledTest { // region Fakes - lateinit var fakeMethod: String - var fakeBody: String? = null - var fakeMediaType: MediaType? = null + private lateinit var fakeMethod: String + private var fakeBody: String? = null + private var fakeMediaType: MediaType? = null @StringForgery(type = StringForgeryType.ASCII) lateinit var fakeResponseBody: String @@ -149,34 +144,32 @@ internal class TracingInterceptorContextInjectionSampledTest { lateinit var fakeRequest: Request lateinit var fakeResponse: Response - @StringForgery(type = StringForgeryType.HEXADECIMAL) - lateinit var fakeSpanId: String + @LongForgery + var fakeSpanId: Long = 0L - @StringForgery(regex = "[a-f][0-9]{32}") + @StringForgery(regex = "[a-f][0-9]{31}") lateinit var fakeTraceIdAsString: String - lateinit var fakeTraceId: BigInteger + private lateinit var fakeTraceId: DatadogTraceId - lateinit var fakeOrigin: String + private lateinit var fakeOrigin: String - lateinit var fakeLocalHosts: Map> + private lateinit var fakeLocalHosts: Map> // endregion @BeforeEach fun `set up`(forge: Forge) { fakeOrigin = forge.anAlphabeticalString() - fakeTraceId = BigInteger(fakeTraceIdAsString, 16) - whenever(mockTracer.buildSpan(TracingInterceptor.SPAN_NAME)) doReturn mockSpanBuilder - whenever(mockLocalTracer.buildSpan(TracingInterceptor.SPAN_NAME)) doReturn mockSpanBuilder - whenever(mockSpanBuilder.withOrigin(fakeOrigin)) doReturn mockSpanBuilder - whenever(mockSpanBuilder.asChildOf(null as SpanContext?)) doReturn mockSpanBuilder - whenever(mockSpanBuilder.start()) doReturn mockSpan - whenever(mockSpan.context()) doReturn mockSpanContext - whenever(mockSpanContext.toSpanId()) doReturn fakeSpanId - whenever(mockSpanContext.traceId).thenReturn(fakeTraceId) - whenever(mockSpanContext.toTraceId()) doReturn fakeTraceId.toString() - whenever(mockTraceSampler.sample(mockSpan)) doReturn true + fakeTraceId = forge.aDatadogTraceId(fakeTraceIdAsString) + mockSpanContext = forge.newSpanContextMock(fakeTraceId, fakeSpanId) + mockSpan = forge.newSpanMock(mockSpanContext) + mockSpanBuilder = forge.newSpanBuilderMock(mockSpan, mockSpanContext) + mockPropagation = newAgentPropagationMock() + mockTracer = forge.newTracerMock(mockSpanBuilder, mockPropagation) + mockLocalTracer = forge.newTracerMock(mockSpanBuilder, mockPropagation) + mockTraceSampler = forge.newTraceSamplerMock(mockSpan) + val mediaType = forge.anElementFrom("application", "image", "text", "model") + "/" + forge.anAlphabeticalString() fakeLocalHosts = @@ -187,23 +180,25 @@ internal class TracingInterceptorContextInjectionSampledTest { whenever(rumMonitor.mockSdkCore.getFeature(Feature.TRACING_FEATURE_NAME)) doReturn mock() whenever(rumMonitor.mockSdkCore.internalLogger) doReturn mockInternalLogger whenever(rumMonitor.mockSdkCore.firstPartyHostResolver) doReturn mockResolver - testedInterceptor = instantiateTestedInterceptor(fakeLocalHosts) { _, _ -> - mockLocalTracer - } - - GlobalTracer.registerIfAbsent(mockTracer) + testedInterceptor = instantiateTestedInterceptor( + fakeLocalHosts, + localTracerFactory = { _, _ -> mockLocalTracer }, + globalTracerProvider = { mockTracer } + ) } - fun instantiateTestedInterceptor( + private fun instantiateTestedInterceptor( tracedHosts: Map> = emptyMap(), - factory: (SdkCore, Set) -> Tracer + globalTracerProvider: () -> DatadogTracer? = { null }, + localTracerFactory: (SdkCore, Set) -> DatadogTracer ): TracingInterceptor { return TracingInterceptor.Builder(tracedHosts) .setTracedRequestListener(mockRequestListener) .setTraceOrigin(fakeOrigin) .setTraceSampler(mockTraceSampler) .setTraceContextInjection(TraceContextInjection.SAMPLED) - .setLocalTracerFactory(factory) + .setLocalTracerFactory(localTracerFactory) + .setGlobalTracerProvider(globalTracerProvider) .set404ResourcesRedacted(fakeRedacted404Resources) .build() } @@ -212,11 +207,6 @@ internal class TracingInterceptorContextInjectionSampledTest { return fakeOrigin } - @AfterEach - fun `tear down`() { - GlobalTracer::class.java.setStaticValue("isRegistered", false) - } - @Test fun `M instantiate with default values W init()`() { // Given @@ -287,10 +277,7 @@ internal class TracingInterceptorContextInjectionSampledTest { ) ) - doAnswer { invocation -> - val carrier = invocation.arguments[2] as TextMapInject - carrier.put(key, value) - }.whenever(mockTracer).inject(any(), any(), any()) + mockPropagation.wheneverInjectThenValueToHeaders(key, value) val response = testedInterceptor.intercept(mockChain) @@ -326,12 +313,12 @@ internal class TracingInterceptorContextInjectionSampledTest { ) ) doAnswer { invocation -> - val carrier = invocation.arguments[2] as TextMapInject + val carrier = invocation.arguments[2] as Request.Builder datadogContextKeys.forEach { - carrier.put(it, forge.anAlphaNumericalString()) + carrier.addHeader(it, forge.anAlphaNumericalString()) } - carrier.put(nonDatadogContextKey, nonDatadogContextKeyValue) - }.whenever(mockTracer).inject(any(), any(), any()) + carrier.addHeader(nonDatadogContextKey, nonDatadogContextKeyValue) + }.whenever(mockPropagation).inject(any(), any(), any()) stubChain(mockChain, statusCode) // When @@ -455,11 +442,11 @@ internal class TracingInterceptorContextInjectionSampledTest { ) ) stubChain(mockChain, statusCode) - doAnswer { invocation -> - val carrier = invocation.arguments[2] as TextMapInject - datadogContext.forEach { carrier.put(it.key, it.value) } - carrier.put(nonDatadogContextKey, nonDatadogContextKeyValue) - }.whenever(mockTracer).inject(any(), any(), any()) + mockPropagation.wheneverInjectCalledPassContextToHeaders( + datadogContext, + nonDatadogContextKey, + nonDatadogContextKeyValue + ) // When val response = testedInterceptor.intercept(mockChain) @@ -482,17 +469,14 @@ internal class TracingInterceptorContextInjectionSampledTest { @IntForgery(min = 200, max = 300) statusCode: Int, forge: Forge ) { - val parentSpan: Span = mock() - val parentSpanContext: SpanContext = mock() + val parentSpanContext: DatadogSpanContext = forge.newSpanContextMock(samplingPriority = 1) + val parentSpan: DatadogSpan = forge.newSpanMock(parentSpanContext) whenever(parentSpan.context()) doReturn parentSpanContext - whenever(mockSpanBuilder.asChildOf(parentSpanContext)) doReturn mockSpanBuilder - fakeRequest = forgeRequest(forge) { it.tag(Span::class.java, parentSpan) } + whenever(mockSpanBuilder.withParentContext(parentSpanContext)) doReturn mockSpanBuilder + fakeRequest = forgeRequest(forge) { it.tag(DatadogSpan::class.java, parentSpan) } whenever(mockResolver.isFirstPartyUrl(fakeUrl.toHttpUrl())).thenReturn(true) stubChain(mockChain, statusCode) - doAnswer { invocation -> - val carrier = invocation.arguments[2] as TextMapInject - carrier.put(key, value) - }.whenever(mockTracer).inject(any(), any(), any()) + mockPropagation.wheneverInjectThenValueToHeaders(key, value) val response = testedInterceptor.intercept(mockChain) @@ -501,7 +485,7 @@ internal class TracingInterceptorContextInjectionSampledTest { verify(mockChain).proceed(capture()) assertThat(firstValue.headers(key)).containsOnly(value) } - verify(mockSpanBuilder).asChildOf(parentSpanContext) + verify(mockSpanBuilder).withParentContext(parentSpanContext) verify(mockSpanBuilder).withOrigin(getExpectedOrigin()) } @@ -514,37 +498,41 @@ internal class TracingInterceptorContextInjectionSampledTest { forge: Forge ) { // Given - val fakeExpectedTraceId = BigInteger(fakeTraceContext.traceId, 16) - val fakeExpectedSpanId = BigInteger(fakeTraceContext.spanId, 16) - whenever(mockSpanBuilder.asChildOf(any())) doReturn mockSpanBuilder + val fakeExpectedTraceId = DatadogTraceId.fromHex(fakeTraceContext.traceId) + val fakeExpectedSpanId = DatadogTracingToolkit.spanIdConverter.fromHex(fakeTraceContext.spanId) + val fakeExtractedContext = forge.newSpanContextMock( + fakeExpectedTraceId, + fakeExpectedSpanId + ) fakeRequest = forgeRequest(forge) { it.tag(TraceContext::class.java, fakeTraceContext) } - whenever(mockResolver.isFirstPartyUrl(fakeUrl.toHttpUrl())).thenReturn(true) + mockPropagation.wheneverInjectThenValueToHeaders(key, value) stubChain(mockChain, statusCode) - doAnswer { invocation -> - val carrier = invocation.arguments[2] as TextMapInject - carrier.put(key, value) - }.whenever(mockTracer).inject(any(), any(), any()) - - // When - val response = testedInterceptor.intercept(mockChain) - - // Then - assertThat(response).isSameAs(fakeResponse) - argumentCaptor { - verify(mockChain).proceed(capture()) - if (fakeTraceContext.samplingPriority > 0) { - assertThat(firstValue.headers(key)).containsOnly(value) - } else { - assertThat(firstValue.headers(key)).isEmpty() + whenever(mockResolver.isFirstPartyUrl(fakeUrl.toHttpUrl())).thenReturn(true) + whenever(mockSpanBuilder.withParentContext(any())) doReturn mockSpanBuilder + whenever(mockPropagationHelper.createExtractedContext(any(), any(), any())).thenReturn(fakeExtractedContext) + + DatadogTracingToolkit.withMockPropagationHelper(mockPropagationHelper) { + // When + val response = testedInterceptor.intercept(mockChain) + + // Then + assertThat(response).isSameAs(fakeResponse) + argumentCaptor { + verify(mockChain).proceed(capture()) + if (fakeTraceContext.samplingPriority > 0) { + assertThat(firstValue.headers(key)).containsOnly(value) + } else { + assertThat(firstValue.headers(key)).isEmpty() + } } + argumentCaptor { + verify(mockSpanBuilder).withParentContext(capture()) + val extractedContext = firstValue // as ExtractedContext + assertThat(extractedContext.traceId).isEqualTo(fakeExpectedTraceId) + assertThat(extractedContext.spanId).isEqualTo(fakeExpectedSpanId) + } + verify(mockSpanBuilder).withOrigin(getExpectedOrigin()) } - argumentCaptor() { - verify(mockSpanBuilder).asChildOf(capture()) - val extractedContext = firstValue as ExtractedContext - assertThat(extractedContext.traceId).isEqualTo(fakeExpectedTraceId) - assertThat(extractedContext.spanId).isEqualTo(fakeExpectedSpanId) - } - verify(mockSpanBuilder).withOrigin(getExpectedOrigin()) } @Test @@ -557,10 +545,7 @@ internal class TracingInterceptorContextInjectionSampledTest { fakeRequest = fakeRequest.newBuilder().addHeader(key, previousValue).build() stubChain(mockChain, statusCode) whenever(mockResolver.isFirstPartyUrl(fakeUrl.toHttpUrl())).thenReturn(true) - doAnswer { invocation -> - val carrier = invocation.arguments[2] as TextMapInject - carrier.put(key, value) - }.whenever(mockTracer).inject(any(), any(), any()) + mockPropagation.wheneverInjectThenValueToHeaders(key, value) val response = testedInterceptor.intercept(mockChain) @@ -583,10 +568,7 @@ internal class TracingInterceptorContextInjectionSampledTest { fakeRequest = forgeRequest(forge).newBuilder().addHeader(key, previousValue).build() whenever(mockResolver.isFirstPartyUrl(fakeUrl.toHttpUrl())).thenReturn(false) stubChain(mockChain, statusCode) - doAnswer { invocation -> - val carrier = invocation.arguments[2] as TextMapInject - carrier.put(key, value) - }.whenever(mockTracer).inject(any(), any(), any()) + mockPropagation.wheneverInjectThenValueToHeaders(key, value) val response = testedInterceptor.intercept(mockChain) @@ -607,8 +589,7 @@ internal class TracingInterceptorContextInjectionSampledTest { fakeRequest = forgeRequest(forge) whenever(mockResolver.isFirstPartyUrl(fakeUrl.toHttpUrl())).thenReturn(false) stubChain(mockChain, statusCode) - doThrow(IllegalStateException(message)).whenever(mockTracer) - .inject(any(), any(), any()) + mockPropagation.wheneverInjectThenThrow(IllegalStateException(message)) val response = testedInterceptor.intercept(mockChain) @@ -625,25 +606,28 @@ internal class TracingInterceptorContextInjectionSampledTest { @StringForgery(type = StringForgeryType.ALPHA_NUMERICAL) value: String, @IntForgery(min = 200, max = 300) statusCode: Int ) { - val parentSpanContext: ExtractedContext = mock() - whenever(mockTracer.extract(any(), any())) doReturn parentSpanContext - whenever(mockSpanBuilder.asChildOf(any())) doReturn mockSpanBuilder + // Given + val parentSpanContext: DatadogSpanContext = mock() + whenever(mockPropagationHelper.isExtractedContext(parentSpanContext)).thenReturn(true) + whenever(mockPropagation.extract(any(), any())) doReturn parentSpanContext + whenever(mockSpanBuilder.withParentContext(any())) doReturn mockSpanBuilder whenever(mockResolver.isFirstPartyUrl(fakeUrl.toHttpUrl())).thenReturn(true) stubChain(mockChain, statusCode) - doAnswer { invocation -> - val carrier = invocation.arguments[2] as TextMapInject - carrier.put(key, value) - }.whenever(mockTracer).inject(any(), any(), any()) + mockPropagation.wheneverInjectThenValueToHeaders(key, value) - val response = testedInterceptor.intercept(mockChain) + DatadogTracingToolkit.withMockPropagationHelper(mockPropagationHelper) { + // When + val response = testedInterceptor.intercept(mockChain) - assertThat(response).isSameAs(fakeResponse) - argumentCaptor { - verify(mockChain).proceed(capture()) - assertThat(firstValue.headers(key)).containsOnly(value) + // Then + assertThat(response).isSameAs(fakeResponse) + argumentCaptor { + verify(mockChain).proceed(capture()) + assertThat(firstValue.headers(key)).containsOnly(value) + } + verify(mockSpanBuilder).withParentContext(parentSpanContext) + verify(mockSpanBuilder).withOrigin(getExpectedOrigin()) } - verify(mockSpanBuilder).asChildOf(parentSpanContext) - verify(mockSpanBuilder).withOrigin(getExpectedOrigin()) } // endregion @@ -663,17 +647,14 @@ internal class TracingInterceptorContextInjectionSampledTest { it.addHeader( TracingInterceptor.DATADOG_SAMPLING_PRIORITY_HEADER, forge.anElementFrom( - PrioritySampling.SAMPLER_KEEP.toString(), - PrioritySampling.USER_KEEP.toString() + DatadogTracingConstants.PrioritySampling.SAMPLER_KEEP.toString(), + DatadogTracingConstants.PrioritySampling.USER_KEEP.toString() ) ) } stubChain(mockChain, statusCode) whenever(mockTraceSampler.sample(mockSpan)).thenReturn(false) - doAnswer { invocation -> - val carrier = invocation.arguments[2] as TextMapInject - carrier.put(key, value) - }.whenever(mockTracer).inject(any(), any(), any()) + mockPropagation.wheneverInjectThenValueToHeaders(key, value) // When val response = testedInterceptor.intercept(mockChain) @@ -698,15 +679,12 @@ internal class TracingInterceptorContextInjectionSampledTest { fakeRequest = forgeRequest(forge) { it.addHeader( TracingInterceptor.B3M_SAMPLING_PRIORITY_KEY, - PrioritySampling.SAMPLER_KEEP.toString() + DatadogTracingConstants.PrioritySampling.SAMPLER_KEEP.toString() ) } stubChain(mockChain, statusCode) whenever(mockTraceSampler.sample(mockSpan)).thenReturn(false) - doAnswer { invocation -> - val carrier = invocation.arguments[2] as TextMapInject - carrier.put(key, value) - }.whenever(mockTracer).inject(any(), any(), any()) + mockPropagation.wheneverInjectThenValueToHeaders(key, value) // When val response = testedInterceptor.intercept(mockChain) @@ -737,10 +715,7 @@ internal class TracingInterceptorContextInjectionSampledTest { } stubChain(mockChain, statusCode) whenever(mockTraceSampler.sample(mockSpan)).thenReturn(false) - doAnswer { invocation -> - val carrier = invocation.arguments[2] as TextMapInject - carrier.put(key, value) - }.whenever(mockTracer).inject(any(), any(), any()) + mockPropagation.wheneverInjectThenValueToHeaders(key, value) // When val response = testedInterceptor.intercept(mockChain) @@ -771,10 +746,7 @@ internal class TracingInterceptorContextInjectionSampledTest { } stubChain(mockChain, statusCode) whenever(mockTraceSampler.sample(mockSpan)).thenReturn(false) - doAnswer { invocation -> - val carrier = invocation.arguments[2] as TextMapInject - carrier.put(key, value) - }.whenever(mockTracer).inject(any(), any(), any()) + mockPropagation.wheneverInjectThenValueToHeaders(key, value) // When val response = testedInterceptor.intercept(mockChain) @@ -802,14 +774,15 @@ internal class TracingInterceptorContextInjectionSampledTest { TracingInterceptor.DATADOG_SPAN_ID_HEADER, TracingInterceptor.DATADOG_LEAST_SIGNIFICANT_64_BITS_TRACE_ID_HEADER, TracingInterceptor.DATADOG_ORIGIN_HEADER - ).associate { it to forge.anAlphabeticalString() } + ).associateWith { forge.anAlphabeticalString() } val nonDatadogContextKey = forge.anAlphabeticalString() val nonDatadogContextKeyValue = forge.anAlphabeticalString() - doAnswer { invocation -> - val carrier = invocation.arguments[2] as TextMapInject - datadogContext.forEach { carrier.put(it.key, it.value) } - carrier.put(nonDatadogContextKey, nonDatadogContextKeyValue) - }.whenever(mockTracer).inject(any(), any(), any()) + mockPropagation.wheneverInjectCalledPassContextToHeaders( + datadogContext, + nonDatadogContextKey, + nonDatadogContextKeyValue + ) + whenever(mockResolver.isFirstPartyUrl(fakeUrl.toHttpUrl())).thenReturn(true) whenever(mockResolver.headerTypesForUrl(fakeUrl.toHttpUrl())).thenReturn( setOf( @@ -820,8 +793,8 @@ internal class TracingInterceptorContextInjectionSampledTest { it.addHeader( TracingInterceptor.DATADOG_SAMPLING_PRIORITY_HEADER, forge.anElementFrom( - PrioritySampling.SAMPLER_DROP.toString(), - PrioritySampling.USER_DROP.toString() + DatadogTracingConstants.PrioritySampling.SAMPLER_DROP.toString(), + DatadogTracingConstants.PrioritySampling.USER_DROP.toString() ) ) } @@ -858,7 +831,7 @@ internal class TracingInterceptorContextInjectionSampledTest { fakeRequest = forgeRequest(forge) { it.addHeader( TracingInterceptor.B3M_SAMPLING_PRIORITY_KEY, - PrioritySampling.SAMPLER_DROP.toString() + DatadogTracingConstants.PrioritySampling.SAMPLER_DROP.toString() ) } stubChain(mockChain, statusCode) @@ -893,7 +866,7 @@ internal class TracingInterceptorContextInjectionSampledTest { it.addHeader( TracingInterceptor.B3_HEADER_KEY, forge.anElementFrom( - PrioritySampling.SAMPLER_DROP.toString(), + DatadogTracingConstants.PrioritySampling.SAMPLER_DROP.toString(), forge.aStringMatching("[a-f0-9]{32}\\-[a-f0-9]{16}\\-0") ) ) @@ -957,11 +930,11 @@ internal class TracingInterceptorContextInjectionSampledTest { val response = testedInterceptor.intercept(mockChain) verify(mockSpanBuilder).withOrigin(getExpectedOrigin()) - verify(mockSpan as MutableSpan).resourceName = fakeBaseUrl.lowercase(Locale.US) + verify(mockSpan).resourceName = fakeBaseUrl.lowercase(Locale.US) verify(mockSpan).setTag("http.url", fakeUrl.lowercase(Locale.US)) verify(mockSpan).setTag("http.method", fakeMethod) verify(mockSpan).setTag("http.status_code", statusCode) - verify(mockSpan).setTag(Tags.SPAN_KIND, Tags.SPAN_KIND_CLIENT) + verify(mockSpan).setTag("span.kind", "client") verify(mockSpan).finish() assertThat(response).isSameAs(fakeResponse) } @@ -980,12 +953,12 @@ internal class TracingInterceptorContextInjectionSampledTest { val response = testedInterceptor.intercept(mockChain) verify(mockSpanBuilder).withOrigin(getExpectedOrigin()) - verify(mockSpan as MutableSpan).resourceName = + verify(mockSpan).resourceName = fakeUrlWithoutQueryParams.lowercase(Locale.US) verify(mockSpan).setTag("http.url", fakeUrlWithoutQueryParams.lowercase(Locale.US)) verify(mockSpan).setTag("http.method", fakeMethod) verify(mockSpan).setTag("http.status_code", statusCode) - verify(mockSpan).setTag(Tags.SPAN_KIND, Tags.SPAN_KIND_CLIENT) + verify(mockSpan).setTag("span.kind", "client") verify(mockSpan).finish() assertThat(response).isSameAs(fakeResponse) } @@ -1000,12 +973,12 @@ internal class TracingInterceptorContextInjectionSampledTest { val response = testedInterceptor.intercept(mockChain) verify(mockSpanBuilder).withOrigin(getExpectedOrigin()) - verify(mockSpan as MutableSpan).resourceName = fakeBaseUrl.lowercase(Locale.US) + verify(mockSpan).resourceName = fakeBaseUrl.lowercase(Locale.US) verify(mockSpan).setTag("http.url", fakeUrl.lowercase(Locale.US)) verify(mockSpan).setTag("http.method", fakeMethod) verify(mockSpan).setTag("http.status_code", statusCode) - verify(mockSpan).setTag(Tags.SPAN_KIND, Tags.SPAN_KIND_CLIENT) - verify(mockSpan as MutableSpan).setError(true) + verify(mockSpan).setTag("span.kind", "client") + verify(mockSpan).isError = true verify(mockSpan).finish() assertThat(response).isSameAs(fakeResponse) } @@ -1020,12 +993,12 @@ internal class TracingInterceptorContextInjectionSampledTest { val response = testedInterceptor.intercept(mockChain) verify(mockSpanBuilder).withOrigin(getExpectedOrigin()) - verify(mockSpan as MutableSpan).resourceName = fakeBaseUrl.lowercase(Locale.US) + verify(mockSpan).resourceName = fakeBaseUrl.lowercase(Locale.US) verify(mockSpan).setTag("http.url", fakeUrl.lowercase(Locale.US)) verify(mockSpan).setTag("http.method", fakeMethod) verify(mockSpan).setTag("http.status_code", statusCode) - verify(mockSpan as MutableSpan, never()).setError(true) - verify(mockSpan).setTag(Tags.SPAN_KIND, Tags.SPAN_KIND_CLIENT) + verify(mockSpan, never()).isError = true + verify(mockSpan).setTag("span.kind", "client") verify(mockSpan).finish() assertThat(response).isSameAs(fakeResponse) } @@ -1041,13 +1014,13 @@ internal class TracingInterceptorContextInjectionSampledTest { verify(mockSpan).setTag("http.url", fakeUrl.lowercase(Locale.US)) verify(mockSpan).setTag("http.method", fakeMethod) verify(mockSpan).setTag("http.status_code", 404) - verify(mockSpan as MutableSpan).setError(true) + verify(mockSpan).isError = true if (fakeRedacted404Resources) { - verify(mockSpan as MutableSpan).setResourceName(TracingInterceptor.RESOURCE_NAME_404) + verify(mockSpan).resourceName = TracingInterceptor.RESOURCE_NAME_404 } else { - verify(mockSpan as MutableSpan, never()).setResourceName(TracingInterceptor.RESOURCE_NAME_404) + verify(mockSpan, never()).resourceName = TracingInterceptor.RESOURCE_NAME_404 } - verify(mockSpan).setTag(Tags.SPAN_KIND, Tags.SPAN_KIND_CLIENT) + verify(mockSpan).setTag("span.kind", "client") verify(mockSpan).finish() assertThat(response).isSameAs(fakeResponse) } @@ -1070,8 +1043,8 @@ internal class TracingInterceptorContextInjectionSampledTest { verify(mockSpan).setTag("error.type", throwable.javaClass.canonicalName) verify(mockSpan).setTag("error.msg", throwable.message) verify(mockSpan).setTag("error.stack", throwable.loggableStackTrace()) - verify(mockSpan).setTag(Tags.SPAN_KIND, Tags.SPAN_KIND_CLIENT) - verify(mockSpan as MutableSpan).resourceName = fakeBaseUrl.lowercase(Locale.US) + verify(mockSpan).setTag("span.kind", "client") + verify(mockSpan).resourceName = fakeBaseUrl.lowercase(Locale.US) verify(mockSpan).finish() } @@ -1079,7 +1052,6 @@ internal class TracingInterceptorContextInjectionSampledTest { fun `M warn the user W intercept() no tracer registered and TracingFeature not initialized`( @IntForgery(min = 200, max = 300) statusCode: Int ) { - GlobalTracer::class.java.setStaticValue("isRegistered", false) whenever(rumMonitor.mockSdkCore.getFeature(Feature.TRACING_FEATURE_NAME)) doReturn null whenever(mockResolver.isFirstPartyUrl(fakeUrl.toHttpUrl())).thenReturn(true) stubChain(mockChain, statusCode) @@ -1099,30 +1071,29 @@ internal class TracingInterceptorContextInjectionSampledTest { @Test fun `M create a span with automatic tracer W intercept() if no tracer registered`( + forge: Forge, @IntForgery(min = 200, max = 300) statusCode: Int ) { - val localSpanBuilder: DDTracer.DDSpanBuilder = mock() - val localSpan: Span = mock(extraInterfaces = arrayOf(MutableSpan::class)) - GlobalTracer::class.java.setStaticValue("isRegistered", false) + val localSpan: DatadogSpan = forge.newSpanMock(mockSpanContext) + val localSpanBuilder: DatadogSpanBuilder = forge.newSpanBuilderMock(localSpan) whenever(mockResolver.isFirstPartyUrl(fakeUrl.toHttpUrl())).thenReturn(true) - stubChain(mockChain, statusCode) - whenever(localSpanBuilder.asChildOf(null as SpanContext?)) doReturn localSpanBuilder - whenever(localSpanBuilder.withOrigin(getExpectedOrigin())) doReturn localSpanBuilder - whenever(localSpanBuilder.start()) doReturn localSpan - whenever(localSpan.context()) doReturn mockSpanContext whenever(mockTraceSampler.sample(localSpan)).thenReturn(true) - whenever(mockSpanContext.toSpanId()) doReturn fakeSpanId - whenever(mockSpanContext.toTraceId()) doReturn fakeTraceIdAsString whenever(mockLocalTracer.buildSpan(TracingInterceptor.SPAN_NAME)) doReturn localSpanBuilder + val testedInterceptorNoGlobal = instantiateTestedInterceptor( + fakeLocalHosts, + localTracerFactory = { _, _ -> mockLocalTracer }, + globalTracerProvider = { null } + ) - val response = testedInterceptor.intercept(mockChain) + stubChain(mockChain, statusCode) + val response = testedInterceptorNoGlobal.intercept(mockChain) verify(localSpanBuilder).withOrigin(getExpectedOrigin()) verify(localSpan).setTag("http.url", fakeUrl.lowercase(Locale.US)) verify(localSpan).setTag("http.method", fakeMethod) verify(localSpan).setTag("http.status_code", statusCode) - verify(localSpan).setTag(Tags.SPAN_KIND, Tags.SPAN_KIND_CLIENT) - verify(localSpan as MutableSpan).resourceName = fakeBaseUrl.lowercase(Locale.US) + verify(localSpan).setTag("span.kind", "client") + verify(localSpan).resourceName = fakeBaseUrl.lowercase(Locale.US) verify(localSpan).finish() assertThat(response).isSameAs(fakeResponse) mockInternalLogger.verifyLog( @@ -1134,25 +1105,23 @@ internal class TracingInterceptorContextInjectionSampledTest { @Test fun `M drop automatic tracer W intercept() and global tracer registered`( + forge: Forge, @IntForgery(min = 200, max = 300) statusCode: Int ) { - val localSpanBuilder: DDTracer.DDSpanBuilder = mock() - val localSpan: Span = mock(extraInterfaces = arrayOf(MutableSpan::class)) - GlobalTracer::class.java.setStaticValue("isRegistered", false) + val localSpan: DatadogSpan = forge.newSpanMock(mockSpanContext) + val localSpanBuilder: DatadogSpanBuilder = forge.newSpanBuilderMock(localSpan, mockSpanContext) whenever(mockResolver.isFirstPartyUrl(fakeUrl.toHttpUrl())).thenReturn(true) stubChain(mockChain, statusCode) - whenever(localSpanBuilder.asChildOf(null as SpanContext?)) doReturn localSpanBuilder - whenever(localSpanBuilder.withOrigin(getExpectedOrigin())) doReturn localSpanBuilder - whenever(localSpanBuilder.start()) doReturn localSpan - whenever(localSpan.context()) doReturn mockSpanContext whenever(mockTraceSampler.sample(localSpan)).thenReturn(true) - whenever(mockSpanContext.toSpanId()) doReturn fakeSpanId - whenever(mockSpanContext.toTraceId()) doReturn fakeTraceIdAsString whenever(mockLocalTracer.buildSpan(TracingInterceptor.SPAN_NAME)) doReturn localSpanBuilder + val testedInterceptorNoGlobal = instantiateTestedInterceptor( + fakeLocalHosts, + localTracerFactory = { _, _ -> mockLocalTracer }, + globalTracerProvider = { null } + ) - val response1 = testedInterceptor.intercept(mockChain) + val response1 = testedInterceptorNoGlobal.intercept(mockChain) val expectedResponse1 = fakeResponse - GlobalTracer.registerIfAbsent(mockTracer) stubChain(mockChain, statusCode) val response2 = testedInterceptor.intercept(mockChain) val expectedResponse2 = fakeResponse @@ -1161,15 +1130,15 @@ internal class TracingInterceptorContextInjectionSampledTest { verify(localSpan).setTag("http.url", fakeUrl.lowercase(Locale.US)) verify(localSpan).setTag("http.method", fakeMethod) verify(localSpan).setTag("http.status_code", statusCode) - verify(localSpan).setTag(Tags.SPAN_KIND, Tags.SPAN_KIND_CLIENT) - verify(localSpan as MutableSpan).resourceName = fakeBaseUrl.lowercase(Locale.US) + verify(localSpan).setTag("span.kind", "client") + verify(localSpan).resourceName = fakeBaseUrl.lowercase(Locale.US) verify(localSpan).finish() verify(mockSpanBuilder).withOrigin(getExpectedOrigin()) verify(mockSpan).setTag("http.url", fakeUrl.lowercase(Locale.US)) verify(mockSpan).setTag("http.method", fakeMethod) verify(mockSpan).setTag("http.status_code", statusCode) - verify(mockSpan).setTag(Tags.SPAN_KIND, Tags.SPAN_KIND_CLIENT) - verify(mockSpan as MutableSpan).resourceName = fakeBaseUrl.lowercase(Locale.US) + verify(mockSpan).setTag("span.kind", "client") + verify(mockSpan).resourceName = fakeBaseUrl.lowercase(Locale.US) verify(mockSpan).finish() assertThat(response1).isSameAs(expectedResponse1) assertThat(response2).isSameAs(expectedResponse2) @@ -1191,7 +1160,7 @@ internal class TracingInterceptorContextInjectionSampledTest { whenever( mockRequestListener.onRequestIntercepted(any(), any(), anyOrNull(), anyOrNull()) ).doAnswer { - val span = it.arguments[1] as Span + val span = it.arguments[1] as DatadogSpan span.setTag(tagKey, tagValue) return@doAnswer Unit } @@ -1202,9 +1171,9 @@ internal class TracingInterceptorContextInjectionSampledTest { verify(mockSpan).setTag("http.url", fakeUrl.lowercase(Locale.US)) verify(mockSpan).setTag("http.method", fakeMethod) verify(mockSpan).setTag("http.status_code", statusCode) - verify(mockSpan as MutableSpan).resourceName = fakeBaseUrl.lowercase(Locale.US) + verify(mockSpan).resourceName = fakeBaseUrl.lowercase(Locale.US) verify(mockSpan).setTag(tagKey, tagValue) - verify(mockSpan).setTag(Tags.SPAN_KIND, Tags.SPAN_KIND_CLIENT) + verify(mockSpan).setTag("span.kind", "client") verify(mockSpan).finish() assertThat(response).isSameAs(fakeResponse) } @@ -1220,7 +1189,7 @@ internal class TracingInterceptorContextInjectionSampledTest { whenever( mockRequestListener.onRequestIntercepted(any(), any(), anyOrNull(), anyOrNull()) ).doAnswer { - val span = it.arguments[1] as Span + val span = it.arguments[1] as DatadogSpan span.setTag(tagKey, tagValue) return@doAnswer Unit } @@ -1232,7 +1201,7 @@ internal class TracingInterceptorContextInjectionSampledTest { verify(mockSpan).setTag("http.method", fakeMethod) verify(mockSpan).setTag("http.status_code", statusCode) verify(mockSpan).setTag(tagKey, tagValue) - verify(mockSpan).setTag(Tags.SPAN_KIND, Tags.SPAN_KIND_CLIENT) + verify(mockSpan).setTag("span.kind", "client") verify(mockSpan).finish() assertThat(response).isSameAs(fakeResponse) } @@ -1264,7 +1233,7 @@ internal class TracingInterceptorContextInjectionSampledTest { whenever( mockRequestListener.onRequestIntercepted(any(), any(), anyOrNull(), anyOrNull()) ).doAnswer { - val span = it.arguments[1] as Span + val span = it.arguments[1] as DatadogSpan span.setTag(tagKey, tagValue) return@doAnswer Unit } @@ -1282,7 +1251,7 @@ internal class TracingInterceptorContextInjectionSampledTest { verify(mockSpan).setTag("error.msg", throwable.message) verify(mockSpan).setTag("error.stack", throwable.loggableStackTrace()) verify(mockSpan).setTag(tagKey, tagValue) - verify(mockSpan).setTag(Tags.SPAN_KIND, Tags.SPAN_KIND_CLIENT) + verify(mockSpan).setTag("span.kind", "client") verify(mockSpan).finish() } @@ -1351,17 +1320,16 @@ internal class TracingInterceptorContextInjectionSampledTest { called++ mockLocalTracer } - GlobalTracer::class.java.setStaticValue("isRegistered", false) whenever(mockResolver.isFirstPartyUrl(fakeUrl.toHttpUrl())).thenReturn(true) stubChain(mockChain, statusCode) // need this setup, otherwise #intercept actually throws NPE, which pollutes the log - val localSpanBuilder: DDTracer.DDSpanBuilder = mock() - val localSpan: Span = mock(extraInterfaces = arrayOf(MutableSpan::class)) - whenever(localSpanBuilder.asChildOf(null as SpanContext?)) doReturn localSpanBuilder + val localSpanBuilder: DatadogSpanBuilder = mock() + val localSpan: DatadogSpan = mock() + whenever(localSpanBuilder.withParentContext(null)) doReturn localSpanBuilder whenever(localSpanBuilder.start()) doReturn localSpan whenever(localSpan.context()) doReturn mockSpanContext - whenever(mockSpanContext.toSpanId()) doReturn fakeSpanId + whenever(mockSpanContext.spanId) doReturn fakeSpanId whenever(mockSpanContext.traceId) doReturn fakeTraceId whenever(mockLocalTracer.buildSpan(TracingInterceptor.SPAN_NAME)) doReturn localSpanBuilder @@ -1384,7 +1352,7 @@ internal class TracingInterceptorContextInjectionSampledTest { // region Internal - internal fun stubChain(chain: Interceptor.Chain, statusCode: Int) { + private fun stubChain(chain: Interceptor.Chain, statusCode: Int) { fakeResponse = forgeResponse(statusCode) whenever(chain.request()) doReturn fakeRequest diff --git a/integrations/dd-sdk-android-okhttp/src/test/kotlin/com/datadog/android/okhttp/trace/TracingInterceptorNonDdTracerNotSendingSpanTest.kt b/integrations/dd-sdk-android-okhttp/src/test/kotlin/com/datadog/android/okhttp/trace/TracingInterceptorNonDdTracerNotSendingSpanTest.kt index 162d6cd705..b346edf51f 100644 --- a/integrations/dd-sdk-android-okhttp/src/test/kotlin/com/datadog/android/okhttp/trace/TracingInterceptorNonDdTracerNotSendingSpanTest.kt +++ b/integrations/dd-sdk-android-okhttp/src/test/kotlin/com/datadog/android/okhttp/trace/TracingInterceptorNonDdTracerNotSendingSpanTest.kt @@ -3,7 +3,6 @@ * This product includes software developed at Datadog (https://www.datadoghq.com/). * Copyright 2016-Present Datadog, Inc. */ - package com.datadog.android.okhttp.trace import com.datadog.android.api.InternalLogger @@ -17,31 +16,29 @@ import com.datadog.android.okhttp.utils.assertj.HeadersAssert.Companion.assertTh import com.datadog.android.okhttp.utils.config.DatadogSingletonTestConfiguration import com.datadog.android.okhttp.utils.verifyLog import com.datadog.android.trace.TracingHeaderType -import com.datadog.legacy.trace.api.interceptor.MutableSpan -import com.datadog.legacy.trace.api.sampling.PrioritySampling -import com.datadog.opentracing.DDSpanContext -import com.datadog.opentracing.DDTracer -import com.datadog.opentracing.propagation.ExtractedContext +import com.datadog.android.trace.api.DatadogTracingConstants +import com.datadog.android.trace.api.propagation.DatadogPropagation +import com.datadog.android.trace.api.span.DatadogSpan +import com.datadog.android.trace.api.span.DatadogSpanBuilder +import com.datadog.android.trace.api.span.DatadogSpanContext +import com.datadog.android.trace.api.trace.DatadogTraceId +import com.datadog.android.trace.api.tracer.DatadogTracer +import com.datadog.android.trace.api.withMockPropagationHelper +import com.datadog.android.trace.internal.DatadogPropagationHelper +import com.datadog.android.trace.internal.DatadogTracingToolkit import com.datadog.tools.unit.annotations.TestConfigurationsProvider import com.datadog.tools.unit.extensions.TestConfigurationExtension import com.datadog.tools.unit.extensions.config.TestConfiguration import com.datadog.tools.unit.forge.BaseConfigurator -import com.datadog.tools.unit.setStaticValue import fr.xgouchet.elmyr.Forge import fr.xgouchet.elmyr.annotation.BoolForgery import fr.xgouchet.elmyr.annotation.Forgery import fr.xgouchet.elmyr.annotation.IntForgery +import fr.xgouchet.elmyr.annotation.LongForgery import fr.xgouchet.elmyr.annotation.StringForgery import fr.xgouchet.elmyr.annotation.StringForgeryType import fr.xgouchet.elmyr.junit5.ForgeConfiguration import fr.xgouchet.elmyr.junit5.ForgeExtension -import io.opentracing.Span -import io.opentracing.SpanContext -import io.opentracing.Tracer -import io.opentracing.propagation.TextMapExtract -import io.opentracing.propagation.TextMapInject -import io.opentracing.tag.Tags -import io.opentracing.util.GlobalTracer import okhttp3.HttpUrl import okhttp3.HttpUrl.Companion.toHttpUrl import okhttp3.Interceptor @@ -53,7 +50,6 @@ import okhttp3.RequestBody.Companion.toRequestBody import okhttp3.Response import okhttp3.ResponseBody.Companion.toResponseBody import org.assertj.core.api.Assertions.assertThat -import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test import org.junit.jupiter.api.assertThrows @@ -75,7 +71,6 @@ import org.mockito.kotlin.verify import org.mockito.kotlin.verifyNoInteractions import org.mockito.kotlin.whenever import org.mockito.quality.Strictness -import java.math.BigInteger import java.util.Locale import java.util.concurrent.CountDownLatch import java.util.concurrent.TimeUnit @@ -93,24 +88,30 @@ import java.util.concurrent.TimeUnit @ForgeConfiguration(BaseConfigurator::class) internal open class TracingInterceptorNonDdTracerNotSendingSpanTest { - lateinit var testedInterceptor: TracingInterceptor + private lateinit var testedInterceptor: TracingInterceptor // region Mocks @Mock - lateinit var mockTracer: Tracer + lateinit var mockTracer: DatadogTracer + + @Mock + lateinit var mockPropagation: DatadogPropagation + + @Mock + lateinit var mockPropagationHelper: DatadogPropagationHelper @Mock - lateinit var mockLocalTracer: Tracer + lateinit var mockLocalTracer: DatadogTracer @Mock - lateinit var mockSpanBuilder: Tracer.SpanBuilder + lateinit var mockSpanBuilder: DatadogSpanBuilder @Mock - lateinit var mockSpanContext: DDSpanContext + lateinit var mockSpanContext: DatadogSpanContext @Mock - lateinit var mockSpan: Span + lateinit var mockSpan: DatadogSpan @Mock lateinit var mockChain: Interceptor.Chain @@ -121,8 +122,7 @@ internal open class TracingInterceptorNonDdTracerNotSendingSpanTest { @Mock lateinit var mockResolver: DefaultFirstPartyHostHeaderTypeResolver - @Mock - lateinit var mockTraceSampler: Sampler + lateinit var mockTraceSampler: Sampler @Mock lateinit var mockInternalLogger: InternalLogger @@ -149,13 +149,13 @@ internal open class TracingInterceptorNonDdTracerNotSendingSpanTest { lateinit var fakeRequest: Request lateinit var fakeResponse: Response - @StringForgery(type = StringForgeryType.HEXADECIMAL) - lateinit var fakeSpanId: String + @LongForgery + var fakeSpanId: Long = 0 - @StringForgery(regex = "[a-f][0-9]{32}") + @StringForgery(regex = "[a-f][0-9]{31}") lateinit var fakeTraceIdAsString: String - lateinit var fakeTraceId: BigInteger + lateinit var fakeTraceId: DatadogTraceId private var fakeOrigin: String? = null @@ -168,16 +168,16 @@ internal open class TracingInterceptorNonDdTracerNotSendingSpanTest { @BeforeEach open fun `set up`(forge: Forge) { - fakeTraceId = BigInteger(fakeTraceIdAsString, 16) - whenever(mockTracer.buildSpan(TracingInterceptor.SPAN_NAME)) doReturn mockSpanBuilder - whenever(mockSpanBuilder.asChildOf(null as SpanContext?)) doReturn mockSpanBuilder - whenever(mockSpanBuilder.start()) doReturn mockSpan - whenever(mockSpan.context()) doReturn mockSpanContext - whenever(mockSpanContext.toSpanId()) doReturn fakeSpanId - whenever(mockSpanContext.traceId) doReturn fakeTraceId - whenever(mockTraceSampler.sample(mockSpan)) doReturn true - + fakeTraceId = forge.aDatadogTraceId(fakeTraceIdAsString) fakeOrigin = forge.aNullable { anAlphabeticalString() } + mockSpanContext = forge.newSpanContextMock(fakeTraceId, fakeSpanId) + mockSpan = forge.newSpanMock(mockSpanContext) + mockSpanBuilder = forge.newSpanBuilderMock(mockSpan, mockSpanContext) + mockTraceSampler = forge.newTraceSamplerMock(mockSpan) + mockPropagation = newAgentPropagationMock() + mockTracer = forge.newTracerMock(mockSpanBuilder, mockPropagation) + mockLocalTracer = forge.newTracerMock(mockSpanBuilder, mockPropagation) + fakeMediaType = if (forge.aBool()) { val mediaType = forge.anElementFrom("application", "image", "text", "model") + "/" + forge.anAlphabeticalString() @@ -198,18 +198,17 @@ internal open class TracingInterceptorNonDdTracerNotSendingSpanTest { doAnswer { false }.whenever(mockResolver).isFirstPartyUrl(any()) doAnswer { true }.whenever(mockResolver).isFirstPartyUrl(fakeUrl.toHttpUrl()) - GlobalTracer.registerIfAbsent(mockTracer) - testedInterceptor = instantiateTestedInterceptor(fakeLocalHosts) { _, _ -> mockLocalTracer } - } - - @AfterEach - open fun `tear down`() { - GlobalTracer::class.java.setStaticValue("isRegistered", false) + testedInterceptor = instantiateTestedInterceptor( + fakeLocalHosts, + localTracerFactory = { _, _ -> mockLocalTracer }, + globalTracerProvider = { mockTracer } + ) } open fun instantiateTestedInterceptor( tracedHosts: Map>, - factory: (SdkCore, Set) -> Tracer + globalTracerProvider: () -> DatadogTracer? = { null }, + localTracerFactory: (SdkCore, Set) -> DatadogTracer ): TracingInterceptor { return object : TracingInterceptor( @@ -220,7 +219,8 @@ internal open class TracingInterceptorNonDdTracerNotSendingSpanTest { traceSampler = mockTraceSampler, traceContextInjection = TraceContextInjection.ALL, redacted404ResourceName = fakeRedacted404Resources, - localTracerFactory = factory + localTracerFactory = localTracerFactory, + globalTracerProvider = globalTracerProvider ) { override fun canSendSpan(): Boolean { return false @@ -236,10 +236,7 @@ internal open class TracingInterceptorNonDdTracerNotSendingSpanTest { ) { whenever(mockResolver.isFirstPartyUrl(fakeUrl.toHttpUrl())).thenReturn(true) stubChain(mockChain, statusCode) - doAnswer { invocation -> - val carrier = invocation.arguments[2] as TextMapInject - carrier.put(key, value) - }.whenever(mockTracer).inject(any(), any(), any()) + mockPropagation.wheneverInjectThenValueToHeaders(key, value) val response = testedInterceptor.intercept(mockChain) @@ -358,11 +355,11 @@ internal open class TracingInterceptorNonDdTracerNotSendingSpanTest { assertThat(lastValue.headers) .hasTraceParentHeader( fakeTraceIdAsString, - mockSpan.context().toSpanId(), + mockSpan.context().spanId.toString(), isSampled = false ) .hasTraceStateHeaderWithOnlyDatadogVendorValues( - mockSpan.context().toSpanId(), + mockSpan.context().spanId.toString(), isSampled = false, fakeOrigin ) @@ -380,10 +377,7 @@ internal open class TracingInterceptorNonDdTracerNotSendingSpanTest { fakeRequest = forgeRequest(forge) whenever(mockResolver.isFirstPartyUrl(fakeUrl.toHttpUrl())).thenReturn(false) stubChain(mockChain, statusCode) - doAnswer { invocation -> - val carrier = invocation.arguments[2] as TextMapInject - carrier.put(key, value) - }.whenever(mockTracer).inject(any(), any(), any()) + mockPropagation.wheneverInjectThenValueToHeaders(key, value) val response = testedInterceptor.intercept(mockChain) @@ -512,11 +506,11 @@ internal open class TracingInterceptorNonDdTracerNotSendingSpanTest { assertThat(lastValue.headers) .hasTraceParentHeader( fakeTraceIdAsString, - mockSpan.context().toSpanId(), + mockSpan.context().spanId.toString(), isSampled = false ) .hasTraceStateHeaderWithOnlyDatadogVendorValues( - mockSpan.context().toSpanId(), + mockSpan.context().spanId.toString(), isSampled = false, fakeOrigin ) @@ -530,18 +524,15 @@ internal open class TracingInterceptorNonDdTracerNotSendingSpanTest { @IntForgery(min = 200, max = 300) statusCode: Int, forge: Forge ) { - val parentSpan: Span = mock() - val parentSpanContext: SpanContext = mock() + val parentSpanContext: DatadogSpanContext = forge.newSpanContextMock(samplingPriority = 1) + val parentSpan: DatadogSpan = forge.newSpanMock(parentSpanContext) whenever(parentSpan.context()) doReturn parentSpanContext - whenever(mockSpanBuilder.asChildOf(parentSpanContext)) doReturn mockSpanBuilder - fakeRequest = forgeRequest(forge) { it.tag(Span::class.java, parentSpan) } + whenever(mockSpanBuilder.withParentContext(parentSpanContext)) doReturn mockSpanBuilder + fakeRequest = forgeRequest(forge) { it.tag(DatadogSpan::class.java, parentSpan) } whenever(mockResolver.isFirstPartyUrl(fakeUrl.toHttpUrl())).thenReturn(true) doAnswer { true }.whenever(mockResolver).isFirstPartyUrl(fakeUrl.toHttpUrl()) stubChain(mockChain, statusCode) - doAnswer { invocation -> - val carrier = invocation.arguments[2] as TextMapInject - carrier.put(key, value) - }.whenever(mockTracer).inject(any(), any(), any()) + mockPropagation.wheneverInjectThenValueToHeaders(key, value) val response = testedInterceptor.intercept(mockChain) @@ -550,7 +541,7 @@ internal open class TracingInterceptorNonDdTracerNotSendingSpanTest { verify(mockChain).proceed(capture()) assertThat(lastValue.header(key)).isEqualTo(value) } - verify(mockSpanBuilder).asChildOf(parentSpanContext) + verify(mockSpanBuilder).withParentContext(parentSpanContext) } @Test @@ -560,26 +551,25 @@ internal open class TracingInterceptorNonDdTracerNotSendingSpanTest { @IntForgery(min = 200, max = 300) statusCode: Int, forge: Forge ) { - val parentSpanContext: ExtractedContext = mock() - whenever(mockTracer.extract(any(), any())) doReturn parentSpanContext - whenever(mockSpanBuilder.asChildOf(any())) doReturn mockSpanBuilder + val parentSpanContext: DatadogSpanContext = mock() + whenever(mockSpanBuilder.withParentContext(any())) doReturn mockSpanBuilder + whenever(mockPropagation.extract(any(), any())) doReturn parentSpanContext + whenever(mockPropagationHelper.isExtractedContext(parentSpanContext)) doReturn true whenever(mockResolver.isFirstPartyUrl(fakeUrl.toHttpUrl())).thenReturn(true) fakeRequest = forgeRequest(forge) doAnswer { true }.whenever(mockResolver).isFirstPartyUrl(fakeUrl.toHttpUrl()) stubChain(mockChain, statusCode) - doAnswer { invocation -> - val carrier = invocation.arguments[2] as TextMapInject - carrier.put(key, value) - }.whenever(mockTracer).inject(any(), any(), any()) - - val response = testedInterceptor.intercept(mockChain) - - assertThat(response).isSameAs(fakeResponse) - argumentCaptor { - verify(mockChain).proceed(capture()) - assertThat(lastValue.header(key)).isEqualTo(value) + mockPropagation.wheneverInjectThenValueToHeaders(key, value) + DatadogTracingToolkit.withMockPropagationHelper(mockPropagationHelper) { + val response = testedInterceptor.intercept(mockChain) + + assertThat(response).isSameAs(fakeResponse) + argumentCaptor { + verify(mockChain).proceed(capture()) + assertThat(lastValue.header(key)).isEqualTo(value) + } + verify(mockSpanBuilder).withParentContext(parentSpanContext) } - verify(mockSpanBuilder).asChildOf(parentSpanContext) } @Test @@ -595,17 +585,14 @@ internal open class TracingInterceptorNonDdTracerNotSendingSpanTest { it.addHeader( TracingInterceptor.DATADOG_SAMPLING_PRIORITY_HEADER, forge.anElementFrom( - PrioritySampling.SAMPLER_KEEP.toString(), - PrioritySampling.USER_KEEP.toString() + DatadogTracingConstants.PrioritySampling.SAMPLER_KEEP.toString(), + DatadogTracingConstants.PrioritySampling.USER_KEEP.toString() ) ) } stubChain(mockChain, statusCode) whenever(mockTraceSampler.sample(mockSpan)).thenReturn(false) - doAnswer { invocation -> - val carrier = invocation.arguments[2] as TextMapInject - carrier.put(key, value) - }.whenever(mockTracer).inject(any(), any(), any()) + mockPropagation.wheneverInjectThenValueToHeaders(key, value) // When val response = testedInterceptor.intercept(mockChain) @@ -630,15 +617,12 @@ internal open class TracingInterceptorNonDdTracerNotSendingSpanTest { fakeRequest = forgeRequest(forge) { it.addHeader( TracingInterceptor.B3M_SAMPLING_PRIORITY_KEY, - PrioritySampling.SAMPLER_KEEP.toString() + DatadogTracingConstants.PrioritySampling.SAMPLER_KEEP.toString() ) } stubChain(mockChain, statusCode) whenever(mockTraceSampler.sample(mockSpan)).thenReturn(false) - doAnswer { invocation -> - val carrier = invocation.arguments[2] as TextMapInject - carrier.put(key, value) - }.whenever(mockTracer).inject(any(), any(), any()) + mockPropagation.wheneverInjectThenValueToHeaders(key, value) // When val response = testedInterceptor.intercept(mockChain) @@ -668,10 +652,7 @@ internal open class TracingInterceptorNonDdTracerNotSendingSpanTest { } stubChain(mockChain, statusCode) whenever(mockTraceSampler.sample(mockSpan)).thenReturn(false) - doAnswer { invocation -> - val carrier = invocation.arguments[2] as TextMapInject - carrier.put(key, value) - }.whenever(mockTracer).inject(any(), any(), any()) + mockPropagation.wheneverInjectThenValueToHeaders(key, value) // When val response = testedInterceptor.intercept(mockChain) @@ -701,10 +682,7 @@ internal open class TracingInterceptorNonDdTracerNotSendingSpanTest { } stubChain(mockChain, statusCode) whenever(mockTraceSampler.sample(mockSpan)).thenReturn(false) - doAnswer { invocation -> - val carrier = invocation.arguments[2] as TextMapInject - carrier.put(key, value) - }.whenever(mockTracer).inject(any(), any(), any()) + mockPropagation.wheneverInjectThenValueToHeaders(key, value) // When val response = testedInterceptor.intercept(mockChain) @@ -734,8 +712,8 @@ internal open class TracingInterceptorNonDdTracerNotSendingSpanTest { it.addHeader( TracingInterceptor.DATADOG_SAMPLING_PRIORITY_HEADER, forge.anElementFrom( - PrioritySampling.SAMPLER_DROP.toString(), - PrioritySampling.USER_DROP.toString() + DatadogTracingConstants.PrioritySampling.SAMPLER_DROP.toString(), + DatadogTracingConstants.PrioritySampling.USER_DROP.toString() ) ) } @@ -774,7 +752,7 @@ internal open class TracingInterceptorNonDdTracerNotSendingSpanTest { fakeRequest = forgeRequest(forge) { it.addHeader( TracingInterceptor.B3M_SAMPLING_PRIORITY_KEY, - PrioritySampling.SAMPLER_DROP.toString() + DatadogTracingConstants.PrioritySampling.SAMPLER_DROP.toString() ) } stubChain(mockChain, statusCode) @@ -811,7 +789,7 @@ internal open class TracingInterceptorNonDdTracerNotSendingSpanTest { it.addHeader( TracingInterceptor.B3_HEADER_KEY, forge.anElementFrom( - PrioritySampling.SAMPLER_DROP.toString(), + DatadogTracingConstants.PrioritySampling.SAMPLER_DROP.toString(), forge.aStringMatching("[a-f0-9]{32}\\-[a-f0-9]{16}\\-0") ) ) @@ -863,11 +841,11 @@ internal open class TracingInterceptorNonDdTracerNotSendingSpanTest { assertThat(lastValue.headers) .hasTraceParentHeader( fakeTraceIdAsString, - mockSpan.context().toSpanId(), + mockSpan.context().spanId.toString(), isSampled = false ) .hasTraceStateHeaderWithOnlyDatadogVendorValues( - mockSpan.context().toSpanId(), + mockSpan.context().spanId.toString(), isSampled = false, fakeOrigin ) @@ -902,7 +880,7 @@ internal open class TracingInterceptorNonDdTracerNotSendingSpanTest { verify(mockSpan).setTag("http.url", fakeUrl) verify(mockSpan).setTag("http.method", fakeMethod) verify(mockSpan).setTag("http.status_code", statusCode) - verify(mockSpan).setTag(Tags.SPAN_KIND, Tags.SPAN_KIND_CLIENT) + verify(mockSpan).setTag("span.kind", "client") verify(mockSpan, never()).finish() assertThat(response).isSameAs(fakeResponse) } @@ -919,7 +897,7 @@ internal open class TracingInterceptorNonDdTracerNotSendingSpanTest { verify(mockSpan).setTag("http.url", fakeUrl) verify(mockSpan).setTag("http.method", fakeMethod) verify(mockSpan).setTag("http.status_code", statusCode) - verify(mockSpan).setTag(Tags.SPAN_KIND, Tags.SPAN_KIND_CLIENT) + verify(mockSpan).setTag("span.kind", "client") verify(mockSpan, never()).finish() assertThat(response).isSameAs(fakeResponse) } @@ -936,7 +914,7 @@ internal open class TracingInterceptorNonDdTracerNotSendingSpanTest { verify(mockSpan).setTag("http.url", fakeUrl) verify(mockSpan).setTag("http.method", fakeMethod) verify(mockSpan).setTag("http.status_code", statusCode) - verify(mockSpan).setTag(Tags.SPAN_KIND, Tags.SPAN_KIND_CLIENT) + verify(mockSpan).setTag("span.kind", "client") verify(mockSpan, never()).finish() assertThat(response).isSameAs(fakeResponse) } @@ -951,7 +929,7 @@ internal open class TracingInterceptorNonDdTracerNotSendingSpanTest { verify(mockSpan).setTag("http.url", fakeUrl) verify(mockSpan).setTag("http.method", fakeMethod) verify(mockSpan).setTag("http.status_code", 404) - verify(mockSpan).setTag(Tags.SPAN_KIND, Tags.SPAN_KIND_CLIENT) + verify(mockSpan).setTag("span.kind", "client") verify(mockSpan, never()).finish() assertThat(response).isSameAs(fakeResponse) } @@ -973,7 +951,7 @@ internal open class TracingInterceptorNonDdTracerNotSendingSpanTest { verify(mockSpan).setTag("error.type", throwable.javaClass.canonicalName) verify(mockSpan).setTag("error.msg", throwable.message) verify(mockSpan).setTag("error.stack", throwable.loggableStackTrace()) - verify(mockSpan).setTag(Tags.SPAN_KIND, Tags.SPAN_KIND_CLIENT) + verify(mockSpan).setTag("span.kind", "client") verify(mockSpan, never()).finish() } @@ -981,7 +959,6 @@ internal open class TracingInterceptorNonDdTracerNotSendingSpanTest { fun `M warn the user W intercept() no tracer registered and TracingFeature not initialized`( @IntForgery(min = 200, max = 300) statusCode: Int ) { - GlobalTracer::class.java.setStaticValue("isRegistered", false) whenever(datadogCore.mockInstance.getFeature(Feature.TRACING_FEATURE_NAME)) doReturn null stubChain(mockChain, statusCode) @@ -1000,27 +977,27 @@ internal open class TracingInterceptorNonDdTracerNotSendingSpanTest { @Test fun `M create a span with automatic tracer W intercept() if no tracer registered`( + forge: Forge, @IntForgery(min = 200, max = 300) statusCode: Int ) { - val localSpanBuilder: Tracer.SpanBuilder = mock() - val localSpan: Span = mock() - GlobalTracer::class.java.setStaticValue("isRegistered", false) + val localSpan: DatadogSpan = forge.newSpanMock() + val localSpanBuilder: DatadogSpanBuilder = forge.newSpanBuilderMock(localSpan) whenever(mockResolver.isFirstPartyUrl(fakeUrl.toHttpUrl())).thenReturn(true) - stubChain(mockChain, statusCode) - whenever(localSpanBuilder.asChildOf(null as SpanContext?)) doReturn localSpanBuilder - whenever(localSpanBuilder.start()) doReturn localSpan - whenever(localSpan.context()) doReturn mockSpanContext whenever(mockTraceSampler.sample(localSpan)).thenReturn(true) - whenever(mockSpanContext.toSpanId()) doReturn fakeSpanId - whenever(mockSpanContext.traceId) doReturn fakeTraceId whenever(mockLocalTracer.buildSpan(TracingInterceptor.SPAN_NAME)) doReturn localSpanBuilder + val testedInterceptorNoGlobal = instantiateTestedInterceptor( + fakeLocalHosts, + localTracerFactory = { _, _ -> mockLocalTracer }, + globalTracerProvider = { null } + ) - val response = testedInterceptor.intercept(mockChain) + stubChain(mockChain, statusCode) + val response = testedInterceptorNoGlobal.intercept(mockChain) verify(localSpan).setTag("http.url", fakeUrl) verify(localSpan).setTag("http.method", fakeMethod) verify(localSpan).setTag("http.status_code", statusCode) - verify(localSpan).setTag(Tags.SPAN_KIND, Tags.SPAN_KIND_CLIENT) + verify(localSpan).setTag("span.kind", "client") verify(localSpan, never()).finish() assertThat(response).isSameAs(fakeResponse) mockInternalLogger.verifyLog( @@ -1032,24 +1009,26 @@ internal open class TracingInterceptorNonDdTracerNotSendingSpanTest { @Test fun `M drop automatic tracer W intercept() and global tracer registered`( + forge: Forge, @IntForgery(min = 200, max = 300) statusCode: Int ) { - val localSpanBuilder: Tracer.SpanBuilder = mock() - val localSpan: Span = mock() - GlobalTracer::class.java.setStaticValue("isRegistered", false) + val localSpan: DatadogSpan = forge.newSpanMock(mockSpanContext) + val localSpanBuilder: DatadogSpanBuilder = forge.newSpanBuilderMock(localSpan, mockSpanContext) + whenever(mockLocalTracer.buildSpan(TracingInterceptor.SPAN_NAME)).thenReturn(localSpanBuilder) + whenever(mockTraceSampler.sample(localSpan)).thenReturn(true) whenever(mockResolver.isFirstPartyUrl(fakeUrl.toHttpUrl())).thenReturn(true) + val testedInterceptorNoGlobal = instantiateTestedInterceptor( + fakeLocalHosts, + localTracerFactory = { _, _ -> mockLocalTracer }, + globalTracerProvider = { null } + ) + stubChain(mockChain, statusCode) - whenever(localSpanBuilder.asChildOf(null as SpanContext?)) doReturn localSpanBuilder - whenever(localSpanBuilder.start()) doReturn localSpan - whenever(localSpan.context()) doReturn mockSpanContext - whenever(mockTraceSampler.sample(localSpan)).thenReturn(true) - whenever(mockSpanContext.toSpanId()) doReturn fakeSpanId - whenever(mockSpanContext.traceId) doReturn fakeTraceId - whenever(mockLocalTracer.buildSpan(TracingInterceptor.SPAN_NAME)) doReturn localSpanBuilder - val response1 = testedInterceptor.intercept(mockChain) + stubChain(mockChain, statusCode) + val response1 = testedInterceptorNoGlobal.intercept(mockChain) val expectedResponse1 = fakeResponse - GlobalTracer.registerIfAbsent(mockTracer) + stubChain(mockChain, statusCode) val response2 = testedInterceptor.intercept(mockChain) val expectedResponse2 = fakeResponse @@ -1057,12 +1036,12 @@ internal open class TracingInterceptorNonDdTracerNotSendingSpanTest { verify(localSpan).setTag("http.url", fakeUrl) verify(localSpan).setTag("http.method", fakeMethod) verify(localSpan).setTag("http.status_code", statusCode) - verify(localSpan).setTag(Tags.SPAN_KIND, Tags.SPAN_KIND_CLIENT) + verify(localSpan).setTag("span.kind", "client") verify(localSpan, never()).finish() verify(mockSpan).setTag("http.url", fakeUrl) verify(mockSpan).setTag("http.method", fakeMethod) verify(mockSpan).setTag("http.status_code", statusCode) - verify(mockSpan).setTag(Tags.SPAN_KIND, Tags.SPAN_KIND_CLIENT) + verify(mockSpan).setTag("span.kind", "client") verify(mockSpan, never()).finish() assertThat(response1).isSameAs(expectedResponse1) assertThat(response2).isSameAs(expectedResponse2) @@ -1084,7 +1063,7 @@ internal open class TracingInterceptorNonDdTracerNotSendingSpanTest { whenever( mockRequestListener.onRequestIntercepted(any(), any(), anyOrNull(), anyOrNull()) ).doAnswer { - val span = it.arguments[1] as Span + val span = it.arguments[1] as DatadogSpan span.setTag(tagKey, tagValue) return@doAnswer Unit } @@ -1095,7 +1074,7 @@ internal open class TracingInterceptorNonDdTracerNotSendingSpanTest { verify(mockSpan).setTag("http.method", fakeMethod) verify(mockSpan).setTag("http.status_code", statusCode) verify(mockSpan).setTag(tagKey, tagValue) - verify(mockSpan).setTag(Tags.SPAN_KIND, Tags.SPAN_KIND_CLIENT) + verify(mockSpan).setTag("span.kind", "client") verify(mockSpan, never()).finish() assertThat(response).isSameAs(fakeResponse) } @@ -1111,7 +1090,7 @@ internal open class TracingInterceptorNonDdTracerNotSendingSpanTest { whenever( mockRequestListener.onRequestIntercepted(any(), any(), anyOrNull(), anyOrNull()) ).doAnswer { - val span = it.arguments[1] as Span + val span = it.arguments[1] as DatadogSpan span.setTag(tagKey, tagValue) return@doAnswer Unit } @@ -1122,7 +1101,7 @@ internal open class TracingInterceptorNonDdTracerNotSendingSpanTest { verify(mockSpan).setTag("http.method", fakeMethod) verify(mockSpan).setTag("http.status_code", statusCode) verify(mockSpan).setTag(tagKey, tagValue) - verify(mockSpan).setTag(Tags.SPAN_KIND, Tags.SPAN_KIND_CLIENT) + verify(mockSpan).setTag("span.kind", "client") verify(mockSpan, never()).finish() assertThat(response).isSameAs(fakeResponse) } @@ -1154,7 +1133,7 @@ internal open class TracingInterceptorNonDdTracerNotSendingSpanTest { whenever( mockRequestListener.onRequestIntercepted(any(), any(), anyOrNull(), anyOrNull()) ).doAnswer { - val span = it.arguments[1] as Span + val span = it.arguments[1] as DatadogSpan span.setTag(tagKey, tagValue) return@doAnswer Unit } @@ -1170,7 +1149,7 @@ internal open class TracingInterceptorNonDdTracerNotSendingSpanTest { verify(mockSpan).setTag("error.type", throwable.javaClass.canonicalName) verify(mockSpan).setTag("error.msg", throwable.message) verify(mockSpan).setTag("error.stack", throwable.loggableStackTrace()) - verify(mockSpan).setTag(Tags.SPAN_KIND, Tags.SPAN_KIND_CLIENT) + verify(mockSpan).setTag("span.kind", "client") verify(mockSpan).setTag(tagKey, tagValue) verify(mockSpan, never()).finish() } @@ -1246,16 +1225,15 @@ internal open class TracingInterceptorNonDdTracerNotSendingSpanTest { called++ mockLocalTracer } - GlobalTracer::class.java.setStaticValue("isRegistered", false) stubChain(mockChain, statusCode) // need this setup, otherwise #intercept actually throws NPE, which pollutes the log - val localSpanBuilder: DDTracer.DDSpanBuilder = mock() - val localSpan: Span = mock(extraInterfaces = arrayOf(MutableSpan::class)) - whenever(localSpanBuilder.asChildOf(null as SpanContext?)) doReturn localSpanBuilder + val localSpanBuilder: DatadogSpanBuilder = mock() + val localSpan: DatadogSpan = mock() + whenever(localSpanBuilder.withParentContext(null)) doReturn localSpanBuilder whenever(localSpanBuilder.start()) doReturn localSpan whenever(localSpan.context()) doReturn mockSpanContext - whenever(mockSpanContext.toSpanId()) doReturn fakeSpanId + whenever(mockSpanContext.spanId) doReturn fakeSpanId whenever(mockSpanContext.traceId) doReturn fakeTraceId whenever(mockLocalTracer.buildSpan(TracingInterceptor.SPAN_NAME)) doReturn localSpanBuilder diff --git a/integrations/dd-sdk-android-okhttp/src/test/kotlin/com/datadog/android/okhttp/trace/TracingInterceptorNonDdTracerTest.kt b/integrations/dd-sdk-android-okhttp/src/test/kotlin/com/datadog/android/okhttp/trace/TracingInterceptorNonDdTracerTest.kt index ecb4cacca7..cd8cbc132c 100644 --- a/integrations/dd-sdk-android-okhttp/src/test/kotlin/com/datadog/android/okhttp/trace/TracingInterceptorNonDdTracerTest.kt +++ b/integrations/dd-sdk-android-okhttp/src/test/kotlin/com/datadog/android/okhttp/trace/TracingInterceptorNonDdTracerTest.kt @@ -3,7 +3,6 @@ * This product includes software developed at Datadog (https://www.datadoghq.com/). * Copyright 2016-Present Datadog, Inc. */ - package com.datadog.android.okhttp.trace import com.datadog.android.api.InternalLogger @@ -17,31 +16,29 @@ import com.datadog.android.okhttp.utils.assertj.HeadersAssert.Companion.assertTh import com.datadog.android.okhttp.utils.config.DatadogSingletonTestConfiguration import com.datadog.android.okhttp.utils.verifyLog import com.datadog.android.trace.TracingHeaderType -import com.datadog.legacy.trace.api.interceptor.MutableSpan -import com.datadog.legacy.trace.api.sampling.PrioritySampling -import com.datadog.opentracing.DDSpanContext -import com.datadog.opentracing.DDTracer -import com.datadog.opentracing.propagation.ExtractedContext +import com.datadog.android.trace.api.DatadogTracingConstants +import com.datadog.android.trace.api.propagation.DatadogPropagation +import com.datadog.android.trace.api.span.DatadogSpan +import com.datadog.android.trace.api.span.DatadogSpanBuilder +import com.datadog.android.trace.api.span.DatadogSpanContext +import com.datadog.android.trace.api.trace.DatadogTraceId +import com.datadog.android.trace.api.tracer.DatadogTracer +import com.datadog.android.trace.api.withMockPropagationHelper +import com.datadog.android.trace.internal.DatadogPropagationHelper +import com.datadog.android.trace.internal.DatadogTracingToolkit import com.datadog.tools.unit.annotations.TestConfigurationsProvider import com.datadog.tools.unit.extensions.TestConfigurationExtension import com.datadog.tools.unit.extensions.config.TestConfiguration import com.datadog.tools.unit.forge.BaseConfigurator -import com.datadog.tools.unit.setStaticValue import fr.xgouchet.elmyr.Forge import fr.xgouchet.elmyr.annotation.BoolForgery import fr.xgouchet.elmyr.annotation.Forgery import fr.xgouchet.elmyr.annotation.IntForgery +import fr.xgouchet.elmyr.annotation.LongForgery import fr.xgouchet.elmyr.annotation.StringForgery import fr.xgouchet.elmyr.annotation.StringForgeryType import fr.xgouchet.elmyr.junit5.ForgeConfiguration import fr.xgouchet.elmyr.junit5.ForgeExtension -import io.opentracing.Span -import io.opentracing.SpanContext -import io.opentracing.Tracer -import io.opentracing.propagation.TextMapExtract -import io.opentracing.propagation.TextMapInject -import io.opentracing.tag.Tags -import io.opentracing.util.GlobalTracer import okhttp3.HttpUrl import okhttp3.HttpUrl.Companion.toHttpUrl import okhttp3.Interceptor @@ -53,7 +50,6 @@ import okhttp3.RequestBody.Companion.toRequestBody import okhttp3.Response import okhttp3.ResponseBody.Companion.toResponseBody import org.assertj.core.api.Assertions.assertThat -import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test import org.junit.jupiter.api.assertThrows @@ -74,12 +70,11 @@ import org.mockito.kotlin.verify import org.mockito.kotlin.verifyNoInteractions import org.mockito.kotlin.whenever import org.mockito.quality.Strictness -import java.math.BigInteger import java.util.concurrent.CountDownLatch import java.util.concurrent.TimeUnit /** - * This test is a duplicate of [TracingInterceptorTest] but assuming the Tracer is not our own + * This test is a duplicate of [TracingInterceptorTest] but assuming the AgentTracer.TracerAPI is not our own * implementation and therefore doesn't implement DD specific methods. */ @Extensions( @@ -91,24 +86,30 @@ import java.util.concurrent.TimeUnit @ForgeConfiguration(BaseConfigurator::class) internal open class TracingInterceptorNonDdTracerTest { - lateinit var testedInterceptor: TracingInterceptor + private lateinit var testedInterceptor: TracingInterceptor // region Mocks @Mock - lateinit var mockTracer: Tracer + lateinit var mockTracer: DatadogTracer + + @Mock + lateinit var mockPropagation: DatadogPropagation @Mock - lateinit var mockLocalTracer: Tracer + lateinit var mockPropagationHelper: DatadogPropagationHelper @Mock - lateinit var mockSpanBuilder: Tracer.SpanBuilder + lateinit var mockLocalTracer: DatadogTracer @Mock - lateinit var mockSpanContext: DDSpanContext + lateinit var mockSpanBuilder: DatadogSpanBuilder @Mock - lateinit var mockSpan: Span + lateinit var mockSpanContext: DatadogSpanContext + + @Mock + lateinit var mockSpan: DatadogSpan @Mock lateinit var mockChain: Interceptor.Chain @@ -119,8 +120,7 @@ internal open class TracingInterceptorNonDdTracerTest { @Mock lateinit var mockResolver: DefaultFirstPartyHostHeaderTypeResolver - @Mock - lateinit var mockTraceSampler: Sampler + lateinit var mockTraceSampler: Sampler @Mock lateinit var mockInternalLogger: InternalLogger @@ -143,13 +143,13 @@ internal open class TracingInterceptorNonDdTracerTest { lateinit var fakeRequest: Request lateinit var fakeResponse: Response - @StringForgery(type = StringForgeryType.HEXADECIMAL) - lateinit var fakeSpanId: String + @LongForgery + var fakeSpanId: Long = 0L - @StringForgery(regex = "[a-f][0-9]{32}") + @StringForgery(regex = "[a-f][0-9]{31}") lateinit var fakeTraceIdAsString: String - lateinit var fakeTraceId: BigInteger + lateinit var fakeTraceId: DatadogTraceId private var fakeOrigin: String? = null @@ -162,17 +162,16 @@ internal open class TracingInterceptorNonDdTracerTest { @BeforeEach fun `set up`(forge: Forge) { - fakeTraceId = BigInteger(fakeTraceIdAsString, 16) - whenever(mockTracer.buildSpan(TracingInterceptor.SPAN_NAME)) doReturn mockSpanBuilder - whenever(mockLocalTracer.buildSpan(TracingInterceptor.SPAN_NAME)) doReturn mockSpanBuilder - whenever(mockSpanBuilder.asChildOf(null as SpanContext?)) doReturn mockSpanBuilder - whenever(mockSpanBuilder.start()) doReturn mockSpan - whenever(mockSpan.context()) doReturn mockSpanContext - whenever(mockSpanContext.toSpanId()) doReturn fakeSpanId - whenever(mockSpanContext.traceId) doReturn fakeTraceId - whenever(mockTraceSampler.sample(mockSpan)) doReturn true - + fakeTraceId = forge.aDatadogTraceId(fakeTraceIdAsString) fakeOrigin = forge.aNullable { anAlphabeticalString() } + mockSpanContext = forge.newSpanContextMock(fakeTraceId, fakeSpanId) + mockSpan = forge.newSpanMock(mockSpanContext) + mockSpanBuilder = forge.newSpanBuilderMock(mockSpan, mockSpanContext) + mockTraceSampler = forge.newTraceSamplerMock(mockSpan) + mockPropagation = newAgentPropagationMock() + mockTracer = forge.newTracerMock(mockSpanBuilder, mockPropagation) + mockLocalTracer = forge.newTracerMock(mockSpanBuilder, mockPropagation) + val mediaType = forge.anElementFrom("application", "image", "text", "model") + "/" + forge.anAlphabeticalString() fakeLocalHosts = forge.aMap { @@ -183,19 +182,21 @@ internal open class TracingInterceptorNonDdTracerTest { fakeMediaType = mediaType.toMediaTypeOrNull() fakeUrl = forgeUrlWithQueryParams(forge) fakeRequest = forgeRequest(forge) + whenever(mockResolver.isFirstPartyUrl(fakeUrl.toHttpUrl())).thenReturn(true) whenever(datadogCore.mockInstance.getFeature(Feature.TRACING_FEATURE_NAME)) doReturn mock() whenever(datadogCore.mockInstance.internalLogger) doReturn mockInternalLogger whenever(datadogCore.mockInstance.firstPartyHostResolver) doReturn mockResolver - testedInterceptor = instantiateTestedInterceptor(fakeLocalHosts) { _, _ -> - mockLocalTracer - } - - GlobalTracer.registerIfAbsent(mockTracer) + testedInterceptor = instantiateTestedInterceptor( + fakeLocalHosts, + localTracerFactory = { _, _ -> mockLocalTracer }, + globalTracerProvider = { mockTracer } + ) } open fun instantiateTestedInterceptor( tracedHosts: Map> = emptyMap(), - factory: (SdkCore, Set) -> Tracer + globalTracerProvider: () -> DatadogTracer? = { null }, + localTracerFactory: (SdkCore, Set) -> DatadogTracer ): TracingInterceptor { return TracingInterceptor( sdkInstanceName = null, @@ -205,15 +206,11 @@ internal open class TracingInterceptorNonDdTracerTest { traceSampler = mockTraceSampler, traceContextInjection = TraceContextInjection.ALL, redacted404ResourceName = fakeRedacted404Resources, - localTracerFactory = factory + localTracerFactory = localTracerFactory, + globalTracerProvider = globalTracerProvider ) } - @AfterEach - fun `tear down`() { - GlobalTracer::class.java.setStaticValue("isRegistered", false) - } - @Test fun `M instantiate with default values W init()`() { // Given @@ -262,10 +259,7 @@ internal open class TracingInterceptorNonDdTracerTest { ) { stubChain(mockChain, statusCode) whenever(mockResolver.isFirstPartyUrl(fakeUrl.toHttpUrl())).thenReturn(true) - doAnswer { invocation -> - val carrier = invocation.arguments[2] as TextMapInject - carrier.put(key, value) - }.whenever(mockTracer).inject(any(), any(), any()) + mockPropagation.wheneverInjectThenValueToHeaders(key, value) val response = testedInterceptor.intercept(mockChain) @@ -297,8 +291,7 @@ internal open class TracingInterceptorNonDdTracerTest { assertThat(response).isSameAs(fakeResponse) argumentCaptor { verify(mockChain).proceed(capture()) - assertThat(lastValue.header(TracingInterceptor.DATADOG_SAMPLING_PRIORITY_HEADER)) - .isEqualTo("0") + assertThat(lastValue.header(TracingInterceptor.DATADOG_SAMPLING_PRIORITY_HEADER)).isEqualTo("0") assertThat(lastValue.header(TracingInterceptor.DATADOG_LEAST_SIGNIFICANT_64_BITS_TRACE_ID_HEADER)).isNull() assertThat(lastValue.header(TracingInterceptor.DATADOG_SPAN_ID_HEADER)).isNull() assertThat(lastValue.header(TracingInterceptor.DATADOG_TAGS_HEADER)).isNull() @@ -384,11 +377,11 @@ internal open class TracingInterceptorNonDdTracerTest { assertThat(lastValue.headers) .hasTraceParentHeader( fakeTraceIdAsString, - mockSpan.context().toSpanId(), + mockSpan.context().spanId.toString(), isSampled = false ) .hasTraceStateHeaderWithOnlyDatadogVendorValues( - mockSpan.context().toSpanId(), + mockSpan.context().spanId.toString(), isSampled = false, fakeOrigin ) @@ -406,10 +399,7 @@ internal open class TracingInterceptorNonDdTracerTest { fakeRequest = forgeRequest(forge) whenever(mockResolver.isFirstPartyUrl(fakeUrl.toHttpUrl())).thenReturn(false) stubChain(mockChain, statusCode) - doAnswer { invocation -> - val carrier = invocation.arguments[2] as TextMapInject - carrier.put(key, value) - }.whenever(mockTracer).inject(any(), any(), any()) + mockPropagation.wheneverInjectThenValueToHeaders(key, value) val response = testedInterceptor.intercept(mockChain) @@ -522,7 +512,11 @@ internal open class TracingInterceptorNonDdTracerTest { TracingHeaderType.TRACECONTEXT ) } - testedInterceptor = instantiateTestedInterceptor(fakeLocalHosts) { _, _ -> mockLocalTracer } + testedInterceptor = instantiateTestedInterceptor( + fakeLocalHosts, + localTracerFactory = { _, _ -> mockLocalTracer }, + globalTracerProvider = { mockTracer } + ) fakeUrl = forgeUrlWithQueryParams(forge, forge.anElementFrom(fakeLocalHosts.keys)) fakeRequest = forgeRequest(forge) whenever(mockResolver.isFirstPartyUrl(fakeUrl.toHttpUrl())).thenReturn(false) @@ -538,11 +532,11 @@ internal open class TracingInterceptorNonDdTracerTest { assertThat(lastValue.headers) .hasTraceParentHeader( fakeTraceIdAsString, - mockSpan.context().toSpanId(), + mockSpan.context().spanId.toString(), isSampled = false ) .hasTraceStateHeaderWithOnlyDatadogVendorValues( - mockSpan.context().toSpanId(), + mockSpan.context().spanId.toString(), isSampled = false, fakeOrigin ) @@ -556,17 +550,14 @@ internal open class TracingInterceptorNonDdTracerTest { @IntForgery(min = 200, max = 300) statusCode: Int, forge: Forge ) { - val parentSpan: Span = mock() - val parentSpanContext: SpanContext = mock() + val parentSpanContext: DatadogSpanContext = forge.newSpanContextMock(samplingPriority = 1) + val parentSpan: DatadogSpan = forge.newSpanMock(parentSpanContext) whenever(parentSpan.context()) doReturn parentSpanContext - whenever(mockSpanBuilder.asChildOf(parentSpanContext)) doReturn mockSpanBuilder - fakeRequest = forgeRequest(forge) { it.tag(Span::class.java, parentSpan) } + whenever(mockSpanBuilder.withParentContext(parentSpanContext)) doReturn mockSpanBuilder + fakeRequest = forgeRequest(forge) { it.tag(DatadogSpan::class.java, parentSpan) } whenever(mockResolver.isFirstPartyUrl(fakeUrl.toHttpUrl())).thenReturn(true) stubChain(mockChain, statusCode) - doAnswer { invocation -> - val carrier = invocation.arguments[2] as TextMapInject - carrier.put(key, value) - }.whenever(mockTracer).inject(any(), any(), any()) + mockPropagation.wheneverInjectThenValueToHeaders(key, value) val response = testedInterceptor.intercept(mockChain) @@ -575,7 +566,7 @@ internal open class TracingInterceptorNonDdTracerTest { verify(mockChain).proceed(capture()) assertThat(firstValue.headers(key)).containsOnly(value) } - verify(mockSpanBuilder).asChildOf(parentSpanContext) + verify(mockSpanBuilder).withParentContext(parentSpanContext) } @Test @@ -588,10 +579,7 @@ internal open class TracingInterceptorNonDdTracerTest { fakeRequest = fakeRequest.newBuilder().addHeader(key, previousValue).build() stubChain(mockChain, statusCode) whenever(mockResolver.isFirstPartyUrl(fakeUrl.toHttpUrl())).thenReturn(true) - doAnswer { invocation -> - val carrier = invocation.arguments[2] as TextMapInject - carrier.put(key, value) - }.whenever(mockTracer).inject(any(), any(), any()) + mockPropagation.wheneverInjectThenValueToHeaders(key, value) val response = testedInterceptor.intercept(mockChain) @@ -614,10 +602,7 @@ internal open class TracingInterceptorNonDdTracerTest { fakeRequest = forgeRequest(forge).newBuilder().addHeader(key, previousValue).build() whenever(mockResolver.isFirstPartyUrl(fakeUrl.toHttpUrl())).thenReturn(false) stubChain(mockChain, statusCode) - doAnswer { invocation -> - val carrier = invocation.arguments[2] as TextMapInject - carrier.put(key, value) - }.whenever(mockTracer).inject(any(), any(), any()) + mockPropagation.wheneverInjectThenValueToHeaders(key, value) val response = testedInterceptor.intercept(mockChain) @@ -638,8 +623,8 @@ internal open class TracingInterceptorNonDdTracerTest { fakeRequest = forgeRequest(forge) whenever(mockResolver.isFirstPartyUrl(fakeUrl.toHttpUrl())).thenReturn(false) stubChain(mockChain, statusCode) - doThrow(IllegalStateException(message)).whenever(mockTracer) - .inject(any(), any(), any()) + doThrow(IllegalStateException(message)).whenever(mockPropagation) + .inject(any(), any(), any()) val response = testedInterceptor.intercept(mockChain) @@ -656,24 +641,24 @@ internal open class TracingInterceptorNonDdTracerTest { @StringForgery(type = StringForgeryType.ALPHA_NUMERICAL) value: String, @IntForgery(min = 200, max = 300) statusCode: Int ) { - val parentSpanContext: ExtractedContext = mock() - whenever(mockTracer.extract(any(), any())) doReturn parentSpanContext - whenever(mockSpanBuilder.asChildOf(any())) doReturn mockSpanBuilder + val parentSpanContext: DatadogSpanContext = mock() + whenever(mockPropagation.extract(any(), any())) doReturn parentSpanContext + whenever(mockPropagationHelper.isExtractedContext(parentSpanContext)) doReturn true + whenever(mockSpanBuilder.withParentContext(any())) doReturn mockSpanBuilder whenever(mockResolver.isFirstPartyUrl(fakeUrl.toHttpUrl())).thenReturn(true) stubChain(mockChain, statusCode) - doAnswer { invocation -> - val carrier = invocation.arguments[2] as TextMapInject - carrier.put(key, value) - }.whenever(mockTracer).inject(any(), any(), any()) + mockPropagation.wheneverInjectThenValueToHeaders(key, value) - val response = testedInterceptor.intercept(mockChain) + DatadogTracingToolkit.withMockPropagationHelper(mockPropagationHelper) { + val response = testedInterceptor.intercept(mockChain) - assertThat(response).isSameAs(fakeResponse) - argumentCaptor { - verify(mockChain).proceed(capture()) - assertThat(firstValue.headers(key)).containsOnly(value) + assertThat(response).isSameAs(fakeResponse) + argumentCaptor { + verify(mockChain).proceed(capture()) + assertThat(firstValue.headers(key)).containsOnly(value) + } + verify(mockSpanBuilder).withParentContext(parentSpanContext) } - verify(mockSpanBuilder).asChildOf(parentSpanContext) } @Test @@ -694,17 +679,14 @@ internal open class TracingInterceptorNonDdTracerTest { it.addHeader( TracingInterceptor.DATADOG_SAMPLING_PRIORITY_HEADER, forge.anElementFrom( - PrioritySampling.SAMPLER_KEEP.toString(), - PrioritySampling.USER_KEEP.toString() + DatadogTracingConstants.PrioritySampling.SAMPLER_KEEP.toString(), + DatadogTracingConstants.PrioritySampling.USER_KEEP.toString() ) ) } stubChain(mockChain, statusCode) whenever(mockTraceSampler.sample(mockSpan)).thenReturn(false) - doAnswer { invocation -> - val carrier = invocation.arguments[2] as TextMapInject - carrier.put(key, value) - }.whenever(mockTracer).inject(any(), any(), any()) + mockPropagation.wheneverInjectThenValueToHeaders(key, value) // When val response = testedInterceptor.intercept(mockChain) @@ -734,15 +716,12 @@ internal open class TracingInterceptorNonDdTracerTest { fakeRequest = forgeRequest(forge) { it.addHeader( TracingInterceptor.B3M_SAMPLING_PRIORITY_KEY, - PrioritySampling.SAMPLER_KEEP.toString() + DatadogTracingConstants.PrioritySampling.SAMPLER_KEEP.toString() ) } stubChain(mockChain, statusCode) whenever(mockTraceSampler.sample(mockSpan)).thenReturn(false) - doAnswer { invocation -> - val carrier = invocation.arguments[2] as TextMapInject - carrier.put(key, value) - }.whenever(mockTracer).inject(any(), any(), any()) + mockPropagation.wheneverInjectThenValueToHeaders(key, value) // When val response = testedInterceptor.intercept(mockChain) @@ -777,10 +756,7 @@ internal open class TracingInterceptorNonDdTracerTest { } stubChain(mockChain, statusCode) whenever(mockTraceSampler.sample(mockSpan)).thenReturn(false) - doAnswer { invocation -> - val carrier = invocation.arguments[2] as TextMapInject - carrier.put(key, value) - }.whenever(mockTracer).inject(any(), any(), any()) + mockPropagation.wheneverInjectThenValueToHeaders(key, value) // When val response = testedInterceptor.intercept(mockChain) @@ -815,10 +791,7 @@ internal open class TracingInterceptorNonDdTracerTest { } stubChain(mockChain, statusCode) whenever(mockTraceSampler.sample(mockSpan)).thenReturn(false) - doAnswer { invocation -> - val carrier = invocation.arguments[2] as TextMapInject - carrier.put(key, value) - }.whenever(mockTracer).inject(any(), any(), any()) + mockPropagation.wheneverInjectThenValueToHeaders(key, value) // When val response = testedInterceptor.intercept(mockChain) @@ -848,8 +821,8 @@ internal open class TracingInterceptorNonDdTracerTest { it.addHeader( TracingInterceptor.DATADOG_SAMPLING_PRIORITY_HEADER, forge.anElementFrom( - PrioritySampling.SAMPLER_DROP.toString(), - PrioritySampling.USER_DROP.toString() + DatadogTracingConstants.PrioritySampling.SAMPLER_DROP.toString(), + DatadogTracingConstants.PrioritySampling.USER_DROP.toString() ) ) } @@ -888,7 +861,7 @@ internal open class TracingInterceptorNonDdTracerTest { fakeRequest = forgeRequest(forge) { it.addHeader( TracingInterceptor.B3M_SAMPLING_PRIORITY_KEY, - PrioritySampling.SAMPLER_DROP.toString() + DatadogTracingConstants.PrioritySampling.SAMPLER_DROP.toString() ) } stubChain(mockChain, statusCode) @@ -925,7 +898,7 @@ internal open class TracingInterceptorNonDdTracerTest { it.addHeader( TracingInterceptor.B3_HEADER_KEY, forge.anElementFrom( - PrioritySampling.SAMPLER_DROP.toString(), + DatadogTracingConstants.PrioritySampling.SAMPLER_DROP.toString(), forge.aStringMatching("[a-f0-9]{32}\\-[a-f0-9]{16}\\-0") ) ) @@ -977,11 +950,11 @@ internal open class TracingInterceptorNonDdTracerTest { assertThat(lastValue.headers) .hasTraceParentHeader( fakeTraceIdAsString, - mockSpan.context().toSpanId(), + mockSpan.context().spanId.toString(), isSampled = false ) .hasTraceStateHeaderWithOnlyDatadogVendorValues( - mockSpan.context().toSpanId(), + mockSpan.context().spanId.toString(), isSampled = false, fakeOrigin ) @@ -1000,7 +973,7 @@ internal open class TracingInterceptorNonDdTracerTest { verify(mockSpan).setTag("http.url", fakeUrl) verify(mockSpan).setTag("http.method", fakeMethod) verify(mockSpan).setTag("http.status_code", statusCode) - verify(mockSpan).setTag(Tags.SPAN_KIND, Tags.SPAN_KIND_CLIENT) + verify(mockSpan).setTag("span.kind", "client") verify(mockSpan).finish() assertThat(response).isSameAs(fakeResponse) } @@ -1021,7 +994,7 @@ internal open class TracingInterceptorNonDdTracerTest { verify(mockSpan).setTag("http.url", fakeUrlWithoutQueryParams) verify(mockSpan).setTag("http.method", fakeMethod) verify(mockSpan).setTag("http.status_code", statusCode) - verify(mockSpan).setTag(Tags.SPAN_KIND, Tags.SPAN_KIND_CLIENT) + verify(mockSpan).setTag("span.kind", "client") verify(mockSpan).finish() assertThat(response).isSameAs(fakeResponse) } @@ -1038,7 +1011,7 @@ internal open class TracingInterceptorNonDdTracerTest { verify(mockSpan).setTag("http.url", fakeUrl) verify(mockSpan).setTag("http.method", fakeMethod) verify(mockSpan).setTag("http.status_code", statusCode) - verify(mockSpan).setTag(Tags.SPAN_KIND, Tags.SPAN_KIND_CLIENT) + verify(mockSpan).setTag("span.kind", "client") verify(mockSpan).finish() assertThat(response).isSameAs(fakeResponse) } @@ -1055,7 +1028,7 @@ internal open class TracingInterceptorNonDdTracerTest { verify(mockSpan).setTag("http.url", fakeUrl) verify(mockSpan).setTag("http.method", fakeMethod) verify(mockSpan).setTag("http.status_code", statusCode) - verify(mockSpan).setTag(Tags.SPAN_KIND, Tags.SPAN_KIND_CLIENT) + verify(mockSpan).setTag("span.kind", "client") verify(mockSpan).finish() assertThat(response).isSameAs(fakeResponse) } @@ -1070,7 +1043,7 @@ internal open class TracingInterceptorNonDdTracerTest { verify(mockSpan).setTag("http.url", fakeUrl) verify(mockSpan).setTag("http.method", fakeMethod) verify(mockSpan).setTag("http.status_code", 404) - verify(mockSpan).setTag(Tags.SPAN_KIND, Tags.SPAN_KIND_CLIENT) + verify(mockSpan).setTag("span.kind", "client") verify(mockSpan).finish() assertThat(response).isSameAs(fakeResponse) } @@ -1092,7 +1065,7 @@ internal open class TracingInterceptorNonDdTracerTest { verify(mockSpan).setTag("error.type", throwable.javaClass.canonicalName) verify(mockSpan).setTag("error.msg", throwable.message) verify(mockSpan).setTag("error.stack", throwable.loggableStackTrace()) - verify(mockSpan).setTag(Tags.SPAN_KIND, Tags.SPAN_KIND_CLIENT) + verify(mockSpan).setTag("span.kind", "client") verify(mockSpan).finish() } @@ -1100,7 +1073,6 @@ internal open class TracingInterceptorNonDdTracerTest { fun `M warn the user W intercept() no tracer registered and TracingFeature not initialized`( @IntForgery(min = 200, max = 300) statusCode: Int ) { - GlobalTracer::class.java.setStaticValue("isRegistered", false) whenever(datadogCore.mockInstance.getFeature(Feature.TRACING_FEATURE_NAME)) doReturn null whenever(mockResolver.isFirstPartyUrl(fakeUrl.toHttpUrl())).thenReturn(true) stubChain(mockChain, statusCode) @@ -1120,27 +1092,27 @@ internal open class TracingInterceptorNonDdTracerTest { @Test fun `M create a span with automatic tracer W intercept() if no tracer registered`( + forge: Forge, @IntForgery(min = 200, max = 300) statusCode: Int ) { - val localSpanBuilder: Tracer.SpanBuilder = mock() - val localSpan: Span = mock() - GlobalTracer::class.java.setStaticValue("isRegistered", false) + val localSpan: DatadogSpan = forge.newSpanMock(mockSpanContext) + val localSpanBuilder: DatadogSpanBuilder = forge.newSpanBuilderMock(localSpan, mockSpanContext) + whenever(mockLocalTracer.buildSpan(TracingInterceptor.SPAN_NAME)) doReturn localSpanBuilder whenever(mockResolver.isFirstPartyUrl(fakeUrl.toHttpUrl())).thenReturn(true) - stubChain(mockChain, statusCode) - whenever(localSpanBuilder.asChildOf(null as SpanContext?)) doReturn localSpanBuilder - whenever(localSpanBuilder.start()) doReturn localSpan - whenever(localSpan.context()) doReturn mockSpanContext whenever(mockTraceSampler.sample(localSpan)).thenReturn(true) - whenever(mockSpanContext.toSpanId()) doReturn fakeSpanId - whenever(mockSpanContext.traceId) doReturn fakeTraceId - whenever(mockLocalTracer.buildSpan(TracingInterceptor.SPAN_NAME)) doReturn localSpanBuilder + val testedInterceptorNoGlobal = instantiateTestedInterceptor( + fakeLocalHosts, + localTracerFactory = { _, _ -> mockLocalTracer }, + globalTracerProvider = { null } + ) - val response = testedInterceptor.intercept(mockChain) + stubChain(mockChain, statusCode) + val response = testedInterceptorNoGlobal.intercept(mockChain) verify(localSpan).setTag("http.url", fakeUrl) verify(localSpan).setTag("http.method", fakeMethod) verify(localSpan).setTag("http.status_code", statusCode) - verify(localSpan).setTag(Tags.SPAN_KIND, Tags.SPAN_KIND_CLIENT) + verify(localSpan).setTag("span.kind", "client") verify(localSpan).finish() assertThat(response).isSameAs(fakeResponse) mockInternalLogger.verifyLog( @@ -1152,24 +1124,23 @@ internal open class TracingInterceptorNonDdTracerTest { @Test fun `M drop automatic tracer W intercept() and global tracer registered`( + forge: Forge, @IntForgery(min = 200, max = 300) statusCode: Int ) { - val localSpanBuilder: Tracer.SpanBuilder = mock() - val localSpan: Span = mock() - GlobalTracer::class.java.setStaticValue("isRegistered", false) - whenever(mockResolver.isFirstPartyUrl(fakeUrl.toHttpUrl())).thenReturn(true) - stubChain(mockChain, statusCode) - whenever(localSpanBuilder.asChildOf(null as SpanContext?)) doReturn localSpanBuilder - whenever(localSpanBuilder.start()) doReturn localSpan - whenever(localSpan.context()) doReturn mockSpanContext + val localSpan: DatadogSpan = forge.newSpanMock(mockSpanContext) + val localSpanBuilder: DatadogSpanBuilder = forge.newSpanBuilderMock(localSpan, mockSpanContext) + whenever(mockLocalTracer.buildSpan(TracingInterceptor.SPAN_NAME)).thenReturn(localSpanBuilder) whenever(mockTraceSampler.sample(localSpan)).thenReturn(true) - whenever(mockSpanContext.toSpanId()) doReturn fakeSpanId - whenever(mockSpanContext.traceId) doReturn fakeTraceId - whenever(mockLocalTracer.buildSpan(TracingInterceptor.SPAN_NAME)) doReturn localSpanBuilder + val testedInterceptorNoGlobal = instantiateTestedInterceptor( + fakeLocalHosts, + localTracerFactory = { _, _ -> mockLocalTracer }, + globalTracerProvider = { null } + ) - val response1 = testedInterceptor.intercept(mockChain) + stubChain(mockChain, statusCode) + val response1 = testedInterceptorNoGlobal.intercept(mockChain) val expectedResponse1 = fakeResponse - GlobalTracer.registerIfAbsent(mockTracer) + stubChain(mockChain, statusCode) val response2 = testedInterceptor.intercept(mockChain) val expectedResponse2 = fakeResponse @@ -1177,12 +1148,12 @@ internal open class TracingInterceptorNonDdTracerTest { verify(localSpan).setTag("http.url", fakeUrl) verify(localSpan).setTag("http.method", fakeMethod) verify(localSpan).setTag("http.status_code", statusCode) - verify(localSpan).setTag(Tags.SPAN_KIND, Tags.SPAN_KIND_CLIENT) + verify(localSpan).setTag("span.kind", "client") verify(localSpan).finish() verify(mockSpan).setTag("http.url", fakeUrl) verify(mockSpan).setTag("http.method", fakeMethod) verify(mockSpan).setTag("http.status_code", statusCode) - verify(mockSpan).setTag(Tags.SPAN_KIND, Tags.SPAN_KIND_CLIENT) + verify(mockSpan).setTag("span.kind", "client") verify(mockSpan).finish() assertThat(response1).isSameAs(expectedResponse1) assertThat(response2).isSameAs(expectedResponse2) @@ -1204,7 +1175,7 @@ internal open class TracingInterceptorNonDdTracerTest { whenever( mockRequestListener.onRequestIntercepted(any(), any(), anyOrNull(), anyOrNull()) ).doAnswer { - val span = it.arguments[1] as Span + val span = it.arguments[1] as DatadogSpan span.setTag(tagKey, tagValue) return@doAnswer Unit } @@ -1215,7 +1186,7 @@ internal open class TracingInterceptorNonDdTracerTest { verify(mockSpan).setTag("http.method", fakeMethod) verify(mockSpan).setTag("http.status_code", statusCode) verify(mockSpan).setTag(tagKey, tagValue) - verify(mockSpan).setTag(Tags.SPAN_KIND, Tags.SPAN_KIND_CLIENT) + verify(mockSpan).setTag("span.kind", "client") verify(mockSpan).finish() assertThat(response).isSameAs(fakeResponse) } @@ -1231,7 +1202,7 @@ internal open class TracingInterceptorNonDdTracerTest { whenever( mockRequestListener.onRequestIntercepted(any(), any(), anyOrNull(), anyOrNull()) ).doAnswer { - val span = it.arguments[1] as Span + val span = it.arguments[1] as DatadogSpan span.setTag(tagKey, tagValue) return@doAnswer Unit } @@ -1242,7 +1213,7 @@ internal open class TracingInterceptorNonDdTracerTest { verify(mockSpan).setTag("http.method", fakeMethod) verify(mockSpan).setTag("http.status_code", statusCode) verify(mockSpan).setTag(tagKey, tagValue) - verify(mockSpan).setTag(Tags.SPAN_KIND, Tags.SPAN_KIND_CLIENT) + verify(mockSpan).setTag("span.kind", "client") verify(mockSpan).finish() assertThat(response).isSameAs(fakeResponse) } @@ -1274,7 +1245,7 @@ internal open class TracingInterceptorNonDdTracerTest { whenever( mockRequestListener.onRequestIntercepted(any(), any(), anyOrNull(), anyOrNull()) ).doAnswer { - val span = it.arguments[1] as Span + val span = it.arguments[1] as DatadogSpan span.setTag(tagKey, tagValue) return@doAnswer Unit } @@ -1291,7 +1262,7 @@ internal open class TracingInterceptorNonDdTracerTest { verify(mockSpan).setTag("error.msg", throwable.message) verify(mockSpan).setTag("error.stack", throwable.loggableStackTrace()) verify(mockSpan).setTag(tagKey, tagValue) - verify(mockSpan).setTag(Tags.SPAN_KIND, Tags.SPAN_KIND_CLIENT) + verify(mockSpan).setTag("span.kind", "client") verify(mockSpan).finish() } @@ -1360,17 +1331,16 @@ internal open class TracingInterceptorNonDdTracerTest { called++ mockLocalTracer } - GlobalTracer::class.java.setStaticValue("isRegistered", false) whenever(mockResolver.isFirstPartyUrl(fakeUrl.toHttpUrl())).thenReturn(true) stubChain(mockChain, statusCode) // need this setup, otherwise #intercept actually throws NPE, which pollutes the log - val localSpanBuilder: DDTracer.DDSpanBuilder = mock() - val localSpan: Span = mock(extraInterfaces = arrayOf(MutableSpan::class)) - whenever(localSpanBuilder.asChildOf(null as SpanContext?)) doReturn localSpanBuilder + val localSpanBuilder: DatadogSpanBuilder = mock() + val localSpan: DatadogSpan = mock() + whenever(localSpanBuilder.withParentContext(null as DatadogSpanContext?)) doReturn localSpanBuilder whenever(localSpanBuilder.start()) doReturn localSpan whenever(localSpan.context()) doReturn mockSpanContext - whenever(mockSpanContext.toSpanId()) doReturn fakeSpanId + whenever(mockSpanContext.spanId) doReturn fakeSpanId whenever(mockSpanContext.traceId) doReturn fakeTraceId whenever(mockLocalTracer.buildSpan(TracingInterceptor.SPAN_NAME)) doReturn localSpanBuilder diff --git a/integrations/dd-sdk-android-okhttp/src/test/kotlin/com/datadog/android/okhttp/trace/TracingInterceptorNotSendingSpanTest.kt b/integrations/dd-sdk-android-okhttp/src/test/kotlin/com/datadog/android/okhttp/trace/TracingInterceptorNotSendingSpanTest.kt index 22c0ecd47d..aed9bd9102 100644 --- a/integrations/dd-sdk-android-okhttp/src/test/kotlin/com/datadog/android/okhttp/trace/TracingInterceptorNotSendingSpanTest.kt +++ b/integrations/dd-sdk-android-okhttp/src/test/kotlin/com/datadog/android/okhttp/trace/TracingInterceptorNotSendingSpanTest.kt @@ -3,7 +3,6 @@ * This product includes software developed at Datadog (https://www.datadoghq.com/). * Copyright 2016-Present Datadog, Inc. */ - package com.datadog.android.okhttp.trace import com.datadog.android.api.InternalLogger @@ -19,31 +18,29 @@ import com.datadog.android.okhttp.utils.config.GlobalRumMonitorTestConfiguration import com.datadog.android.okhttp.utils.verifyLog import com.datadog.android.rum.RumResourceMethod import com.datadog.android.trace.TracingHeaderType -import com.datadog.legacy.trace.api.interceptor.MutableSpan -import com.datadog.legacy.trace.api.sampling.PrioritySampling -import com.datadog.opentracing.DDSpanContext -import com.datadog.opentracing.DDTracer -import com.datadog.opentracing.propagation.ExtractedContext +import com.datadog.android.trace.api.DatadogTracingConstants +import com.datadog.android.trace.api.propagation.DatadogPropagation +import com.datadog.android.trace.api.span.DatadogSpan +import com.datadog.android.trace.api.span.DatadogSpanBuilder +import com.datadog.android.trace.api.span.DatadogSpanContext +import com.datadog.android.trace.api.trace.DatadogTraceId +import com.datadog.android.trace.api.tracer.DatadogTracer +import com.datadog.android.trace.api.withMockPropagationHelper +import com.datadog.android.trace.internal.DatadogPropagationHelper +import com.datadog.android.trace.internal.DatadogTracingToolkit import com.datadog.tools.unit.annotations.TestConfigurationsProvider import com.datadog.tools.unit.extensions.TestConfigurationExtension import com.datadog.tools.unit.extensions.config.TestConfiguration import com.datadog.tools.unit.forge.BaseConfigurator -import com.datadog.tools.unit.setStaticValue import fr.xgouchet.elmyr.Forge import fr.xgouchet.elmyr.annotation.BoolForgery import fr.xgouchet.elmyr.annotation.Forgery import fr.xgouchet.elmyr.annotation.IntForgery +import fr.xgouchet.elmyr.annotation.LongForgery import fr.xgouchet.elmyr.annotation.StringForgery import fr.xgouchet.elmyr.annotation.StringForgeryType import fr.xgouchet.elmyr.junit5.ForgeConfiguration import fr.xgouchet.elmyr.junit5.ForgeExtension -import io.opentracing.Span -import io.opentracing.SpanContext -import io.opentracing.Tracer -import io.opentracing.propagation.TextMapExtract -import io.opentracing.propagation.TextMapInject -import io.opentracing.tag.Tags -import io.opentracing.util.GlobalTracer import okhttp3.HttpUrl import okhttp3.HttpUrl.Companion.toHttpUrl import okhttp3.Interceptor @@ -55,7 +52,6 @@ import okhttp3.RequestBody.Companion.toRequestBody import okhttp3.Response import okhttp3.ResponseBody.Companion.toResponseBody import org.assertj.core.api.Assertions.assertThat -import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test import org.junit.jupiter.api.assertThrows @@ -77,7 +73,6 @@ import org.mockito.kotlin.verify import org.mockito.kotlin.verifyNoInteractions import org.mockito.kotlin.whenever import org.mockito.quality.Strictness -import java.math.BigInteger import java.util.Locale import java.util.concurrent.CountDownLatch import java.util.concurrent.TimeUnit @@ -96,19 +91,24 @@ internal open class TracingInterceptorNotSendingSpanTest { // region Mocks @Mock - lateinit var mockTracer: Tracer + lateinit var mockTracer: DatadogTracer + + @Mock + lateinit var mockLocalTracer: DatadogTracer @Mock - lateinit var mockLocalTracer: Tracer + lateinit var mockPropagation: DatadogPropagation @Mock - lateinit var mockSpanBuilder: DDTracer.DDSpanBuilder + lateinit var mockPropagationHelper: DatadogPropagationHelper @Mock - lateinit var mockSpanContext: DDSpanContext + lateinit var mockSpanBuilder: DatadogSpanBuilder - @Mock(extraInterfaces = [MutableSpan::class]) - lateinit var mockSpan: Span + @Mock + lateinit var mockSpanContext: DatadogSpanContext + + lateinit var mockSpan: DatadogSpan @Mock lateinit var mockChain: Interceptor.Chain @@ -119,8 +119,7 @@ internal open class TracingInterceptorNotSendingSpanTest { @Mock lateinit var mockResolver: DefaultFirstPartyHostHeaderTypeResolver - @Mock - lateinit var mockTraceSampler: Sampler + lateinit var mockTraceSampler: Sampler @Mock lateinit var mockInternalLogger: InternalLogger @@ -147,13 +146,13 @@ internal open class TracingInterceptorNotSendingSpanTest { lateinit var fakeRequest: Request lateinit var fakeResponse: Response - @StringForgery(type = StringForgeryType.HEXADECIMAL) - lateinit var fakeSpanId: String + @LongForgery + var fakeSpanId: Long = 0L - @StringForgery(regex = "[a-f][0-9]{32}") + @StringForgery(regex = "[a-f][0-9]{31}") lateinit var fakeTraceIdAsString: String - private lateinit var fakeTraceId: BigInteger + private lateinit var fakeTraceId: DatadogTraceId private var fakeOrigin: String? = null @@ -166,16 +165,16 @@ internal open class TracingInterceptorNotSendingSpanTest { @BeforeEach open fun `set up`(forge: Forge) { - fakeTraceId = BigInteger(fakeTraceIdAsString, 16) - whenever(mockTracer.buildSpan(TracingInterceptor.SPAN_NAME)) doReturn mockSpanBuilder - whenever(mockSpanBuilder.asChildOf(null as SpanContext?)) doReturn mockSpanBuilder - whenever(mockSpanBuilder.start()) doReturn mockSpan - whenever(mockSpan.context()) doReturn mockSpanContext - whenever(mockSpanContext.toSpanId()) doReturn fakeSpanId - whenever(mockSpanContext.traceId) doReturn fakeTraceId - whenever(mockTraceSampler.sample(mockSpan)) doReturn true - + fakeTraceId = forge.aDatadogTraceId(fakeTraceIdAsString) fakeOrigin = forge.aNullable { anAlphabeticalString() } + mockSpanContext = forge.newSpanContextMock(fakeTraceId, fakeSpanId) + mockSpan = forge.newSpanMock(mockSpanContext) + mockSpanBuilder = forge.newSpanBuilderMock(mockSpan, mockSpanContext) + mockTraceSampler = forge.newTraceSamplerMock(mockSpan) + mockPropagation = newAgentPropagationMock() + mockTracer = forge.newTracerMock(mockSpanBuilder, mockPropagation) + mockLocalTracer = forge.newTracerMock(mockSpanBuilder, mockPropagation) + fakeMediaType = if (forge.aBool()) { val mediaType = forge.anElementFrom("application", "image", "text", "model") + "/" + forge.anAlphabeticalString() @@ -196,18 +195,17 @@ internal open class TracingInterceptorNotSendingSpanTest { doAnswer { false }.whenever(mockResolver).isFirstPartyUrl(any()) doAnswer { true }.whenever(mockResolver).isFirstPartyUrl(fakeUrl.toHttpUrl()) - GlobalTracer.registerIfAbsent(mockTracer) - testedInterceptor = instantiateTestedInterceptor(fakeLocalHosts) { _, _ -> mockLocalTracer } - } - - @AfterEach - open fun `tear down`() { - GlobalTracer::class.java.setStaticValue("isRegistered", false) + testedInterceptor = instantiateTestedInterceptor( + fakeLocalHosts, + localTracerFactory = { _, _ -> mockLocalTracer }, + globalTracerProvider = { mockTracer } + ) } open fun instantiateTestedInterceptor( tracedHosts: Map>, - factory: (SdkCore, Set) -> Tracer + globalTracerProvider: () -> DatadogTracer? = { null }, + localTracerFactory: (SdkCore, Set) -> DatadogTracer ): TracingInterceptor { return object : TracingInterceptor( @@ -216,7 +214,8 @@ internal open class TracingInterceptorNotSendingSpanTest { tracedRequestListener = mockRequestListener, traceOrigin = fakeOrigin, traceSampler = mockTraceSampler, - localTracerFactory = factory, + localTracerFactory = localTracerFactory, + globalTracerProvider = globalTracerProvider, redacted404ResourceName = fakeRedacted404Resources, traceContextInjection = TraceContextInjection.ALL ) { @@ -251,10 +250,7 @@ internal open class TracingInterceptorNotSendingSpanTest { ) ) stubChain(mockChain, statusCode) - doAnswer { invocation -> - val carrier = invocation.arguments[2] as TextMapInject - carrier.put(key, value) - }.whenever(mockTracer).inject(any(), any(), any()) + mockPropagation.wheneverInjectThenValueToHeaders(key, value) val response = testedInterceptor.intercept(mockChain) @@ -373,11 +369,11 @@ internal open class TracingInterceptorNotSendingSpanTest { assertThat(lastValue.headers) .hasTraceParentHeader( fakeTraceIdAsString, - mockSpan.context().toSpanId(), + mockSpan.context().spanId.toString(), isSampled = false ) .hasTraceStateHeaderWithOnlyDatadogVendorValues( - mockSpan.context().toSpanId(), + mockSpan.context().spanId.toString(), isSampled = false, getExpectedOrigin() ) @@ -423,11 +419,11 @@ internal open class TracingInterceptorNotSendingSpanTest { assertThat(lastValue.headers) .hasTraceParentHeader( fakeTraceIdAsString, - mockSpan.context().toSpanId(), + mockSpan.context().spanId.toString(), isSampled = false ) .hasTraceStateHeaderWithOnlyDatadogVendorValues( - mockSpan.context().toSpanId(), + mockSpan.context().spanId.toString(), isSampled = false, getExpectedOrigin() ) @@ -445,10 +441,7 @@ internal open class TracingInterceptorNotSendingSpanTest { fakeRequest = forgeRequest(forge) whenever(mockResolver.isFirstPartyUrl(fakeUrl.toHttpUrl())).thenReturn(false) stubChain(mockChain, statusCode) - doAnswer { invocation -> - val carrier = invocation.arguments[2] as TextMapInject - carrier.put(key, value) - }.whenever(mockTracer).inject(any(), any(), any()) + mockPropagation.wheneverInjectThenValueToHeaders(key, value) val response = testedInterceptor.intercept(mockChain) @@ -577,11 +570,11 @@ internal open class TracingInterceptorNotSendingSpanTest { assertThat(lastValue.headers) .hasTraceParentHeader( fakeTraceIdAsString, - mockSpan.context().toSpanId(), + mockSpan.context().spanId.toString(), isSampled = false ) .hasTraceStateHeaderWithOnlyDatadogVendorValues( - mockSpan.context().toSpanId(), + mockSpan.context().spanId.toString(), isSampled = false, getExpectedOrigin() ) @@ -595,18 +588,14 @@ internal open class TracingInterceptorNotSendingSpanTest { @IntForgery(min = 200, max = 300) statusCode: Int, forge: Forge ) { - val parentSpan: Span = mock() - val parentSpanContext: SpanContext = mock() - whenever(parentSpan.context()) doReturn parentSpanContext - whenever(mockSpanBuilder.asChildOf(parentSpanContext)) doReturn mockSpanBuilder - fakeRequest = forgeRequest(forge) { it.tag(Span::class.java, parentSpan) } + val parentSpanContext: DatadogSpanContext = forge.newSpanContextMock(samplingPriority = 1) + val parentSpan: DatadogSpan = forge.newSpanMock(parentSpanContext) + whenever(mockSpanBuilder.withParentContext(parentSpanContext)) doReturn mockSpanBuilder + fakeRequest = forgeRequest(forge) { it.tag(DatadogSpan::class.java, parentSpan) } whenever(mockResolver.isFirstPartyUrl(fakeUrl.toHttpUrl())).thenReturn(true) doAnswer { true }.whenever(mockResolver).isFirstPartyUrl(fakeUrl.toHttpUrl()) stubChain(mockChain, statusCode) - doAnswer { invocation -> - val carrier = invocation.arguments[2] as TextMapInject - carrier.put(key, value) - }.whenever(mockTracer).inject(any(), any(), any()) + mockPropagation.wheneverInjectThenValueToHeaders(key, value) val response = testedInterceptor.intercept(mockChain) @@ -615,7 +604,7 @@ internal open class TracingInterceptorNotSendingSpanTest { verify(mockChain).proceed(capture()) assertThat(lastValue.header(key)).isEqualTo(value) } - verify(mockSpanBuilder).asChildOf(parentSpanContext) + verify(mockSpanBuilder).withParentContext(parentSpanContext) verify(mockSpanBuilder).withOrigin(getExpectedOrigin()) } @@ -626,27 +615,27 @@ internal open class TracingInterceptorNotSendingSpanTest { @IntForgery(min = 200, max = 300) statusCode: Int, forge: Forge ) { - val parentSpanContext: ExtractedContext = mock() - whenever(mockTracer.extract(any(), any())) doReturn parentSpanContext - whenever(mockSpanBuilder.asChildOf(any())) doReturn mockSpanBuilder + val parentSpanContext: DatadogSpanContext = mock() + whenever(mockPropagation.extract(any(), any())) doReturn parentSpanContext + whenever(mockPropagationHelper.isExtractedContext(parentSpanContext)) doReturn true + whenever(mockSpanBuilder.withParentContext(any())) doReturn mockSpanBuilder whenever(mockResolver.isFirstPartyUrl(fakeUrl.toHttpUrl())).thenReturn(true) fakeRequest = forgeRequest(forge) doAnswer { true }.whenever(mockResolver).isFirstPartyUrl(fakeUrl.toHttpUrl()) stubChain(mockChain, statusCode) - doAnswer { invocation -> - val carrier = invocation.arguments[2] as TextMapInject - carrier.put(key, value) - }.whenever(mockTracer).inject(any(), any(), any()) + mockPropagation.wheneverInjectThenValueToHeaders(key, value) - val response = testedInterceptor.intercept(mockChain) + DatadogTracingToolkit.withMockPropagationHelper(mockPropagationHelper) { + val response = testedInterceptor.intercept(mockChain) - assertThat(response).isSameAs(fakeResponse) - argumentCaptor { - verify(mockChain).proceed(capture()) - assertThat(lastValue.header(key)).isEqualTo(value) + assertThat(response).isSameAs(fakeResponse) + argumentCaptor { + verify(mockChain).proceed(capture()) + assertThat(lastValue.header(key)).isEqualTo(value) + } + verify(mockSpanBuilder).withParentContext(parentSpanContext) + verify(mockSpanBuilder).withOrigin(getExpectedOrigin()) } - verify(mockSpanBuilder).asChildOf(parentSpanContext) - verify(mockSpanBuilder).withOrigin(getExpectedOrigin()) } @Test @@ -662,17 +651,14 @@ internal open class TracingInterceptorNotSendingSpanTest { it.addHeader( TracingInterceptor.DATADOG_SAMPLING_PRIORITY_HEADER, forge.anElementFrom( - PrioritySampling.SAMPLER_KEEP.toString(), - PrioritySampling.USER_KEEP.toString() + DatadogTracingConstants.PrioritySampling.SAMPLER_KEEP.toString(), + DatadogTracingConstants.PrioritySampling.USER_KEEP.toString() ) ) } stubChain(mockChain, statusCode) whenever(mockTraceSampler.sample(mockSpan)).thenReturn(false) - doAnswer { invocation -> - val carrier = invocation.arguments[2] as TextMapInject - carrier.put(key, value) - }.whenever(mockTracer).inject(any(), any(), any()) + mockPropagation.wheneverInjectThenValueToHeaders(key, value) // When val response = testedInterceptor.intercept(mockChain) @@ -697,15 +683,12 @@ internal open class TracingInterceptorNotSendingSpanTest { fakeRequest = forgeRequest(forge) { it.addHeader( TracingInterceptor.B3M_SAMPLING_PRIORITY_KEY, - PrioritySampling.SAMPLER_KEEP.toString() + DatadogTracingConstants.PrioritySampling.SAMPLER_KEEP.toString() ) } stubChain(mockChain, statusCode) whenever(mockTraceSampler.sample(mockSpan)).thenReturn(false) - doAnswer { invocation -> - val carrier = invocation.arguments[2] as TextMapInject - carrier.put(key, value) - }.whenever(mockTracer).inject(any(), any(), any()) + mockPropagation.wheneverInjectThenValueToHeaders(key, value) // When val response = testedInterceptor.intercept(mockChain) @@ -735,10 +718,7 @@ internal open class TracingInterceptorNotSendingSpanTest { } stubChain(mockChain, statusCode) whenever(mockTraceSampler.sample(mockSpan)).thenReturn(false) - doAnswer { invocation -> - val carrier = invocation.arguments[2] as TextMapInject - carrier.put(key, value) - }.whenever(mockTracer).inject(any(), any(), any()) + mockPropagation.wheneverInjectThenValueToHeaders(key, value) // When val response = testedInterceptor.intercept(mockChain) @@ -768,10 +748,7 @@ internal open class TracingInterceptorNotSendingSpanTest { } stubChain(mockChain, statusCode) whenever(mockTraceSampler.sample(mockSpan)).thenReturn(false) - doAnswer { invocation -> - val carrier = invocation.arguments[2] as TextMapInject - carrier.put(key, value) - }.whenever(mockTracer).inject(any(), any(), any()) + mockPropagation.wheneverInjectThenValueToHeaders(key, value) // When val response = testedInterceptor.intercept(mockChain) @@ -800,8 +777,8 @@ internal open class TracingInterceptorNotSendingSpanTest { it.addHeader( TracingInterceptor.DATADOG_SAMPLING_PRIORITY_HEADER, forge.anElementFrom( - PrioritySampling.SAMPLER_DROP.toString(), - PrioritySampling.USER_DROP.toString() + DatadogTracingConstants.PrioritySampling.SAMPLER_DROP.toString(), + DatadogTracingConstants.PrioritySampling.USER_DROP.toString() ) ) } @@ -825,21 +802,20 @@ internal open class TracingInterceptorNotSendingSpanTest { } @Test - fun `M respect b3multi sampling decision W intercept() {sampled out in upstream interceptor}`( + fun `M respect b3multi sampling decision W intercept1`( @IntForgery(min = 200, max = 600) statusCode: Int, forge: Forge ) { // Given + val localSpanBuilder = forge.newSpanBuilderMock() + whenever(mockLocalTracer.buildSpan(any())).thenReturn(localSpanBuilder) whenever(mockResolver.isFirstPartyUrl(fakeUrl.toHttpUrl())).thenReturn(true) - whenever(mockResolver.headerTypesForUrl(fakeUrl.toHttpUrl())).thenReturn( - setOf( - TracingHeaderType.B3MULTI - ) - ) + whenever(mockResolver.headerTypesForUrl(fakeUrl.toHttpUrl())).thenReturn(setOf(TracingHeaderType.B3MULTI)) + fakeRequest = forgeRequest(forge) { it.addHeader( TracingInterceptor.B3M_SAMPLING_PRIORITY_KEY, - PrioritySampling.SAMPLER_DROP.toString() + DatadogTracingConstants.PrioritySampling.SAMPLER_DROP.toString() ) } stubChain(mockChain, statusCode) @@ -852,8 +828,7 @@ internal open class TracingInterceptorNotSendingSpanTest { assertThat(response).isSameAs(fakeResponse) argumentCaptor { verify(mockChain).proceed(capture()) - assertThat(lastValue.header(TracingInterceptor.B3M_SAMPLING_PRIORITY_KEY)) - .isEqualTo("0") + assertThat(lastValue.header(TracingInterceptor.B3M_SAMPLING_PRIORITY_KEY)).isEqualTo("0") assertThat(lastValue.header(TracingInterceptor.B3M_SPAN_ID_KEY)).isNull() assertThat(lastValue.header(TracingInterceptor.B3M_TRACE_ID_KEY)).isNull() } @@ -875,7 +850,7 @@ internal open class TracingInterceptorNotSendingSpanTest { it.addHeader( TracingInterceptor.B3_HEADER_KEY, forge.anElementFrom( - PrioritySampling.SAMPLER_DROP.toString(), + DatadogTracingConstants.PrioritySampling.SAMPLER_DROP.toString(), forge.aStringMatching("[a-f0-9]{32}\\-[a-f0-9]{16}\\-0") ) ) @@ -926,11 +901,11 @@ internal open class TracingInterceptorNotSendingSpanTest { assertThat(lastValue.headers) .hasTraceParentHeader( fakeTraceIdAsString, - mockSpan.context().toSpanId(), + mockSpan.context().spanId.toString(), isSampled = false ) .hasTraceStateHeaderWithOnlyDatadogVendorValues( - mockSpan.context().toSpanId(), + mockSpan.context().spanId.toString(), isSampled = false, getExpectedOrigin() ) @@ -949,9 +924,9 @@ internal open class TracingInterceptorNotSendingSpanTest { verify(mockSpan).setTag("http.url", fakeUrl) verify(mockSpan).setTag("http.method", fakeMethod.name) verify(mockSpan).setTag("http.status_code", statusCode) - verify(mockSpan).setTag(Tags.SPAN_KIND, Tags.SPAN_KIND_CLIENT) + verify(mockSpan).setTag("span.kind", "client") verify(mockSpan, never()).finish() - verify(mockSpan as MutableSpan).drop() + verify(mockSpan).drop() assertThat(response).isSameAs(fakeResponse) } @@ -965,14 +940,14 @@ internal open class TracingInterceptorNotSendingSpanTest { val response = testedInterceptor.intercept(mockChain) verify(mockSpanBuilder).withOrigin(getExpectedOrigin()) - verify(mockSpan as MutableSpan).setResourceName(fakeUrl) + verify(mockSpan).resourceName = fakeUrl verify(mockSpan).setTag("http.url", fakeUrl) verify(mockSpan).setTag("http.method", fakeMethod.name) verify(mockSpan).setTag("http.status_code", statusCode) - verify(mockSpan).setTag(Tags.SPAN_KIND, Tags.SPAN_KIND_CLIENT) - verify(mockSpan as MutableSpan).setError(true) + verify(mockSpan).setTag("span.kind", "client") + verify(mockSpan).isError = true verify(mockSpan, never()).finish() - verify(mockSpan as MutableSpan).drop() + verify(mockSpan).drop() assertThat(response).isSameAs(fakeResponse) } @@ -986,14 +961,14 @@ internal open class TracingInterceptorNotSendingSpanTest { val response = testedInterceptor.intercept(mockChain) verify(mockSpanBuilder).withOrigin(getExpectedOrigin()) - verify(mockSpan as MutableSpan).setResourceName(fakeUrl) + verify(mockSpan).resourceName = fakeUrl verify(mockSpan).setTag("http.url", fakeUrl) verify(mockSpan).setTag("http.method", fakeMethod.name) verify(mockSpan).setTag("http.status_code", statusCode) - verify(mockSpan as MutableSpan, never()).setError(true) - verify(mockSpan).setTag(Tags.SPAN_KIND, Tags.SPAN_KIND_CLIENT) + verify(mockSpan, never()).isError = true + verify(mockSpan).setTag("span.kind", "client") verify(mockSpan, never()).finish() - verify(mockSpan as MutableSpan).drop() + verify(mockSpan).drop() assertThat(response).isSameAs(fakeResponse) } @@ -1008,15 +983,15 @@ internal open class TracingInterceptorNotSendingSpanTest { verify(mockSpan).setTag("http.url", fakeUrl) verify(mockSpan).setTag("http.method", fakeMethod.name) verify(mockSpan).setTag("http.status_code", 404) - verify(mockSpan).setTag(Tags.SPAN_KIND, Tags.SPAN_KIND_CLIENT) - verify(mockSpan as MutableSpan).setError(true) + verify(mockSpan).setTag("span.kind", "client") + verify(mockSpan).isError = true if (fakeRedacted404Resources) { - verify(mockSpan as MutableSpan).setResourceName(TracingInterceptor.RESOURCE_NAME_404) + verify(mockSpan).resourceName = TracingInterceptor.RESOURCE_NAME_404 } else { - verify(mockSpan as MutableSpan, never()).setResourceName(TracingInterceptor.RESOURCE_NAME_404) + verify(mockSpan, never()).resourceName = TracingInterceptor.RESOURCE_NAME_404 } verify(mockSpan, never()).finish() - verify(mockSpan as MutableSpan).drop() + verify(mockSpan).drop() assertThat(response).isSameAs(fakeResponse) } @@ -1038,16 +1013,15 @@ internal open class TracingInterceptorNotSendingSpanTest { verify(mockSpan).setTag("error.type", throwable.javaClass.canonicalName) verify(mockSpan).setTag("error.msg", throwable.message) verify(mockSpan).setTag("error.stack", throwable.loggableStackTrace()) - verify(mockSpan).setTag(Tags.SPAN_KIND, Tags.SPAN_KIND_CLIENT) + verify(mockSpan).setTag("span.kind", "client") verify(mockSpan, never()).finish() - verify(mockSpan as MutableSpan).drop() + verify(mockSpan).drop() } @Test fun `M warn the user W intercept() no tracer registered and TracingFeature not initialized`( @IntForgery(min = 200, max = 300) statusCode: Int ) { - GlobalTracer::class.java.setStaticValue("isRegistered", false) whenever(rumMonitor.mockSdkCore.getFeature(Feature.TRACING_FEATURE_NAME)) doReturn null stubChain(mockChain, statusCode) @@ -1066,30 +1040,31 @@ internal open class TracingInterceptorNotSendingSpanTest { @Test fun `M create a span with automatic tracer W intercept() if no tracer registered`( + forge: Forge, @IntForgery(min = 200, max = 300) statusCode: Int ) { - val localSpanBuilder: DDTracer.DDSpanBuilder = mock() - val localSpan: Span = mock(extraInterfaces = arrayOf(MutableSpan::class)) - GlobalTracer::class.java.setStaticValue("isRegistered", false) + val localSpan: DatadogSpan = forge.newSpanMock(mockSpanContext) + val localSpanBuilder: DatadogSpanBuilder = forge.newSpanBuilderMock(localSpan) + whenever(mockResolver.isFirstPartyUrl(fakeUrl.toHttpUrl())).thenReturn(true) stubChain(mockChain, statusCode) - whenever(localSpanBuilder.asChildOf(null as SpanContext?)) doReturn localSpanBuilder - whenever(localSpanBuilder.start()) doReturn localSpan - whenever(localSpan.context()) doReturn mockSpanContext whenever(mockTraceSampler.sample(localSpan)).thenReturn(true) - whenever(mockSpanContext.toSpanId()) doReturn fakeSpanId - whenever(mockSpanContext.traceId) doReturn fakeTraceId whenever(mockLocalTracer.buildSpan(TracingInterceptor.SPAN_NAME)) doReturn localSpanBuilder + val testedInterceptorNoGlobal = instantiateTestedInterceptor( + fakeLocalHosts, + localTracerFactory = { _, _ -> mockLocalTracer }, + globalTracerProvider = { null } + ) - val response = testedInterceptor.intercept(mockChain) + val response = testedInterceptorNoGlobal.intercept(mockChain) verify(localSpanBuilder).withOrigin(getExpectedOrigin()) verify(localSpan).setTag("http.url", fakeUrl) verify(localSpan).setTag("http.method", fakeMethod.name) verify(localSpan).setTag("http.status_code", statusCode) - verify(localSpan).setTag(Tags.SPAN_KIND, Tags.SPAN_KIND_CLIENT) + verify(localSpan).setTag("span.kind", "client") verify(localSpan, never()).finish() - verify(localSpan as MutableSpan).drop() + verify(localSpan).drop() assertThat(response).isSameAs(fakeResponse) mockInternalLogger.verifyLog( InternalLogger.Level.WARN, @@ -1100,44 +1075,44 @@ internal open class TracingInterceptorNotSendingSpanTest { @Test fun `M drop automatic tracer W intercept() and global tracer registered`( + forge: Forge, @IntForgery(min = 200, max = 300) statusCode: Int ) { - val localSpanBuilder: DDTracer.DDSpanBuilder = mock() - val localSpan: Span = mock(extraInterfaces = arrayOf(MutableSpan::class)) - GlobalTracer::class.java.setStaticValue("isRegistered", false) - whenever(mockResolver.isFirstPartyUrl(fakeUrl.toHttpUrl())).thenReturn(true) - stubChain(mockChain, statusCode) - whenever(localSpanBuilder.asChildOf(null as SpanContext?)) doReturn localSpanBuilder - whenever(localSpanBuilder.start()) doReturn localSpan - whenever(localSpan.context()) doReturn mockSpanContext + val localSpan: DatadogSpan = forge.newSpanMock(mockSpanContext) + val localSpanBuilder: DatadogSpanBuilder = forge.newSpanBuilderMock(localSpan, mockSpanContext) + whenever(mockLocalTracer.buildSpan(TracingInterceptor.SPAN_NAME)).thenReturn(localSpanBuilder) whenever(mockTraceSampler.sample(localSpan)).thenReturn(true) - whenever(mockSpanContext.toSpanId()) doReturn fakeSpanId - whenever(mockSpanContext.traceId) doReturn fakeTraceId - whenever(mockLocalTracer.buildSpan(TracingInterceptor.SPAN_NAME)) doReturn localSpanBuilder + whenever(mockResolver.isFirstPartyUrl(fakeUrl.toHttpUrl())).thenReturn(true) + val testedInterceptorNoGlobal = instantiateTestedInterceptor( + fakeLocalHosts, + localTracerFactory = { _, _ -> mockLocalTracer }, + globalTracerProvider = { null } + ) + stubChain(mockChain, statusCode) val response1 = testedInterceptor.intercept(mockChain) val expectedResponse1 = fakeResponse - GlobalTracer.registerIfAbsent(mockTracer) + stubChain(mockChain, statusCode) - val response2 = testedInterceptor.intercept(mockChain) + val response2 = testedInterceptorNoGlobal.intercept(mockChain) val expectedResponse2 = fakeResponse verify(localSpanBuilder).withOrigin(getExpectedOrigin()) verify(localSpan).setTag("http.url", fakeUrl) verify(localSpan).setTag("http.method", fakeMethod.name) verify(localSpan).setTag("http.status_code", statusCode) - verify(localSpan).setTag(Tags.SPAN_KIND, Tags.SPAN_KIND_CLIENT) + verify(localSpan).setTag("span.kind", "client") verify(localSpan, never()).finish() - verify(localSpan as MutableSpan).drop() + verify(localSpan).drop() verify(mockSpanBuilder).withOrigin(getExpectedOrigin()) verify(mockSpan).setTag("http.url", fakeUrl) verify(mockSpan).setTag("http.method", fakeMethod.name) verify(mockSpan).setTag("http.status_code", statusCode) - verify(mockSpan).setTag(Tags.SPAN_KIND, Tags.SPAN_KIND_CLIENT) + verify(mockSpan).setTag("span.kind", "client") verify(mockSpan, never()).finish() - verify(mockSpan as MutableSpan).drop() + verify(mockSpan).drop() assertThat(response1).isSameAs(expectedResponse1) assertThat(response2).isSameAs(expectedResponse2) mockInternalLogger.verifyLog( @@ -1158,7 +1133,7 @@ internal open class TracingInterceptorNotSendingSpanTest { whenever( mockRequestListener.onRequestIntercepted(any(), any(), anyOrNull(), anyOrNull()) ).doAnswer { - val span = it.arguments[1] as Span + val span = it.arguments[1] as DatadogSpan span.setTag(tagKey, tagValue) return@doAnswer Unit } @@ -1170,10 +1145,10 @@ internal open class TracingInterceptorNotSendingSpanTest { verify(mockSpan).setTag("http.method", fakeMethod.name) verify(mockSpan).setTag("http.status_code", statusCode) verify(mockSpan).setTag(tagKey, tagValue) - verify(mockSpan).setTag(Tags.SPAN_KIND, Tags.SPAN_KIND_CLIENT) + verify(mockSpan).setTag("span.kind", "client") verify(mockSpan, never()).finish() - verify(mockSpan as MutableSpan).drop() + verify(mockSpan).drop() assertThat(response).isSameAs(fakeResponse) } @@ -1188,7 +1163,7 @@ internal open class TracingInterceptorNotSendingSpanTest { whenever( mockRequestListener.onRequestIntercepted(any(), any(), anyOrNull(), anyOrNull()) ).doAnswer { - val span = it.arguments[1] as Span + val span = it.arguments[1] as DatadogSpan span.setTag(tagKey, tagValue) return@doAnswer Unit } @@ -1200,10 +1175,10 @@ internal open class TracingInterceptorNotSendingSpanTest { verify(mockSpan).setTag("http.method", fakeMethod.name) verify(mockSpan).setTag("http.status_code", statusCode) verify(mockSpan).setTag(tagKey, tagValue) - verify(mockSpan).setTag(Tags.SPAN_KIND, Tags.SPAN_KIND_CLIENT) + verify(mockSpan).setTag("span.kind", "client") verify(mockSpan, never()).finish() - verify(mockSpan as MutableSpan).drop() + verify(mockSpan).drop() assertThat(response).isSameAs(fakeResponse) } @@ -1234,7 +1209,7 @@ internal open class TracingInterceptorNotSendingSpanTest { whenever( mockRequestListener.onRequestIntercepted(any(), any(), anyOrNull(), anyOrNull()) ).doAnswer { - val span = it.arguments[1] as Span + val span = it.arguments[1] as DatadogSpan span.setTag(tagKey, tagValue) return@doAnswer Unit } @@ -1252,10 +1227,10 @@ internal open class TracingInterceptorNotSendingSpanTest { verify(mockSpan).setTag("error.msg", throwable.message) verify(mockSpan).setTag("error.stack", throwable.loggableStackTrace()) verify(mockSpan).setTag(tagKey, tagValue) - verify(mockSpan).setTag(Tags.SPAN_KIND, Tags.SPAN_KIND_CLIENT) + verify(mockSpan).setTag("span.kind", "client") verify(mockSpan, never()).finish() - verify(mockSpan as MutableSpan).drop() + verify(mockSpan).drop() } @Test @@ -1318,6 +1293,7 @@ internal open class TracingInterceptorNotSendingSpanTest { @Test fun `M create only one local tracer W intercept() called from multiple threads`( + forge: Forge, @IntForgery(min = 200, max = 300) statusCode: Int ) { // Given @@ -1329,16 +1305,12 @@ internal open class TracingInterceptorNotSendingSpanTest { called++ mockLocalTracer } - GlobalTracer::class.java.setStaticValue("isRegistered", false) stubChain(mockChain, statusCode) // need this setup, otherwise #intercept actually throws NPE, which pollutes the log - val localSpanBuilder: DDTracer.DDSpanBuilder = mock() - val localSpan: Span = mock(extraInterfaces = arrayOf(MutableSpan::class)) - whenever(localSpanBuilder.asChildOf(null as SpanContext?)) doReturn localSpanBuilder - whenever(localSpanBuilder.start()) doReturn localSpan - whenever(localSpan.context()) doReturn mockSpanContext - whenever(mockSpanContext.toSpanId()) doReturn fakeSpanId + val localSpan: DatadogSpan = forge.newSpanMock(mockSpanContext) + val localSpanBuilder: DatadogSpanBuilder = forge.newSpanBuilderMock(localSpan) + whenever(mockSpanContext.spanId) doReturn fakeSpanId whenever(mockSpanContext.traceId) doReturn fakeTraceId whenever(mockLocalTracer.buildSpan(TracingInterceptor.SPAN_NAME)) doReturn localSpanBuilder diff --git a/integrations/dd-sdk-android-okhttp/src/test/kotlin/com/datadog/android/okhttp/trace/TracingInterceptorTest.kt b/integrations/dd-sdk-android-okhttp/src/test/kotlin/com/datadog/android/okhttp/trace/TracingInterceptorTest.kt index aa6d867c9c..7a719866d1 100644 --- a/integrations/dd-sdk-android-okhttp/src/test/kotlin/com/datadog/android/okhttp/trace/TracingInterceptorTest.kt +++ b/integrations/dd-sdk-android-okhttp/src/test/kotlin/com/datadog/android/okhttp/trace/TracingInterceptorTest.kt @@ -3,7 +3,6 @@ * This product includes software developed at Datadog (https://www.datadoghq.com/). * Copyright 2016-Present Datadog, Inc. */ - package com.datadog.android.okhttp.trace import com.datadog.android.api.InternalLogger @@ -24,31 +23,30 @@ import com.datadog.android.okhttp.utils.config.DatadogSingletonTestConfiguration import com.datadog.android.okhttp.utils.config.GlobalRumMonitorTestConfiguration import com.datadog.android.okhttp.utils.verifyLog import com.datadog.android.trace.TracingHeaderType -import com.datadog.legacy.trace.api.interceptor.MutableSpan -import com.datadog.legacy.trace.api.sampling.PrioritySampling -import com.datadog.opentracing.DDSpanContext -import com.datadog.opentracing.DDTracer -import com.datadog.opentracing.propagation.ExtractedContext +import com.datadog.android.trace.api.DatadogTracingConstants.PrioritySampling +import com.datadog.android.trace.api.propagation.DatadogPropagation +import com.datadog.android.trace.api.span.DatadogSpan +import com.datadog.android.trace.api.span.DatadogSpanBuilder +import com.datadog.android.trace.api.span.DatadogSpanContext +import com.datadog.android.trace.api.trace.DatadogTraceId +import com.datadog.android.trace.api.tracer.DatadogTracer +import com.datadog.android.trace.api.withMockPropagationHelper +import com.datadog.android.trace.internal.DatadogPropagationHelper +import com.datadog.android.trace.internal.DatadogTracingToolkit +import com.datadog.android.trace.internal.fromHex import com.datadog.tools.unit.annotations.TestConfigurationsProvider import com.datadog.tools.unit.extensions.TestConfigurationExtension import com.datadog.tools.unit.extensions.config.TestConfiguration -import com.datadog.tools.unit.setStaticValue import fr.xgouchet.elmyr.Forge import fr.xgouchet.elmyr.annotation.BoolForgery import fr.xgouchet.elmyr.annotation.FloatForgery import fr.xgouchet.elmyr.annotation.Forgery import fr.xgouchet.elmyr.annotation.IntForgery +import fr.xgouchet.elmyr.annotation.LongForgery import fr.xgouchet.elmyr.annotation.StringForgery import fr.xgouchet.elmyr.annotation.StringForgeryType import fr.xgouchet.elmyr.junit5.ForgeConfiguration import fr.xgouchet.elmyr.junit5.ForgeExtension -import io.opentracing.Span -import io.opentracing.SpanContext -import io.opentracing.Tracer -import io.opentracing.propagation.TextMapExtract -import io.opentracing.propagation.TextMapInject -import io.opentracing.tag.Tags -import io.opentracing.util.GlobalTracer import okhttp3.HttpUrl import okhttp3.HttpUrl.Companion.toHttpUrl import okhttp3.Interceptor @@ -60,7 +58,6 @@ import okhttp3.RequestBody.Companion.toRequestBody import okhttp3.Response import okhttp3.ResponseBody.Companion.toResponseBody import org.assertj.core.api.Assertions.assertThat -import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test import org.junit.jupiter.api.assertThrows @@ -84,7 +81,6 @@ import org.mockito.kotlin.verifyNoInteractions import org.mockito.kotlin.verifyNoMoreInteractions import org.mockito.kotlin.whenever import org.mockito.quality.Strictness -import java.math.BigInteger import java.util.Locale import java.util.concurrent.CountDownLatch import java.util.concurrent.TimeUnit @@ -102,32 +98,31 @@ internal open class TracingInterceptorTest { // region Mocks - @Mock - lateinit var mockTracer: Tracer + private lateinit var mockTracer: DatadogTracer - @Mock - lateinit var mockLocalTracer: Tracer + private lateinit var mockPropagation: DatadogPropagation - @Mock - lateinit var mockSpanBuilder: DDTracer.DDSpanBuilder + private lateinit var mockSpan: DatadogSpan - @Mock - lateinit var mockSpanContext: DDSpanContext + private lateinit var mockSpanContext: DatadogSpanContext + + private lateinit var mockSpanBuilder: DatadogSpanBuilder - @Mock(extraInterfaces = [MutableSpan::class]) - lateinit var mockSpan: Span + private lateinit var mockLocalTracer: DatadogTracer @Mock lateinit var mockChain: Interceptor.Chain + @Mock + private lateinit var mockPropagationHelper: DatadogPropagationHelper + @Mock lateinit var mockRequestListener: TracedRequestListener @Mock lateinit var mockResolver: DefaultFirstPartyHostHeaderTypeResolver - @Mock - lateinit var mockTraceSampler: Sampler + lateinit var mockTraceSampler: Sampler @Mock lateinit var mockInternalLogger: InternalLogger @@ -136,9 +131,9 @@ internal open class TracingInterceptorTest { // region Fakes - lateinit var fakeMethod: String - var fakeBody: String? = null - var fakeMediaType: MediaType? = null + private lateinit var fakeMethod: String + private var fakeBody: String? = null + private var fakeMediaType: MediaType? = null @StringForgery(type = StringForgeryType.ASCII) lateinit var fakeResponseBody: String @@ -150,13 +145,13 @@ internal open class TracingInterceptorTest { lateinit var fakeRequest: Request lateinit var fakeResponse: Response - @StringForgery(type = StringForgeryType.HEXADECIMAL) - lateinit var fakeSpanId: String + @LongForgery + var fakeSpanId: Long = 0L - @StringForgery(regex = "[a-f][0-9]{32}") + @StringForgery(regex = "[a-f][0-9]{31}") lateinit var fakeTraceIdAsString: String - lateinit var fakeTraceId: BigInteger + lateinit var fakeTraceId: DatadogTraceId private var fakeOrigin: String? = null @@ -169,19 +164,16 @@ internal open class TracingInterceptorTest { @BeforeEach fun `set up`(forge: Forge) { - fakeTraceId = BigInteger(fakeTraceIdAsString, 16) - whenever(mockTracer.buildSpan(TracingInterceptor.SPAN_NAME)) doReturn mockSpanBuilder - whenever(mockLocalTracer.buildSpan(TracingInterceptor.SPAN_NAME)) doReturn mockSpanBuilder - whenever(mockSpanBuilder.withOrigin(fakeOrigin)) doReturn mockSpanBuilder - whenever(mockSpanBuilder.asChildOf(null as SpanContext?)) doReturn mockSpanBuilder - whenever(mockSpanBuilder.start()) doReturn mockSpan - whenever(mockSpan.context()) doReturn mockSpanContext - whenever(mockSpanContext.toSpanId()) doReturn fakeSpanId - whenever(mockSpanContext.traceId).thenReturn(fakeTraceId) - whenever(mockSpanContext.toTraceId()) doReturn fakeTraceId.toString() - whenever(mockTraceSampler.sample(mockSpan)) doReturn true - + fakeTraceId = forge.aDatadogTraceId(fakeTraceIdAsString) fakeOrigin = forge.aNullable { anAlphabeticalString() } + mockSpanContext = forge.newSpanContextMock(fakeTraceId, fakeSpanId) + mockSpan = forge.newSpanMock(mockSpanContext) + mockSpanBuilder = forge.newSpanBuilderMock(mockSpan, context = mockSpanContext) + mockTraceSampler = forge.newTraceSamplerMock(mockSpan) + mockPropagation = newAgentPropagationMock(mockSpanContext) + mockTracer = forge.newTracerMock(mockSpanBuilder, mockPropagation) + mockLocalTracer = forge.newTracerMock(mockSpanBuilder, mockPropagation) + val mediaType = forge.anElementFrom("application", "image", "text", "model") + "/" + forge.anAlphabeticalString() fakeLocalHosts = @@ -192,16 +184,46 @@ internal open class TracingInterceptorTest { whenever(rumMonitor.mockSdkCore.getFeature(Feature.TRACING_FEATURE_NAME)) doReturn mock() whenever(rumMonitor.mockSdkCore.internalLogger) doReturn mockInternalLogger whenever(rumMonitor.mockSdkCore.firstPartyHostResolver) doReturn mockResolver - testedInterceptor = instantiateTestedInterceptor(fakeLocalHosts) { _, _ -> - mockLocalTracer + testedInterceptor = instantiateTestedInterceptor( + fakeLocalHosts, + globalTracerProvider = { mockTracer }, + localTracerFactory = { _, _ -> mockLocalTracer } + ) + } + + @Test + fun `M call the listener W intercept() for successful request`( + @IntForgery(min = 200, max = 300) statusCode: Int, + @StringForgery tagKey: String, + @StringForgery(type = StringForgeryType.ALPHA_NUMERICAL) tagValue: String + ) { + whenever(mockResolver.isFirstPartyUrl(fakeUrl.toHttpUrl())).thenReturn(true) + stubChain(mockChain, statusCode) + whenever( + mockRequestListener.onRequestIntercepted(any(), any(), anyOrNull(), anyOrNull()) + ).doAnswer { + val span = it.arguments[1] as DatadogSpan + span.setTag(tagKey, tagValue) + return@doAnswer Unit } - GlobalTracer.registerIfAbsent(mockTracer) + val response = testedInterceptor.intercept(mockChain) + + verify(mockSpanBuilder).withOrigin(getExpectedOrigin()) + verify(mockSpan).setTag("http.url", fakeUrl.lowercase(Locale.US)) + verify(mockSpan).setTag("http.method", fakeMethod) + verify(mockSpan).setTag("http.status_code", statusCode) + verify(mockSpan).setTag("span.kind", "client") + verify(mockSpan).resourceName = fakeBaseUrl.lowercase(Locale.US) + verify(mockSpan).setTag(tagKey, tagValue) + verify(mockSpan).finish() + assertThat(response).isSameAs(fakeResponse) } open fun instantiateTestedInterceptor( tracedHosts: Map> = emptyMap(), - factory: (SdkCore, Set) -> Tracer + globalTracerProvider: () -> DatadogTracer? = { null }, + localTracerFactory: (SdkCore, Set) -> DatadogTracer ): TracingInterceptor { return TracingInterceptor( sdkInstanceName = null, @@ -209,9 +231,10 @@ internal open class TracingInterceptorTest { tracedRequestListener = mockRequestListener, traceOrigin = fakeOrigin, traceSampler = mockTraceSampler, - localTracerFactory = factory, + localTracerFactory = localTracerFactory, redacted404ResourceName = fakeRedacted404Resources, - traceContextInjection = TraceContextInjection.ALL + traceContextInjection = TraceContextInjection.ALL, + globalTracerProvider = globalTracerProvider ) } @@ -219,11 +242,6 @@ internal open class TracingInterceptorTest { return fakeOrigin } - @AfterEach - fun `tear down`() { - GlobalTracer::class.java.setStaticValue("isRegistered", false) - } - @Test fun `M instantiate with default values W init()`() { // Given @@ -294,10 +312,7 @@ internal open class TracingInterceptorTest { ) ) - doAnswer { invocation -> - val carrier = invocation.arguments[2] as TextMapInject - carrier.put(key, value) - }.whenever(mockTracer).inject(any(), any(), any()) + mockPropagation.wheneverInjectThenValueToHeaders(key, value) val response = testedInterceptor.intercept(mockChain) @@ -386,11 +401,11 @@ internal open class TracingInterceptorTest { assertThat(lastValue.headers) .hasTraceParentHeader( fakeTraceIdAsString, - mockSpan.context().toSpanId(), + mockSpan.context().spanId.toString(), isSampled = false ) .hasTraceStateHeaderWithOnlyDatadogVendorValues( - mockSpan.context().toSpanId(), + mockSpan.context().spanId.toString(), isSampled = false, getExpectedOrigin() ) @@ -403,19 +418,19 @@ internal open class TracingInterceptorTest { forge: Forge ) { // Given + val nonDatadogContextKey = forge.anAlphabeticalString() + val nonDatadogContextKeyValue = forge.anAlphabeticalString() val datadogContext = listOf( TracingInterceptor.DATADOG_SPAN_ID_HEADER, TracingInterceptor.DATADOG_LEAST_SIGNIFICANT_64_BITS_TRACE_ID_HEADER, TracingInterceptor.DATADOG_ORIGIN_HEADER, TracingInterceptor.DATADOG_TAGS_HEADER ).associateWith { forge.anAlphabeticalString() } - val nonDatadogContextKey = forge.anAlphabeticalString() - val nonDatadogContextKeyValue = forge.anAlphabeticalString() - doAnswer { invocation -> - val carrier = invocation.arguments[2] as TextMapInject - datadogContext.forEach { carrier.put(it.key, it.value) } - carrier.put(nonDatadogContextKey, nonDatadogContextKeyValue) - }.whenever(mockTracer).inject(any(), any(), any()) + mockPropagation.wheneverInjectCalledPassContextToHeaders( + datadogContext, + nonDatadogContextKey, + nonDatadogContextKeyValue + ) whenever(mockTraceSampler.sample(mockSpan)).thenReturn(false) whenever(mockResolver.isFirstPartyUrl(fakeUrl.toHttpUrl())).thenReturn(true) whenever(mockResolver.headerTypesForUrl(fakeUrl.toHttpUrl())).thenReturn( @@ -435,26 +450,23 @@ internal open class TracingInterceptorTest { assertThat(response).isSameAs(fakeResponse) argumentCaptor { verify(mockChain).proceed(capture()) - assertThat(lastValue.header(TracingInterceptor.DATADOG_SAMPLING_PRIORITY_HEADER)) - .isEqualTo("0") + assertThat(lastValue.header(TracingInterceptor.DATADOG_SAMPLING_PRIORITY_HEADER)).isEqualTo("0") datadogContext.forEach { assertThat(lastValue.header(it.key)).isEqualTo(it.value) } assertThat(lastValue.header(nonDatadogContextKey)).isNull() - assertThat(lastValue.header(TracingInterceptor.B3M_SAMPLING_PRIORITY_KEY)) - .isEqualTo("0") + assertThat(lastValue.header(TracingInterceptor.B3M_SAMPLING_PRIORITY_KEY)).isEqualTo("0") assertThat(lastValue.header(TracingInterceptor.B3M_SPAN_ID_KEY)).isNull() assertThat(lastValue.header(TracingInterceptor.B3M_TRACE_ID_KEY)).isNull() - assertThat(lastValue.header(TracingInterceptor.B3_HEADER_KEY)) - .isEqualTo("0") + assertThat(lastValue.header(TracingInterceptor.B3_HEADER_KEY)).isEqualTo("0") assertThat(lastValue.headers) .hasTraceParentHeader( fakeTraceIdAsString, - mockSpan.context().toSpanId(), + mockSpan.context().spanId.toString(), isSampled = false ) .hasTraceStateHeaderWithOnlyDatadogVendorValues( - mockSpan.context().toSpanId(), + mockSpan.context().spanId.toString(), isSampled = false, getExpectedOrigin() ) @@ -472,10 +484,7 @@ internal open class TracingInterceptorTest { fakeRequest = forgeRequest(forge) whenever(mockResolver.isFirstPartyUrl(fakeUrl.toHttpUrl())).thenReturn(false) stubChain(mockChain, statusCode) - doAnswer { invocation -> - val carrier = invocation.arguments[2] as TextMapInject - carrier.put(key, value) - }.whenever(mockTracer).inject(any(), any(), any()) + mockPropagation.wheneverInjectThenValueToHeaders(key, value) val response = testedInterceptor.intercept(mockChain) @@ -492,19 +501,21 @@ internal open class TracingInterceptorTest { forge: Forge ) { // Given + val nonDatadogContextKey = forge.anAlphabeticalString() + val nonDatadogContextKeyValue = forge.anAlphabeticalString() val datadogContext = listOf( TracingInterceptor.DATADOG_TAGS_HEADER, TracingInterceptor.DATADOG_SPAN_ID_HEADER, TracingInterceptor.DATADOG_LEAST_SIGNIFICANT_64_BITS_TRACE_ID_HEADER, TracingInterceptor.DATADOG_ORIGIN_HEADER ).associateWith { forge.anAlphabeticalString() } - val nonDatadogContextKey = forge.anAlphabeticalString() - val nonDatadogContextKeyValue = forge.anAlphabeticalString() - doAnswer { invocation -> - val carrier = invocation.arguments[2] as TextMapInject - datadogContext.forEach { carrier.put(it.key, it.value) } - carrier.put(nonDatadogContextKey, nonDatadogContextKeyValue) - }.whenever(mockTracer).inject(any(), any(), any()) + + mockPropagation.wheneverInjectCalledPassContextToHeaders( + datadogContext, + nonDatadogContextKey, + nonDatadogContextKeyValue + ) + whenever(mockTraceSampler.sample(mockSpan)).thenReturn(false) fakeUrl = forgeUrlWithQueryParams(forge, forge.anElementFrom(fakeLocalHosts.keys)) fakeRequest = forgeRequest(forge) @@ -611,11 +622,11 @@ internal open class TracingInterceptorTest { assertThat(lastValue.headers) .hasTraceParentHeader( fakeTraceIdAsString, - mockSpan.context().toSpanId(), + mockSpan.context().spanId.toString(), isSampled = false ) .hasTraceStateHeaderWithOnlyDatadogVendorValues( - mockSpan.context().toSpanId(), + mockSpan.context().spanId.toString(), isSampled = false, getExpectedOrigin() ) @@ -667,11 +678,11 @@ internal open class TracingInterceptorTest { assertThat(lastValue.headers) .hasTraceParentHeader( fakeTraceIdAsString, - mockSpan.context().toSpanId(), + mockSpan.context().spanId.toString(), isSampled = false ) .hasTraceStateHeaderWithOnlyDatadogVendorValues( - mockSpan.context().toSpanId(), + mockSpan.context().spanId.toString(), isSampled = false, getExpectedOrigin() ) @@ -685,17 +696,14 @@ internal open class TracingInterceptorTest { @IntForgery(min = 200, max = 300) statusCode: Int, forge: Forge ) { - val parentSpan = mock() - val parentSpanContext = mock() - whenever(parentSpan.context()) doReturn parentSpanContext - whenever(mockSpanBuilder.asChildOf(parentSpanContext)) doReturn mockSpanBuilder - fakeRequest = forgeRequest(forge) { it.tag(Span::class.java, parentSpan) } + val parentSpanContext: DatadogSpanContext = forge.newSpanContextMock(samplingPriority = 1) + val parentSpan: DatadogSpan = forge.newSpanMock(parentSpanContext) + + whenever(mockSpanBuilder.withParentContext(parentSpanContext)) doReturn mockSpanBuilder + fakeRequest = forgeRequest(forge) { it.tag(DatadogSpan::class.java, parentSpan) } whenever(mockResolver.isFirstPartyUrl(fakeUrl.toHttpUrl())).thenReturn(true) stubChain(mockChain, statusCode) - doAnswer { invocation -> - val carrier = invocation.arguments[2] as TextMapInject - carrier.put(key, value) - }.whenever(mockTracer).inject(any(), any(), any()) + mockPropagation.wheneverInjectThenValueToHeaders(key, value) val response = testedInterceptor.intercept(mockChain) @@ -704,7 +712,7 @@ internal open class TracingInterceptorTest { verify(mockChain).proceed(capture()) assertThat(firstValue.headers(key)).containsOnly(value) } - verify(mockSpanBuilder).asChildOf(parentSpanContext) + verify(mockSpanBuilder).withParentContext(parentSpanContext) verify(mockSpanBuilder).withOrigin(getExpectedOrigin()) } @@ -717,37 +725,42 @@ internal open class TracingInterceptorTest { forge: Forge ) { // Given - val fakeExpectedTraceId = BigInteger(fakeTraceContext.traceId, 16) - val fakeExpectedSpanId = BigInteger(fakeTraceContext.spanId, 16) - whenever(mockSpanBuilder.asChildOf(any())) doReturn mockSpanBuilder + + val fakeExpectedTraceId = DatadogTraceId.fromHex(fakeTraceContext.traceId) + val fakeExpectedSpanId = DatadogTracingToolkit.spanIdConverter.fromHex(fakeTraceContext.spanId) + val fakeExtractedContext = forge.newSpanContextMock( + fakeExpectedTraceId, + fakeExpectedSpanId + ) + whenever(mockPropagationHelper.createExtractedContext(any(), any(), any())) doReturn fakeExtractedContext + whenever(mockSpanBuilder.withParentContext(any())) doReturn mockSpanBuilder fakeRequest = forgeRequest(forge) { it.tag(TraceContext::class.java, fakeTraceContext) } whenever(mockResolver.isFirstPartyUrl(fakeUrl.toHttpUrl())).thenReturn(true) stubChain(mockChain, statusCode) - doAnswer { invocation -> - val carrier = invocation.arguments[2] as TextMapInject - carrier.put(key, value) - }.whenever(mockTracer).inject(any(), any(), any()) - - // When - val response = testedInterceptor.intercept(mockChain) - - // Then - assertThat(response).isSameAs(fakeResponse) - argumentCaptor { - verify(mockChain).proceed(capture()) - if (fakeTraceContext.samplingPriority > 0) { - assertThat(firstValue.headers(key)).containsOnly(value) - } else { - assertThat(firstValue.headers(key)).isEmpty() + mockPropagation.wheneverInjectThenValueToHeaders(key, value) + + DatadogTracingToolkit.withMockPropagationHelper(mockPropagationHelper) { + // When + val response = testedInterceptor.intercept(mockChain) + + // Then + assertThat(response).isSameAs(fakeResponse) + argumentCaptor { + verify(mockChain).proceed(capture()) + if (fakeTraceContext.samplingPriority > 0) { + assertThat(firstValue.headers(key)).containsOnly(value) + } else { + assertThat(firstValue.headers(key)).isEmpty() + } } + argumentCaptor { + verify(mockSpanBuilder).withParentContext(capture()) + val extractedContext: DatadogSpanContext = firstValue + assertThat(extractedContext.traceId).isEqualTo(fakeExpectedTraceId) + assertThat(extractedContext.spanId).isEqualTo(fakeExpectedSpanId) + } + verify(mockSpanBuilder).withOrigin(getExpectedOrigin()) } - argumentCaptor() { - verify(mockSpanBuilder).asChildOf(capture()) - val extractedContext = firstValue as ExtractedContext - assertThat(extractedContext.traceId).isEqualTo(fakeExpectedTraceId) - assertThat(extractedContext.spanId).isEqualTo(fakeExpectedSpanId) - } - verify(mockSpanBuilder).withOrigin(getExpectedOrigin()) } @Test @@ -760,10 +773,7 @@ internal open class TracingInterceptorTest { fakeRequest = fakeRequest.newBuilder().addHeader(key, previousValue).build() stubChain(mockChain, statusCode) whenever(mockResolver.isFirstPartyUrl(fakeUrl.toHttpUrl())).thenReturn(true) - doAnswer { invocation -> - val carrier = invocation.arguments[2] as TextMapInject - carrier.put(key, value) - }.whenever(mockTracer).inject(any(), any(), any()) + mockPropagation.wheneverInjectThenValueToHeaders(key, value) val response = testedInterceptor.intercept(mockChain) @@ -786,10 +796,7 @@ internal open class TracingInterceptorTest { fakeRequest = forgeRequest(forge).newBuilder().addHeader(key, previousValue).build() whenever(mockResolver.isFirstPartyUrl(fakeUrl.toHttpUrl())).thenReturn(false) stubChain(mockChain, statusCode) - doAnswer { invocation -> - val carrier = invocation.arguments[2] as TextMapInject - carrier.put(key, value) - }.whenever(mockTracer).inject(any(), any(), any()) + mockPropagation.wheneverInjectThenValueToHeaders(key, value) val response = testedInterceptor.intercept(mockChain) @@ -810,8 +817,10 @@ internal open class TracingInterceptorTest { fakeRequest = forgeRequest(forge) whenever(mockResolver.isFirstPartyUrl(fakeUrl.toHttpUrl())).thenReturn(false) stubChain(mockChain, statusCode) - doThrow(IllegalStateException(message)).whenever(mockTracer) - .inject(any(), any(), any()) + + doThrow(IllegalStateException(message)) + .whenever(mockPropagation) + .inject(any(), any(), any()) val response = testedInterceptor.intercept(mockChain) @@ -828,31 +837,34 @@ internal open class TracingInterceptorTest { @StringForgery(type = StringForgeryType.ALPHA_NUMERICAL) value: String, @IntForgery(min = 200, max = 300) statusCode: Int ) { - val parentSpanContext: ExtractedContext = mock() - whenever(mockTracer.extract(any(), any())) doReturn parentSpanContext - whenever(mockSpanBuilder.asChildOf(any())) doReturn mockSpanBuilder + // Given + val parentSpanContext: DatadogSpanContext = mock() + whenever(mockPropagation.extract(any(), any())) doReturn parentSpanContext + whenever(mockPropagationHelper.isExtractedContext(parentSpanContext)) doReturn true + whenever(mockSpanBuilder.withParentContext(any())) doReturn mockSpanBuilder whenever(mockResolver.isFirstPartyUrl(fakeUrl.toHttpUrl())).thenReturn(true) stubChain(mockChain, statusCode) - doAnswer { invocation -> - val carrier = invocation.arguments[2] as TextMapInject - carrier.put(key, value) - }.whenever(mockTracer).inject(any(), any(), any()) + mockPropagation.wheneverInjectThenValueToHeaders(key, value) - val response = testedInterceptor.intercept(mockChain) + DatadogTracingToolkit.withMockPropagationHelper(mockPropagationHelper) { + // When + val response = testedInterceptor.intercept(mockChain) - assertThat(response).isSameAs(fakeResponse) - argumentCaptor { - verify(mockChain).proceed(capture()) - assertThat(firstValue.headers(key)).containsOnly(value) + // Then + assertThat(response).isSameAs(fakeResponse) + argumentCaptor { + verify(mockChain).proceed(capture()) + assertThat(firstValue.headers(key)).containsOnly(value) + } + verify(mockSpanBuilder).withParentContext(parentSpanContext) + verify(mockSpanBuilder).withOrigin(getExpectedOrigin()) } - verify(mockSpanBuilder).asChildOf(parentSpanContext) - verify(mockSpanBuilder).withOrigin(getExpectedOrigin()) } @Test fun `M respect sampling decision W intercept() {sampled in upstream interceptor}`( - @IntForgery(min = 200, max = 600) statusCode: Int, @StringForgery key: String, + @IntForgery(min = 200, max = 600) statusCode: Int, @StringForgery(type = StringForgeryType.ALPHA_NUMERICAL) value: String, forge: Forge ) { @@ -869,10 +881,7 @@ internal open class TracingInterceptorTest { } stubChain(mockChain, statusCode) whenever(mockTraceSampler.sample(mockSpan)).thenReturn(false) - doAnswer { invocation -> - val carrier = invocation.arguments[2] as TextMapInject - carrier.put(key, value) - }.whenever(mockTracer).inject(any(), any(), any()) + mockPropagation.wheneverInjectThenValueToHeaders(key, value) // When val response = testedInterceptor.intercept(mockChain) @@ -902,10 +911,7 @@ internal open class TracingInterceptorTest { } stubChain(mockChain, statusCode) whenever(mockTraceSampler.sample(mockSpan)).thenReturn(false) - doAnswer { invocation -> - val carrier = invocation.arguments[2] as TextMapInject - carrier.put(key, value) - }.whenever(mockTracer).inject(any(), any(), any()) + mockPropagation.wheneverInjectThenValueToHeaders(key, value) // When val response = testedInterceptor.intercept(mockChain) @@ -936,10 +942,7 @@ internal open class TracingInterceptorTest { } stubChain(mockChain, statusCode) whenever(mockTraceSampler.sample(mockSpan)).thenReturn(false) - doAnswer { invocation -> - val carrier = invocation.arguments[2] as TextMapInject - carrier.put(key, value) - }.whenever(mockTracer).inject(any(), any(), any()) + mockPropagation.wheneverInjectThenValueToHeaders(key, value) // When val response = testedInterceptor.intercept(mockChain) @@ -970,10 +973,7 @@ internal open class TracingInterceptorTest { } stubChain(mockChain, statusCode) whenever(mockTraceSampler.sample(mockSpan)).thenReturn(false) - doAnswer { invocation -> - val carrier = invocation.arguments[2] as TextMapInject - carrier.put(key, value) - }.whenever(mockTracer).inject(any(), any(), any()) + mockPropagation.wheneverInjectThenValueToHeaders(key, value) // When val response = testedInterceptor.intercept(mockChain) @@ -1128,11 +1128,11 @@ internal open class TracingInterceptorTest { assertThat(lastValue.headers) .hasTraceParentHeader( fakeTraceIdAsString, - mockSpan.context().toSpanId(), + mockSpan.context().spanId.toString(), isSampled = false ) .hasTraceStateHeaderWithOnlyDatadogVendorValues( - mockSpan.context().toSpanId(), + mockSpan.context().spanId.toString(), isSampled = false, getExpectedOrigin() ) @@ -1149,11 +1149,11 @@ internal open class TracingInterceptorTest { val response = testedInterceptor.intercept(mockChain) verify(mockSpanBuilder).withOrigin(getExpectedOrigin()) - verify(mockSpan as MutableSpan).resourceName = fakeBaseUrl.lowercase(Locale.US) + verify(mockSpan).resourceName = fakeBaseUrl.lowercase(Locale.US) verify(mockSpan).setTag("http.url", fakeUrl.lowercase(Locale.US)) verify(mockSpan).setTag("http.method", fakeMethod) verify(mockSpan).setTag("http.status_code", statusCode) - verify(mockSpan).setTag(Tags.SPAN_KIND, Tags.SPAN_KIND_CLIENT) + verify(mockSpan).setTag("span.kind", "client") verify(mockSpan).finish() assertThat(response).isSameAs(fakeResponse) } @@ -1172,12 +1172,12 @@ internal open class TracingInterceptorTest { val response = testedInterceptor.intercept(mockChain) verify(mockSpanBuilder).withOrigin(getExpectedOrigin()) - verify(mockSpan as MutableSpan).resourceName = + verify(mockSpan).resourceName = fakeUrlWithoutQueryParams.lowercase(Locale.US) verify(mockSpan).setTag("http.url", fakeUrlWithoutQueryParams.lowercase(Locale.US)) verify(mockSpan).setTag("http.method", fakeMethod) verify(mockSpan).setTag("http.status_code", statusCode) - verify(mockSpan).setTag(Tags.SPAN_KIND, Tags.SPAN_KIND_CLIENT) + verify(mockSpan).setTag("span.kind", "client") verify(mockSpan).finish() assertThat(response).isSameAs(fakeResponse) } @@ -1192,12 +1192,12 @@ internal open class TracingInterceptorTest { val response = testedInterceptor.intercept(mockChain) verify(mockSpanBuilder).withOrigin(getExpectedOrigin()) - verify(mockSpan as MutableSpan).resourceName = fakeBaseUrl.lowercase(Locale.US) + verify(mockSpan).resourceName = fakeBaseUrl.lowercase(Locale.US) verify(mockSpan).setTag("http.url", fakeUrl.lowercase(Locale.US)) verify(mockSpan).setTag("http.method", fakeMethod) verify(mockSpan).setTag("http.status_code", statusCode) - verify(mockSpan).setTag(Tags.SPAN_KIND, Tags.SPAN_KIND_CLIENT) - verify(mockSpan as MutableSpan).setError(true) + verify(mockSpan).setTag("span.kind", "client") + verify(mockSpan).isError = true verify(mockSpan).finish() assertThat(response).isSameAs(fakeResponse) } @@ -1212,12 +1212,12 @@ internal open class TracingInterceptorTest { val response = testedInterceptor.intercept(mockChain) verify(mockSpanBuilder).withOrigin(getExpectedOrigin()) - verify(mockSpan as MutableSpan).resourceName = fakeBaseUrl.lowercase(Locale.US) + verify(mockSpan).resourceName = fakeBaseUrl.lowercase(Locale.US) verify(mockSpan).setTag("http.url", fakeUrl.lowercase(Locale.US)) verify(mockSpan).setTag("http.method", fakeMethod) verify(mockSpan).setTag("http.status_code", statusCode) - verify(mockSpan).setTag(Tags.SPAN_KIND, Tags.SPAN_KIND_CLIENT) - verify(mockSpan as MutableSpan, never()).setError(true) + verify(mockSpan).setTag("span.kind", "client") + verify(mockSpan, never()).isError = true verify(mockSpan).finish() assertThat(response).isSameAs(fakeResponse) } @@ -1233,12 +1233,12 @@ internal open class TracingInterceptorTest { verify(mockSpan).setTag("http.url", fakeUrl.lowercase(Locale.US)) verify(mockSpan).setTag("http.method", fakeMethod) verify(mockSpan).setTag("http.status_code", 404) - verify(mockSpan).setTag(Tags.SPAN_KIND, Tags.SPAN_KIND_CLIENT) - verify(mockSpan as MutableSpan).setError(true) + verify(mockSpan).setTag("span.kind", "client") + verify(mockSpan).isError = true if (fakeRedacted404Resources) { - verify(mockSpan as MutableSpan).setResourceName(TracingInterceptor.RESOURCE_NAME_404) + verify(mockSpan).resourceName = TracingInterceptor.RESOURCE_NAME_404 } else { - verify(mockSpan as MutableSpan, never()).setResourceName(TracingInterceptor.RESOURCE_NAME_404) + verify(mockSpan, never()).resourceName = TracingInterceptor.RESOURCE_NAME_404 } verify(mockSpan).finish() assertThat(response).isSameAs(fakeResponse) @@ -1262,8 +1262,8 @@ internal open class TracingInterceptorTest { verify(mockSpan).setTag("error.type", throwable.javaClass.canonicalName) verify(mockSpan).setTag("error.msg", throwable.message) verify(mockSpan).setTag("error.stack", throwable.loggableStackTrace()) - verify(mockSpan).setTag(Tags.SPAN_KIND, Tags.SPAN_KIND_CLIENT) - verify(mockSpan as MutableSpan).resourceName = fakeBaseUrl.lowercase(Locale.US) + verify(mockSpan).setTag("span.kind", "client") + verify(mockSpan).resourceName = fakeBaseUrl.lowercase(Locale.US) verify(mockSpan).finish() } @@ -1271,7 +1271,6 @@ internal open class TracingInterceptorTest { fun `M warn the user W intercept() no tracer registered and TracingFeature not initialized`( @IntForgery(min = 200, max = 300) statusCode: Int ) { - GlobalTracer::class.java.setStaticValue("isRegistered", false) whenever(rumMonitor.mockSdkCore.getFeature(Feature.TRACING_FEATURE_NAME)) doReturn null whenever(mockResolver.isFirstPartyUrl(fakeUrl.toHttpUrl())).thenReturn(true) stubChain(mockChain, statusCode) @@ -1291,30 +1290,31 @@ internal open class TracingInterceptorTest { @Test fun `M create a span with automatic tracer W intercept() if no tracer registered`( + forge: Forge, @IntForgery(min = 200, max = 300) statusCode: Int ) { - val localSpanBuilder: DDTracer.DDSpanBuilder = mock() - val localSpan: Span = mock(extraInterfaces = arrayOf(MutableSpan::class)) - GlobalTracer::class.java.setStaticValue("isRegistered", false) + val localSpanContext = forge.newSpanContextMock(fakeTraceId, fakeSpanId) + val localSpan: DatadogSpan = forge.newSpanMock(localSpanContext) + val localSpanBuilder: DatadogSpanBuilder = forge.newSpanBuilderMock(localSpan) + whenever(mockResolver.isFirstPartyUrl(fakeUrl.toHttpUrl())).thenReturn(true) stubChain(mockChain, statusCode) - whenever(localSpanBuilder.asChildOf(null as SpanContext?)) doReturn localSpanBuilder - whenever(localSpanBuilder.withOrigin(getExpectedOrigin())) doReturn localSpanBuilder - whenever(localSpanBuilder.start()) doReturn localSpan - whenever(localSpan.context()) doReturn mockSpanContext whenever(mockTraceSampler.sample(localSpan)).thenReturn(true) - whenever(mockSpanContext.toSpanId()) doReturn fakeSpanId - whenever(mockSpanContext.toTraceId()) doReturn fakeTraceIdAsString whenever(mockLocalTracer.buildSpan(TracingInterceptor.SPAN_NAME)) doReturn localSpanBuilder + val testedInterceptorNoGlobal = instantiateTestedInterceptor( + fakeLocalHosts, + localTracerFactory = { _, _ -> mockLocalTracer }, + globalTracerProvider = { null } + ) - val response = testedInterceptor.intercept(mockChain) + val response = testedInterceptorNoGlobal.intercept(mockChain) verify(localSpanBuilder).withOrigin(getExpectedOrigin()) verify(localSpan).setTag("http.url", fakeUrl.lowercase(Locale.US)) verify(localSpan).setTag("http.method", fakeMethod) verify(localSpan).setTag("http.status_code", statusCode) - verify(localSpan).setTag(Tags.SPAN_KIND, Tags.SPAN_KIND_CLIENT) - verify(localSpan as MutableSpan).resourceName = fakeBaseUrl.lowercase(Locale.US) + verify(localSpan).setTag("span.kind", "client") + verify(localSpan).resourceName = fakeBaseUrl.lowercase(Locale.US) verify(localSpan).finish() assertThat(response).isSameAs(fakeResponse) mockInternalLogger.verifyLog( @@ -1326,25 +1326,26 @@ internal open class TracingInterceptorTest { @Test fun `M drop automatic tracer W intercept() and global tracer registered`( + forge: Forge, @IntForgery(min = 200, max = 300) statusCode: Int ) { - val localSpanBuilder: DDTracer.DDSpanBuilder = mock() - val localSpan: Span = mock(extraInterfaces = arrayOf(MutableSpan::class)) - GlobalTracer::class.java.setStaticValue("isRegistered", false) + val localSpan: DatadogSpan = forge.newSpanMock() + val localSpanBuilder = forge.newSpanBuilderMock(localSpan) + whenever(mockResolver.isFirstPartyUrl(fakeUrl.toHttpUrl())).thenReturn(true) stubChain(mockChain, statusCode) - whenever(localSpanBuilder.asChildOf(null as SpanContext?)) doReturn localSpanBuilder - whenever(localSpanBuilder.withOrigin(getExpectedOrigin())) doReturn localSpanBuilder - whenever(localSpanBuilder.start()) doReturn localSpan - whenever(localSpan.context()) doReturn mockSpanContext whenever(mockTraceSampler.sample(localSpan)).thenReturn(true) - whenever(mockSpanContext.toSpanId()) doReturn fakeSpanId - whenever(mockSpanContext.toTraceId()) doReturn fakeTraceIdAsString + whenever(mockSpanContext.spanId) doReturn fakeSpanId + whenever(mockSpanContext.traceId) doReturn fakeTraceId whenever(mockLocalTracer.buildSpan(TracingInterceptor.SPAN_NAME)) doReturn localSpanBuilder + val testedInterceptorNoGlobal = instantiateTestedInterceptor( + fakeLocalHosts, + localTracerFactory = { _, _ -> mockLocalTracer }, + globalTracerProvider = { null } + ) - val response1 = testedInterceptor.intercept(mockChain) + val response1 = testedInterceptorNoGlobal.intercept(mockChain) val expectedResponse1 = fakeResponse - GlobalTracer.registerIfAbsent(mockTracer) stubChain(mockChain, statusCode) val response2 = testedInterceptor.intercept(mockChain) val expectedResponse2 = fakeResponse @@ -1353,15 +1354,15 @@ internal open class TracingInterceptorTest { verify(localSpan).setTag("http.url", fakeUrl.lowercase(Locale.US)) verify(localSpan).setTag("http.method", fakeMethod) verify(localSpan).setTag("http.status_code", statusCode) - verify(localSpan).setTag(Tags.SPAN_KIND, Tags.SPAN_KIND_CLIENT) - verify(localSpan as MutableSpan).resourceName = fakeBaseUrl.lowercase(Locale.US) + verify(localSpan).setTag("span.kind", "client") + verify(localSpan).resourceName = fakeBaseUrl.lowercase(Locale.US) verify(localSpan).finish() verify(mockSpanBuilder).withOrigin(getExpectedOrigin()) verify(mockSpan).setTag("http.url", fakeUrl.lowercase(Locale.US)) verify(mockSpan).setTag("http.method", fakeMethod) verify(mockSpan).setTag("http.status_code", statusCode) - verify(mockSpan).setTag(Tags.SPAN_KIND, Tags.SPAN_KIND_CLIENT) - verify(mockSpan as MutableSpan).resourceName = fakeBaseUrl.lowercase(Locale.US) + verify(mockSpan).setTag("span.kind", "client") + verify(mockSpan).resourceName = fakeBaseUrl.lowercase(Locale.US) verify(mockSpan).finish() assertThat(response1).isSameAs(expectedResponse1) assertThat(response2).isSameAs(expectedResponse2) @@ -1372,35 +1373,6 @@ internal open class TracingInterceptorTest { ) } - @Test - fun `M call the listener W intercept() for successful request`( - @IntForgery(min = 200, max = 300) statusCode: Int, - @StringForgery tagKey: String, - @StringForgery(type = StringForgeryType.ALPHA_NUMERICAL) tagValue: String - ) { - whenever(mockResolver.isFirstPartyUrl(fakeUrl.toHttpUrl())).thenReturn(true) - stubChain(mockChain, statusCode) - whenever( - mockRequestListener.onRequestIntercepted(any(), any(), anyOrNull(), anyOrNull()) - ).doAnswer { - val span = it.arguments[1] as Span - span.setTag(tagKey, tagValue) - return@doAnswer Unit - } - - val response = testedInterceptor.intercept(mockChain) - - verify(mockSpanBuilder).withOrigin(getExpectedOrigin()) - verify(mockSpan).setTag("http.url", fakeUrl.lowercase(Locale.US)) - verify(mockSpan).setTag("http.method", fakeMethod) - verify(mockSpan).setTag("http.status_code", statusCode) - verify(mockSpan).setTag(Tags.SPAN_KIND, Tags.SPAN_KIND_CLIENT) - verify(mockSpan as MutableSpan).resourceName = fakeBaseUrl.lowercase(Locale.US) - verify(mockSpan).setTag(tagKey, tagValue) - verify(mockSpan).finish() - assertThat(response).isSameAs(fakeResponse) - } - @Test fun `M call the listener W intercept() for failing request`( @IntForgery(min = 400, max = 600) statusCode: Int, @@ -1412,7 +1384,7 @@ internal open class TracingInterceptorTest { whenever( mockRequestListener.onRequestIntercepted(any(), any(), anyOrNull(), anyOrNull()) ).doAnswer { - val span = it.arguments[1] as Span + val span = it.arguments[1] as DatadogSpan span.setTag(tagKey, tagValue) return@doAnswer Unit } @@ -1423,7 +1395,7 @@ internal open class TracingInterceptorTest { verify(mockSpan).setTag("http.url", fakeUrl.lowercase(Locale.US)) verify(mockSpan).setTag("http.method", fakeMethod) verify(mockSpan).setTag("http.status_code", statusCode) - verify(mockSpan).setTag(Tags.SPAN_KIND, Tags.SPAN_KIND_CLIENT) + verify(mockSpan).setTag("span.kind", "client") verify(mockSpan).setTag(tagKey, tagValue) verify(mockSpan).finish() assertThat(response).isSameAs(fakeResponse) @@ -1456,7 +1428,7 @@ internal open class TracingInterceptorTest { whenever( mockRequestListener.onRequestIntercepted(any(), any(), anyOrNull(), anyOrNull()) ).doAnswer { - val span = it.arguments[1] as Span + val span = it.arguments[1] as DatadogSpan span.setTag(tagKey, tagValue) return@doAnswer Unit } @@ -1473,7 +1445,7 @@ internal open class TracingInterceptorTest { verify(mockSpan).setTag("error.type", throwable.javaClass.canonicalName) verify(mockSpan).setTag("error.msg", throwable.message) verify(mockSpan).setTag("error.stack", throwable.loggableStackTrace()) - verify(mockSpan).setTag(Tags.SPAN_KIND, Tags.SPAN_KIND_CLIENT) + verify(mockSpan).setTag("span.kind", "client") verify(mockSpan).setTag(tagKey, tagValue) verify(mockSpan).finish() } @@ -1543,17 +1515,16 @@ internal open class TracingInterceptorTest { called++ mockLocalTracer } - GlobalTracer::class.java.setStaticValue("isRegistered", false) whenever(mockResolver.isFirstPartyUrl(fakeUrl.toHttpUrl())).thenReturn(true) stubChain(mockChain, statusCode) // need this setup, otherwise #intercept actually throws NPE, which pollutes the log - val localSpanBuilder: DDTracer.DDSpanBuilder = mock() - val localSpan: Span = mock(extraInterfaces = arrayOf(MutableSpan::class)) - whenever(localSpanBuilder.asChildOf(null as SpanContext?)) doReturn localSpanBuilder + val localSpanBuilder: DatadogSpanBuilder = mock() + val localSpan: DatadogSpan = mock() + whenever(localSpanBuilder.withParentContext(null as DatadogSpanContext?)) doReturn localSpanBuilder whenever(localSpanBuilder.start()) doReturn localSpan whenever(localSpan.context()) doReturn mockSpanContext - whenever(mockSpanContext.toSpanId()) doReturn fakeSpanId + whenever(mockSpanContext.spanId) doReturn fakeSpanId whenever(mockSpanContext.traceId) doReturn fakeTraceId whenever(mockLocalTracer.buildSpan(TracingInterceptor.SPAN_NAME)) doReturn localSpanBuilder diff --git a/integrations/dd-sdk-android-okhttp/src/test/kotlin/com/datadog/android/okhttp/trace/TracingInterceptorTestsExt.kt b/integrations/dd-sdk-android-okhttp/src/test/kotlin/com/datadog/android/okhttp/trace/TracingInterceptorTestsExt.kt new file mode 100644 index 0000000000..8a65b29c41 --- /dev/null +++ b/integrations/dd-sdk-android-okhttp/src/test/kotlin/com/datadog/android/okhttp/trace/TracingInterceptorTestsExt.kt @@ -0,0 +1,106 @@ +/* + * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. + * This product includes software developed at Datadog (https://www.datadoghq.com/). + * Copyright 2016-Present Datadog, Inc. + */ +package com.datadog.android.okhttp.trace + +import com.datadog.android.core.sampling.Sampler +import com.datadog.android.trace.api.propagation.DatadogPropagation +import com.datadog.android.trace.api.span.DatadogSpan +import com.datadog.android.trace.api.span.DatadogSpanBuilder +import com.datadog.android.trace.api.span.DatadogSpanContext +import com.datadog.android.trace.api.trace.DatadogTraceId +import com.datadog.android.trace.api.tracer.DatadogTracer +import com.datadog.android.trace.internal.fromHex +import fr.xgouchet.elmyr.Forge +import okhttp3.Request +import org.mockito.kotlin.any +import org.mockito.kotlin.anyOrNull +import org.mockito.kotlin.doAnswer +import org.mockito.kotlin.doReturn +import org.mockito.kotlin.doThrow +import org.mockito.kotlin.mock +import org.mockito.kotlin.whenever + +internal fun newAgentPropagationMock( + extractedContext: DatadogSpanContext = mock() +) = mock { + on { extract(any(), any()) } doReturn extractedContext +} +internal fun DatadogPropagation.wheneverInjectThenThrow(throwable: Throwable) { + doThrow(throwable) + .whenever(this) + .inject(any(), any(), any()) +} + +internal fun DatadogPropagation.wheneverInjectThenValueToHeaders(key: String, value: String) { + doAnswer { invocation -> + val carrier = invocation.getArgument(1) + val setter = invocation.getArgument<(carrier: Request.Builder, key: String, value: String) -> Unit>(2) + setter.invoke(carrier, key, value) + } + .whenever(this) + .inject(any(), any(), any()) +} + +internal fun DatadogPropagation.wheneverInjectCalledPassContextToHeaders( + datadogContext: Map, + nonDatadogContextKey: String, + nonDatadogContextKeyValue: String +) { + doAnswer { invocation -> + val carrier = invocation.getArgument(1) + val setter = invocation.getArgument<(carrier: Request.Builder, key: String, value: String) -> Unit>(2) + datadogContext.forEach { setter.invoke(carrier, it.key, it.value) } + setter.invoke(carrier, nonDatadogContextKey, nonDatadogContextKeyValue) + } + .whenever(this) + .inject(any(), any(), any()) +} + +internal fun Forge.aDatadogTraceId( + fakeString: String? = null +) = DatadogTraceId.fromHex(fakeString ?: aStringMatching("[a-f0-9]{31}")) + +internal fun Forge.newTraceSamplerMock( + span: DatadogSpan = newSpanMock() +) = mock> { + on { sample(span) } doReturn true +} + +internal fun Forge.newTracerMock( + spanBuilder: DatadogSpanBuilder = newSpanBuilderMock(), + propagation: DatadogPropagation = newAgentPropagationMock() +) = mock { + on { buildSpan(TracingInterceptor.SPAN_NAME) } doReturn spanBuilder + on { propagate() } doReturn propagation +} + +internal inline fun Forge.newSpanContextMock( + fakeTraceId: DatadogTraceId = aDatadogTraceId(), + fakeSpanId: Long = aLong(), + samplingPriority: Int = 0 +): T = mock { + on { spanId } doReturn fakeSpanId + on { traceId } doReturn fakeTraceId + on { mock.samplingPriority } doReturn samplingPriority +} + +internal fun Forge.newSpanMock( + context: DatadogSpanContext = newSpanContextMock(), + samplingPriority: Int? = null +) = mock { + on { context() } doReturn context + on { this.samplingPriority } doReturn samplingPriority +} + +internal fun Forge.newSpanBuilderMock( + localSpan: DatadogSpan = newSpanMock(), + context: DatadogSpanContext = newSpanContextMock() +) = mock { + on { withOrigin(anyOrNull()) } doReturn it + on { withParentContext(context) } doReturn it + on { withParentContext(null as DatadogSpanContext?) } doReturn it + on { start() } doReturn localSpan +} diff --git a/integrations/dd-sdk-android-okhttp/transitiveDependencies b/integrations/dd-sdk-android-okhttp/transitiveDependencies index ee148e1af0..37e7b3fcfb 100644 --- a/integrations/dd-sdk-android-okhttp/transitiveDependencies +++ b/integrations/dd-sdk-android-okhttp/transitiveDependencies @@ -3,9 +3,6 @@ Dependencies List androidx.annotation:annotation-jvm:1.9.1 : 59 Kb com.squareup.okhttp3:okhttp:4.12.0 : 771 Kb com.squareup.okio:okio-jvm:3.6.0 : 351 Kb -io.opentracing:opentracing-api:0.32.0 : 18 Kb -io.opentracing:opentracing-noop:0.32.0 : 10 Kb -io.opentracing:opentracing-util:0.32.0 : 10 Kb org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.9.10 : 959 b org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.9.10 : 965 b org.jetbrains.kotlin:kotlin-stdlib:2.0.21 : 1706 Kb diff --git a/integrations/dd-sdk-android-sqldelight/api/apiSurface b/integrations/dd-sdk-android-sqldelight/api/apiSurface index a01ed493aa..80118056fb 100644 --- a/integrations/dd-sdk-android-sqldelight/api/apiSurface +++ b/integrations/dd-sdk-android-sqldelight/api/apiSurface @@ -3,5 +3,5 @@ class com.datadog.android.sqldelight.DatadogSqliteCallback : com.squareup.sqldel override fun onCorruption(androidx.sqlite.db.SupportSQLiteDatabase) fun T.transactionTraced(String, Boolean = false, TransactionWithSpanAndWithoutReturn.() -> Unit) fun T.transactionTracedWithResult(String, Boolean = false, TransactionWithSpanAndWithReturn.() -> R): R -interface com.datadog.android.sqldelight.TransactionWithSpanAndWithReturn : com.squareup.sqldelight.TransactionWithReturn, io.opentracing.Span -interface com.datadog.android.sqldelight.TransactionWithSpanAndWithoutReturn : com.squareup.sqldelight.TransactionWithoutReturn, io.opentracing.Span +interface com.datadog.android.sqldelight.TransactionWithSpanAndWithReturn : com.squareup.sqldelight.TransactionWithReturn, com.datadog.android.trace.api.span.DatadogSpan +interface com.datadog.android.sqldelight.TransactionWithSpanAndWithoutReturn : com.squareup.sqldelight.TransactionWithoutReturn, com.datadog.android.trace.api.span.DatadogSpan diff --git a/integrations/dd-sdk-android-sqldelight/api/dd-sdk-android-sqldelight.api b/integrations/dd-sdk-android-sqldelight/api/dd-sdk-android-sqldelight.api index 2c9840ab23..4d5fde5f1d 100644 --- a/integrations/dd-sdk-android-sqldelight/api/dd-sdk-android-sqldelight.api +++ b/integrations/dd-sdk-android-sqldelight/api/dd-sdk-android-sqldelight.api @@ -12,9 +12,9 @@ public final class com/datadog/android/sqldelight/SqlDelightExtKt { public static synthetic fun transactionTracedWithResult$default (Lcom/squareup/sqldelight/Transacter;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;ILjava/lang/Object;)Ljava/lang/Object; } -public abstract interface class com/datadog/android/sqldelight/TransactionWithSpanAndWithReturn : com/squareup/sqldelight/TransactionWithReturn, io/opentracing/Span { +public abstract interface class com/datadog/android/sqldelight/TransactionWithSpanAndWithReturn : com/datadog/android/trace/api/span/DatadogSpan, com/squareup/sqldelight/TransactionWithReturn { } -public abstract interface class com/datadog/android/sqldelight/TransactionWithSpanAndWithoutReturn : com/squareup/sqldelight/TransactionWithoutReturn, io/opentracing/Span { +public abstract interface class com/datadog/android/sqldelight/TransactionWithSpanAndWithoutReturn : com/datadog/android/trace/api/span/DatadogSpan, com/squareup/sqldelight/TransactionWithoutReturn { } diff --git a/integrations/dd-sdk-android-sqldelight/build.gradle.kts b/integrations/dd-sdk-android-sqldelight/build.gradle.kts index 4b5caf080a..2fadc5c483 100644 --- a/integrations/dd-sdk-android-sqldelight/build.gradle.kts +++ b/integrations/dd-sdk-android-sqldelight/build.gradle.kts @@ -59,6 +59,7 @@ dependencies { testImplementation(libs.bundles.jUnit5) testImplementation(libs.bundles.testTools) testImplementation(libs.okHttpMock) + testImplementation(testFixtures(project(":features:dd-sdk-android-trace"))) } kotlinConfig(jvmBytecodeTarget = JvmTarget.JVM_11) diff --git a/integrations/dd-sdk-android-sqldelight/src/main/kotlin/com/datadog/android/sqldelight/SqlDelightExt.kt b/integrations/dd-sdk-android-sqldelight/src/main/kotlin/com/datadog/android/sqldelight/SqlDelightExt.kt index 80336c6016..77272fc420 100644 --- a/integrations/dd-sdk-android-sqldelight/src/main/kotlin/com/datadog/android/sqldelight/SqlDelightExt.kt +++ b/integrations/dd-sdk-android-sqldelight/src/main/kotlin/com/datadog/android/sqldelight/SqlDelightExt.kt @@ -9,10 +9,9 @@ package com.datadog.android.sqldelight import com.datadog.android.sqldelight.internal.TransactionWithSpanAndWithReturnImpl import com.datadog.android.sqldelight.internal.TransactionWithSpanAndWithoutReturnImpl import com.datadog.android.sqldelight.internal.withinSpan +import com.datadog.android.trace.GlobalDatadogTracer import com.squareup.sqldelight.Transacter import com.squareup.sqldelight.Transacter.Transaction -import io.opentracing.Span -import io.opentracing.util.GlobalTracer /** * Starts a [Transaction] and runs [body] in that transaction. @@ -30,7 +29,7 @@ fun T.transactionTraced( noEnclosing: Boolean = false, body: TransactionWithSpanAndWithoutReturn.() -> Unit ) { - withinSpan(operationName, GlobalTracer.get().activeSpan()) { + withinSpan(operationName, GlobalDatadogTracer.get().activeSpan()) { @Suppress("UnsafeThirdPartyFunctionCall") // handled by caller transaction(noEnclosing = noEnclosing) { @Suppress("UnsafeThirdPartyFunctionCall") // handled by caller @@ -56,7 +55,7 @@ fun T.transactionTracedWithResult( noEnclosing: Boolean = false, body: TransactionWithSpanAndWithReturn.() -> R ): R { - withinSpan(operationName, GlobalTracer.get().activeSpan()) { + withinSpan(operationName, GlobalDatadogTracer.get().activeSpan()) { @Suppress("UnsafeThirdPartyFunctionCall") // handled by caller return transactionWithResult(noEnclosing = noEnclosing) { @Suppress("UnsafeThirdPartyFunctionCall") // handled by caller diff --git a/integrations/dd-sdk-android-sqldelight/src/main/kotlin/com/datadog/android/sqldelight/TransactionWithSpanAndWithReturn.kt b/integrations/dd-sdk-android-sqldelight/src/main/kotlin/com/datadog/android/sqldelight/TransactionWithSpanAndWithReturn.kt index e36806748e..a3f96bc027 100644 --- a/integrations/dd-sdk-android-sqldelight/src/main/kotlin/com/datadog/android/sqldelight/TransactionWithSpanAndWithReturn.kt +++ b/integrations/dd-sdk-android-sqldelight/src/main/kotlin/com/datadog/android/sqldelight/TransactionWithSpanAndWithReturn.kt @@ -6,10 +6,10 @@ package com.datadog.android.sqldelight +import com.datadog.android.trace.api.span.DatadogSpan import com.squareup.sqldelight.TransactionWithReturn -import io.opentracing.Span /** - * An object that implements both [Span] and [TransactionWithReturn]. + * An object that implements both [DatadogSpan] and [TransactionWithReturn]. */ -interface TransactionWithSpanAndWithReturn : TransactionWithReturn, Span +interface TransactionWithSpanAndWithReturn : TransactionWithReturn, DatadogSpan diff --git a/integrations/dd-sdk-android-sqldelight/src/main/kotlin/com/datadog/android/sqldelight/TransactionWithSpanAndWithoutReturn.kt b/integrations/dd-sdk-android-sqldelight/src/main/kotlin/com/datadog/android/sqldelight/TransactionWithSpanAndWithoutReturn.kt index ad0bfbfedc..9297e53ad9 100644 --- a/integrations/dd-sdk-android-sqldelight/src/main/kotlin/com/datadog/android/sqldelight/TransactionWithSpanAndWithoutReturn.kt +++ b/integrations/dd-sdk-android-sqldelight/src/main/kotlin/com/datadog/android/sqldelight/TransactionWithSpanAndWithoutReturn.kt @@ -6,10 +6,10 @@ package com.datadog.android.sqldelight +import com.datadog.android.trace.api.span.DatadogSpan import com.squareup.sqldelight.TransactionWithoutReturn -import io.opentracing.Span /** - * An object that implements both [Span] and [TransactionWithoutReturn]. + * An object that implements both [DatadogSpan] and [TransactionWithoutReturn]. */ -interface TransactionWithSpanAndWithoutReturn : TransactionWithoutReturn, Span +interface TransactionWithSpanAndWithoutReturn : TransactionWithoutReturn, DatadogSpan diff --git a/integrations/dd-sdk-android-sqldelight/src/main/kotlin/com/datadog/android/sqldelight/internal/SqlDelightInternalExt.kt b/integrations/dd-sdk-android-sqldelight/src/main/kotlin/com/datadog/android/sqldelight/internal/SqlDelightInternalExt.kt index 102a2bf190..16f0fdbdcd 100644 --- a/integrations/dd-sdk-android-sqldelight/src/main/kotlin/com/datadog/android/sqldelight/internal/SqlDelightInternalExt.kt +++ b/integrations/dd-sdk-android-sqldelight/src/main/kotlin/com/datadog/android/sqldelight/internal/SqlDelightInternalExt.kt @@ -6,20 +6,19 @@ package com.datadog.android.sqldelight.internal -import com.datadog.android.trace.AndroidTracer -import io.opentracing.Span -import io.opentracing.util.GlobalTracer +import com.datadog.android.trace.GlobalDatadogTracer +import com.datadog.android.trace.api.span.DatadogSpan @Suppress("ThrowingInternalException", "TooGenericExceptionCaught") internal inline fun withinSpan( operationName: String, - parentSpan: Span? = null, - block: Span.() -> T + parentSpan: DatadogSpan? = null, + block: DatadogSpan.() -> T ): T { - val tracer = GlobalTracer.get() + val tracer = GlobalDatadogTracer.get() val span = tracer.buildSpan(operationName) - .asChildOf(parentSpan) + .withParentSpan(parentSpan) .start() val scope = tracer.activateSpan(span) @@ -27,10 +26,10 @@ internal inline fun withinSpan( return try { span.block() } catch (e: Throwable) { - AndroidTracer.logThrowable(span, e) + span.addThrowable(e) throw e } finally { span.finish() - scope.close() + scope?.close() } } diff --git a/integrations/dd-sdk-android-sqldelight/src/main/kotlin/com/datadog/android/sqldelight/internal/TransactionWithSpanAndWithReturnImpl.kt b/integrations/dd-sdk-android-sqldelight/src/main/kotlin/com/datadog/android/sqldelight/internal/TransactionWithSpanAndWithReturnImpl.kt index 08f3ac8684..c727acc3e8 100644 --- a/integrations/dd-sdk-android-sqldelight/src/main/kotlin/com/datadog/android/sqldelight/internal/TransactionWithSpanAndWithReturnImpl.kt +++ b/integrations/dd-sdk-android-sqldelight/src/main/kotlin/com/datadog/android/sqldelight/internal/TransactionWithSpanAndWithReturnImpl.kt @@ -7,10 +7,10 @@ package com.datadog.android.sqldelight.internal import com.datadog.android.sqldelight.TransactionWithSpanAndWithReturn +import com.datadog.android.trace.api.span.DatadogSpan import com.squareup.sqldelight.TransactionWithReturn -import io.opentracing.Span internal class TransactionWithSpanAndWithReturnImpl( - private val span: Span, + private val span: DatadogSpan, private val transaction: TransactionWithReturn -) : TransactionWithSpanAndWithReturn, Span by span, TransactionWithReturn by transaction +) : TransactionWithSpanAndWithReturn, DatadogSpan by span, TransactionWithReturn by transaction diff --git a/integrations/dd-sdk-android-sqldelight/src/main/kotlin/com/datadog/android/sqldelight/internal/TransactionWithSpanAndWithoutReturnImpl.kt b/integrations/dd-sdk-android-sqldelight/src/main/kotlin/com/datadog/android/sqldelight/internal/TransactionWithSpanAndWithoutReturnImpl.kt index 98635f09b9..c37aee05a4 100644 --- a/integrations/dd-sdk-android-sqldelight/src/main/kotlin/com/datadog/android/sqldelight/internal/TransactionWithSpanAndWithoutReturnImpl.kt +++ b/integrations/dd-sdk-android-sqldelight/src/main/kotlin/com/datadog/android/sqldelight/internal/TransactionWithSpanAndWithoutReturnImpl.kt @@ -7,10 +7,12 @@ package com.datadog.android.sqldelight.internal import com.datadog.android.sqldelight.TransactionWithSpanAndWithoutReturn +import com.datadog.android.trace.api.span.DatadogSpan import com.squareup.sqldelight.TransactionWithoutReturn -import io.opentracing.Span internal class TransactionWithSpanAndWithoutReturnImpl( - private val span: Span, + private val span: DatadogSpan, private val transaction: TransactionWithoutReturn -) : TransactionWithSpanAndWithoutReturn, Span by span, TransactionWithoutReturn by transaction +) : DatadogSpan by span, + TransactionWithSpanAndWithoutReturn, + TransactionWithoutReturn by transaction diff --git a/integrations/dd-sdk-android-sqldelight/src/test/kotlin/com/datadog/android/sqldelight/SqlDelightExtTest.kt b/integrations/dd-sdk-android-sqldelight/src/test/kotlin/com/datadog/android/sqldelight/SqlDelightExtTest.kt index 349e609de9..c6bf9463dc 100644 --- a/integrations/dd-sdk-android-sqldelight/src/test/kotlin/com/datadog/android/sqldelight/SqlDelightExtTest.kt +++ b/integrations/dd-sdk-android-sqldelight/src/test/kotlin/com/datadog/android/sqldelight/SqlDelightExtTest.kt @@ -6,8 +6,12 @@ package com.datadog.android.sqldelight +import com.datadog.android.trace.GlobalDatadogTracer +import com.datadog.android.trace.api.scope.DatadogScope +import com.datadog.android.trace.api.span.DatadogSpan +import com.datadog.android.trace.api.span.DatadogSpanBuilder +import com.datadog.android.trace.api.tracer.DatadogTracer import com.datadog.tools.unit.forge.BaseConfigurator -import com.datadog.tools.unit.setStaticValue import com.squareup.sqldelight.Transacter import com.squareup.sqldelight.TransactionWithReturn import com.squareup.sqldelight.TransactionWithoutReturn @@ -16,11 +20,6 @@ import fr.xgouchet.elmyr.annotation.Forgery import fr.xgouchet.elmyr.annotation.StringForgery import fr.xgouchet.elmyr.junit5.ForgeConfiguration import fr.xgouchet.elmyr.junit5.ForgeExtension -import io.opentracing.Scope -import io.opentracing.Span -import io.opentracing.Tracer -import io.opentracing.log.Fields -import io.opentracing.util.GlobalTracer import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.BeforeEach @@ -31,7 +30,6 @@ import org.mockito.Mock import org.mockito.junit.jupiter.MockitoExtension import org.mockito.junit.jupiter.MockitoSettings import org.mockito.kotlin.any -import org.mockito.kotlin.argThat import org.mockito.kotlin.doAnswer import org.mockito.kotlin.doReturn import org.mockito.kotlin.eq @@ -49,19 +47,19 @@ import org.mockito.quality.Strictness @MockitoSettings(strictness = Strictness.LENIENT) class SqlDelightExtTest { @Mock - lateinit var mockTracer: Tracer + lateinit var mockTracer: DatadogTracer @Mock - lateinit var mockSpanBuilder: Tracer.SpanBuilder + lateinit var mockSpanBuilder: DatadogSpanBuilder @Mock - lateinit var mockSpan: Span + lateinit var mockSpan: DatadogSpan @Mock - lateinit var mockParentSpan: Span + lateinit var mockParentSpan: DatadogSpan @Mock - lateinit var mockScope: Scope + lateinit var mockScope: DatadogScope @StringForgery lateinit var fakeOperationName: String @@ -80,7 +78,7 @@ class SqlDelightExtTest { @BeforeEach fun `set up`() { - GlobalTracer.registerIfAbsent(mockTracer) + GlobalDatadogTracer.registerIfAbsent(mockTracer) whenever(mockTracer.buildSpan(fakeOperationName)) doReturn mockSpanBuilder whenever(mockTracer.activateSpan(mockSpan)) doReturn mockScope whenever(mockSpanBuilder.start()) doReturn mockSpan @@ -99,14 +97,14 @@ class SqlDelightExtTest { @AfterEach fun `tear down`() { - GlobalTracer::class.java.setStaticValue("isRegistered", false) + GlobalDatadogTracer.clear() } @Test fun `M create Span around transaction W transactionTraced() {nonEnclosing = true}`() { // GIVEN whenever(mockTracer.activeSpan()) doReturn mockParentSpan - whenever(mockSpanBuilder.asChildOf(mockParentSpan)) doReturn mockSpanBuilder + whenever(mockSpanBuilder.withParentSpan(mockParentSpan)) doReturn mockSpanBuilder var transactionExecuted = false val body: TransactionWithoutReturn.() -> Unit = { transactionExecuted = true @@ -117,7 +115,7 @@ class SqlDelightExtTest { // THEN assertThat(transactionExecuted).isTrue() - verify(mockSpanBuilder).asChildOf(mockParentSpan) + verify(mockSpanBuilder).withParentSpan(mockParentSpan) inOrder(mockSpan, mockScope) { verify(mockSpan).finish() verify(mockScope).close() @@ -130,7 +128,7 @@ class SqlDelightExtTest { fun `M create Span around transaction W transactionTraced() {nonEnclosing = false}`() { // GIVEN whenever(mockTracer.activeSpan()) doReturn mockParentSpan - whenever(mockSpanBuilder.asChildOf(mockParentSpan)) doReturn mockSpanBuilder + whenever(mockSpanBuilder.withParentSpan(mockParentSpan)) doReturn mockSpanBuilder var transactionExecuted = false val body: TransactionWithoutReturn.() -> Unit = { transactionExecuted = true @@ -141,7 +139,7 @@ class SqlDelightExtTest { // THEN assertThat(transactionExecuted).isTrue() - verify(mockSpanBuilder).asChildOf(mockParentSpan) + verify(mockSpanBuilder).withParentSpan(mockParentSpan) inOrder(mockSpan, mockScope) { verify(mockSpan).finish() verify(mockScope).close() @@ -155,7 +153,7 @@ class SqlDelightExtTest { // GIVEN val fakeNoEnclosing = forge.aBool() whenever(mockTracer.activeSpan()) doReturn null - whenever(mockSpanBuilder.asChildOf(null as Span?)) doReturn mockSpanBuilder + whenever(mockSpanBuilder.withParentSpan(null as DatadogSpan?)) doReturn mockSpanBuilder var transactionExecuted = false val body: TransactionWithSpanAndWithoutReturn.() -> Unit = { transactionExecuted = true @@ -166,7 +164,7 @@ class SqlDelightExtTest { // THEN assertThat(transactionExecuted).isTrue() - verify(mockSpanBuilder).asChildOf(null as Span?) + verify(mockSpanBuilder).withParentSpan(null as DatadogSpan?) inOrder(mockSpan, mockScope) { verify(mockSpan).finish() verify(mockScope).close() @@ -181,7 +179,7 @@ class SqlDelightExtTest { val fakeNoEnclosing = forge.aBool() var caughtException: Throwable? = null whenever(mockTracer.activeSpan()) doReturn null - whenever(mockSpanBuilder.asChildOf(null as Span?)) doReturn mockSpanBuilder + whenever(mockSpanBuilder.withParentSpan(null as DatadogSpan?)) doReturn mockSpanBuilder // WHEN try { @@ -194,13 +192,9 @@ class SqlDelightExtTest { // THEN assertThat(caughtException).isEqualTo(fakeException) - verify(mockSpanBuilder).asChildOf(null as Span?) + verify(mockSpanBuilder).withParentSpan(null as DatadogSpan?) inOrder(mockSpan, mockScope) { - verify(mockSpan).log( - argThat> { - this[Fields.ERROR_OBJECT] == fakeException - } - ) + verify(mockSpan).addThrowable(fakeException) verify(mockSpan).finish() verify(mockScope).close() } @@ -212,7 +206,7 @@ class SqlDelightExtTest { val fakeNoEnclosing = forge.aBool() var caughtException: Throwable? = null whenever(mockTracer.activeSpan()) doReturn null - whenever(mockSpanBuilder.asChildOf(null as Span?)) doReturn mockSpanBuilder + whenever(mockSpanBuilder.withParentSpan(null as DatadogSpan?)) doReturn mockSpanBuilder // WHEN try { @@ -225,12 +219,8 @@ class SqlDelightExtTest { // THEN assertThat(caughtException).isEqualTo(fakeException) - verify(mockSpanBuilder).asChildOf(null as Span?) - verify(mockSpan).log( - argThat> { - this[Fields.ERROR_OBJECT] == fakeException - } - ) + verify(mockSpanBuilder).withParentSpan(null as DatadogSpan?) + verify(mockSpan).addThrowable(fakeException) } @Test @@ -238,7 +228,7 @@ class SqlDelightExtTest { // GIVEN val fakeNoEnclosing = forge.aBool() whenever(mockTracer.activeSpan()) doReturn null - whenever(mockSpanBuilder.asChildOf(null as Span?)) doReturn mockSpanBuilder + whenever(mockSpanBuilder.withParentSpan(null as DatadogSpan?)) doReturn mockSpanBuilder var transactionExecuted = false val afterCommitLambda = { } @@ -262,7 +252,7 @@ class SqlDelightExtTest { val fakeTagValue = forge.anAlphabeticalString() val fakeNoEnclosing = forge.aBool() whenever(mockTracer.activeSpan()) doReturn null - whenever(mockSpanBuilder.asChildOf(null as Span?)) doReturn mockSpanBuilder + whenever(mockSpanBuilder.withParentSpan(null as DatadogSpan?)) doReturn mockSpanBuilder var transactionExecuted = false val body: TransactionWithSpanAndWithoutReturn.() -> Unit = { transactionExecuted = true @@ -281,7 +271,7 @@ class SqlDelightExtTest { fun `M create Span around W transactionTracedWithResult() {noEnclosing = true}`() { // // GIVEN whenever(mockTracer.activeSpan()) doReturn mockParentSpan - whenever(mockSpanBuilder.asChildOf(mockParentSpan)) doReturn mockSpanBuilder + whenever(mockSpanBuilder.withParentSpan(mockParentSpan)) doReturn mockSpanBuilder val body: TransactionWithSpanAndWithReturn.() -> Boolean = { true } @@ -292,7 +282,7 @@ class SqlDelightExtTest { // THEN assertThat(transactionExecuted).isTrue() - verify(mockSpanBuilder).asChildOf(mockParentSpan) + verify(mockSpanBuilder).withParentSpan(mockParentSpan) inOrder(mockSpan, mockScope) { verify(mockSpan).finish() verify(mockScope).close() @@ -308,7 +298,7 @@ class SqlDelightExtTest { fun `M create Span around W transactionTracedWithResult() {noEnclosing = false}`() { // GIVEN whenever(mockTracer.activeSpan()) doReturn mockParentSpan - whenever(mockSpanBuilder.asChildOf(mockParentSpan)) doReturn mockSpanBuilder + whenever(mockSpanBuilder.withParentSpan(mockParentSpan)) doReturn mockSpanBuilder val body: TransactionWithSpanAndWithReturn.() -> Boolean = { true } @@ -319,7 +309,7 @@ class SqlDelightExtTest { // THEN assertThat(transactionExecuted).isTrue() - verify(mockSpanBuilder).asChildOf(mockParentSpan) + verify(mockSpanBuilder).withParentSpan(mockParentSpan) inOrder(mockSpan, mockScope) { verify(mockSpan).finish() verify(mockScope).close() @@ -336,7 +326,7 @@ class SqlDelightExtTest { // GIVEN val fakeNoEnclosing = forge.aBool() whenever(mockTracer.activeSpan()) doReturn null - whenever(mockSpanBuilder.asChildOf(null as Span?)) doReturn mockSpanBuilder + whenever(mockSpanBuilder.withParentSpan(null as DatadogSpan?)) doReturn mockSpanBuilder val body: TransactionWithSpanAndWithReturn.() -> Boolean = { true } @@ -347,7 +337,7 @@ class SqlDelightExtTest { // THEN assertThat(transactionExecuted).isTrue() - verify(mockSpanBuilder).asChildOf(null as Span?) + verify(mockSpanBuilder).withParentSpan(null as DatadogSpan?) inOrder(mockSpan, mockScope) { verify(mockSpan).finish() verify(mockScope).close() @@ -365,7 +355,7 @@ class SqlDelightExtTest { val fakeNoEnclosing = forge.aBool() var caughtException: Throwable? = null whenever(mockTracer.activeSpan()) doReturn null - whenever(mockSpanBuilder.asChildOf(null as Span?)) doReturn mockSpanBuilder + whenever(mockSpanBuilder.withParentSpan(null as DatadogSpan?)) doReturn mockSpanBuilder // WHEN try { @@ -381,13 +371,9 @@ class SqlDelightExtTest { // THEN assertThat(caughtException).isEqualTo(fakeException) - verify(mockSpanBuilder).asChildOf(null as Span?) + verify(mockSpanBuilder).withParentSpan(null as DatadogSpan?) inOrder(mockSpan, mockScope) { - verify(mockSpan).log( - argThat> { - this[Fields.ERROR_OBJECT] == fakeException - } - ) + verify(mockSpan).addThrowable(fakeException) verify(mockSpan).finish() verify(mockScope).close() } @@ -399,7 +385,7 @@ class SqlDelightExtTest { val fakeNoEnclosing = forge.aBool() var caughtException: Throwable? = null whenever(mockTracer.activeSpan()) doReturn null - whenever(mockSpanBuilder.asChildOf(null as Span?)) doReturn mockSpanBuilder + whenever(mockSpanBuilder.withParentSpan(null as DatadogSpan?)) doReturn mockSpanBuilder // WHEN try { @@ -415,12 +401,8 @@ class SqlDelightExtTest { // THEN assertThat(caughtException).isEqualTo(fakeException) - verify(mockSpanBuilder).asChildOf(null as Span?) - verify(mockSpan).log( - argThat> { - this[Fields.ERROR_OBJECT] == fakeException - } - ) + verify(mockSpanBuilder).withParentSpan(null as DatadogSpan?) + verify(mockSpan).addThrowable(fakeException) } @Test @@ -428,7 +410,7 @@ class SqlDelightExtTest { // GIVEN val fakeNoEnclosing = forge.aBool() whenever(mockTracer.activeSpan()) doReturn null - whenever(mockSpanBuilder.asChildOf(null as Span?)) doReturn mockSpanBuilder + whenever(mockSpanBuilder.withParentSpan(null as DatadogSpan?)) doReturn mockSpanBuilder val afterCommitLambda = { } val body: TransactionWithSpanAndWithReturn.() -> Boolean = { @@ -452,7 +434,7 @@ class SqlDelightExtTest { val fakeTagValue = forge.anAlphabeticalString() val fakeNoEnclosing = forge.aBool() whenever(mockTracer.activeSpan()) doReturn null - whenever(mockSpanBuilder.asChildOf(null as Span?)) doReturn mockSpanBuilder + whenever(mockSpanBuilder.withParentSpan(null as DatadogSpan?)) doReturn mockSpanBuilder val body: TransactionWithSpanAndWithReturn.() -> Boolean = { setTag(fakeTagKey, fakeTagValue) true diff --git a/integrations/dd-sdk-android-sqldelight/transitiveDependencies b/integrations/dd-sdk-android-sqldelight/transitiveDependencies index 1852cd3616..a18911b873 100644 --- a/integrations/dd-sdk-android-sqldelight/transitiveDependencies +++ b/integrations/dd-sdk-android-sqldelight/transitiveDependencies @@ -6,9 +6,6 @@ com.squareup.okhttp3:okhttp:4.12.0 : 771 Kb com.squareup.okio:okio-jvm:3.6.0 : 351 Kb com.squareup.sqldelight:android-driver:1.5.5 : 23 Kb com.squareup.sqldelight:runtime-jvm:1.5.5 : 44 Kb -io.opentracing:opentracing-api:0.32.0 : 18 Kb -io.opentracing:opentracing-noop:0.32.0 : 10 Kb -io.opentracing:opentracing-util:0.32.0 : 10 Kb org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.9.10 : 959 b org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.9.10 : 965 b org.jetbrains.kotlin:kotlin-stdlib:2.0.21 : 1706 Kb diff --git a/integrations/dd-sdk-android-trace-coroutines/api/apiSurface b/integrations/dd-sdk-android-trace-coroutines/api/apiSurface index 9ec0649242..b2afa605ae 100644 --- a/integrations/dd-sdk-android-trace-coroutines/api/apiSurface +++ b/integrations/dd-sdk-android-trace-coroutines/api/apiSurface @@ -3,4 +3,4 @@ fun runBlockingTraced(String, kotlin.coroutines.CoroutineContext = EmptyCoro fun kotlinx.coroutines.CoroutineScope.asyncTraced(String, kotlin.coroutines.CoroutineContext = EmptyCoroutineContext, kotlinx.coroutines.CoroutineStart = CoroutineStart.DEFAULT, CoroutineScopeSpan.() -> T): kotlinx.coroutines.Deferred fun kotlinx.coroutines.Deferred.awaitTraced(String): T fun withContextTraced(String, kotlin.coroutines.CoroutineContext, CoroutineScopeSpan.() -> T): T -interface com.datadog.android.trace.coroutines.CoroutineScopeSpan : kotlinx.coroutines.CoroutineScope, io.opentracing.Span +interface com.datadog.android.trace.coroutines.CoroutineScopeSpan : kotlinx.coroutines.CoroutineScope, com.datadog.android.trace.api.span.DatadogSpan diff --git a/integrations/dd-sdk-android-trace-coroutines/build.gradle.kts b/integrations/dd-sdk-android-trace-coroutines/build.gradle.kts index f1d0f9c4c4..fc9cefd77c 100644 --- a/integrations/dd-sdk-android-trace-coroutines/build.gradle.kts +++ b/integrations/dd-sdk-android-trace-coroutines/build.gradle.kts @@ -41,9 +41,9 @@ android { } dependencies { - implementation(project(":features:dd-sdk-android-trace")) implementation(libs.kotlin) implementation(libs.coroutinesCore) + implementation(project(":features:dd-sdk-android-trace")) testImplementation(project(":tools:unit")) { attributes { @@ -56,6 +56,7 @@ dependencies { testImplementation(libs.bundles.jUnit5) testImplementation(libs.bundles.testTools) testImplementation(libs.okHttpMock) + testImplementation(testFixtures(project(":features:dd-sdk-android-trace"))) } kotlinConfig(jvmBytecodeTarget = JvmTarget.JVM_11) diff --git a/integrations/dd-sdk-android-trace-coroutines/src/main/kotlin/com/datadog/android/trace/coroutines/CoroutineExt.kt b/integrations/dd-sdk-android-trace-coroutines/src/main/kotlin/com/datadog/android/trace/coroutines/CoroutineExt.kt index d3e384989c..fb5a104954 100644 --- a/integrations/dd-sdk-android-trace-coroutines/src/main/kotlin/com/datadog/android/trace/coroutines/CoroutineExt.kt +++ b/integrations/dd-sdk-android-trace-coroutines/src/main/kotlin/com/datadog/android/trace/coroutines/CoroutineExt.kt @@ -6,10 +6,9 @@ package com.datadog.android.trace.coroutines +import com.datadog.android.trace.GlobalDatadogTracer import com.datadog.android.trace.internal.coroutines.withinCoroutineSpan import com.datadog.android.trace.withinSpan -import io.opentracing.Span -import io.opentracing.util.GlobalTracer import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineStart import kotlinx.coroutines.Deferred @@ -38,7 +37,7 @@ fun CoroutineScope.launchTraced( start: CoroutineStart = CoroutineStart.DEFAULT, block: suspend CoroutineScopeSpan.() -> Unit ): Job { - val parentSpan = GlobalTracer.get().activeSpan() + val parentSpan = GlobalDatadogTracer.get().activeSpan() return launch(context, start) { withinCoroutineSpan(operationName, parentSpan, context, block) } @@ -64,7 +63,7 @@ fun runBlockingTraced( context: CoroutineContext = EmptyCoroutineContext, block: suspend CoroutineScope.() -> T ): T { - val parentSpan = GlobalTracer.get().activeSpan() + val parentSpan = GlobalDatadogTracer.get().activeSpan() @Suppress("UnsafeThirdPartyFunctionCall") // handled by caller return runBlocking(context) { withinCoroutineSpan(operationName, parentSpan, context, block) @@ -91,7 +90,7 @@ fun CoroutineScope.asyncTraced( start: CoroutineStart = CoroutineStart.DEFAULT, block: suspend CoroutineScopeSpan.() -> T ): Deferred { - val parentSpan = GlobalTracer.get().activeSpan() + val parentSpan = GlobalDatadogTracer.get().activeSpan() return async(context, start) { withinCoroutineSpan(operationName, parentSpan, context, block) } @@ -109,7 +108,7 @@ fun CoroutineScope.asyncTraced( * @param operationName the name of the [Span] created around the coroutine code. */ suspend fun Deferred.awaitTraced(operationName: String): T { - val parentSpan = GlobalTracer.get().activeSpan() + val parentSpan = GlobalDatadogTracer.get().activeSpan() return withinSpan(operationName, parentSpan, false) { @Suppress("UnsafeThirdPartyFunctionCall") // handled by caller this@awaitTraced.await() @@ -133,7 +132,7 @@ suspend fun withContextTraced( context: CoroutineContext, block: suspend CoroutineScopeSpan.() -> T ): T { - val parentSpan = GlobalTracer.get().activeSpan() + val parentSpan = GlobalDatadogTracer.get().activeSpan() @Suppress("UnsafeThirdPartyFunctionCall") // handled by caller return withContext(context) { withinCoroutineSpan(operationName, parentSpan, context, block) diff --git a/integrations/dd-sdk-android-trace-coroutines/src/main/kotlin/com/datadog/android/trace/coroutines/CoroutineScopeSpan.kt b/integrations/dd-sdk-android-trace-coroutines/src/main/kotlin/com/datadog/android/trace/coroutines/CoroutineScopeSpan.kt index 9b728b0c1d..2e9b6ad08f 100644 --- a/integrations/dd-sdk-android-trace-coroutines/src/main/kotlin/com/datadog/android/trace/coroutines/CoroutineScopeSpan.kt +++ b/integrations/dd-sdk-android-trace-coroutines/src/main/kotlin/com/datadog/android/trace/coroutines/CoroutineScopeSpan.kt @@ -6,10 +6,10 @@ package com.datadog.android.trace.coroutines -import io.opentracing.Span +import com.datadog.android.trace.api.span.DatadogSpan import kotlinx.coroutines.CoroutineScope /** - * An object that implements both [Span] and [CoroutineScope]. + * An object that implements both [DatadogSpan] and [CoroutineScope]. */ -interface CoroutineScopeSpan : CoroutineScope, Span +interface CoroutineScopeSpan : CoroutineScope, DatadogSpan diff --git a/integrations/dd-sdk-android-trace-coroutines/src/main/kotlin/com/datadog/android/trace/internal/coroutines/CoroutineScopeSpanImpl.kt b/integrations/dd-sdk-android-trace-coroutines/src/main/kotlin/com/datadog/android/trace/internal/coroutines/CoroutineScopeSpanImpl.kt index 368ef73b92..9345032f18 100644 --- a/integrations/dd-sdk-android-trace-coroutines/src/main/kotlin/com/datadog/android/trace/internal/coroutines/CoroutineScopeSpanImpl.kt +++ b/integrations/dd-sdk-android-trace-coroutines/src/main/kotlin/com/datadog/android/trace/internal/coroutines/CoroutineScopeSpanImpl.kt @@ -6,13 +6,13 @@ package com.datadog.android.trace.internal.coroutines +import com.datadog.android.trace.api.span.DatadogSpan import com.datadog.android.trace.coroutines.CoroutineScopeSpan -import io.opentracing.Span import kotlinx.coroutines.CoroutineScope internal class CoroutineScopeSpanImpl( private val scope: CoroutineScope, - private val span: Span + private val span: DatadogSpan ) : CoroutineScopeSpan, CoroutineScope by scope, - Span by span + DatadogSpan by span diff --git a/integrations/dd-sdk-android-trace-coroutines/src/main/kotlin/com/datadog/android/trace/internal/coroutines/InternalCoroutineExt.kt b/integrations/dd-sdk-android-trace-coroutines/src/main/kotlin/com/datadog/android/trace/internal/coroutines/InternalCoroutineExt.kt index 3b25c24d72..ff05e1d0f2 100644 --- a/integrations/dd-sdk-android-trace-coroutines/src/main/kotlin/com/datadog/android/trace/internal/coroutines/InternalCoroutineExt.kt +++ b/integrations/dd-sdk-android-trace-coroutines/src/main/kotlin/com/datadog/android/trace/internal/coroutines/InternalCoroutineExt.kt @@ -6,9 +6,9 @@ package com.datadog.android.trace.internal.coroutines +import com.datadog.android.trace.api.span.DatadogSpan import com.datadog.android.trace.coroutines.CoroutineScopeSpan import com.datadog.android.trace.withinSpan -import io.opentracing.Span import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers @@ -18,7 +18,7 @@ private const val TAG_DISPATCHER: String = "coroutine.dispatcher" internal suspend fun CoroutineScope.withinCoroutineSpan( operationName: String, - parentSpan: Span? = null, + parentSpan: DatadogSpan? = null, context: CoroutineContext, block: suspend CoroutineScopeSpan.() -> T ): T { diff --git a/integrations/dd-sdk-android-trace-coroutines/src/test/kotlin/com/datadog/android/trace/coroutines/CoroutineExtTest.kt b/integrations/dd-sdk-android-trace-coroutines/src/test/kotlin/com/datadog/android/trace/coroutines/CoroutineExtTest.kt index 7327b9b747..25db9cf360 100644 --- a/integrations/dd-sdk-android-trace-coroutines/src/test/kotlin/com/datadog/android/trace/coroutines/CoroutineExtTest.kt +++ b/integrations/dd-sdk-android-trace-coroutines/src/test/kotlin/com/datadog/android/trace/coroutines/CoroutineExtTest.kt @@ -6,13 +6,13 @@ package com.datadog.android.trace.coroutines -import com.datadog.tools.unit.setStaticValue +import com.datadog.android.trace.GlobalDatadogTracer +import com.datadog.android.trace.api.scope.DatadogScope +import com.datadog.android.trace.api.span.DatadogSpan +import com.datadog.android.trace.api.span.DatadogSpanBuilder +import com.datadog.android.trace.api.tracer.DatadogTracer import fr.xgouchet.elmyr.annotation.StringForgery import fr.xgouchet.elmyr.junit5.ForgeExtension -import io.opentracing.Scope -import io.opentracing.Span -import io.opentracing.Tracer -import io.opentracing.util.GlobalTracer import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.MainScope import kotlinx.coroutines.async @@ -42,26 +42,26 @@ import org.mockito.quality.Strictness class CoroutineExtTest { @Mock - lateinit var mockTracer: Tracer + lateinit var mockTracer: DatadogTracer @Mock - lateinit var mockSpanBuilder: Tracer.SpanBuilder + lateinit var mockSpanBuilder: DatadogSpanBuilder @Mock - lateinit var mockSpan: Span + lateinit var mockSpan: DatadogSpan @Mock - lateinit var mockParentSpan: Span + lateinit var mockParentSpan: DatadogSpan @Mock - lateinit var mockScope: Scope + lateinit var mockScope: DatadogScope @StringForgery lateinit var fakeOperationName: String @BeforeEach fun `set up`() { - GlobalTracer.registerIfAbsent(mockTracer) + GlobalDatadogTracer.registerIfAbsent(mockTracer) whenever(mockTracer.buildSpan(fakeOperationName)) doReturn mockSpanBuilder whenever(mockTracer.activateSpan(mockSpan)) doReturn mockScope whenever(mockSpanBuilder.start()) doReturn mockSpan @@ -69,7 +69,7 @@ class CoroutineExtTest { @AfterEach fun `tear down`() { - GlobalTracer::class.java.setStaticValue("isRegistered", false) + GlobalDatadogTracer.clear() } @Test @@ -77,7 +77,7 @@ class CoroutineExtTest { // Given var lambdaCalled = false whenever(mockTracer.activeSpan()) doReturn mockParentSpan - whenever(mockSpanBuilder.asChildOf(mockParentSpan)) doReturn mockSpanBuilder + whenever(mockSpanBuilder.withParentSpan(mockParentSpan)) doReturn mockSpanBuilder // When MainScope().launchTraced( @@ -90,7 +90,7 @@ class CoroutineExtTest { // Then assertThat(lambdaCalled).isTrue() - verify(mockSpanBuilder).asChildOf(mockParentSpan) + verify(mockSpanBuilder).withParentSpan(mockParentSpan) inOrder(mockSpan, mockScope) { verify(mockSpan).finish() verify(mockScope).close() @@ -102,7 +102,7 @@ class CoroutineExtTest { // Given var lambdaCalled = false whenever(mockTracer.activeSpan()) doReturn null - whenever(mockSpanBuilder.asChildOf(null as Span?)) doReturn mockSpanBuilder + whenever(mockSpanBuilder.withParentSpan(null as DatadogSpan?)) doReturn mockSpanBuilder // When MainScope().launchTraced( @@ -115,7 +115,7 @@ class CoroutineExtTest { // Then assertThat(lambdaCalled).isTrue() - verify(mockSpanBuilder).asChildOf(null as Span?) + verify(mockSpanBuilder).withParentSpan(null as DatadogSpan?) inOrder(mockSpan, mockScope) { verify(mockSpan).finish() verify(mockScope).close() @@ -127,7 +127,7 @@ class CoroutineExtTest { // Given var lambdaCalled = false whenever(mockTracer.activeSpan()) doReturn mockParentSpan - whenever(mockSpanBuilder.asChildOf(mockParentSpan)) doReturn mockSpanBuilder + whenever(mockSpanBuilder.withParentSpan(mockParentSpan)) doReturn mockSpanBuilder // When MainScope().launchTraced( @@ -140,7 +140,7 @@ class CoroutineExtTest { // Then assertThat(lambdaCalled).isTrue() - verify(mockSpanBuilder).asChildOf(mockParentSpan) + verify(mockSpanBuilder).withParentSpan(mockParentSpan) verify(mockTracer, never()).activateSpan(mockSpan) verify(mockSpan).finish() } @@ -150,7 +150,7 @@ class CoroutineExtTest { // Given var lambdaCalled = false whenever(mockTracer.activeSpan()) doReturn mockParentSpan - whenever(mockSpanBuilder.asChildOf(mockParentSpan)) doReturn mockSpanBuilder + whenever(mockSpanBuilder.withParentSpan(mockParentSpan)) doReturn mockSpanBuilder // When runBlocking { @@ -165,7 +165,7 @@ class CoroutineExtTest { // Then assertThat(lambdaCalled).isTrue() - verify(mockSpanBuilder).asChildOf(mockParentSpan) + verify(mockSpanBuilder).withParentSpan(mockParentSpan) inOrder(mockSpan, mockScope) { verify(mockSpan).finish() verify(mockScope).close() @@ -177,7 +177,7 @@ class CoroutineExtTest { // Given var lambdaCalled = false whenever(mockTracer.activeSpan()) doReturn null - whenever(mockSpanBuilder.asChildOf(null as Span?)) doReturn mockSpanBuilder + whenever(mockSpanBuilder.withParentSpan(null as DatadogSpan?)) doReturn mockSpanBuilder // When runBlocking { @@ -192,7 +192,7 @@ class CoroutineExtTest { // Then assertThat(lambdaCalled).isTrue() - verify(mockSpanBuilder).asChildOf(null as Span?) + verify(mockSpanBuilder).withParentSpan(null as DatadogSpan?) inOrder(mockSpan, mockScope) { verify(mockSpan).finish() verify(mockScope).close() @@ -204,7 +204,7 @@ class CoroutineExtTest { // Given var lambdaCalled = false whenever(mockTracer.activeSpan()) doReturn mockParentSpan - whenever(mockSpanBuilder.asChildOf(mockParentSpan)) doReturn mockSpanBuilder + whenever(mockSpanBuilder.withParentSpan(mockParentSpan)) doReturn mockSpanBuilder // When runBlocking { @@ -219,7 +219,7 @@ class CoroutineExtTest { // Then assertThat(lambdaCalled).isTrue() - verify(mockSpanBuilder).asChildOf(mockParentSpan) + verify(mockSpanBuilder).withParentSpan(mockParentSpan) verify(mockTracer, never()).activateSpan(mockSpan) verify(mockSpan).finish() } @@ -229,7 +229,7 @@ class CoroutineExtTest { // Given var lambdaCalled = false whenever(mockTracer.activeSpan()) doReturn mockParentSpan - whenever(mockSpanBuilder.asChildOf(mockParentSpan)) doReturn mockSpanBuilder + whenever(mockSpanBuilder.withParentSpan(mockParentSpan)) doReturn mockSpanBuilder // When runBlockingTraced(fakeOperationName, Dispatchers.Default) { @@ -241,7 +241,7 @@ class CoroutineExtTest { // Then assertThat(lambdaCalled).isTrue() - verify(mockSpanBuilder).asChildOf(mockParentSpan) + verify(mockSpanBuilder).withParentSpan(mockParentSpan) inOrder(mockSpan, mockScope) { verify(mockSpan).finish() verify(mockScope).close() @@ -253,7 +253,7 @@ class CoroutineExtTest { // Given var lambdaCalled = false whenever(mockTracer.activeSpan()) doReturn null - whenever(mockSpanBuilder.asChildOf(null as Span?)) doReturn mockSpanBuilder + whenever(mockSpanBuilder.withParentSpan(null as DatadogSpan?)) doReturn mockSpanBuilder // When runBlockingTraced(fakeOperationName, Dispatchers.Default) { @@ -265,7 +265,7 @@ class CoroutineExtTest { // Then assertThat(lambdaCalled).isTrue() - verify(mockSpanBuilder).asChildOf(null as Span?) + verify(mockSpanBuilder).withParentSpan(null as DatadogSpan?) inOrder(mockSpan, mockScope) { verify(mockSpan).finish() verify(mockScope).close() @@ -277,7 +277,7 @@ class CoroutineExtTest { // Given var lambdaCalled = false whenever(mockTracer.activeSpan()) doReturn mockParentSpan - whenever(mockSpanBuilder.asChildOf(mockParentSpan)) doReturn mockSpanBuilder + whenever(mockSpanBuilder.withParentSpan(mockParentSpan)) doReturn mockSpanBuilder // When runBlockingTraced(fakeOperationName, Dispatchers.Unconfined) { @@ -289,7 +289,7 @@ class CoroutineExtTest { // Then assertThat(lambdaCalled).isTrue() - verify(mockSpanBuilder).asChildOf(mockParentSpan) + verify(mockSpanBuilder).withParentSpan(mockParentSpan) verify(mockTracer, never()).activateSpan(mockSpan) verify(mockSpan).finish() } @@ -302,7 +302,7 @@ class CoroutineExtTest { var lambdaCalled = false var result: String? = null whenever(mockTracer.activeSpan()) doReturn mockParentSpan - whenever(mockSpanBuilder.asChildOf(mockParentSpan)) doReturn mockSpanBuilder + whenever(mockSpanBuilder.withParentSpan(mockParentSpan)) doReturn mockSpanBuilder // When runBlocking { @@ -320,7 +320,7 @@ class CoroutineExtTest { // Then assertThat(lambdaCalled).isTrue() assertThat(result).isEqualTo(data) - verify(mockSpanBuilder).asChildOf(mockParentSpan) + verify(mockSpanBuilder).withParentSpan(mockParentSpan) inOrder(mockSpan, mockScope) { verify(mockSpan).finish() verify(mockScope).close() @@ -335,7 +335,7 @@ class CoroutineExtTest { var lambdaCalled = false var result: String? = null whenever(mockTracer.activeSpan()) doReturn null - whenever(mockSpanBuilder.asChildOf(null as Span?)) doReturn mockSpanBuilder + whenever(mockSpanBuilder.withParentSpan(null as DatadogSpan?)) doReturn mockSpanBuilder // When runBlocking { @@ -353,7 +353,7 @@ class CoroutineExtTest { // Then assertThat(lambdaCalled).isTrue() assertThat(result).isEqualTo(data) - verify(mockSpanBuilder).asChildOf(null as Span?) + verify(mockSpanBuilder).withParentSpan(null as DatadogSpan?) inOrder(mockSpan, mockScope) { verify(mockSpan).finish() verify(mockScope).close() @@ -368,7 +368,7 @@ class CoroutineExtTest { var lambdaCalled = false var result: String? = null whenever(mockTracer.activeSpan()) doReturn mockParentSpan - whenever(mockSpanBuilder.asChildOf(mockParentSpan)) doReturn mockSpanBuilder + whenever(mockSpanBuilder.withParentSpan(mockParentSpan)) doReturn mockSpanBuilder // When runBlocking { @@ -387,7 +387,7 @@ class CoroutineExtTest { // Then assertThat(lambdaCalled).isTrue() assertThat(result).isEqualTo(data) - verify(mockSpanBuilder).asChildOf(mockParentSpan) + verify(mockSpanBuilder).withParentSpan(mockParentSpan) verify(mockTracer, never()).activateSpan(mockSpan) verify(mockSpan).finish() } @@ -400,7 +400,7 @@ class CoroutineExtTest { var lambdaCalled = false var result: String? = null whenever(mockTracer.activeSpan()) doReturn mockParentSpan - whenever(mockSpanBuilder.asChildOf(mockParentSpan)) doReturn mockSpanBuilder + whenever(mockSpanBuilder.withParentSpan(mockParentSpan)) doReturn mockSpanBuilder // When runBlocking { @@ -415,7 +415,7 @@ class CoroutineExtTest { // Then assertThat(lambdaCalled).isTrue() assertThat(result).isEqualTo(data) - verify(mockSpanBuilder).asChildOf(mockParentSpan) + verify(mockSpanBuilder).withParentSpan(mockParentSpan) verify(mockTracer, never()).activateSpan(mockSpan) verify(mockSpan).finish() } @@ -428,7 +428,7 @@ class CoroutineExtTest { var lambdaCalled = false var result: String? = null whenever(mockTracer.activeSpan()) doReturn null - whenever(mockSpanBuilder.asChildOf(null as Span?)) doReturn mockSpanBuilder + whenever(mockSpanBuilder.withParentSpan(null as DatadogSpan?)) doReturn mockSpanBuilder // When runBlocking { @@ -443,7 +443,7 @@ class CoroutineExtTest { // Then assertThat(lambdaCalled).isTrue() assertThat(result).isEqualTo(data) - verify(mockSpanBuilder).asChildOf(null as Span?) + verify(mockSpanBuilder).withParentSpan(null as DatadogSpan?) verify(mockTracer, never()).activateSpan(mockSpan) verify(mockSpan).finish() } @@ -456,7 +456,7 @@ class CoroutineExtTest { var lambdaCalled = false var result: String? = null whenever(mockTracer.activeSpan()) doReturn mockParentSpan - whenever(mockSpanBuilder.asChildOf(mockParentSpan)) doReturn mockSpanBuilder + whenever(mockSpanBuilder.withParentSpan(mockParentSpan)) doReturn mockSpanBuilder // When runBlocking { @@ -472,7 +472,7 @@ class CoroutineExtTest { // Then assertThat(lambdaCalled).isTrue() assertThat(result).isEqualTo(data) - verify(mockSpanBuilder).asChildOf(mockParentSpan) + verify(mockSpanBuilder).withParentSpan(mockParentSpan) verify(mockTracer, never()).activateSpan(mockSpan) verify(mockSpan).finish() } diff --git a/integrations/dd-sdk-android-trace-coroutines/transitiveDependencies b/integrations/dd-sdk-android-trace-coroutines/transitiveDependencies index 723731b27b..0df1f1378e 100644 --- a/integrations/dd-sdk-android-trace-coroutines/transitiveDependencies +++ b/integrations/dd-sdk-android-trace-coroutines/transitiveDependencies @@ -1,8 +1,5 @@ Dependencies List -io.opentracing:opentracing-api:0.32.0 : 18 Kb -io.opentracing:opentracing-noop:0.32.0 : 10 Kb -io.opentracing:opentracing-util:0.32.0 : 10 Kb org.jetbrains.kotlin:kotlin-stdlib:2.0.21 : 1706 Kb org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.4.2 : 1635 Kb org.jetbrains:annotations:13.0 : 17 Kb diff --git a/reliability/core-it/src/androidTest/kotlin/com/datadog/android/core/integration/tests/SdkCoreTest.kt b/reliability/core-it/src/androidTest/kotlin/com/datadog/android/core/integration/tests/SdkCoreTest.kt index 0b9fa55159..e04e3b84de 100644 --- a/reliability/core-it/src/androidTest/kotlin/com/datadog/android/core/integration/tests/SdkCoreTest.kt +++ b/reliability/core-it/src/androidTest/kotlin/com/datadog/android/core/integration/tests/SdkCoreTest.kt @@ -27,6 +27,7 @@ import fr.xgouchet.elmyr.jvm.useJvmFactories import org.assertj.core.api.Assertions.assertThat import org.junit.After import org.junit.Before +import org.junit.Ignore import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith @@ -123,17 +124,18 @@ class SdkCoreTest : MockServerTest() { } @Test + @Ignore("RUM-11016 disabling because it's flaky") fun must_clearUserInformation_when_clearUserInfo() { // Given - testedSdkCore?.setUserInfo(fakeUserId, fakeUserName, fakeUserEmail, fakeUserAdditionalProperties) + testedSdkCore.setUserInfo(fakeUserId, fakeUserName, fakeUserEmail, fakeUserAdditionalProperties) // When - testedSdkCore?.clearUserInfo() + testedSdkCore.clearUserInfo() // Then val countDownLatch = CountDownLatch(1) var readUserInfo: UserInfo? = null - featureSdkCore?.getFeature(stubFeature.name)?.withWriteContext { datadogContext, _ -> + featureSdkCore.getFeature(stubFeature.name)?.withWriteContext { datadogContext, _ -> readUserInfo = datadogContext.userInfo countDownLatch.countDown() } diff --git a/reliability/single-fit/okhttp/build.gradle.kts b/reliability/single-fit/okhttp/build.gradle.kts index a6dd124741..c4e242e0e0 100644 --- a/reliability/single-fit/okhttp/build.gradle.kts +++ b/reliability/single-fit/okhttp/build.gradle.kts @@ -45,6 +45,7 @@ dependencies { } testImplementation(project(":dd-sdk-android-internal")) testImplementation(testFixtures(project(":dd-sdk-android-core"))) + testImplementation(testFixtures(project(":features:dd-sdk-android-trace"))) testImplementation(project(":reliability:stub-core")) testImplementation(libs.bundles.jUnit5) testImplementation(libs.bundles.testTools) diff --git a/reliability/single-fit/okhttp/src/test/kotlin/com/datadog/android/okhttp/HeadBasedSamplingTest.kt b/reliability/single-fit/okhttp/src/test/kotlin/com/datadog/android/okhttp/HeadBasedSamplingTest.kt index fc69305472..34aca4bedd 100644 --- a/reliability/single-fit/okhttp/src/test/kotlin/com/datadog/android/okhttp/HeadBasedSamplingTest.kt +++ b/reliability/single-fit/okhttp/src/test/kotlin/com/datadog/android/okhttp/HeadBasedSamplingTest.kt @@ -15,27 +15,25 @@ import com.datadog.android.okhttp.otel.addParentSpan import com.datadog.android.okhttp.tests.assertj.SpansPayloadAssert import com.datadog.android.okhttp.tests.elmyr.OkHttpConfigurator import com.datadog.android.okhttp.trace.TracingInterceptor -import com.datadog.android.okhttp.trace.parentSpan import com.datadog.android.tests.ktx.getString -import com.datadog.android.trace.AndroidTracer +import com.datadog.android.trace.DatadogTracing +import com.datadog.android.trace.GlobalDatadogTracer import com.datadog.android.trace.Trace import com.datadog.android.trace.TraceConfiguration import com.datadog.android.trace.TracingHeaderType +import com.datadog.android.trace.api.DatadogTracingConstants +import com.datadog.android.trace.api.span.DatadogSpan +import com.datadog.android.trace.internal.DatadogTracingToolkit import com.datadog.android.trace.opentelemetry.OtelTracerProvider -import com.datadog.legacy.trace.api.sampling.PrioritySampling import com.datadog.tools.unit.extensions.TestConfigurationExtension import com.datadog.tools.unit.getFieldValue import com.datadog.tools.unit.getStaticValue -import com.datadog.tools.unit.setStaticValue import com.google.gson.JsonObject import com.google.gson.JsonParser import fr.xgouchet.elmyr.Forge import fr.xgouchet.elmyr.annotation.StringForgery import fr.xgouchet.elmyr.junit5.ForgeConfiguration import fr.xgouchet.elmyr.junit5.ForgeExtension -import io.opentracing.propagation.Format -import io.opentracing.propagation.TextMapInject -import io.opentracing.util.GlobalTracer import okhttp3.OkHttpClient import okhttp3.Request import okhttp3.mockwebserver.MockResponse @@ -81,6 +79,11 @@ class HeadBasedSamplingTest { mockServer.shutdown() } + private fun Request.Builder.parentSpan(span: DatadogSpan): Request.Builder { + tag(DatadogSpan::class.java, span) + return this + } + @Test fun `M use network sampling rate W call is made { no parent context }`(forge: Forge) { // Given @@ -135,7 +138,7 @@ class HeadBasedSamplingTest { leastSignificantTraceId.toLong().toHexString().padStart(16, '0') ) hasMostSignificant64BitsTraceId(mostSignificantTraceId) - hasSpanId(spanId.toLong().toHexString()) + hasSpanId(spanId.cleanDecimalFormat()) hasVersion(stubSdkCore.getDatadogContext().version) hasSource(stubSdkCore.getDatadogContext().source) hasTracerVersion(stubSdkCore.getDatadogContext().sdkVersion) @@ -197,7 +200,7 @@ class HeadBasedSamplingTest { } @Test - fun `M respect parent sampling decision W call is made { parent context = OpenTracing Span, parent not sampled }`( + fun `M respect parent sampling decision W call is made {parent context DatadogTracing Span, parent not sampled}`( @StringForgery fakeSpanName: String ) { // Given @@ -217,7 +220,7 @@ class HeadBasedSamplingTest { ) .build() - val parentSpan = GlobalTracer.get() + val parentSpan = GlobalDatadogTracer.get() .buildSpan(fakeSpanName) .start() @@ -291,7 +294,7 @@ class HeadBasedSamplingTest { } @Test - fun `M respect parent sampling decision W call is made { parent context = OpenTracing Span, parent sampled }`( + fun `M respect parent sampling decision W call is made { parent context = DatadogTracing Span, parent sampled }`( @StringForgery fakeSpanName: String ) { // Given @@ -311,7 +314,7 @@ class HeadBasedSamplingTest { ) .build() - val parentSpan = GlobalTracer.get() + val parentSpan = GlobalDatadogTracer.get() .buildSpan(fakeSpanName) .start() @@ -348,17 +351,13 @@ class HeadBasedSamplingTest { leastSignificantTraceId.toLong().toHexString().padStart(16, '0') ) hasMostSignificant64BitsTraceId(mostSignificantTraceId) - hasParentId("0") + hasParentId("0000000000000000") hasAgentPsr(1.0) - hasSamplingPriority(PrioritySampling.SAMPLER_KEEP) + hasSamplingPriority(DatadogTracingConstants.PrioritySampling.SAMPLER_KEEP) hasGenericMetricValue("_top_level", 1) } - val localSpanId = localSpanPayload.getAsJsonArray("spans") - .first() - .asJsonObject - .getString("span_id") - .orEmpty() + val localSpanId = getLocalSpanId(localSpanPayload) val okHttpSpanPayload = JsonParser.parseString(eventsWritten[1].eventData) as JsonObject SpansPayloadAssert.assertThat(okHttpSpanPayload) @@ -368,8 +367,8 @@ class HeadBasedSamplingTest { leastSignificantTraceId.toLong().toHexString().padStart(16, '0') ) hasMostSignificant64BitsTraceId(mostSignificantTraceId) - hasSpanId(spanId.toLong().toHexString()) - hasParentId(localSpanId) + hasSpanId(spanId.cleanDecimalFormat()) + hasParentId(localSpanId.cleanHexFormat()) hasVersion(stubSdkCore.getDatadogContext().version) hasSource(stubSdkCore.getDatadogContext().source) hasTracerVersion(stubSdkCore.getDatadogContext().sdkVersion) @@ -451,15 +450,11 @@ class HeadBasedSamplingTest { hasParentId("0000000000000000") // OpenTelemetry span will have _dd.rule_psr instead of _dd.agent_psr hasRulePsr(1.0) - hasSamplingPriority(PrioritySampling.USER_KEEP) + hasSamplingPriority(DatadogTracingConstants.PrioritySampling.USER_KEEP) hasGenericMetricValue("_top_level", 1) } - val localSpanId = localSpanPayload.getAsJsonArray("spans") - .first() - .asJsonObject - .getString("span_id") - .orEmpty() + val localSpanId = getLocalSpanId(localSpanPayload) val okHttpSpanPayload = JsonParser.parseString(eventsWritten[0].eventData) as JsonObject SpansPayloadAssert.assertThat(okHttpSpanPayload) @@ -469,9 +464,8 @@ class HeadBasedSamplingTest { leastSignificantTraceId.toLong().toHexString().padStart(16, '0') ) hasMostSignificant64BitsTraceId(mostSignificantTraceId) - hasSpanId(spanId.toLong().toHexString()) - // OpenTelemetry Span IDs are always padded with 0 - hasParentId(localSpanId.dropWhile { it == '0' }) + hasSpanId(spanId.cleanDecimalFormat()) + hasParentId(localSpanId.cleanHexFormat()) hasVersion(stubSdkCore.getDatadogContext().version) hasSource(stubSdkCore.getDatadogContext().source) hasTracerVersion(stubSdkCore.getDatadogContext().sdkVersion) @@ -479,7 +473,7 @@ class HeadBasedSamplingTest { hasName("okhttp.request") hasResource("http://${mockServer.hostName}:${mockServer.port}/") hasNoAgentPsr() - hasSamplingPriority(PrioritySampling.USER_KEEP) + hasSamplingPriority(DatadogTracingConstants.PrioritySampling.USER_KEEP) hasNoGenericMetric("_top_level") hasSpanKind("client") hasHttpMethod("GET") @@ -509,7 +503,7 @@ class HeadBasedSamplingTest { ) .build() - val parentSpan = GlobalTracer.get() + val parentSpan = GlobalDatadogTracer.get() .buildSpan(fakeSpanName) .start() @@ -518,11 +512,10 @@ class HeadBasedSamplingTest { Request.Builder() .url(mockServer.url("/")) .apply { - GlobalTracer.get().inject( - parentSpan.context(), - Format.Builtin.TEXT_MAP_INJECT, - TextMapInject { key, value -> addHeader(key, value) } - ) + GlobalDatadogTracer.get().propagate().inject( + context = parentSpan.context(), + carrier = this + ) { carrier, key, value -> carrier.addHeader(key, value) } } .build() ).execute() @@ -560,7 +553,7 @@ class HeadBasedSamplingTest { ) .build() - val parentSpan = GlobalTracer.get() + val parentSpan = GlobalDatadogTracer.get() .buildSpan(fakeSpanName) .start() @@ -569,11 +562,11 @@ class HeadBasedSamplingTest { Request.Builder() .url(mockServer.url("/")) .apply { - GlobalTracer.get().inject( - parentSpan.context(), - Format.Builtin.TEXT_MAP_INJECT, - TextMapInject { key, value -> addHeader(key, value) } - ) + GlobalDatadogTracer.get() + .propagate() + .inject(parentSpan.context(), this) { _, key, value -> + addHeader(key, value) + } } .build() ).execute() @@ -603,17 +596,13 @@ class HeadBasedSamplingTest { leastSignificantTraceId.toLong().toHexString().padStart(16, '0') ) hasMostSignificant64BitsTraceId(mostSignificantTraceId) - hasParentId("0") + hasParentId("0000000000000000") hasAgentPsr(1.0) - hasSamplingPriority(PrioritySampling.SAMPLER_KEEP) + hasSamplingPriority(DatadogTracingConstants.PrioritySampling.SAMPLER_KEEP) hasGenericMetricValue("_top_level", 1) } - val localSpanId = localSpanPayload.getAsJsonArray("spans") - .first() - .asJsonObject - .getString("span_id") - .orEmpty() + val localSpanId = getLocalSpanId(localSpanPayload) val okHttpSpanPayload = JsonParser.parseString(eventsWritten[0].eventData) as JsonObject SpansPayloadAssert.assertThat(okHttpSpanPayload) @@ -623,8 +612,8 @@ class HeadBasedSamplingTest { leastSignificantTraceId.toLong().toHexString().padStart(16, '0') ) hasMostSignificant64BitsTraceId(mostSignificantTraceId) - hasSpanId(spanId.toLong().toHexString()) - hasParentId(localSpanId) + hasSpanId(spanId.cleanDecimalFormat()) + hasParentId(localSpanId.cleanHexFormat()) hasVersion(stubSdkCore.getDatadogContext().version) hasSource(stubSdkCore.getDatadogContext().source) hasTracerVersion(stubSdkCore.getDatadogContext().sdkVersion) @@ -643,29 +632,44 @@ class HeadBasedSamplingTest { } } + private fun getLocalSpanId(localSpanPayload: JsonObject): String { + return localSpanPayload.getAsJsonArray("spans") + .first() + .asJsonObject + .getString("span_id") + .orEmpty() + } + private fun registerGlobalTracer(sampleRate: Double) { - GlobalTracer.registerIfAbsent( - AndroidTracer.Builder(stubSdkCore) - .setTracingHeaderTypes(setOf(TracingHeaderType.DATADOG)) + GlobalDatadogTracer.registerIfAbsent( + DatadogTracing.newTracerBuilder(stubSdkCore) + .withTracingHeadersTypes(setOf(TracingHeaderType.DATADOG)) // this is on purpose, we want to make sure that it is not taken into account - .setSampleRate(sampleRate) + .withSampleRate(sampleRate) .build() ) } private fun unregisterGlobalTracer() { - GlobalTracer::class.java.setStaticValue("isRegistered", false) + GlobalDatadogTracer.clear() } private fun String.toTags(): Map = split(",") .associate { it.split("=").let { it[0] to it[1] } } + private fun String.cleanHexFormat(): String = with(DatadogTracingToolkit.spanIdConverter) { + toHexStringPadded(fromHex(this@cleanHexFormat)) + } + + private fun String.cleanDecimalFormat(): String = with(DatadogTracingToolkit.spanIdConverter) { + toHexStringPadded(this@cleanDecimalFormat.toLong()) + } + companion object { - private const val DATADOG_TRACE_ID_HEADER = "x-datadog-trace-id" private const val DATADOG_TAGS_HEADER = "x-datadog-tags" + private const val DATADOG_TRACE_ID_HEADER = "x-datadog-trace-id" private const val DATADOG_SPAN_ID_HEADER = "x-datadog-parent-id" private const val DATADOG_SAMPLING_PRIORITY_HEADER = "x-datadog-sampling-priority" - private const val MOST_SIGNIFICANT_TRACE_ID_KEY = "_dd.p.tid" } } diff --git a/reliability/single-fit/okhttp/src/test/kotlin/com/datadog/android/okhttp/SpanExtIntegrationTest.kt b/reliability/single-fit/okhttp/src/test/kotlin/com/datadog/android/okhttp/SpanExtIntegrationTest.kt new file mode 100644 index 0000000000..cf7bfe7750 --- /dev/null +++ b/reliability/single-fit/okhttp/src/test/kotlin/com/datadog/android/okhttp/SpanExtIntegrationTest.kt @@ -0,0 +1,274 @@ +/* + * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. + * This product includes software developed at Datadog (https://www.datadoghq.com/). + * Copyright 2016-Present Datadog, Inc. + */ +package com.datadog.android.okhttp + +import com.datadog.android.Datadog +import com.datadog.android.api.SdkCore +import com.datadog.android.api.feature.Feature +import com.datadog.android.core.stub.StubEvent +import com.datadog.android.core.stub.StubSDKCore +import com.datadog.android.okhttp.tests.assertj.SpansPayloadAssert +import com.datadog.android.okhttp.tests.elmyr.OkHttpConfigurator +import com.datadog.android.trace.DatadogTracing +import com.datadog.android.trace.GlobalDatadogTracer +import com.datadog.android.trace.Trace +import com.datadog.android.trace.TraceConfiguration +import com.datadog.android.trace.api.span.DatadogSpan +import com.datadog.android.trace.internal.DatadogTracingToolkit +import com.datadog.android.trace.withinSpan +import com.datadog.tools.unit.extensions.TestConfigurationExtension +import com.datadog.tools.unit.getFieldValue +import com.datadog.tools.unit.getStaticValue +import com.google.gson.JsonArray +import com.google.gson.JsonObject +import com.google.gson.JsonParser +import fr.xgouchet.elmyr.Forge +import fr.xgouchet.elmyr.junit5.ForgeConfiguration +import fr.xgouchet.elmyr.junit5.ForgeExtension +import okhttp3.mockwebserver.MockResponse +import okhttp3.mockwebserver.MockWebServer +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.AfterEach +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith +import org.junit.jupiter.api.extension.Extensions +import org.mockito.junit.jupiter.MockitoExtension +import org.mockito.junit.jupiter.MockitoSettings +import org.mockito.quality.Strictness + +@Extensions( + ExtendWith(MockitoExtension::class), + ExtendWith(ForgeExtension::class), + ExtendWith(TestConfigurationExtension::class) +) +@ForgeConfiguration(OkHttpConfigurator::class) +@MockitoSettings(strictness = Strictness.LENIENT) +class SpanExtIntegrationTest { + private lateinit var stubSdkCore: StubSDKCore + private lateinit var mockServer: MockWebServer + + private val expectedEnv: String get() = stubSdkCore.getDatadogContext().env + + private val JsonObject.spanId: String + get() = get("span_id").asString + + private val JsonObject.spans: JsonArray + get() = getAsJsonArray("spans") + + private fun JsonArray.getObject(index: Int) = get(index).asJsonObject + private fun StubEvent.asJson(): JsonObject = JsonParser.parseString(eventData).asJsonObject + private fun DatadogSpan.getSpanId(): String = DatadogTracingToolkit.spanIdConverter + .toHexStringPadded(context().spanId) + private fun registerTracer( + sampleRate: Double? = null, + partialFlushMinSpans: Int? = null + ) = GlobalDatadogTracer.registerIfAbsent( + DatadogTracing.newTracerBuilder(stubSdkCore) + .also { + if (sampleRate != null) it.withSampleRate(sampleRate) + if (partialFlushMinSpans != null) it.withPartialFlushMinSpans(partialFlushMinSpans) + } + .build() + ) + + @BeforeEach + fun `set up`(forge: Forge) { + stubSdkCore = StubSDKCore(forge) + val registry: Any = Datadog::class.java.getStaticValue("registry") + val instances: MutableMap = registry.getFieldValue("instances") + instances += stubSdkCore.name to stubSdkCore + mockServer = MockWebServer() + + val fakeTraceConfiguration = TraceConfiguration.Builder().build() + Trace.enable(fakeTraceConfiguration, stubSdkCore) + + mockServer.enqueue(MockResponse()) + mockServer.start() + } + + @AfterEach + fun `tear down`() { + GlobalDatadogTracer.clear() + Datadog.stopInstance(stubSdkCore.name) + mockServer.shutdown() + } + + @Test + fun `M return expected events W withinSpan {two composite spans}`() { + registerTracer(partialFlushMinSpans = 1) + var isCalled = false + var rootSpanId = "" + var childSpanId = "" + withinSpan("rootSpanOperation") { + rootSpanId = getSpanId() + withinSpan("childSpanOperation") { + childSpanId = getSpanId() + isCalled = true + } + } + + val eventsWritten = stubSdkCore.eventsWritten(Feature.TRACING_FEATURE_NAME) + val rootSpanJson = eventsWritten[0].asJson() + val childSpanJson = eventsWritten[1].asJson() + + assertThat(isCalled).isTrue + SpansPayloadAssert.assertThat(rootSpanJson) + .hasEnv(expectedEnv) + .hasSpanAtIndexWith(0) { + hasSamplingPriority(1) + hasSpanId(rootSpanId) + hasResource("rootSpanOperation") + hasName("rootSpanOperation") + hasType("custom") + hasAgentPsr(1.0) + hasSamplingPriority(1) + hasValidMostSignificant64BitsTraceId() + hasValidLeastSignificant64BitsTraceId() + } + + SpansPayloadAssert.assertThat(childSpanJson) + .hasEnv(expectedEnv) + .hasSpanAtIndexWith(0) { + hasSpanId(childSpanId) + hasNoSamplingPriority() + hasParentId(rootSpanId) + hasResource("childSpanOperation") + hasName("childSpanOperation") + hasType("custom") + hasSpanKind("client") + hasNoAgentPsr() + hasNoSamplingPriority() + hasValidMostSignificant64BitsTraceId() + hasValidLeastSignificant64BitsTraceId() + } + } + + @Test + fun `M return expected events W withinSpan {three composite spans}`() { + registerTracer(partialFlushMinSpans = 1) + var isCalled = false + var rootSpanId = "" + var level1ChildSpanId = "" + var level2ChildSpanId = "" + withinSpan("rootSpanOperation") { + rootSpanId = getSpanId() + withinSpan("level1ChildSpanOperation") { + level1ChildSpanId = getSpanId() + withinSpan("level2ChildSpanOperation") { + isCalled = true + level2ChildSpanId = getSpanId() + } + } + } + + val eventsWritten = stubSdkCore.eventsWritten(Feature.TRACING_FEATURE_NAME) + val eventsJson = eventsWritten + .map { it.asJson() } + .associateBy { it.spans.getObject(0).spanId } + + val rootSpanJson = checkNotNull(eventsJson[rootSpanId]) + val child1Json = checkNotNull(eventsJson[level1ChildSpanId]) + val child2Json = checkNotNull(eventsJson[level2ChildSpanId]) + + assertThat(isCalled).isTrue + SpansPayloadAssert.assertThat(rootSpanJson) + .hasEnv(expectedEnv) + .hasSpanAtIndexWith(0) { + hasSamplingPriority(1) + hasSpanId(rootSpanId) + hasResource("rootSpanOperation") + hasName("rootSpanOperation") + hasType("custom") + hasAgentPsr(1.0) + hasSamplingPriority(1) + hasValidMostSignificant64BitsTraceId() + hasValidLeastSignificant64BitsTraceId() + } + + SpansPayloadAssert.assertThat(child1Json) + .hasEnv(expectedEnv) + .hasSpanAtIndexWith(0) { + hasSpanId(level1ChildSpanId) + hasNoSamplingPriority() + hasParentId(rootSpanId) + hasResource("level1ChildSpanOperation") + hasName("level1ChildSpanOperation") + hasType("custom") + hasSpanKind("client") + hasNoAgentPsr() + hasNoSamplingPriority() + hasValidMostSignificant64BitsTraceId() + hasValidLeastSignificant64BitsTraceId() + } + + SpansPayloadAssert.assertThat(child2Json) + .hasEnv(expectedEnv) + .hasSpanAtIndexWith(0) { + hasSpanId(level2ChildSpanId) + hasNoSamplingPriority() + hasParentId(level1ChildSpanId) + hasResource("level2ChildSpanOperation") + hasName("level2ChildSpanOperation") + hasType("custom") + hasSpanKind("client") + hasNoAgentPsr() + hasNoSamplingPriority() + hasValidMostSignificant64BitsTraceId() + hasValidLeastSignificant64BitsTraceId() + } + } + + @Test + fun `M return expected events W withinSpan {composite spans, explicit sample rate = 100}`() { + registerTracer(sampleRate = 100.0, partialFlushMinSpans = 1) + var isCalled = false + var rootSpanId = "" + var childSpanId = "" + withinSpan("rootSpanOperation") { + rootSpanId = getSpanId() + withinSpan("childSpanOperation") { + childSpanId = getSpanId() + isCalled = true + } + } + + val eventsWritten = stubSdkCore.eventsWritten(Feature.TRACING_FEATURE_NAME) + val rootSpanJson = eventsWritten[0].asJson() + val childSpanJson = eventsWritten[1].asJson() + + assertThat(isCalled).isTrue + SpansPayloadAssert.assertThat(rootSpanJson) + .hasEnv(expectedEnv) + .hasSpanAtIndexWith(0) { + hasSamplingPriority(1) + hasSpanId(rootSpanId) + hasResource("rootSpanOperation") + hasName("rootSpanOperation") + hasType("custom") + hasAgentPsr(1.0) + hasSamplingPriority(1) + hasValidMostSignificant64BitsTraceId() + hasValidLeastSignificant64BitsTraceId() + } + + SpansPayloadAssert.assertThat(childSpanJson) + .hasEnv(expectedEnv) + .hasSpanAtIndexWith(0) { + hasSpanId(childSpanId) + hasNoSamplingPriority() + hasParentId(rootSpanId) + hasResource("childSpanOperation") + hasName("childSpanOperation") + hasType("custom") + hasSpanKind("client") + hasNoAgentPsr() + hasNoSamplingPriority() + hasValidMostSignificant64BitsTraceId() + hasValidLeastSignificant64BitsTraceId() + } + } +} diff --git a/reliability/single-fit/okhttp/src/test/kotlin/com/datadog/android/okhttp/tests/assertj/SpansPayloadAssert.kt b/reliability/single-fit/okhttp/src/test/kotlin/com/datadog/android/okhttp/tests/assertj/SpansPayloadAssert.kt index fd71289a99..3490f093b2 100644 --- a/reliability/single-fit/okhttp/src/test/kotlin/com/datadog/android/okhttp/tests/assertj/SpansPayloadAssert.kt +++ b/reliability/single-fit/okhttp/src/test/kotlin/com/datadog/android/okhttp/tests/assertj/SpansPayloadAssert.kt @@ -108,6 +108,14 @@ internal class SpansPayloadAssert(actual: JsonObject) : return this } + fun hasType(type: String): SpanAssert { + val actualType = actualSpan.getString(TYPE_KEY) + assertThat(actualType).overridingErrorMessage( + "Expected type to be $type but was $actualType for index $index" + ).isEqualTo(type) + return this + } + fun hasResource(resource: String): SpanAssert { val actualResource = actualSpan.getString(RESOURCE_KEY) assertThat(actualResource).overridingErrorMessage( @@ -437,6 +445,7 @@ internal class SpansPayloadAssert(actual: JsonObject) : private const val SERVICE_KEY = "service" private const val ERROR_KEY = "error" private const val NAME_KEY = "name" + private const val TYPE_KEY = "type" private const val RESOURCE_KEY = "resource" private const val USR_KEY = "meta.usr" private const val USR_ID_KEY = "meta.usr.id" diff --git a/reliability/single-fit/trace/build.gradle.kts b/reliability/single-fit/trace/build.gradle.kts index 93cb9e2b18..d7ce17e36c 100644 --- a/reliability/single-fit/trace/build.gradle.kts +++ b/reliability/single-fit/trace/build.gradle.kts @@ -49,9 +49,11 @@ dependencies { ) } } + testImplementation(project(":reliability:stub-core")) testImplementation(project(":dd-sdk-android-internal")) + testImplementation(project(":features:dd-sdk-android-trace-internal")) testImplementation(testFixtures(project(":dd-sdk-android-core"))) - testImplementation(project(":reliability:stub-core")) + testImplementation(testFixtures(project(":features:dd-sdk-android-trace"))) testImplementation(libs.bundles.jUnit5) testImplementation(libs.bundles.testTools) testImplementation(libs.okHttp) diff --git a/reliability/single-fit/trace/src/test/kotlin/com/datadog/android/trace/integration/api/SpanExt.kt b/reliability/single-fit/trace/src/test/kotlin/com/datadog/android/trace/integration/api/SpanExt.kt new file mode 100644 index 0000000000..16b1f1aa67 --- /dev/null +++ b/reliability/single-fit/trace/src/test/kotlin/com/datadog/android/trace/integration/api/SpanExt.kt @@ -0,0 +1,33 @@ +/* + * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. + * This product includes software developed at Datadog (https://www.datadoghq.com/). + * Copyright 2016-Present Datadog, Inc. + */ + +package com.datadog.android.trace.integration.api + +import com.datadog.android.trace.api.span.DatadogSpan +import com.datadog.android.trace.internal.DatadogTracingToolkit + +/** + * Returns the span's least significant trace id in hex format (the last 64 bits from the 128 bits trace id) + */ +fun DatadogSpan.leastSignificant64BitsTraceId(): String { + return traceId.toHexString().padStart(32, '0').takeLast(16) +} + +/** + * Returns the span's most significant trace id in hex format (the first 64 bits from the 128 bits trace id) + */ +fun DatadogSpan.mostSignificant64BitsTraceId(): String { + return traceId.toHexString().padStart(32, '0').take(16) +} + +/** + * Returns the span's spanId in hex format. + * The [DatadogSpanContext.toSpanId] method returns a string in decimal format, + * which doesn't match what we send in our events + */ +fun DatadogSpan.spanIdAsHexString(): String { + return DatadogTracingToolkit.spanIdConverter.toHexStringPadded(context().spanId) +} diff --git a/reliability/single-fit/trace/src/test/kotlin/com/datadog/android/trace/integration/opentracing/TraceConfigurationTest.kt b/reliability/single-fit/trace/src/test/kotlin/com/datadog/android/trace/integration/api/TraceConfigurationTest.kt similarity index 96% rename from reliability/single-fit/trace/src/test/kotlin/com/datadog/android/trace/integration/opentracing/TraceConfigurationTest.kt rename to reliability/single-fit/trace/src/test/kotlin/com/datadog/android/trace/integration/api/TraceConfigurationTest.kt index 915df61c09..60f8ac1faa 100644 --- a/reliability/single-fit/trace/src/test/kotlin/com/datadog/android/trace/integration/opentracing/TraceConfigurationTest.kt +++ b/reliability/single-fit/trace/src/test/kotlin/com/datadog/android/trace/integration/api/TraceConfigurationTest.kt @@ -4,7 +4,7 @@ * Copyright 2016-Present Datadog, Inc. */ -package com.datadog.android.trace.integration.opentracing +package com.datadog.android.trace.integration.api import com.datadog.android.api.feature.Feature import com.datadog.android.api.feature.StorageBackedFeature @@ -14,14 +14,14 @@ import com.datadog.android.core.stub.StubSDKCore import com.datadog.android.tests.ktx.getInt import com.datadog.android.tests.ktx.getLong import com.datadog.android.tests.ktx.getString -import com.datadog.android.trace.AndroidTracer +import com.datadog.android.trace.DatadogTracing +import com.datadog.android.trace.GlobalDatadogTracer import com.datadog.android.trace.Trace import com.datadog.android.trace.TraceConfiguration import com.datadog.android.trace.event.SpanEventMapper import com.datadog.android.trace.integration.tests.elmyr.TraceIntegrationForgeConfigurator import com.datadog.android.trace.model.SpanEvent import com.datadog.tools.unit.extensions.TestConfigurationExtension -import com.datadog.tools.unit.setStaticValue import com.google.gson.JsonObject import com.google.gson.JsonParser import fr.xgouchet.elmyr.Forge @@ -29,7 +29,6 @@ import fr.xgouchet.elmyr.annotation.Forgery import fr.xgouchet.elmyr.annotation.StringForgery import fr.xgouchet.elmyr.junit5.ForgeConfiguration import fr.xgouchet.elmyr.junit5.ForgeExtension -import io.opentracing.util.GlobalTracer import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.BeforeEach @@ -66,7 +65,7 @@ class TraceConfigurationTest { @AfterEach fun `tear down`() { - GlobalTracer::class.java.setStaticValue("isRegistered", false) + GlobalDatadogTracer.clear() } @RepeatedTest(16) @@ -142,7 +141,7 @@ class TraceConfigurationTest { .setNetworkInfoEnabled(false) .build() Trace.enable(fakeTraceConfiguration, stubSdkCore) - val testedTracer = AndroidTracer.Builder(stubSdkCore).build() + val testedTracer = DatadogTracing.newTracerBuilder(stubSdkCore).build() // When var leastSignificantTraceId: String @@ -188,7 +187,7 @@ class TraceConfigurationTest { .setNetworkInfoEnabled(true) .build() Trace.enable(fakeTraceConfiguration, stubSdkCore) - val testedTracer = AndroidTracer.Builder(stubSdkCore).build() + val testedTracer = DatadogTracing.newTracerBuilder(stubSdkCore).build() // When var leastSignificantTraceId: String @@ -246,7 +245,7 @@ class TraceConfigurationTest { .setEventMapper(stubMapper) .build() Trace.enable(fakeTraceConfiguration, stubSdkCore) - val testedTracer = AndroidTracer.Builder(stubSdkCore).build() + val testedTracer = DatadogTracing.newTracerBuilder(stubSdkCore).build() // When val fullDuration = measureNanoTime { diff --git a/reliability/single-fit/trace/src/test/kotlin/com/datadog/android/trace/integration/opentracing/UtilitiesTest.kt b/reliability/single-fit/trace/src/test/kotlin/com/datadog/android/trace/integration/api/UtilitiesTest.kt similarity index 92% rename from reliability/single-fit/trace/src/test/kotlin/com/datadog/android/trace/integration/opentracing/UtilitiesTest.kt rename to reliability/single-fit/trace/src/test/kotlin/com/datadog/android/trace/integration/api/UtilitiesTest.kt index 3f4ab60e02..a501547bcb 100644 --- a/reliability/single-fit/trace/src/test/kotlin/com/datadog/android/trace/integration/opentracing/UtilitiesTest.kt +++ b/reliability/single-fit/trace/src/test/kotlin/com/datadog/android/trace/integration/api/UtilitiesTest.kt @@ -4,23 +4,22 @@ * Copyright 2016-Present Datadog, Inc. */ -package com.datadog.android.trace.integration.opentracing +package com.datadog.android.trace.integration.api import com.datadog.android.api.feature.Feature import com.datadog.android.core.stub.StubSDKCore -import com.datadog.android.internal.utils.toHexString import com.datadog.android.tests.ktx.getInt import com.datadog.android.tests.ktx.getLong import com.datadog.android.tests.ktx.getString -import com.datadog.android.trace.AndroidTracer +import com.datadog.android.trace.DatadogTracing +import com.datadog.android.trace.GlobalDatadogTracer import com.datadog.android.trace.Trace import com.datadog.android.trace.TraceConfiguration +import com.datadog.android.trace.api.clear import com.datadog.android.trace.integration.tests.elmyr.TraceIntegrationForgeConfigurator -import com.datadog.android.trace.setError +import com.datadog.android.trace.internal.DatadogTracingToolkit import com.datadog.android.trace.withinSpan -import com.datadog.opentracing.DDSpan import com.datadog.tools.unit.extensions.TestConfigurationExtension -import com.datadog.tools.unit.setStaticValue import com.google.gson.JsonObject import com.google.gson.JsonParser import fr.xgouchet.elmyr.Forge @@ -28,7 +27,6 @@ import fr.xgouchet.elmyr.annotation.Forgery import fr.xgouchet.elmyr.annotation.StringForgery import fr.xgouchet.elmyr.junit5.ForgeConfiguration import fr.xgouchet.elmyr.junit5.ForgeExtension -import io.opentracing.util.GlobalTracer import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.BeforeEach @@ -62,7 +60,8 @@ class UtilitiesTest { @AfterEach fun `tear down`() { - GlobalTracer::class.java.setStaticValue("isRegistered", false) + DatadogTracingToolkit.clear() + GlobalDatadogTracer.clear() } @RepeatedTest(16) @@ -72,7 +71,7 @@ class UtilitiesTest { ) { // Given stubSdkCore.stubFeature(Feature.LOGS_FEATURE_NAME) - val testedTracer = AndroidTracer.Builder(stubSdkCore).build() + val testedTracer = DatadogTracing.newTracerBuilder(stubSdkCore).build() // When var traceId: String @@ -84,7 +83,7 @@ class UtilitiesTest { mostSignificantTraceId = span.mostSignificant64BitsTraceId() spanId = span.spanIdAsHexString() Thread.sleep(OP_DURATION_MS) - span.setError(fakeThrowable) + span.logThrowable(fakeThrowable) span.finish() } @@ -120,7 +119,7 @@ class UtilitiesTest { ) { // Given stubSdkCore.stubFeature(Feature.LOGS_FEATURE_NAME) - val testedTracer = AndroidTracer.Builder(stubSdkCore).build() + val testedTracer = DatadogTracing.newTracerBuilder(stubSdkCore).build() // When var leastSignificantTraceId: String @@ -131,10 +130,10 @@ class UtilitiesTest { val span = testedTracer.buildSpan(fakeOperation).start() leastSignificantTraceId = span.leastSignificant64BitsTraceId() mostSignificantTraceId = span.mostSignificant64BitsTraceId() - traceId = (span as? DDSpan)?.traceId?.toString(16) ?: "" - spanId = span.spanIdAsLong() + traceId = span.traceId.toHexString() + spanId = span.context().spanId Thread.sleep(OP_DURATION_MS) - span.setError(fakeErrorMessage) + span.logErrorMessage(fakeErrorMessage) span.finish() } @@ -145,7 +144,9 @@ class UtilitiesTest { assertThat(event0.getString("env")).isEqualTo(stubSdkCore.getDatadogContext().env) assertThat(event0.getString("spans[0].trace_id")).isEqualTo(leastSignificantTraceId) assertThat(event0.getString("spans[0].meta._dd.p.id")).isEqualTo(mostSignificantTraceId) - assertThat(event0.getString("spans[0].span_id")).isEqualTo(spanId.toHexString()) + assertThat( + event0.getString("spans[0].span_id") + ).isEqualTo(DatadogTracingToolkit.spanIdConverter.toHexStringPadded(spanId)) assertThat(event0.getString("spans[0].service")).isEqualTo(stubSdkCore.getDatadogContext().service) assertThat(event0.getString("spans[0].meta.version")).isEqualTo(stubSdkCore.getDatadogContext().version) assertThat(event0.getString("spans[0].meta._dd.source")).isEqualTo(stubSdkCore.getDatadogContext().source) @@ -156,6 +157,7 @@ class UtilitiesTest { assertThat(event0.getString("spans[0].resource")).isEqualTo(fakeOperation) assertThat(event0.getLong("spans[0].duration")).isBetween(OP_DURATION_NS, fullDuration) assertThat(event0.getString("spans[0].meta.error.msg")).isNull() + val eventsReceived = stubSdkCore.eventsReceived(Feature.LOGS_FEATURE_NAME) assertThat(eventsReceived).hasSize(1) val logEvent0: Map = eventsReceived[0] as Map @@ -173,7 +175,7 @@ class UtilitiesTest { ) { // Given stubSdkCore.stubFeature(Feature.LOGS_FEATURE_NAME) - val testedTracer = AndroidTracer.Builder(stubSdkCore).build() + val testedTracer = DatadogTracing.newTracerBuilder(stubSdkCore).build() // When val mostSignificantTraceId: String @@ -185,7 +187,7 @@ class UtilitiesTest { traceId = span.leastSignificant64BitsTraceId() mostSignificantTraceId = span.mostSignificant64BitsTraceId() spanId = span.spanIdAsHexString() - AndroidTracer.Companion.logThrowable(span, fakeThrowable) + span.logThrowable(fakeThrowable) span.finish() } @@ -221,7 +223,7 @@ class UtilitiesTest { ) { // Given stubSdkCore.stubFeature(Feature.LOGS_FEATURE_NAME) - val testedTracer = AndroidTracer.Builder(stubSdkCore).build() + val testedTracer = DatadogTracing.newTracerBuilder(stubSdkCore).build() // When var leastSignificantTraceId: String @@ -232,10 +234,10 @@ class UtilitiesTest { val span = testedTracer.buildSpan(fakeOperation).start() leastSignificantTraceId = span.leastSignificant64BitsTraceId() mostSignificantTraceId = span.mostSignificant64BitsTraceId() - traceId = (span as? DDSpan)?.traceId?.toString(16) ?: "" - spanId = span.spanIdAsLong() + traceId = span.traceId.toHexString() + spanId = span.context().spanId Thread.sleep(OP_DURATION_MS) - AndroidTracer.Companion.logErrorMessage(span, fakeErrorMessage) + span.logErrorMessage(fakeErrorMessage) span.finish() } @@ -246,7 +248,11 @@ class UtilitiesTest { assertThat(event0.getString("env")).isEqualTo(stubSdkCore.getDatadogContext().env) assertThat(event0.getString("spans[0].trace_id")).isEqualTo(leastSignificantTraceId) assertThat(event0.getString("spans[0].meta._dd.p.id")).isEqualTo(mostSignificantTraceId) - assertThat(event0.getString("spans[0].span_id")).isEqualTo(spanId.toHexString()) + assertThat( + event0.getString("spans[0].span_id") + ).isEqualTo( + DatadogTracingToolkit.spanIdConverter.toHexStringPadded(spanId) + ) assertThat(event0.getString("spans[0].service")).isEqualTo(stubSdkCore.getDatadogContext().service) assertThat(event0.getString("spans[0].meta.version")).isEqualTo(stubSdkCore.getDatadogContext().version) assertThat(event0.getString("spans[0].meta._dd.source")).isEqualTo(stubSdkCore.getDatadogContext().source) @@ -272,8 +278,8 @@ class UtilitiesTest { @StringForgery fakeOperation: String ) { // Given - val testedTracer = AndroidTracer.Builder(stubSdkCore).build() - GlobalTracer.registerIfAbsent(testedTracer) + val testedTracer = DatadogTracing.newTracerBuilder(stubSdkCore).build() + GlobalDatadogTracer.registerIfAbsent(testedTracer) // When var traceId = "" @@ -314,8 +320,8 @@ class UtilitiesTest { @StringForgery fakeOperation2: String ) { // Given - val testedTracer = AndroidTracer.Builder(stubSdkCore).build() - GlobalTracer.registerIfAbsent(testedTracer) + val testedTracer = DatadogTracing.newTracerBuilder(stubSdkCore).build() + GlobalDatadogTracer.registerIfAbsent(testedTracer) // When var traceId0 = "" @@ -373,8 +379,8 @@ class UtilitiesTest { @Forgery fakeThrowable: Throwable ) { // Given - val testedTracer = AndroidTracer.Builder(stubSdkCore).build() - GlobalTracer.registerIfAbsent(testedTracer) + val testedTracer = DatadogTracing.newTracerBuilder(stubSdkCore).build() + GlobalDatadogTracer.registerIfAbsent(testedTracer) // When var traceId = "" diff --git a/reliability/single-fit/trace/src/test/kotlin/com/datadog/android/trace/integration/opentracing/AndroidTracerTest.kt b/reliability/single-fit/trace/src/test/kotlin/com/datadog/android/trace/integration/opentracing/AndroidTracerTest.kt deleted file mode 100644 index ff62deaf71..0000000000 --- a/reliability/single-fit/trace/src/test/kotlin/com/datadog/android/trace/integration/opentracing/AndroidTracerTest.kt +++ /dev/null @@ -1,637 +0,0 @@ -/* - * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. - * This product includes software developed at Datadog (https://www.datadoghq.com/). - * Copyright 2016-Present Datadog, Inc. - */ - -package com.datadog.android.trace.integration.opentracing - -import com.datadog.android.api.feature.Feature -import com.datadog.android.core.stub.StubSDKCore -import com.datadog.android.tests.ktx.getInt -import com.datadog.android.trace.AndroidTracer -import com.datadog.android.trace.Trace -import com.datadog.android.trace.TraceConfiguration -import com.datadog.android.trace.integration.tests.assertj.SpansPayloadAssert -import com.datadog.android.trace.integration.tests.elmyr.TraceIntegrationForgeConfigurator -import com.datadog.tools.unit.extensions.TestConfigurationExtension -import com.datadog.tools.unit.setStaticValue -import com.google.gson.JsonObject -import com.google.gson.JsonParser -import fr.xgouchet.elmyr.Forge -import fr.xgouchet.elmyr.annotation.DoubleForgery -import fr.xgouchet.elmyr.annotation.StringForgery -import fr.xgouchet.elmyr.junit5.ForgeConfiguration -import fr.xgouchet.elmyr.junit5.ForgeExtension -import io.opentracing.util.GlobalTracer -import org.assertj.core.api.Assertions.assertThat -import org.assertj.core.api.Assertions.offset -import org.assertj.core.data.Offset -import org.junit.jupiter.api.AfterEach -import org.junit.jupiter.api.BeforeEach -import org.junit.jupiter.api.RepeatedTest -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.extension.ExtendWith -import org.junit.jupiter.api.extension.Extensions -import org.mockito.junit.jupiter.MockitoExtension -import org.mockito.junit.jupiter.MockitoSettings -import org.mockito.quality.Strictness -import java.util.concurrent.TimeUnit -import kotlin.system.measureNanoTime - -@Extensions( - ExtendWith(MockitoExtension::class), - ExtendWith(ForgeExtension::class), - ExtendWith(TestConfigurationExtension::class) -) -@ForgeConfiguration(TraceIntegrationForgeConfigurator::class) -@MockitoSettings(strictness = Strictness.LENIENT) -class AndroidTracerTest { - - private lateinit var stubSdkCore: StubSDKCore - - @BeforeEach - fun `set up`(forge: Forge) { - stubSdkCore = StubSDKCore(forge) - - val fakeTraceConfiguration = TraceConfiguration.Builder().build() - Trace.enable(fakeTraceConfiguration, stubSdkCore) - } - - @AfterEach - fun `tear down`() { - GlobalTracer::class.java.setStaticValue("isRegistered", false) - } - - @RepeatedTest(16) - fun `M send trace with custom service W setService() + buildSpan() + start() + finish()`( - @StringForgery fakeOperation: String, - @StringForgery fakeService: String - ) { - // Given - val testedTracer = AndroidTracer.Builder(stubSdkCore).setService(fakeService).build() - - // When - var leastSignificantTraceId: String - var mostSignificantTraceId: String - var spanId: String - val fullDuration = measureNanoTime { - val span = testedTracer.buildSpan(fakeOperation).start() - leastSignificantTraceId = span.leastSignificant64BitsTraceId() - mostSignificantTraceId = span.mostSignificant64BitsTraceId() - spanId = span.spanIdAsHexString() - Thread.sleep(OP_DURATION_MS) - span.finish() - } - - // Then - val eventsWritten = stubSdkCore.eventsWritten(Feature.TRACING_FEATURE_NAME) - assertThat(eventsWritten).hasSize(1) - val payload0 = JsonParser.parseString(eventsWritten[0].eventData) as JsonObject - SpansPayloadAssert.assertThat(payload0) - .hasEnv(stubSdkCore.getDatadogContext().env) - .hasSpanAtIndexWith(0) { - hasLeastSignificant64BitsTraceId(leastSignificantTraceId) - hasMostSignificant64BitsTraceId(mostSignificantTraceId) - hasSpanId(spanId) - hasService(fakeService) - hasVersion(stubSdkCore.getDatadogContext().version) - hasSource(stubSdkCore.getDatadogContext().source) - hasTracerVersion(stubSdkCore.getDatadogContext().sdkVersion) - hasError(0) - hasName(fakeOperation) - hasResource(fakeOperation) - hasDurationBetween(OP_DURATION_NS, fullDuration) - } - } - - @RepeatedTest(16) - fun `M send span with tag W addTag() + buildSpan() + start() + finish()`( - @StringForgery fakeTagKey: String, - @StringForgery fakeTagValue: String, - @StringForgery fakeOperation: String - ) { - // Given - val testedTracer = AndroidTracer.Builder(stubSdkCore).addTag(fakeTagKey, fakeTagValue).build() - - // When - var leastSignificantTraceId: String - var mostSignificantTraceId: String - var spanId: String - val fullDuration = measureNanoTime { - val span = testedTracer.buildSpan(fakeOperation).start() - leastSignificantTraceId = span.leastSignificant64BitsTraceId() - mostSignificantTraceId = span.mostSignificant64BitsTraceId() - spanId = span.spanIdAsHexString() - Thread.sleep(OP_DURATION_MS) - span.finish() - } - - // Then - val eventsWritten = stubSdkCore.eventsWritten(Feature.TRACING_FEATURE_NAME) - assertThat(eventsWritten).hasSize(1) - val payload0 = JsonParser.parseString(eventsWritten[0].eventData) as JsonObject - SpansPayloadAssert.assertThat(payload0) - .hasEnv(stubSdkCore.getDatadogContext().env) - .hasSpanAtIndexWith(0) { - hasLeastSignificant64BitsTraceId(leastSignificantTraceId) - hasMostSignificant64BitsTraceId(mostSignificantTraceId) - hasValidMostSignificant64BitsTraceId() - hasValidLeastSignificant64BitsTraceId() - hasSpanId(spanId) - hasService(stubSdkCore.getDatadogContext().service) - hasVersion(stubSdkCore.getDatadogContext().version) - hasSource(stubSdkCore.getDatadogContext().source) - hasTracerVersion(stubSdkCore.getDatadogContext().sdkVersion) - hasError(0) - hasName(fakeOperation) - hasResource(fakeOperation) - hasDurationBetween(OP_DURATION_NS, fullDuration) - hasGenericMetaValue(fakeTagKey, fakeTagValue) - } - } - - @RepeatedTest(16) - @Suppress("DEPRECATION") - fun `M send span with global tag W addGlobalTag() + buildSpan() + start() + finish()`( - @StringForgery fakeTagKey: String, - @StringForgery fakeTagValue: String, - @StringForgery fakeOperation: String - ) { - // Given - val testedTracer = AndroidTracer.Builder(stubSdkCore).addGlobalTag(fakeTagKey, fakeTagValue).build() - - // When - var leastSignificantTraceId: String - var mostSignificantTraceId: String - var spanId: String - val fullDuration = measureNanoTime { - val span = testedTracer.buildSpan(fakeOperation).start() - leastSignificantTraceId = span.leastSignificant64BitsTraceId() - mostSignificantTraceId = span.mostSignificant64BitsTraceId() - spanId = span.spanIdAsHexString() - Thread.sleep(OP_DURATION_MS) - span.finish() - } - - // Then - val eventsWritten = stubSdkCore.eventsWritten(Feature.TRACING_FEATURE_NAME) - assertThat(eventsWritten).hasSize(1) - val payload0 = JsonParser.parseString(eventsWritten[0].eventData) as JsonObject - SpansPayloadAssert.assertThat(payload0) - .hasEnv(stubSdkCore.getDatadogContext().env) - .hasSpanAtIndexWith(0) { - hasLeastSignificant64BitsTraceId(leastSignificantTraceId) - hasMostSignificant64BitsTraceId(mostSignificantTraceId) - hasValidMostSignificant64BitsTraceId() - hasValidLeastSignificant64BitsTraceId() - hasSpanId(spanId) - hasService(stubSdkCore.getDatadogContext().service) - hasVersion(stubSdkCore.getDatadogContext().version) - hasSource(stubSdkCore.getDatadogContext().source) - hasTracerVersion(stubSdkCore.getDatadogContext().sdkVersion) - hasError(0) - hasName(fakeOperation) - hasResource(fakeOperation) - hasDurationBetween(OP_DURATION_NS, fullDuration) - hasGenericMetaValue(fakeTagKey, fakeTagValue) - } - } - - @RepeatedTest(16) - fun `M send span without rum context W setBundleWithRumEnabled(false) + buildSpan() + start() + finish()`( - @StringForgery fakeRumApplicationId: String, - @StringForgery fakeRumSessionId: String, - @StringForgery fakeRumViewId: String, - @StringForgery fakeRumActionId: String, - @StringForgery fakeOperation: String - ) { - // Given - stubSdkCore.stubFeature(Feature.RUM_FEATURE_NAME) - stubSdkCore.updateFeatureContext(Feature.RUM_FEATURE_NAME) { - it["application_id"] = fakeRumApplicationId - it["session_id"] = fakeRumSessionId - it["view_id"] = fakeRumViewId - it["action_id"] = fakeRumActionId - } - val testedTracer = AndroidTracer.Builder(stubSdkCore).setBundleWithRumEnabled(false).build() - - // When - var leastSignificantTraceId: String - var mostSignificantTraceId: String - var spanId: String - val fullDuration = measureNanoTime { - val span = testedTracer.buildSpan(fakeOperation).start() - leastSignificantTraceId = span.leastSignificant64BitsTraceId() - mostSignificantTraceId = span.mostSignificant64BitsTraceId() - spanId = span.spanIdAsHexString() - Thread.sleep(OP_DURATION_MS) - span.finish() - } - - // Then - val eventsWritten = stubSdkCore.eventsWritten(Feature.TRACING_FEATURE_NAME) - assertThat(eventsWritten).hasSize(1) - val payload0 = JsonParser.parseString(eventsWritten[0].eventData) as JsonObject - SpansPayloadAssert.assertThat(payload0) - .hasEnv(stubSdkCore.getDatadogContext().env) - .hasSpanAtIndexWith(0) { - hasLeastSignificant64BitsTraceId(leastSignificantTraceId) - hasMostSignificant64BitsTraceId(mostSignificantTraceId) - hasValidMostSignificant64BitsTraceId() - hasValidLeastSignificant64BitsTraceId() - hasSpanId(spanId) - hasService(stubSdkCore.getDatadogContext().service) - hasVersion(stubSdkCore.getDatadogContext().version) - hasSource(stubSdkCore.getDatadogContext().source) - hasTracerVersion(stubSdkCore.getDatadogContext().sdkVersion) - hasError(0) - hasName(fakeOperation) - hasResource(fakeOperation) - hasDurationBetween(OP_DURATION_NS, fullDuration) - hasApplicationId(null) - hasSessionId(null) - hasViewId(null) - } - } - - @RepeatedTest(16) - fun `M send span with rum context W setBundleWithRumEnabled(true) + buildSpan() + start() + finish()`( - @StringForgery fakeRumApplicationId: String, - @StringForgery fakeRumSessionId: String, - @StringForgery fakeRumViewId: String, - @StringForgery fakeRumActionId: String, - @StringForgery fakeOperation: String - ) { - // Given - stubSdkCore.stubFeature(Feature.RUM_FEATURE_NAME) - stubSdkCore.updateFeatureContext(Feature.RUM_FEATURE_NAME) { - it["application_id"] = fakeRumApplicationId - it["session_id"] = fakeRumSessionId - it["view_id"] = fakeRumViewId - it["action_id"] = fakeRumActionId - } - val testedTracer = AndroidTracer.Builder(stubSdkCore).setBundleWithRumEnabled(true).build() - - // When - var leastSignificantTraceId: String - var mostSignificantTraceId: String - var spanId: String - val fullDuration = measureNanoTime { - val span = testedTracer.buildSpan(fakeOperation).start() - leastSignificantTraceId = span.leastSignificant64BitsTraceId() - mostSignificantTraceId = span.mostSignificant64BitsTraceId() - spanId = span.spanIdAsHexString() - Thread.sleep(OP_DURATION_MS) - span.finish() - } - - // Then - val eventsWritten = stubSdkCore.eventsWritten(Feature.TRACING_FEATURE_NAME) - assertThat(eventsWritten).hasSize(1) - val payload0 = JsonParser.parseString(eventsWritten[0].eventData) as JsonObject - SpansPayloadAssert.assertThat(payload0) - .hasEnv(stubSdkCore.getDatadogContext().env) - .hasSpanAtIndexWith(0) { - hasLeastSignificant64BitsTraceId(leastSignificantTraceId) - hasMostSignificant64BitsTraceId(mostSignificantTraceId) - hasValidMostSignificant64BitsTraceId() - hasValidLeastSignificant64BitsTraceId() - hasSpanId(spanId) - hasService(stubSdkCore.getDatadogContext().service) - hasVersion(stubSdkCore.getDatadogContext().version) - hasSource(stubSdkCore.getDatadogContext().source) - hasTracerVersion(stubSdkCore.getDatadogContext().sdkVersion) - hasError(0) - hasName(fakeOperation) - hasResource(fakeOperation) - hasDurationBetween(OP_DURATION_NS, fullDuration) - hasApplicationId(fakeRumApplicationId) - hasSessionId(fakeRumSessionId) - hasViewId(fakeRumViewId) - } - } - - @RepeatedTest(4) - fun `M send sampled spans W setSampleRate() + buildSpan() + start() + finish()`( - @StringForgery fakeOperation: String, - @DoubleForgery(0.0, 100.0) fakeSampleRate: Double - ) { - // Given - val repeatCount = 256 - val expectedCount = (repeatCount * fakeSampleRate / 100f).toInt() - val offsetMargin = repeatCount / 10 // allow 10% margin - val testedTracer = AndroidTracer.Builder(stubSdkCore).setSampleRate(fakeSampleRate).build() - - // When - repeat(repeatCount) { - val span = testedTracer.buildSpan(fakeOperation).start() - Thread.sleep(OP_DURATION_MS) - span.finish() - } - - // Then - val eventsWritten = stubSdkCore.eventsWritten(Feature.TRACING_FEATURE_NAME) - val keptSpans = eventsWritten.count { - val payload = JsonParser.parseString(it.eventData) as JsonObject - SpansPayloadAssert.assertThat(payload).hasSpanAtIndexWith(0) { - hasAgentPsrCloseTo(fakeSampleRate / 100.0, offset(0.01)) - } - payload.getInt("spans[0].metrics._sampling_priority_v1") == 1 - } - assertThat(keptSpans).isCloseTo(expectedCount, Offset.offset(offsetMargin)) - } - - @RepeatedTest(16) - fun `M send span with defaults W buildSpan() + start() + finish()`( - @StringForgery fakeOperation: String - ) { - // Given - val testedTracer = AndroidTracer.Builder(stubSdkCore).build() - - // When - var leastSignificantTraceId: String - var mostSignificantTraceId: String - var spanId: String - val fullDuration = measureNanoTime { - val span = testedTracer.buildSpan(fakeOperation).start() - leastSignificantTraceId = span.leastSignificant64BitsTraceId() - mostSignificantTraceId = span.mostSignificant64BitsTraceId() - spanId = span.spanIdAsHexString() - Thread.sleep(OP_DURATION_MS) - span.finish() - } - - // Then - val eventsWritten = stubSdkCore.eventsWritten(Feature.TRACING_FEATURE_NAME) - assertThat(eventsWritten).hasSize(1) - val payload0 = JsonParser.parseString(eventsWritten[0].eventData) as JsonObject - SpansPayloadAssert.assertThat(payload0) - .hasEnv(stubSdkCore.getDatadogContext().env) - .hasSpanAtIndexWith(0) { - hasLeastSignificant64BitsTraceId(leastSignificantTraceId) - hasMostSignificant64BitsTraceId(mostSignificantTraceId) - hasValidMostSignificant64BitsTraceId() - hasValidLeastSignificant64BitsTraceId() - hasSpanId(spanId) - hasService(stubSdkCore.getDatadogContext().service) - hasVersion(stubSdkCore.getDatadogContext().version) - hasSource(stubSdkCore.getDatadogContext().source) - hasTracerVersion(stubSdkCore.getDatadogContext().sdkVersion) - hasError(0) - hasName(fakeOperation) - hasResource(fakeOperation) - hasDurationBetween(OP_DURATION_NS, fullDuration) - } - } - - @RepeatedTest(16) - fun `M send trace with base user info W SDKCore#setUserInfo() + buildSpan() + start() + finish()`( - @StringForgery fakeOperation: String, - @StringForgery fakeUserId: String, - @StringForgery fakeUserName: String, - @StringForgery fakeUserEmail: String - ) { - // Given - stubSdkCore.setUserInfo(fakeUserId, fakeUserName, fakeUserEmail) - val testedTracer = AndroidTracer.Builder(stubSdkCore).build() - - // When - var leastSignificantTraceId: String - var mostSignificantTraceId: String - var spanId: String - val fullDuration = measureNanoTime { - val span = testedTracer.buildSpan(fakeOperation).start() - leastSignificantTraceId = span.leastSignificant64BitsTraceId() - mostSignificantTraceId = span.mostSignificant64BitsTraceId() - spanId = span.spanIdAsHexString() - Thread.sleep(OP_DURATION_MS) - span.finish() - } - - // Then - val eventsWritten = stubSdkCore.eventsWritten(Feature.TRACING_FEATURE_NAME) - assertThat(eventsWritten).hasSize(1) - val payload0 = JsonParser.parseString(eventsWritten[0].eventData) as JsonObject - SpansPayloadAssert.assertThat(payload0) - .hasEnv(stubSdkCore.getDatadogContext().env) - .hasSpanAtIndexWith(0) { - hasLeastSignificant64BitsTraceId(leastSignificantTraceId) - hasMostSignificant64BitsTraceId(mostSignificantTraceId) - hasValidMostSignificant64BitsTraceId() - hasValidLeastSignificant64BitsTraceId() - hasSpanId(spanId) - hasService(stubSdkCore.getDatadogContext().service) - hasVersion(stubSdkCore.getDatadogContext().version) - hasSource(stubSdkCore.getDatadogContext().source) - hasTracerVersion(stubSdkCore.getDatadogContext().sdkVersion) - hasError(0) - hasName(fakeOperation) - hasResource(fakeOperation) - hasDurationBetween(OP_DURATION_NS, fullDuration) - hasUserId(fakeUserId) - hasUserName(fakeUserName) - hasUserEmail(fakeUserEmail) - } - } - - @RepeatedTest(16) - fun `M send trace with custom user info W SDKCore#setUserInfo() + buildSpan() + start() + finish()`( - @StringForgery fakeUserId: String, - @StringForgery fakeOperation: String, - @StringForgery fakeUserKey: String, - @StringForgery fakeUserValue: String - ) { - // Given - stubSdkCore.setUserInfo(id = fakeUserId, extraInfo = mapOf(fakeUserKey to fakeUserValue)) - val testedTracer = AndroidTracer.Builder(stubSdkCore).build() - - // When - var leastSignificantTraceId: String - var spanId: String - var mostSignificantTraceId: String - val fullDuration = measureNanoTime { - val span = testedTracer.buildSpan(fakeOperation).start() - leastSignificantTraceId = span.leastSignificant64BitsTraceId() - mostSignificantTraceId = span.mostSignificant64BitsTraceId() - spanId = span.spanIdAsHexString() - Thread.sleep(OP_DURATION_MS) - span.finish() - } - - // Then - val eventsWritten = stubSdkCore.eventsWritten(Feature.TRACING_FEATURE_NAME) - assertThat(eventsWritten).hasSize(1) - val payload0 = JsonParser.parseString(eventsWritten[0].eventData) as JsonObject - SpansPayloadAssert.assertThat(payload0) - .hasEnv(stubSdkCore.getDatadogContext().env) - .hasSpanAtIndexWith(0) { - hasLeastSignificant64BitsTraceId(leastSignificantTraceId) - hasMostSignificant64BitsTraceId(mostSignificantTraceId) - hasValidMostSignificant64BitsTraceId() - hasValidLeastSignificant64BitsTraceId() - hasSpanId(spanId) - hasService(stubSdkCore.getDatadogContext().service) - hasVersion(stubSdkCore.getDatadogContext().version) - hasSource(stubSdkCore.getDatadogContext().source) - hasTracerVersion(stubSdkCore.getDatadogContext().sdkVersion) - hasError(0) - hasName(fakeOperation) - hasResource(fakeOperation) - hasDurationBetween(OP_DURATION_NS, fullDuration) - hasGenericMetaValue("usr.$fakeUserKey", fakeUserValue) - } - } - - @Test - fun `M send trace with base account info W SDKCore#setAccountInfo() + buildSpan() + start() + finish()`( - @StringForgery fakeOperation: String, - @StringForgery fakeAccountId: String, - @StringForgery fakeAccountName: String - ) { - // Given - stubSdkCore.setAccountInfo(fakeAccountId, fakeAccountName) - val testedTracer = AndroidTracer.Builder(stubSdkCore).build() - - // When - var leastSignificantTraceId: String - var mostSignificantTraceId: String - var spanId: String - val fullDuration = measureNanoTime { - val span = testedTracer.buildSpan(fakeOperation).start() - leastSignificantTraceId = span.leastSignificant64BitsTraceId() - mostSignificantTraceId = span.mostSignificant64BitsTraceId() - spanId = span.spanIdAsHexString() - Thread.sleep(OP_DURATION_MS) - span.finish() - } - - // Then - val eventsWritten = stubSdkCore.eventsWritten(Feature.TRACING_FEATURE_NAME) - assertThat(eventsWritten).hasSize(1) - val payload0 = JsonParser.parseString(eventsWritten[0].eventData) as JsonObject - SpansPayloadAssert.assertThat(payload0) - .hasEnv(stubSdkCore.getDatadogContext().env) - .hasSpanAtIndexWith(0) { - hasLeastSignificant64BitsTraceId(leastSignificantTraceId) - hasMostSignificant64BitsTraceId(mostSignificantTraceId) - hasValidMostSignificant64BitsTraceId() - hasValidLeastSignificant64BitsTraceId() - hasSpanId(spanId) - hasService(stubSdkCore.getDatadogContext().service) - hasVersion(stubSdkCore.getDatadogContext().version) - hasSource(stubSdkCore.getDatadogContext().source) - hasTracerVersion(stubSdkCore.getDatadogContext().sdkVersion) - hasError(0) - hasName(fakeOperation) - hasResource(fakeOperation) - hasDurationBetween(OP_DURATION_NS, fullDuration) - hasGenericMetaValue("account.id", fakeAccountId) - hasGenericMetaValue("account.name", fakeAccountName) - } - } - - @RepeatedTest(16) - fun `M send trace with custom account info W SDKCore#setAccountInfo() + buildSpan() + start() + finish()`( - @StringForgery fakeOperation: String, - @StringForgery fakeAccountId: String, - @StringForgery fakeAccountKey: String, - @StringForgery fakeAccountValue: String - ) { - // Given - stubSdkCore.setAccountInfo( - id = fakeAccountId, - extraInfo = mapOf(fakeAccountKey to fakeAccountValue) - ) - val testedTracer = AndroidTracer.Builder(stubSdkCore).build() - - // When - var leastSignificantTraceId: String - var mostSignificantTraceId: String - var spanId: String - val fullDuration = measureNanoTime { - val span = testedTracer.buildSpan(fakeOperation).start() - leastSignificantTraceId = span.leastSignificant64BitsTraceId() - mostSignificantTraceId = span.mostSignificant64BitsTraceId() - spanId = span.spanIdAsHexString() - Thread.sleep(OP_DURATION_MS) - span.finish() - } - - // Then - val eventsWritten = stubSdkCore.eventsWritten(Feature.TRACING_FEATURE_NAME) - assertThat(eventsWritten).hasSize(1) - val payload0 = JsonParser.parseString(eventsWritten[0].eventData) as JsonObject - SpansPayloadAssert.assertThat(payload0) - .hasEnv(stubSdkCore.getDatadogContext().env) - .hasSpanAtIndexWith(0) { - hasLeastSignificant64BitsTraceId(leastSignificantTraceId) - hasMostSignificant64BitsTraceId(mostSignificantTraceId) - hasValidMostSignificant64BitsTraceId() - hasValidLeastSignificant64BitsTraceId() - hasSpanId(spanId) - hasService(stubSdkCore.getDatadogContext().service) - hasVersion(stubSdkCore.getDatadogContext().version) - hasSource(stubSdkCore.getDatadogContext().source) - hasTracerVersion(stubSdkCore.getDatadogContext().sdkVersion) - hasError(0) - hasName(fakeOperation) - hasResource(fakeOperation) - hasDurationBetween(OP_DURATION_NS, fullDuration) - hasGenericMetaValue("account.$fakeAccountKey", fakeAccountValue) - } - } - - @RepeatedTest(16) - fun `M send span with parent W buildSpan() + start() + finish()`( - @StringForgery fakeOperation: String - ) { - // Given - val testedTracer = AndroidTracer.Builder(stubSdkCore).build() - - // When - val span = testedTracer.buildSpan(fakeOperation).start() - val leastSignificantTraceId = span.leastSignificant64BitsTraceId() - val mostSignificantTraceId = span.mostSignificant64BitsTraceId() - val spanId = span.spanIdAsHexString() - val childSpan = testedTracer.buildSpan(fakeOperation).asChildOf(span).start() - val childLeastSignificantTraceId = childSpan.leastSignificant64BitsTraceId() - val childMostSignificantTraceId = childSpan.mostSignificant64BitsTraceId() - val childSpanId = childSpan.spanIdAsHexString() - Thread.sleep(OP_DURATION_MS) - childSpan.finish() - span.finish() - - // Then - assertThat(childLeastSignificantTraceId).isEqualTo(leastSignificantTraceId) - assertThat(childMostSignificantTraceId).isEqualTo(mostSignificantTraceId) - assertThat(childSpanId).isNotEqualTo(spanId) - val eventsWritten = stubSdkCore.eventsWritten(Feature.TRACING_FEATURE_NAME) - assertThat(eventsWritten).hasSize(2) - val payload0 = JsonParser.parseString(eventsWritten[0].eventData) as JsonObject - val payload1 = JsonParser.parseString(eventsWritten[1].eventData) as JsonObject - SpansPayloadAssert.assertThat(payload0) - .hasEnv(stubSdkCore.getDatadogContext().env) - .hasSpanAtIndexWith(0) { - hasLeastSignificant64BitsTraceId(leastSignificantTraceId) - hasMostSignificant64BitsTraceId(mostSignificantTraceId) - hasValidMostSignificant64BitsTraceId() - hasValidLeastSignificant64BitsTraceId() - hasSpanId(spanId) - } - SpansPayloadAssert.assertThat(payload1) - .hasEnv(stubSdkCore.getDatadogContext().env) - .hasSpanAtIndexWith(0) { - hasLeastSignificant64BitsTraceId(childLeastSignificantTraceId) - hasMostSignificant64BitsTraceId(childMostSignificantTraceId) - hasValidMostSignificant64BitsTraceId() - hasValidLeastSignificant64BitsTraceId() - hasSpanId(childSpanId) - } - } - - companion object { - const val OP_DURATION_MS = 10L - val OP_DURATION_NS = TimeUnit.MILLISECONDS.toNanos(OP_DURATION_MS) - } -} diff --git a/reliability/single-fit/trace/src/test/kotlin/com/datadog/android/trace/integration/opentracing/SpanExt.kt b/reliability/single-fit/trace/src/test/kotlin/com/datadog/android/trace/integration/opentracing/SpanExt.kt deleted file mode 100644 index d8a6e9948d..0000000000 --- a/reliability/single-fit/trace/src/test/kotlin/com/datadog/android/trace/integration/opentracing/SpanExt.kt +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. - * This product includes software developed at Datadog (https://www.datadoghq.com/). - * Copyright 2016-Present Datadog, Inc. - */ - -package com.datadog.android.trace.integration.opentracing - -import com.datadog.android.internal.utils.toHexString -import com.datadog.opentracing.DDSpan -import io.opentracing.Span -import io.opentracing.SpanContext - -/** - * Returns the span's least significant trace id in hex format (the last 64 bits from the 128 bits trace id) - */ -fun Span.leastSignificant64BitsTraceId(): String { - return (this as? DDSpan)?.traceId?.toString(16)?.padStart(32, '0')?.takeLast(16) ?: "" -} - -/** - * Returns the span's most significant trace id in hex format (the first 64 bits from the 128 bits trace id) - */ -fun Span.mostSignificant64BitsTraceId(): String { - return (this as? DDSpan)?.traceId?.toString(16)?.padStart(32, '0')?.take(16) ?: "" -} - -/** - * Returns the span's spanId in hex format. - * The [SpanContext.toSpanId] method returns a string in decimal format, - * which doesn't match what we send in our events - */ -fun Span.spanIdAsHexString(): String { - return context().toSpanId().toLong().toHexString() -} - -/** - * Returns the span's spanId in as Long. - * The [SpanContext.toSpanId] method returns a string in decimal format, - * which doesn't match what we send in our events - */ -fun Span.spanIdAsLong(): Long { - return context().toSpanId().toLong() -} diff --git a/reliability/single-fit/trace/src/test/kotlin/com/datadog/android/trace/integration/otel/OtelTraceConfigurationTest.kt b/reliability/single-fit/trace/src/test/kotlin/com/datadog/android/trace/integration/otel/OtelTraceConfigurationTest.kt index 1eb937c9d0..65c49be8c1 100644 --- a/reliability/single-fit/trace/src/test/kotlin/com/datadog/android/trace/integration/otel/OtelTraceConfigurationTest.kt +++ b/reliability/single-fit/trace/src/test/kotlin/com/datadog/android/trace/integration/otel/OtelTraceConfigurationTest.kt @@ -55,25 +55,26 @@ class OtelTraceConfigurationTest { @StringForgery fakeOperation: String ) { // Given - val fakeTraceConfiguration = TraceConfiguration.Builder() - .setNetworkInfoEnabled(false) - .build() - Trace.enable(fakeTraceConfiguration, stubSdkCore) + Trace.enable( + sdkCore = stubSdkCore, + traceConfiguration = TraceConfiguration.Builder().setNetworkInfoEnabled(false).build() + ) + val blockingWriterWrapper = checkNotNull(stubSdkCore.getFeature(Feature.TRACING_FEATURE_NAME)) + .useBlockingWriter() val testedProvider = OtelTracerProvider.Builder(stubSdkCore).build() val tracer = testedProvider.tracerBuilder(fakeInstrumentationName).build() - val blockingWriterWrapper = tracer.useBlockingWriter() // When var leastSignificantTraceId: String var mostSignificantTraceId: String var spanId: String val fullDuration = measureNanoTime { - val span = tracer.spanBuilder(fakeOperation).startSpan() - leastSignificantTraceId = span.leastSignificant64BitsTraceIdAsHex() - mostSignificantTraceId = span.mostSignificant64BitsTraceIdAsHex() - spanId = span.spanIdAsHex() + val otelSpan = tracer.spanBuilder(fakeOperation).startSpan() + leastSignificantTraceId = otelSpan.leastSignificant64BitsTraceIdAsHex() + mostSignificantTraceId = otelSpan.mostSignificant64BitsTraceIdAsHex() + spanId = otelSpan.spanIdAsHex() Thread.sleep(OP_DURATION_MS) - span.end() + otelSpan.end() } // Then @@ -107,25 +108,26 @@ class OtelTraceConfigurationTest { @StringForgery fakeOperation: String ) { // Given - val fakeTraceConfiguration = TraceConfiguration.Builder() - .setNetworkInfoEnabled(true) - .build() - Trace.enable(fakeTraceConfiguration, stubSdkCore) + Trace.enable( + sdkCore = stubSdkCore, + traceConfiguration = TraceConfiguration.Builder().setNetworkInfoEnabled(true).build() + ) + val blockingWriterWrapper = checkNotNull(stubSdkCore.getFeature(Feature.TRACING_FEATURE_NAME)) + .useBlockingWriter() val testedProvider = OtelTracerProvider.Builder(stubSdkCore).build() val tracer = testedProvider.tracerBuilder(fakeInstrumentationName).build() - val blockingWriterWrapper = tracer.useBlockingWriter() // When var leastSignificantTraceId: String var mostSignificantTraceId: String var spanId: String val fullDuration = measureNanoTime { - val span = tracer.spanBuilder(fakeOperation).startSpan() - leastSignificantTraceId = span.leastSignificant64BitsTraceIdAsHex() - mostSignificantTraceId = span.mostSignificant64BitsTraceIdAsHex() - spanId = span.spanIdAsHex() + val otelSpan = tracer.spanBuilder(fakeOperation).startSpan() + leastSignificantTraceId = otelSpan.leastSignificant64BitsTraceIdAsHex() + mostSignificantTraceId = otelSpan.mostSignificant64BitsTraceIdAsHex() + spanId = otelSpan.spanIdAsHex() Thread.sleep(OP_DURATION_MS) - span.end() + otelSpan.end() } // Then @@ -168,13 +170,15 @@ class OtelTraceConfigurationTest { return event } } - val fakeTraceConfiguration = TraceConfiguration.Builder() - .setEventMapper(stubMapper) - .build() - Trace.enable(fakeTraceConfiguration, stubSdkCore) + Trace.enable( + sdkCore = stubSdkCore, + traceConfiguration = TraceConfiguration.Builder().setEventMapper(stubMapper).build() + ) + val blockingWriterWrapper = checkNotNull( + stubSdkCore.getFeature(Feature.TRACING_FEATURE_NAME) + ).useBlockingWriter() val testedProvider = OtelTracerProvider.Builder(stubSdkCore).build() val tracer = testedProvider.tracerBuilder(fakeInstrumentationName).build() - val blockingWriterWrapper = tracer.useBlockingWriter() // When var leastSignificantTraceId: String diff --git a/reliability/single-fit/trace/src/test/kotlin/com/datadog/android/trace/integration/otel/OtelTracerProviderTest.kt b/reliability/single-fit/trace/src/test/kotlin/com/datadog/android/trace/integration/otel/OtelTracerProviderTest.kt index b563b11505..88034a6175 100644 --- a/reliability/single-fit/trace/src/test/kotlin/com/datadog/android/trace/integration/otel/OtelTracerProviderTest.kt +++ b/reliability/single-fit/trace/src/test/kotlin/com/datadog/android/trace/integration/otel/OtelTracerProviderTest.kt @@ -11,9 +11,9 @@ import com.datadog.android.core.stub.StubSDKCore import com.datadog.android.tests.ktx.getInt import com.datadog.android.trace.Trace import com.datadog.android.trace.TraceConfiguration -import com.datadog.android.trace.integration.opentracing.AndroidTracerTest import com.datadog.android.trace.integration.tests.assertj.SpansPayloadAssert import com.datadog.android.trace.integration.tests.elmyr.TraceIntegrationForgeConfigurator +import com.datadog.android.trace.integration.tests.utils.BlockingWriterWrapper import com.datadog.android.trace.opentelemetry.OtelTracerProvider import com.datadog.opentelemetry.trace.OtelConventions import com.datadog.tools.unit.extensions.TestConfigurationExtension @@ -56,12 +56,14 @@ import kotlin.system.measureNanoTime internal class OtelTracerProviderTest { private lateinit var stubSdkCore: StubSDKCore + private lateinit var blockingWriterWrapper: BlockingWriterWrapper @BeforeEach fun `set up`(forge: Forge) { stubSdkCore = StubSDKCore(forge) val fakeTraceConfiguration = TraceConfiguration.Builder().build() Trace.enable(fakeTraceConfiguration, stubSdkCore) + blockingWriterWrapper = checkNotNull(stubSdkCore.getFeature(Feature.TRACING_FEATURE_NAME)).useBlockingWriter() } // region Span attributes @@ -75,7 +77,6 @@ internal class OtelTracerProviderTest { // Given val testedProvider = OtelTracerProvider.Builder(stubSdkCore).setService(fakeService).build() val tracer = testedProvider.tracerBuilder(fakeInstrumentationName).build() - val blockingWriterWrapper = tracer.useBlockingWriter() // When var leastSignificantTraceId: String @@ -126,7 +127,6 @@ internal class OtelTracerProviderTest { .addTag(fakeGlobalTagKey, fakeGlobalTagValue) .build() val tracer = testedProvider.tracerBuilder(fakeInstrumentationName).build() - val blockingWriterWrapper = tracer.useBlockingWriter() // When var leastSignificantTraceId: String @@ -183,7 +183,6 @@ internal class OtelTracerProviderTest { val fakeBooleanAttributeValue = forge.aBool() val testedProvider = OtelTracerProvider.Builder(stubSdkCore).build() val tracer = testedProvider.tracerBuilder(fakeInstrumentationName).build() - val blockingWriterWrapper = tracer.useBlockingWriter() // When var leastSignificantTraceId: String @@ -248,7 +247,6 @@ internal class OtelTracerProviderTest { val fakeBooleanAttributeValue = forge.aBool() val testedProvider = OtelTracerProvider.Builder(stubSdkCore).build() val tracer = testedProvider.tracerBuilder(fakeInstrumentationName).build() - val blockingWriterWrapper = tracer.useBlockingWriter() // When var leastSignificantTraceId: String @@ -309,7 +307,6 @@ internal class OtelTracerProviderTest { // Given val testedProvider = OtelTracerProvider.Builder(stubSdkCore).build() val tracer = testedProvider.tracerBuilder(fakeInstrumentationName).build() - val blockingWriterWrapper = tracer.useBlockingWriter() var leastSignificantParentSpanTraceId: String var mostSignficantParentSpanTraceId: String var parentSpanId: String @@ -389,7 +386,6 @@ internal class OtelTracerProviderTest { // Given val testedProvider = OtelTracerProvider.Builder(stubSdkCore).build() val tracer = testedProvider.tracerBuilder(fakeInstrumentationName).build() - val blockingWriterWrapper = tracer.useBlockingWriter() val spanKind = forge.aValueFrom(SpanKind::class.java) // When @@ -451,7 +447,6 @@ internal class OtelTracerProviderTest { val fakeStartTimestamp = System.currentTimeMillis() - forge.aPositiveLong() val testedProvider = OtelTracerProvider.Builder(stubSdkCore).build() val tracer = testedProvider.tracerBuilder(fakeInstrumentationName).build() - val blockingWriterWrapper = tracer.useBlockingWriter() // When var leastSignificantTraceId: String @@ -512,7 +507,6 @@ internal class OtelTracerProviderTest { val fakeStringArray = forge.aList { forge.aString() } val testedProvider = OtelTracerProvider.Builder(stubSdkCore).build() val tracer = testedProvider.tracerBuilder(fakeInstrumentationName).build() - val blockingWriterWrapper = tracer.useBlockingWriter() val attributes = Attributes.builder() .put(fakeStringKey, fakeStringAttribute) .put(fakeLongKey, fakeLongAttribute) @@ -575,7 +569,6 @@ internal class OtelTracerProviderTest { // Given val testedProvider = OtelTracerProvider.Builder(stubSdkCore).build() val tracer = testedProvider.tracerBuilder(fakeInstrumentationName).build() - val blockingWriterWrapper = tracer.useBlockingWriter() // When val parentSpan = tracer.spanBuilder(fakeParentSpanName).startSpan() @@ -639,7 +632,6 @@ internal class OtelTracerProviderTest { // Given val testedProvider = OtelTracerProvider.Builder(stubSdkCore).build() val tracer = testedProvider.tracerBuilder(fakeInstrumentationName).build() - val blockingWriterWrapper = tracer.useBlockingWriter() // When val parentSpan = tracer.spanBuilder(fakeParentSpanName).startSpan() @@ -712,7 +704,6 @@ internal class OtelTracerProviderTest { // Given val testedProvider = OtelTracerProvider.Builder(stubSdkCore).setSampleRate(100.0).build() val tracer = testedProvider.tracerBuilder(fakeInstrumentationName).build() - val blockingWriterWrapper = tracer.useBlockingWriter() // When val span = tracer @@ -756,7 +747,6 @@ internal class OtelTracerProviderTest { // Given val testedProvider = OtelTracerProvider.Builder(stubSdkCore).setSampleRate(0.0).build() val tracer = testedProvider.tracerBuilder(fakeInstrumentationName).build() - val blockingWriterWrapper = tracer.useBlockingWriter() // When val span = tracer @@ -779,7 +769,6 @@ internal class OtelTracerProviderTest { // Given val testedProvider = OtelTracerProvider.Builder(stubSdkCore).build() val tracer = testedProvider.tracerBuilder(fakeInstrumentationName).build() - val blockingWriterWrapper = tracer.useBlockingWriter() // When val span = tracer @@ -824,7 +813,6 @@ internal class OtelTracerProviderTest { // Given val testedProvider = OtelTracerProvider.Builder(stubSdkCore).setSampleRate(sampleRate).build() val tracer = testedProvider.tracerBuilder(fakeInstrumentationName).build() - val blockingWriterWrapper = tracer.useBlockingWriter() val numberOfSpans = 300 val normalizedSampleRate = sampleRate / 100.0 val expectedKeptSpans = (numberOfSpans * normalizedSampleRate).toInt() @@ -874,7 +862,6 @@ internal class OtelTracerProviderTest { .setSampleRate(100.0) .build() val tracer = testedProvider.tracerBuilder(fakeInstrumentationName).build() - val blockingWriterWrapper = tracer.useBlockingWriter() // When val startNanos = System.nanoTime() @@ -914,7 +901,6 @@ internal class OtelTracerProviderTest { .setTraceRateLimit(traceLimit) .build() val tracer = testedProvider.tracerBuilder(fakeInstrumentationName).build() - val blockingWriterWrapper = tracer.useBlockingWriter() // When val startNanos = System.nanoTime() @@ -951,7 +937,6 @@ internal class OtelTracerProviderTest { // Given val testedProvider = OtelTracerProvider.Builder(stubSdkCore).setTraceRateLimit(1).build() val tracer = testedProvider.tracerBuilder(fakeInstrumentationName).build() - val blockingWriterWrapper = tracer.useBlockingWriter() // When val startNanos = System.nanoTime() @@ -1000,7 +985,6 @@ internal class OtelTracerProviderTest { .setSampleRate(100.0) .build() val tracer = testedProvider.tracerBuilder(fakeInstrumentationName).build() - val blockingWriterWrapper = tracer.useBlockingWriter() // When repeat(numberOfSpans) { @@ -1068,7 +1052,7 @@ internal class OtelTracerProviderTest { leastSignificantTraceId = span.leastSignificant64BitsTraceIdAsHex() mostSignificantTraceId = span.mostSignificant64BitsTraceIdAsHex() spanId = span.spanIdAsHex() - Thread.sleep(AndroidTracerTest.OP_DURATION_MS) + Thread.sleep(OP_DURATION_MS) span.end() } @@ -1091,7 +1075,7 @@ internal class OtelTracerProviderTest { hasError(0) hasName(DEFAULT_SPAN_NAME) hasResource(fakeOperationName) - hasDurationBetween(AndroidTracerTest.OP_DURATION_NS, fullDuration) + hasDurationBetween(OP_DURATION_NS, fullDuration) hasApplicationId(fakeRumApplicationId) hasSessionId(fakeRumSessionId) hasViewId(fakeRumViewId) @@ -1124,7 +1108,7 @@ internal class OtelTracerProviderTest { leastSignificantTraceId = span.leastSignificant64BitsTraceIdAsHex() mostSignificantTraceId = span.mostSignificant64BitsTraceIdAsHex() spanId = span.spanIdAsHex() - Thread.sleep(AndroidTracerTest.OP_DURATION_MS) + Thread.sleep(OP_DURATION_MS) span.end() } @@ -1147,7 +1131,7 @@ internal class OtelTracerProviderTest { hasError(0) hasName(DEFAULT_SPAN_NAME) hasResource(fakeOperationName) - hasDurationBetween(AndroidTracerTest.OP_DURATION_NS, fullDuration) + hasDurationBetween(OP_DURATION_NS, fullDuration) hasUserId(fakeUserId) hasUserName(fakeUserName) hasUserEmail(fakeUserEmail) @@ -1176,7 +1160,7 @@ internal class OtelTracerProviderTest { leastSignificantTraceId = span.leastSignificant64BitsTraceIdAsHex() mostSignificantTraceId = span.mostSignificant64BitsTraceIdAsHex() spanId = span.spanIdAsHex() - Thread.sleep(AndroidTracerTest.OP_DURATION_MS) + Thread.sleep(OP_DURATION_MS) span.end() } @@ -1199,7 +1183,7 @@ internal class OtelTracerProviderTest { hasError(0) hasName(DEFAULT_SPAN_NAME) hasResource(fakeOperation) - hasDurationBetween(AndroidTracerTest.OP_DURATION_NS, fullDuration) + hasDurationBetween(OP_DURATION_NS, fullDuration) hasGenericMetaValue("usr.$fakeUserKey", fakeUserValue) } } @@ -1226,7 +1210,7 @@ internal class OtelTracerProviderTest { leastSignificantTraceId = span.leastSignificant64BitsTraceIdAsHex() mostSignificantTraceId = span.mostSignificant64BitsTraceIdAsHex() spanId = span.spanIdAsHex() - Thread.sleep(AndroidTracerTest.OP_DURATION_MS) + Thread.sleep(OP_DURATION_MS) span.end() } @@ -1249,7 +1233,7 @@ internal class OtelTracerProviderTest { hasError(0) hasName(DEFAULT_SPAN_NAME) hasResource(fakeOperationName) - hasDurationBetween(AndroidTracerTest.OP_DURATION_NS, fullDuration) + hasDurationBetween(OP_DURATION_NS, fullDuration) hasGenericMetaValue("account.id", fakeAccountId) hasGenericMetaValue("account.name", fakeAccountName) } @@ -1281,7 +1265,7 @@ internal class OtelTracerProviderTest { leastSignificantTraceId = span.leastSignificant64BitsTraceIdAsHex() mostSignificantTraceId = span.mostSignificant64BitsTraceIdAsHex() spanId = span.spanIdAsHex() - Thread.sleep(AndroidTracerTest.OP_DURATION_MS) + Thread.sleep(OP_DURATION_MS) span.end() } @@ -1304,7 +1288,7 @@ internal class OtelTracerProviderTest { hasError(0) hasName(DEFAULT_SPAN_NAME) hasResource(fakeOperation) - hasDurationBetween(AndroidTracerTest.OP_DURATION_NS, fullDuration) + hasDurationBetween(OP_DURATION_NS, fullDuration) hasGenericMetaValue("account.$fakeAccountKey", fakeAccountValue) } } diff --git a/reliability/single-fit/trace/src/test/kotlin/com/datadog/android/trace/integration/otel/SpanExt.kt b/reliability/single-fit/trace/src/test/kotlin/com/datadog/android/trace/integration/otel/SpanExt.kt index 7ca767096e..48b1b0dfa3 100644 --- a/reliability/single-fit/trace/src/test/kotlin/com/datadog/android/trace/integration/otel/SpanExt.kt +++ b/reliability/single-fit/trace/src/test/kotlin/com/datadog/android/trace/integration/otel/SpanExt.kt @@ -6,8 +6,8 @@ package com.datadog.android.trace.integration.otel +import com.datadog.android.trace.api.span.DatadogSpan import com.datadog.tools.unit.getFieldValue -import com.datadog.trace.bootstrap.instrumentation.api.AgentSpan import io.opentelemetry.api.trace.Span internal fun Span.leastSignificant64BitsTraceIdAsHex(): String { @@ -23,6 +23,6 @@ internal fun Span.spanIdAsHex(): String { } internal fun Span.expectedSpanName(): String { - val agentSpan: AgentSpan = this.getFieldValue("delegate") - return agentSpan.operationName.toString() + val datadogSpan: DatadogSpan = this.getFieldValue("delegate") + return datadogSpan.operationName } diff --git a/reliability/single-fit/trace/src/test/kotlin/com/datadog/android/trace/integration/otel/TracerExt.kt b/reliability/single-fit/trace/src/test/kotlin/com/datadog/android/trace/integration/otel/TracerExt.kt index 3d5cf8932e..86739ff69d 100644 --- a/reliability/single-fit/trace/src/test/kotlin/com/datadog/android/trace/integration/otel/TracerExt.kt +++ b/reliability/single-fit/trace/src/test/kotlin/com/datadog/android/trace/integration/otel/TracerExt.kt @@ -6,21 +6,23 @@ package com.datadog.android.trace.integration.otel +import com.datadog.android.api.feature.Feature +import com.datadog.android.api.feature.FeatureScope import com.datadog.android.trace.integration.tests.utils.BlockingWriterWrapper import com.datadog.tools.unit.getFieldValue import com.datadog.tools.unit.setFieldValue import com.datadog.trace.common.writer.Writer -import com.datadog.trace.core.CoreTracer -import io.opentelemetry.api.trace.Tracer -internal fun Tracer.useBlockingWriter(): BlockingWriterWrapper { - val coreTracer: CoreTracer = this.getFieldValue("tracer") - val writer: Writer = coreTracer.getFieldValue("writer") - return if (writer !is BlockingWriterWrapper) { +private const val WRITER_FIELD_NAME = "coreTracerDataWriter" + +internal fun FeatureScope.useBlockingWriter(): BlockingWriterWrapper { + val feature = this.unwrap() + val writer: Writer = feature.getFieldValue(WRITER_FIELD_NAME) + return if (writer is BlockingWriterWrapper) { + writer + } else { val blockingWriterWrapper = BlockingWriterWrapper(writer) - coreTracer.setFieldValue("writer", blockingWriterWrapper) + feature.setFieldValue(WRITER_FIELD_NAME, blockingWriterWrapper) blockingWriterWrapper - } else { - writer } } diff --git a/sample/benchmark/src/main/java/com/datadog/benchmark/sample/ui/trace/TraceScenarioViewModel.kt b/sample/benchmark/src/main/java/com/datadog/benchmark/sample/ui/trace/TraceScenarioViewModel.kt index 22ffe8e385..67aba8faed 100644 --- a/sample/benchmark/src/main/java/com/datadog/benchmark/sample/ui/trace/TraceScenarioViewModel.kt +++ b/sample/benchmark/src/main/java/com/datadog/benchmark/sample/ui/trace/TraceScenarioViewModel.kt @@ -9,7 +9,7 @@ package com.datadog.benchmark.sample.ui.trace import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import com.datadog.trace.api.DDTags +import com.datadog.android.trace.api.DatadogTracingConstants import io.opentelemetry.api.trace.Span import io.opentelemetry.api.trace.StatusCode import io.opentelemetry.api.trace.Tracer @@ -182,14 +182,14 @@ internal class TraceScenarioViewModel( private fun launchTracingJob(task: TraceScenarioScreenState.TracingTask): Job { return viewModelScope.launch(defaultDispatcher) { val rootSpan = tracer.spanBuilder(task.config.spanOperation).apply { - setAttribute(DDTags.RESOURCE_NAME, task.config.spanResource) + setAttribute(DatadogTracingConstants.Tags.RESOURCE_NAME, task.config.spanResource) }.startSpan() if (task.config.isError) { rootSpan.apply { setStatus(StatusCode.ERROR) - setAttribute(DDTags.ERROR_TYPE, "simulated_error") - setAttribute(DDTags.ERROR_MSG, "Simulated error message") + setAttribute(DatadogTracingConstants.Tags.ERROR_TYPE, "simulated_error") + setAttribute(DatadogTracingConstants.Tags.ERROR_MSG, "Simulated error message") } } diff --git a/sample/benchmark/transitiveDependencies b/sample/benchmark/transitiveDependencies index ec39267a8b..c1f75ed037 100644 --- a/sample/benchmark/transitiveDependencies +++ b/sample/benchmark/transitiveDependencies @@ -109,9 +109,6 @@ io.ktor:ktor-websocket-serialization-jvm:2.3.13 : 6 Kb io.ktor:ktor-websockets-jvm:2.3.13 : 173 Kb io.opentelemetry:opentelemetry-api:1.40.0 : 138 Kb io.opentelemetry:opentelemetry-context:1.40.0 : 46 Kb -io.opentracing:opentracing-api:0.32.0 : 18 Kb -io.opentracing:opentracing-noop:0.32.0 : 10 Kb -io.opentracing:opentracing-util:0.32.0 : 10 Kb jakarta.inject:jakarta.inject-api:2.0.1 : 10 Kb javax.inject:javax.inject:1 : 2 Kb org.jetbrains.kotlin:kotlin-android-extensions-runtime:2.0.21 : 9 Kb diff --git a/sample/kotlin/build.gradle.kts b/sample/kotlin/build.gradle.kts index 5edde43e47..41a2245dc9 100644 --- a/sample/kotlin/build.gradle.kts +++ b/sample/kotlin/build.gradle.kts @@ -194,9 +194,6 @@ dependencies { implementation("androidx.legacy:legacy-support-v4:1.0.0") implementation("androidx.lifecycle:lifecycle-extensions:2.2.0") implementation("androidx.preference:preference-ktx:1.1.1") - implementation("io.opentracing.contrib:opentracing-rxjava-3:0.1.4") { - exclude(group = "io.opentracing") - } // Image Loading Library implementation(libs.coil) diff --git a/sample/kotlin/src/main/kotlin/com/datadog/android/sample/SampleApplication.kt b/sample/kotlin/src/main/kotlin/com/datadog/android/sample/SampleApplication.kt index 5f60530ae7..e67fef4211 100644 --- a/sample/kotlin/src/main/kotlin/com/datadog/android/sample/SampleApplication.kt +++ b/sample/kotlin/src/main/kotlin/com/datadog/android/sample/SampleApplication.kt @@ -51,21 +51,17 @@ import com.datadog.android.sessionreplay.TouchPrivacy import com.datadog.android.sessionreplay.compose.ComposeExtensionSupport import com.datadog.android.sessionreplay.material.MaterialExtensionSupport import com.datadog.android.timber.DatadogTree -import com.datadog.android.trace.AndroidTracer +import com.datadog.android.trace.DatadogTracing +import com.datadog.android.trace.GlobalDatadogTracer import com.datadog.android.trace.Trace import com.datadog.android.trace.TraceConfiguration -import com.datadog.android.trace.opentelemetry.OtelTracerProvider +import com.datadog.android.trace.opentelemetry.DatadogOpenTelemetry import com.datadog.android.vendor.sample.LocalServer import com.facebook.stetho.Stetho import com.google.gson.GsonBuilder import com.google.gson.JsonArray import com.google.gson.JsonObject import io.opentelemetry.api.GlobalOpenTelemetry -import io.opentelemetry.api.OpenTelemetry -import io.opentelemetry.api.trace.TracerProvider -import io.opentelemetry.context.propagation.ContextPropagators -import io.opentracing.rxjava3.TracingRxJava3Utils -import io.opentracing.util.GlobalTracer import okhttp3.OkHttpClient import retrofit2.Retrofit import retrofit2.adapter.rxjava3.RxJava3CallAdapterFactory @@ -146,7 +142,6 @@ class SampleApplication : Application() { private fun initializeDatadog() { val preferences = Preferences.defaultPreferences(this) - Datadog.setVerbosity(Log.VERBOSE) Datadog.initialize( this, @@ -163,30 +158,9 @@ class SampleApplication : Application() { initializeUserInfo(preferences) initializeAccountInfo(preferences) - GlobalTracer.registerIfAbsent( - AndroidTracer.Builder() - .setService(BuildConfig.APPLICATION_ID) - .build() - ) - - val rumConfig = createRumConfiguration() - Rum.enable(rumConfig) - - GlobalOpenTelemetry.set(object : OpenTelemetry { - private val tracerProvider = OtelTracerProvider.Builder() - .setService(BuildConfig.APPLICATION_ID) - .build() - - override fun getTracerProvider(): TracerProvider { - return tracerProvider - } + Rum.enable(createRumConfiguration()) - override fun getPropagators(): ContextPropagators { - return ContextPropagators.noop() - } - }) GlobalRumMonitor.get().debug = true - TracingRxJava3Utils.enableTracing(GlobalTracer.get()) } private fun initializeUserInfo(preferences: Preferences.DefaultPreferences) { @@ -221,6 +195,16 @@ class SampleApplication : Application() { } }.build() Trace.enable(tracesConfig) + + GlobalDatadogTracer.registerIfAbsent( + DatadogTracing.newTracerBuilder() + .withPartialFlushMinSpans(1) + .build() + ) + + GlobalOpenTelemetry.set( + DatadogOpenTelemetry(BuildConfig.APPLICATION_ID) + ) } private fun initializeLogs() { diff --git a/sample/kotlin/src/main/kotlin/com/datadog/android/sample/account/AccountFragment.kt b/sample/kotlin/src/main/kotlin/com/datadog/android/sample/account/AccountFragment.kt index 922f31031b..31bedc4714 100644 --- a/sample/kotlin/src/main/kotlin/com/datadog/android/sample/account/AccountFragment.kt +++ b/sample/kotlin/src/main/kotlin/com/datadog/android/sample/account/AccountFragment.kt @@ -69,7 +69,7 @@ internal class AccountFragment : Fragment(), View.OnClickListener { AGE_KEY to age ) ) - log("Updated account info") + logMessage("Updated account info") } Snackbar.make(view ?: v.rootView, "Account info updated", Snackbar.LENGTH_SHORT).show() } diff --git a/sample/kotlin/src/main/kotlin/com/datadog/android/sample/data/DataRepository.kt b/sample/kotlin/src/main/kotlin/com/datadog/android/sample/data/DataRepository.kt index 91f39c90a6..0ae676a723 100644 --- a/sample/kotlin/src/main/kotlin/com/datadog/android/sample/data/DataRepository.kt +++ b/sample/kotlin/src/main/kotlin/com/datadog/android/sample/data/DataRepository.kt @@ -10,8 +10,8 @@ import com.datadog.android.sample.data.db.LocalDataSource import com.datadog.android.sample.data.model.Log import com.datadog.android.sample.data.remote.RemoteDataSource import com.datadog.android.sample.datalist.DataSourceType -import io.opentracing.Scope -import io.opentracing.util.GlobalTracer +import com.datadog.android.trace.GlobalDatadogTracer +import com.datadog.android.trace.api.scope.DatadogScope import io.reactivex.rxjava3.core.Flowable import io.reactivex.rxjava3.core.Single import io.reactivex.rxjava3.schedulers.Schedulers @@ -23,7 +23,7 @@ internal class DataRepository( @Suppress("SimpleRedundantLet") fun getLogs(query: String): Flowable> { - var spanScope: Scope? = null + var spanScope: DatadogScope? = null return Single.concat( localDataSource.fetchLogs(), remoteDataSource.getLogs(query) @@ -32,13 +32,14 @@ internal class DataRepository( localDataSource.persistLogs(it) } .doOnSubscribe { - val span = GlobalTracer.get() + val tracer = GlobalDatadogTracer.get() + val span = tracer .buildSpan("Fetch recent logs") .start() - spanScope = GlobalTracer.get().scopeManager().activate(span) + spanScope = tracer.activateSpan(span) } .doFinally { - GlobalTracer.get().activeSpan()?.let { + GlobalDatadogTracer.get().activeSpan()?.let { it.finish() } spanScope?.close() diff --git a/sample/kotlin/src/main/kotlin/com/datadog/android/sample/traces/TracesFragment.kt b/sample/kotlin/src/main/kotlin/com/datadog/android/sample/traces/TracesFragment.kt index 076fc7c9d5..ef37011e17 100644 --- a/sample/kotlin/src/main/kotlin/com/datadog/android/sample/traces/TracesFragment.kt +++ b/sample/kotlin/src/main/kotlin/com/datadog/android/sample/traces/TracesFragment.kt @@ -18,6 +18,7 @@ import androidx.fragment.app.Fragment import androidx.lifecycle.ViewModelProviders import com.datadog.android.sample.R import com.datadog.android.sample.SampleApplication +import com.datadog.android.trace.withinSpan internal class TracesFragment : Fragment(), View.OnClickListener { @@ -36,6 +37,7 @@ internal class TracesFragment : Fragment(), View.OnClickListener { ): View? { val rootView = inflater.inflate(R.layout.fragment_traces, container, false) rootView.findViewById