Skip to content

RUM-3536 Introduce the TraceContextInjection to handle sampling in distributed traces#2111

Merged
mariusc83 merged 3 commits into
developfrom
mconstantin/rum-3536/correctly-handle-sampling-in-distributed-traces
Jul 19, 2024
Merged

RUM-3536 Introduce the TraceContextInjection to handle sampling in distributed traces#2111
mariusc83 merged 3 commits into
developfrom
mconstantin/rum-3536/correctly-handle-sampling-in-distributed-traces

Conversation

@mariusc83

@mariusc83 mariusc83 commented Jun 27, 2024

Copy link
Copy Markdown
Member

What does this PR do?

Following the requirements specified in this RFC we are adding this new API and fixing the traces propagation in our TracingInterceptor.

This also fixes the issue: #2124

Motivation

What inspired you to submit this pull request?

Additional Notes

Anything else we should know when reviewing?

Review checklist (to be filled by reviewers)

  • Feature or bugfix MUST have appropriate tests (unit, integration, e2e)
  • Make sure you discussed the feature or bugfix with the maintaining team in an Issue
  • Make sure each commit and the PR mention the Issue number (cf the CONTRIBUTING doc)

@mariusc83 mariusc83 self-assigned this Jun 27, 2024
@mariusc83
mariusc83 force-pushed the mconstantin/rum-1829/use-128-bit-trace-ids branch 3 times, most recently from 256c37e to 8b3c6ab Compare June 27, 2024 12:41
@mariusc83
mariusc83 force-pushed the mconstantin/rum-3536/correctly-handle-sampling-in-distributed-traces branch from 07e0c12 to bc386dd Compare June 28, 2024 07:44
private val sampleRate: Float = DEFAULT_SAMPLE_RATE
) : AppGlideModule() {

private val firstPartyHostsWithHeaderType = firstPartyHosts.associateWith {

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the only problem I see with this is that some users will not know what to use for their tracing header types values so we might want to provide a Public Constant: DEFAULT_TYPES here ?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why then we need to provide association if setOf(TracingHeaderType.DATADOG, TracingHeaderType.TRACECONTEXT) is what already set in the SDK by default? Shouldn't we have a public API for both firstPartyHosts and firstPartyHostsWithHeaderType as we have now?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we could but the way I see it firstPartyHosts is mandatory (not optional) so it will have to be in the Builder constructor. If we want to introduce both APIs and if a user wants to provide custom headerTypes for each host it will look like this:

val builder = Builder(tracedHosts).setFirstPartyHostsWithHeaderTypes(firstPartyHostsWithHeaderTypes)

I do not like that.

@mariusc83 mariusc83 Jun 28, 2024

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

otherwise we could have:

val builder = Builder().setFirstPartyHostsWithHeaderTypes(firstPartyHostsWithHeaderTypes)
val builder = Builder().setFirstPartyHosts(firstPartyHosts)

But this will mean that some users will forget to set this up in the builder and will create support issues.

)
val interceptorBuilder = DatadogInterceptor.Builder(firstPartyHostsWithHeaderType)
.setTraceSampler(RateBasedSampler(sampleRate))
sdkInstanceName?.let { interceptorBuilder.setSdkInstanceName(it) }

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would pull sdkInstanceName to the DatadogInterceptor.Builder constructor - SDK core association is a major part of the API, not something optional.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in the same time we have it optional now, I would only do that if we do not allow it as optional anymore otherwise it doesn't make sense to have it in the constructor if it can be NULL

private val sampleRate: Float = DEFAULT_SAMPLE_RATE
) : AppGlideModule() {

private val firstPartyHostsWithHeaderType = firstPartyHosts.associateWith {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why then we need to provide association if setOf(TracingHeaderType.DATADOG, TracingHeaderType.TRACECONTEXT) is what already set in the SDK by default? Shouldn't we have a public API for both firstPartyHosts and firstPartyHostsWithHeaderType as we have now?

/**
* A Builder for the [DatadogInterceptor].
*/
open class Builder(tracedHosts: Map<String, Set<TracingHeaderType>>) :

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We probably need to remove open modifier from here and DatadogInterceptor as well. I think it was originally added because we have RumInterceptor : DatadogInterceptor.

But removing it involves a risk - some customers may have already sub-classed DatadogInterceptor.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't want to do that, at least not now.

/**
* A Builder for the [DatadogInterceptor].
*/
open class Builder(tracedHosts: Map<String, Set<TracingHeaderType>>) :

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO having a map here is unnecessary complication and it should be just tracedHosts: List<String>. Asking to provide tracing header types may be too advanced and increases setup curve. Probably default header types will work for 90%+ installations.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it will but then we will have to provide the setTracedHostWithHeaderTypes method in the Builder and that will create some confusion. It will look like this:

val builder = Builder(tracedHosts).setFirstPartyHostsWithHeaderTypes(firstPartyHostsWithHeaderTypes)

It will look ugly and users will wonder what is the need for the tracedHosts in the Builder.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we will have to provide the setTracedHostWithHeaderTypes method in the Builder

It won't be needed if constructor itself will have another overload. APM setup is already quite complicated, we need to have it simpler with reasonable defaults.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so we provide overloads to this Builder constructor ? I would rather not and just provide a public api DEFAULT_HEADER_TYPES = setOf(TracingHeaderType.DATADOG, TracingHeaderType.TRACECONTEXT)
We are trying to run away from overloads. I am opened to any of this options but I would prefer the one without overloads.
@xgouchet @plousada , what do you think ?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We are trying to run away from overloads
It is just because there were too much of them in the original constructor. Basically we need to put what is mandatory in the constructor and cannot be replaced by some default value.

For now it is only the list of the traced hosts, and the rest of the pieces needed to make interceptor work can have a default value, so they go into the methods of the Builder - this separation was missing in the original design of the DatadogInterceptor. So having an overload just because of the single mandatory parameter looks fine to me.


private var rumResourceAttributesProvider: RumResourceAttributesProvider = NoOpRumResourceAttributesProvider()

override fun getThis(): Builder {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

getThis? why is it needed?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Generics check the Builder and BaseBuilder class. It is just a patter for having a BaseBuilder using Java Generics. In any case this is internal

@xgouchet xgouchet Jul 17, 2024

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tip

Maybe this method could be protected, otherwise it might be confusing to our customers

@xgouchet it is internal and not visible from outside the package. Check the method definition in the BaseBuilder class.


/**
* Sets the [RumResourceAttributesProvider] to use to provide custom attributes to the RUM.
* By default, it is a NoOp.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* By default, it is a NoOp.
* By default, it won't attach any custom attributes.

@mariusc83
mariusc83 force-pushed the mconstantin/rum-1829/use-128-bit-trace-ids branch from 8b3c6ab to 446a7b4 Compare June 28, 2024 09:44
Base automatically changed from mconstantin/rum-1829/use-128-bit-trace-ids to develop June 28, 2024 12:33
@mariusc83
mariusc83 force-pushed the mconstantin/rum-3536/correctly-handle-sampling-in-distributed-traces branch 6 times, most recently from 595cd99 to 9e0b008 Compare July 16, 2024 15:36
@codecov-commenter

codecov-commenter commented Jul 16, 2024

Copy link
Copy Markdown

Codecov Report

Attention: Patch coverage is 92.50000% with 9 lines in your changes missing coverage. Please review.

Project coverage is 69.92%. Comparing base (fc53808) to head (0470b37).
Report is 5 commits behind head on develop.

Additional details and impacted files
@@             Coverage Diff             @@
##           develop    #2111      +/-   ##
===========================================
- Coverage    70.03%   69.92%   -0.11%     
===========================================
  Files          721      722       +1     
  Lines        26773    26859      +86     
  Branches      4499     4506       +7     
===========================================
+ Hits         18749    18780      +31     
- Misses        6771     6819      +48     
- Partials      1253     1260       +7     
Files Coverage Δ
...om/datadog/android/okhttp/TraceContextInjection.kt 100.00% <100.00%> (ø)
...in/com/datadog/android/glide/DatadogGlideModule.kt 95.65% <75.00%> (-4.35%) ⬇️
...n/com/datadog/android/okhttp/DatadogInterceptor.kt 60.12% <86.36%> (-13.35%) ⬇️
...datadog/android/okhttp/trace/TracingInterceptor.kt 76.80% <94.57%> (-5.19%) ⬇️

... and 31 files with indirect coverage changes

@mariusc83
mariusc83 marked this pull request as ready for review July 16, 2024 16:54
@mariusc83
mariusc83 requested review from a team as code owners July 16, 2024 16:54
@mariusc83
mariusc83 force-pushed the mconstantin/rum-3536/correctly-handle-sampling-in-distributed-traces branch from 9e0b008 to 1225d92 Compare July 17, 2024 08:37

private var rumResourceAttributesProvider: RumResourceAttributesProvider = NoOpRumResourceAttributesProvider()

override fun getThis(): Builder {

@xgouchet xgouchet Jul 17, 2024

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tip

Maybe this method could be protected, otherwise it might be confusing to our customers

@xgouchet it is internal and not visible from outside the package. Check the method definition in the BaseBuilder class.

@mariusc83
mariusc83 force-pushed the mconstantin/rum-3536/correctly-handle-sampling-in-distributed-traces branch from 1225d92 to 5dfc191 Compare July 17, 2024 12:32
@mariusc83
mariusc83 requested a review from 0xnm July 17, 2024 14:41
class Builder : BaseBuilder<DatadogInterceptor, Builder>
constructor(Map<String, Set<com.datadog.android.trace.TracingHeaderType>>)
constructor(List<String>)
override fun getThis(): Builder

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

blocking: we shouldn't have this as a part of the public API. Is there a way to avoid that?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmm I really do not know why is added here as it is marked as internal. I need to check again. Thanks for spotting this. Do you have any idea why it still appears here ?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe I should mark it as internal also when I override...let me try that...that will be stupid

}

fun getExpectedOrigin(): String? {
return fakeOrigin

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor: Why do we need this method and not compare directly to fakeOrigin? Also, can this return a null value? fakeOrigin is defined as an alphabetical string so should be non-null

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just legacy code that I did not change

@ganeshnj
ganeshnj self-requested a review July 18, 2024 13:05
@mariusc83
mariusc83 force-pushed the mconstantin/rum-3536/correctly-handle-sampling-in-distributed-traces branch 2 times, most recently from e22f2fd to 39f6e13 Compare July 18, 2024 14:28
@mariusc83
mariusc83 requested a review from 0xnm July 18, 2024 14:59
@datadog-staging-us1-crawler-test

Copy link
Copy Markdown

Code Vulnerabilities

Error badge  Notice badge

Code Quality Violations

Error badge  Warning badge  Notice badge

@0xnm 0xnm left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

reviewed only prod files (not tests), looks good to me. I left some suggestions.

public fun <init> (Ljava/util/Map;)V
public fun build ()Lcom/datadog/android/okhttp/DatadogInterceptor;
public synthetic fun build ()Lcom/datadog/android/okhttp/trace/TracingInterceptor;
public synthetic fun getThis$dd_sdk_android_okhttp_release ()Lcom/datadog/android/okhttp/trace/TracingInterceptor$BaseBuilder;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we are still exposing such things in Java API. Maybe there is some annotation which may hide it?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will try but honestly if there's not any I will not fix this tool in this PR

/**
* 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 GlobalTracer, the tracer must be

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* be automatically tracked by this interceptor. If registering a GlobalTracer, the tracer must be
* be automatically tracked by this interceptor. If registering a [GlobalTracer], the tracer must be

/**
* 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

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* be automatically tracked by this interceptor. If registering a GlobalTracer, the tracer must be
* be automatically tracked by this interceptor. If registering a [GlobalTracer], the tracer must be

return getThis()
}

@Suppress("UndocumentedPublicFunction")

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can be removed, it is not a public function anymore

internal abstract fun getThis(): R

/**
* Build the [TracingInterceptor].

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* Build the [TracingInterceptor].
* Build the interceptor.

@mariusc83
mariusc83 force-pushed the mconstantin/rum-3536/correctly-handle-sampling-in-distributed-traces branch from 39f6e13 to 0470b37 Compare July 19, 2024 09:05
@mariusc83
mariusc83 merged commit 6286611 into develop Jul 19, 2024
@mariusc83
mariusc83 deleted the mconstantin/rum-3536/correctly-handle-sampling-in-distributed-traces branch July 19, 2024 09:33
@xgouchet xgouchet added this to the 2.12.x milestone Jul 31, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants