Skip to content

Conversation

@satween
Copy link
Contributor

@satween satween commented Nov 27, 2025

What does this PR do?

This is a draft PR. work in progress

  • Adds instrumentation for the Crontet library.
  • Currently, only RUM Resources are supported (see the RFC in the JIRA ticket).
  • The common code base aimed to be shared between OkHttp and Cronet has been moved into RumResourceInstrumentation, but neither DatadogInterceptor nor TracingInterceptor are using it.
  • The instrumentation is added as a wrapper on top of the Cronet API that allows customer to use several instrumentation libraries at the same time.

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)

@satween satween changed the title RUM-12635: RUM support for Cronet [WIP] RUM-12635: RUM support for Cronet Nov 27, 2025
@satween satween force-pushed the tvaleev/feature/RUM-12635-wrapper-support branch from 0a842f7 to ec8f689 Compare November 27, 2025 13:51
@datadog-datadog-prod-us1

This comment has been minimized.

@codecov-commenter
Copy link

Codecov Report

❌ Patch coverage is 56.90608% with 156 lines in your changes missing coverage. Please review.
✅ Project coverage is 71.00%. Comparing base (0c156cc) to head (ec8f689).

Files with missing lines Patch % Lines
...oid/rum/internal/net/RumResourceInstrumentation.kt 0.00% 70 Missing ⚠️
...adog/android/okhttp/internal/OkHttpResponseInfo.kt 0.00% 32 Missing ⚠️
...lin/com/datadog/android/rum/resource/RequestExt.kt 0.00% 15 Missing ⚠️
.../com/datadog/android/cronet/DatadogCronetEngine.kt 88.17% 11 Missing ⚠️
.../com/datadog/android/core/internal/net/HttpSpec.kt 0.00% 8 Missing ⚠️
...adog/android/cronet/internal/CronetResponseInfo.kt 25.00% 6 Missing ⚠️
...net/internal/DatadogRequestFinishedInfoListener.kt 89.29% 3 Missing and 3 partials ⚠️
...tadog/android/cronet/internal/CronetRequestInfo.kt 80.00% 1 Missing and 2 partials ⚠️
...tadog/android/okhttp/internal/OkHttpRequestInfo.kt 75.00% 1 Missing and 1 partial ⚠️
...kotlin/com/datadog/android/api/net/ResponseInfo.kt 0.00% 1 Missing ⚠️
... and 2 more
Additional details and impacted files
@@                Coverage Diff                 @@
##           feature/cronet    #3029      +/-   ##
==================================================
- Coverage           71.22%   71.00%   -0.22%     
==================================================
  Files                 859      869      +10     
  Lines               31457    31802     +345     
  Branches             5306     5352      +46     
==================================================
+ Hits                22405    22581     +176     
- Misses               7544     7711     +167     
- Partials             1508     1510       +2     
Files with missing lines Coverage Δ
...tadog/android/cronet/internal/DatadogUrlRequest.kt 100.00% <100.00%> (ø)
...com/datadog/android/okhttp/DatadogEventListener.kt 96.15% <ø> (ø)
...n/com/datadog/android/okhttp/DatadogInterceptor.kt 73.86% <ø> (-0.15%) ⬇️
...kotlin/com/datadog/android/api/net/ResponseInfo.kt 0.00% <0.00%> (ø)
...tadog/android/rum/RumResourceAttributesProvider.kt 0.00% <0.00%> (ø)
...ndroid/cronet/internal/DatadogUrlRequestBuilder.kt 97.67% <97.67%> (ø)
...tadog/android/okhttp/internal/OkHttpRequestInfo.kt 75.00% <75.00%> (ø)
...tadog/android/cronet/internal/CronetRequestInfo.kt 80.00% <80.00%> (ø)
...adog/android/cronet/internal/CronetResponseInfo.kt 25.00% <25.00%> (ø)
...net/internal/DatadogRequestFinishedInfoListener.kt 89.29% <89.29%> (ø)
... and 5 more

... and 29 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@satween satween force-pushed the tvaleev/feature/RUM-12635-wrapper-support branch from ec8f689 to a111d25 Compare November 28, 2025 10:09
@satween satween changed the title [WIP] RUM-12635: RUM support for Cronet RUM-12635: RUM support for Cronet Nov 28, 2025
@satween satween marked this pull request as ready for review November 28, 2025 10:10
@satween satween requested review from a team as code owners November 28, 2025 10:10
Copy link
Member

@ambushwork ambushwork left a comment

Choose a reason for hiding this comment

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

Just finish the review of the half, will continue on next week.

* Represents information about an HTTP request.
* Isolates specific library's details from the Datadog SDK.
*/
interface RequestInfo {
Copy link
Member

Choose a reason for hiding this comment

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

Question: should this be put inside internal module instead of core?


/**
* Creates a new builder with Datadog instrumentation.
* @param context the Android context
Copy link
Member

Choose a reason for hiding this comment

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

nip: should specify as application context


// endregion
companion object {
// Exception thrown only for wrong arguments, buy those ones is correct
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
// Exception thrown only for wrong arguments, buy those ones is correct
// Exception thrown only for wrong arguments, but those ones are correct

internal val networkLayerName: String,
internal val rumResourceAttributesProvider: RumResourceAttributesProvider
) {
private val sdkCoreReference = SdkReference(sdkInstanceName) {
Copy link
Member

Choose a reason for hiding this comment

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

Do you consider to add integration test for cronet <-> rum resoureces?

Copy link
Member

@0xnm 0xnm left a comment

Choose a reason for hiding this comment

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

I went only through the production files so far, didn't cover tests

val headers: Map<String, List<String>>
val contentType: String?
fun contentLength(com.datadog.android.api.InternalLogger): Long?
fun headerValue(String): String?
Copy link
Member

Choose a reason for hiding this comment

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

strictly speaking, HTTP standard allows multiple occurrences of the same header, so return type should be List<String>, this can be seen in OkHttp API:

  /** Returns an immutable list of the header values for `name`. */
  fun values(name: String): List<String> {

* @return the length of the content in bytes, or null if the content length is unavailable.
* @throws IOException if an error occurs while calculating the content length.
*/
@Throws(IOException::class)
Copy link
Member

Choose a reason for hiding this comment

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

should we throw/rethrow anything?

companion object {
fun assertThat(info: RequestInfo) = RequestInfoAssertions(info)
}
} No newline at end of file
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
}
}


import org.assertj.core.api.Assertions

class RequestInfoAssertions private constructor(private val info: RequestInfo) {
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
class RequestInfoAssertions private constructor(private val info: RequestInfo) {
class RequestInfoAssert private constructor(private val info: RequestInfo) {

also it is worth to subclass AbstractObjectAssert<RequestInfoAssert, RequestInfo>

request: RequestInfo,
response: ResponseInfo?,
throwable: Throwable?
): Map<String, Any?> = emptyMap()
Copy link
Member

Choose a reason for hiding this comment

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

nit: default return value is not needed here

) : RequestFinishedInfo.Listener(executor) {

override fun onRequestFinished(finishedInfo: RequestFinishedInfo) {
val requestInfo = finishedInfo.annotations?.firstOrNull { it is RequestInfo } as? RequestInfo
Copy link
Member

Choose a reason for hiding this comment

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

minor

Suggested change
val requestInfo = finishedInfo.annotations?.firstOrNull { it is RequestInfo } as? RequestInfo
val requestInfo = finishedInfo.annotations?.filterIsInstance<RequestInfo>()?.firstOrNull()

}

val downloadStartMs = metrics.responseStart - metrics.requestStart
val downloadDurationMs = metrics.requestEnd - metrics.responseStart
Copy link
Member

Choose a reason for hiding this comment

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

probably a typo here

Suggested change
val downloadDurationMs = metrics.requestEnd - metrics.responseStart
val downloadDurationMs = metrics. responseEnd - metrics.responseStart

return if (thisTime == null || otherTime == null) 0L else thisTime - otherTime
}

private fun Long.toNanos(): Long = TimeUnit.MILLISECONDS.toNanos(this)
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
private fun Long.toNanos(): Long = TimeUnit.MILLISECONDS.toNanos(this)
private fun Long.millisToNanos(): Long = TimeUnit.MILLISECONDS.toNanos(this)

internal val url: String,
internal val delegate: UrlRequest.Builder,
internal val rumResourceInstrumentation: RumResourceInstrumentation
) : UrlRequest.Builder() {
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
) : UrlRequest.Builder() {
) : UrlRequest.Builder() by delegate {

)

addRequestAnnotation(requestInfo)
addRequestAnnotation(requestInfo.buildResourceId(generateUuid = true))
Copy link
Member

Choose a reason for hiding this comment

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

should we just add it as a part of CronetRequestInfo?

}

@Test
fun `M call delegate first THEN deprecated W onProvideAttributes { RequestInfo }`(
Copy link
Member

Choose a reason for hiding this comment

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

what does THEN deprecated mean here?

internal companion object {

// We need to limit this value as the body will be loaded in memory
private const val MAX_BODY_PEEK: Long = 32 * 1024L * 1024L
Copy link
Member

Choose a reason for hiding this comment

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

it will be nice to add unit (bytes) in the name

}

@Test
fun `M return null W contentLength() { upload data provider length is unknown }`() {
Copy link
Member

Choose a reason for hiding this comment

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

Not sure the name of this function corresponds the body, upload data provider is not unknown, it is -1, and the return value is not null, it is -1.

override val method: String get() = request.method

override fun <T> tag(type: Class<out T>): T? = request.tag(type)
override fun contentLength(): Long = request.body?.contentLength() ?: 0L
Copy link
Member

Choose a reason for hiding this comment

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

Question, CronetRequestInfo has Long? as return value here, but OkHttpRequestInfo return 0 as default, is that expected?

@@ -0,0 +1,69 @@
package com.datadog.android.okhttp.internal
Copy link
Member

Choose a reason for hiding this comment

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

minor: Missing copyright

@@ -0,0 +1,127 @@
package com.datadog.android.cronet.internal
Copy link
Member

Choose a reason for hiding this comment

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

minor: Missing copyright

import com.datadog.android.okhttp.internal.rum.buildResourceId
import com.datadog.android.okhttp.internal.buildResourceId
import com.datadog.android.okhttp.utils.config.DatadogSingletonTestConfiguration
import com.datadog.android.rum.resource.buildResourceId
Copy link
Member

Choose a reason for hiding this comment

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

how does this import work with the other buildResourceId import?

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.

6 participants