Skip to content

Commit

Permalink
RUM-3249 Provide the bundleWithRum capability for OtelTracer
Browse files Browse the repository at this point in the history
  • Loading branch information
mariusc83 committed Apr 3, 2024
1 parent 0e43b0d commit 5fa2926
Show file tree
Hide file tree
Showing 8 changed files with 467 additions and 19 deletions.
1 change: 1 addition & 0 deletions detekt_custom.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1257,6 +1257,7 @@ datadog:
# endregion
# region Opentelemetry
- "io.opentelemetry.api.trace.TracerBuilder.build()"
- "io.opentelemetry.api.trace.SpanBuilder.setAttribute(kotlin.String?, kotlin.String?)"
- "io.opentelemetry.api.trace.TracerBuilder.setInstrumentationVersion(kotlin.String?)"
# endregion
# region RxJava
Expand Down
3 changes: 2 additions & 1 deletion features/dd-sdk-android-trace/api/apiSurface
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ class com.datadog.android.trace.AndroidTracer : com.datadog.opentracing.DDTracer
fun logThrowable(io.opentracing.Span, Throwable)
fun logErrorMessage(io.opentracing.Span, String)
class com.datadog.android.trace.OtelTracerProvider : io.opentelemetry.api.trace.TracerProvider
constructor(com.datadog.trace.bootstrap.instrumentation.api.AgentTracer.TracerAPI, com.datadog.android.api.InternalLogger)
constructor(com.datadog.android.api.feature.FeatureSdkCore, com.datadog.trace.bootstrap.instrumentation.api.AgentTracer.TracerAPI, com.datadog.android.api.InternalLogger, Boolean)
override fun get(String): io.opentelemetry.api.trace.Tracer
override fun get(String, String): io.opentelemetry.api.trace.Tracer
override fun tracerBuilder(String): io.opentelemetry.api.trace.TracerBuilder
Expand All @@ -27,6 +27,7 @@ class com.datadog.android.trace.OtelTracerProvider : io.opentelemetry.api.trace.
fun setPartialFlushThreshold(Int): Builder
fun addTag(String, String): Builder
fun setSampleRate(Double): Builder
fun setBundleWithRumEnabled(Boolean): Builder
override fun toString(): String
companion object
fun io.opentracing.Span.setError(Throwable)
Expand Down
6 changes: 4 additions & 2 deletions features/dd-sdk-android-trace/api/dd-sdk-android-trace.api
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ public final class com/datadog/android/trace/AndroidTracer$Companion {

public final class com/datadog/android/trace/OtelTracerProvider : io/opentelemetry/api/trace/TracerProvider {
public static final field Companion Lcom/datadog/android/trace/OtelTracerProvider$Companion;
public fun <init> (Lcom/datadog/trace/bootstrap/instrumentation/api/AgentTracer$TracerAPI;Lcom/datadog/android/api/InternalLogger;)V
public fun <init> (Lcom/datadog/android/api/feature/FeatureSdkCore;Lcom/datadog/trace/bootstrap/instrumentation/api/AgentTracer$TracerAPI;Lcom/datadog/android/api/InternalLogger;Z)V
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;
public fun toString ()Ljava/lang/String;
Expand All @@ -41,6 +41,7 @@ public final class com/datadog/android/trace/OtelTracerProvider$Builder {
public synthetic fun <init> (Lcom/datadog/android/api/SdkCore;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun addTag (Ljava/lang/String;Ljava/lang/String;)Lcom/datadog/android/trace/OtelTracerProvider$Builder;
public final fun build ()Lcom/datadog/android/trace/OtelTracerProvider;
public final fun setBundleWithRumEnabled (Z)Lcom/datadog/android/trace/OtelTracerProvider$Builder;
public final fun setPartialFlushThreshold (I)Lcom/datadog/android/trace/OtelTracerProvider$Builder;
public final fun setSampleRate (D)Lcom/datadog/android/trace/OtelTracerProvider$Builder;
public final fun setService (Ljava/lang/String;)Lcom/datadog/android/trace/OtelTracerProvider$Builder;
Expand Down Expand Up @@ -928,11 +929,12 @@ public class com/datadog/opentelemetry/trace/OtelSpanLink : com/datadog/trace/bo

public class com/datadog/opentelemetry/trace/OtelTracer : io/opentelemetry/api/trace/Tracer {
public fun <init> (Ljava/lang/String;Lcom/datadog/trace/bootstrap/instrumentation/api/AgentTracer$TracerAPI;Lcom/datadog/android/api/InternalLogger;)V
public fun <init> (Ljava/lang/String;Lcom/datadog/trace/bootstrap/instrumentation/api/AgentTracer$TracerAPI;Lcom/datadog/android/api/InternalLogger;Ljava/util/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 <init> (Ljava/lang/String;Lcom/datadog/trace/bootstrap/instrumentation/api/AgentTracer$TracerAPI;Lcom/datadog/android/api/InternalLogger;)V
public fun <init> (Ljava/lang/String;Lcom/datadog/trace/bootstrap/instrumentation/api/AgentTracer$TracerAPI;Lcom/datadog/android/api/InternalLogger;Ljava/util/function/Function;)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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,30 +11,49 @@
import com.datadog.android.api.InternalLogger;
import com.datadog.trace.bootstrap.instrumentation.api.AgentTracer;

import java.util.function.Function;

import io.opentelemetry.api.trace.SpanBuilder;
import io.opentelemetry.api.trace.Tracer;

public class OtelTracer implements Tracer {

private static final Function<SpanBuilder, SpanBuilder> NO_OP_DECORATOR = spanBuilder -> spanBuilder;

@NonNull
private final AgentTracer.TracerAPI tracer;
@NonNull
private final String instrumentationScopeName;
@NonNull
private final InternalLogger logger;

@NonNull
private final Function<SpanBuilder, SpanBuilder> spanBuilderDecorator;

public OtelTracer(
@NonNull String instrumentationScopeName,
@NonNull AgentTracer.TracerAPI tracer,
@NonNull InternalLogger logger) {
@NonNull InternalLogger logger,
@NonNull Function<SpanBuilder, SpanBuilder> spanBuilderDecorator) {
this.instrumentationScopeName = instrumentationScopeName;
this.tracer = tracer;
this.logger = logger;
this.spanBuilderDecorator = spanBuilderDecorator;
}

public OtelTracer(
@NonNull String instrumentationScopeName,
@NonNull AgentTracer.TracerAPI tracer,
@NonNull InternalLogger logger) {
this(instrumentationScopeName, tracer, logger, NO_OP_DECORATOR);
}

@Override
public SpanBuilder spanBuilder(String spanName) {
AgentTracer.SpanBuilder delegate =
this.tracer.buildSpan(instrumentationScopeName, OtelConventions.SPAN_KIND_INTERNAL).withResourceName(spanName);
return new OtelSpanBuilder(delegate, logger);
this.tracer
.buildSpan(instrumentationScopeName, OtelConventions.SPAN_KIND_INTERNAL)
.withResourceName(spanName);
return spanBuilderDecorator.apply(new OtelSpanBuilder(delegate, logger));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@
import com.datadog.android.api.InternalLogger;
import com.datadog.trace.bootstrap.instrumentation.api.AgentTracer;

import java.util.function.Function;

import io.opentelemetry.api.trace.SpanBuilder;
import io.opentelemetry.api.trace.Tracer;
import io.opentelemetry.api.trace.TracerBuilder;

Expand All @@ -25,13 +28,19 @@ public class OtelTracerBuilder implements TracerBuilder {
@NonNull
private final InternalLogger logger;

@NonNull
private final Function<SpanBuilder, SpanBuilder> spanBuilderDecorator;


public OtelTracerBuilder(
@NonNull String instrumentationScopeName,
@NonNull AgentTracer.TracerAPI coreTracer,
@NonNull InternalLogger logger) {
@NonNull InternalLogger logger,
@NonNull Function<SpanBuilder, SpanBuilder> spanBuilderDecorator) {
this.coreTracer = coreTracer;
this.instrumentationScopeName = instrumentationScopeName;
this.logger = logger;
this.spanBuilderDecorator = spanBuilderDecorator;
}

@Override
Expand All @@ -48,6 +57,6 @@ public TracerBuilder setInstrumentationVersion(String instrumentationScopeVersio

@Override
public Tracer build() {
return new OtelTracer(this.instrumentationScopeName, this.coreTracer, this.logger);
return new OtelTracer(this.instrumentationScopeName, this.coreTracer, this.logger, this.spanBuilderDecorator);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,15 @@ 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.log.LogAttributes
import com.datadog.android.trace.internal.TracingFeature
import com.datadog.android.trace.internal.data.NoOpOtelWriter
import com.datadog.opentelemetry.trace.OtelTracerBuilder
import com.datadog.trace.api.IdGenerationStrategy
import com.datadog.trace.api.config.TracerConfig
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
Expand All @@ -34,8 +36,10 @@ import java.util.Properties
*
*/
class OtelTracerProvider(
private val sdkCore: FeatureSdkCore,
private val coreTracer: AgentTracer.TracerAPI,
private val internalLogger: InternalLogger
private val internalLogger: InternalLogger,
private val bundleWithRumEnabled: Boolean
) : TracerProvider {

private val tracers: MutableMap<String, Tracer> = mutableMapOf()
Expand Down Expand Up @@ -76,15 +80,12 @@ class OtelTracerProvider(
/** @inheritDoc */
override fun tracerBuilder(instrumentationScopeName: String): TracerBuilder {
val resolvedInstrumentationScopeName = resolveInstrumentationScopeName(instrumentationScopeName)
return OtelTracerBuilder(resolvedInstrumentationScopeName, coreTracer, internalLogger)
}

private fun resolveInstrumentationScopeName(instrumentationScopeName: String): String {
return if (instrumentationScopeName.trim { it <= ' ' }.isEmpty()) {
DEFAULT_TRACER_NAME
} else {
instrumentationScopeName
}
return OtelTracerBuilder(
resolvedInstrumentationScopeName,
coreTracer,
internalLogger,
resolveSpanBuilderDecorator()
)
}

/**
Expand Down Expand Up @@ -112,6 +113,7 @@ class OtelTracerProvider(
}
private var partialFlushThreshold = DEFAULT_PARTIAL_MIN_FLUSH
private val globalTags: MutableMap<String, String> = mutableMapOf()
private var bundleWithRumEnabled: Boolean = true

/**
* @param sdkCore SDK instance to bind to. If not provided, default instance will be used.
Expand All @@ -134,14 +136,23 @@ class OtelTracerProvider(
{ TRACING_NOT_ENABLED_ERROR_MESSAGE }
)
}
val rumFeature = sdkCore.getFeature(Feature.RUM_FEATURE_NAME)
if (bundleWithRumEnabled && rumFeature == null) {
sdkCore.internalLogger.log(
InternalLogger.Level.WARN,
InternalLogger.Target.USER,
{ RUM_NOT_ENABLED_ERROR_MESSAGE }
)
bundleWithRumEnabled = false
}
val coreTracer = CoreTracer.CoreTracerBuilder(sdkCore.internalLogger)
.withProperties(properties())
.serviceName(serviceName)
.writer(tracingFeature?.otelDataWriter ?: NoOpOtelWriter())
.partialFlushMinSpans(partialFlushThreshold)
.idGenerationStrategy(IdGenerationStrategy.fromName("SECURE_RANDOM", false))
.build()
return OtelTracerProvider(coreTracer, sdkCore.internalLogger)
return OtelTracerProvider(sdkCore, coreTracer, sdkCore.internalLogger, bundleWithRumEnabled)
}

/**
Expand Down Expand Up @@ -194,6 +205,17 @@ class OtelTracerProvider(
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
}

internal fun properties(): Properties {
val properties = Properties()
properties.setProperty(
Expand Down Expand Up @@ -228,11 +250,59 @@ class OtelTracerProvider(
// endregion
}

// region Internal

private fun resolveInstrumentationScopeName(instrumentationScopeName: String): String {
return if (instrumentationScopeName.trim { it <= ' ' }.isEmpty()) {
DEFAULT_TRACER_NAME
} else {
instrumentationScopeName
}
}

private fun resolveSpanBuilderDecorator(): (SpanBuilder) -> SpanBuilder {
return if (bundleWithRumEnabled) {
resolveSpanBuilderDecoratorFromContext()
} else {
NO_OP_SPAN_BUILDER_DECORATOR
}
}

private fun resolveSpanBuilderDecoratorFromContext(): (SpanBuilder) -> SpanBuilder = { spanBuilder ->
val rumContext = sdkCore.getFeatureContext(Feature.RUM_FEATURE_NAME)
val applicationId = rumContext[RUM_APPLICATION_ID_KEY] as? String
val sessionId = rumContext[RUM_SESSION_ID_KEY] as? String
val viewId = rumContext[RUM_VIEW_ID_KEY] as? String
val actionId = rumContext[RUM_ACTION_ID_KEY] as? String
if (applicationId != null && sessionId != null && viewId != null) {
spanBuilder.setAttribute(LogAttributes.RUM_APPLICATION_ID, applicationId)
spanBuilder.setAttribute(LogAttributes.RUM_SESSION_ID, sessionId)
spanBuilder.setAttribute(LogAttributes.RUM_VIEW_ID, viewId)
if (actionId != null) {
spanBuilder.setAttribute(LogAttributes.RUM_ACTION_ID, actionId)
}
} else {
internalLogger.log(
InternalLogger.Level.WARN,
InternalLogger.Target.USER,
{ RUM_CONTEXT_MISSING_ERROR_MESSAGE }
)
}
spanBuilder
}

override fun toString(): String {
return "OtelTracerProvider/${super.toString()}"
}

// endregion

companion object {
internal const val RUM_APPLICATION_ID_KEY = "application_id"
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"
Expand All @@ -245,6 +315,13 @@ class OtelTracerProvider(
internal const val DEFAULT_SERVICE_NAME_IS_MISSING_ERROR_MESSAGE =
"Default service name is missing during" +
" OtelTracerProvider creation, did you initialize SDK?"
internal const val RUM_NOT_ENABLED_ERROR_MESSAGE =
"You're trying to bundle the traces with a RUM context, " +
"but the RUM feature was disabled in your Configuration. " +
"No RUM context will be attached to your traces in this case."
internal const val RUM_CONTEXT_MISSING_ERROR_MESSAGE =
"You are trying to bundle the traces with a RUM context, " +
"but the RUM context is missing. No RUM context will be attached to your traces in this case."

// the minimum closed spans required for triggering a flush and deliver
// everything to the writer
Expand Down
Loading

0 comments on commit 5fa2926

Please sign in to comment.