RUM-593: [Android] - Add traceSampleRate to Telemetry Configuration Event#2563
Conversation
904734a to
530a0d1
Compare
| val mockTracingFeature = mock<SdkFeature>() | ||
| testedCore.features += Feature.TRACING_FEATURE_NAME to mockTracingFeature | ||
|
|
||
| val traceSampleRate = 42f |
There was a problem hiding this comment.
you should use a random value here with forge.
There was a problem hiding this comment.
fixed
thanks for spotting this
mariusc83
left a comment
There was a problem hiding this comment.
Except for that comment in the way you're building the test data looks ok to me.
mariusc83
left a comment
There was a problem hiding this comment.
Actually this is not enough, this is the InternalTelemetryEvent and this information needs to be propagated in the TelemetryEventHandler to the real TelemetryConfigurationEvent. I would suggest not to read the data from feature context here but read it directly in the TelemetryEventHandler the way we do it for others.
Actually I see you are doing this below is just that you are doing it a bit differently by adding an extra property to the |
There was a problem hiding this comment.
probably we need to rollback this change, because real use-case scenario is to initialize OkHttp before Datadog SDK, probably, so that is why lazy here is not used.
There was a problem hiding this comment.
probably we need to rollback this change, because real use-case scenario is to initialize OkHttp before Datadog SDK
In my experience OkHttp and Datadog could be initialized in some Dagger component that is stored in Application and there is no way to know 100% what will be first.
If Datadog.initialize is called in this scenario in Application.onCreate, then OkHttp will not be created unless it is used explicitly in Application.onCreate or something else that uses OkHttp gets created in Application.onCreate.
Like I said in the description, the solution in this pr is far from perfect.
I have no problem removing these changes, but in this case it will 100% not work in the sample.
There was a problem hiding this comment.
Yes, the point of not using lazy here is to check that things are working even if OkHttp client is initialized before Datadog SDK initialized and not depend on the particular initialization order/assumption.
Ideally, we need to have a solution which works in this scenario as well, but if it is not, this is also fine.
There was a problem hiding this comment.
probably we need to rollback this change,
done
| whenever(mockSpanContext.toSpanId()) doReturn fakeSpanId | ||
| whenever(mockSpanContext.toTraceId()) doReturn fakeTraceId | ||
| whenever(mockTraceSampler.sample(any())) doReturn true | ||
| whenever(rumMonitor.mockSdkCore.firstPartyHostResolver) doReturn mockResolver |
There was a problem hiding this comment.
had to do this because some tests started failing like this
java.lang.NullPointerException: Cannot invoke "com.datadog.android.core.internal.net.FirstPartyHostHeaderTypeResolver.isEmpty()" because the return value of "com.datadog.android.core.InternalSdkCore.getFirstPartyHostResolver()" is null
at com.datadog.android.okhttp.trace.TracingInterceptor.onSdkInstanceReady$dd_sdk_android_okhttp_debug(TracingInterceptor.kt:306)
at com.datadog.android.okhttp.DatadogInterceptor.onSdkInstanceReady$dd_sdk_android_okhttp_debug(DatadogInterceptor.kt:305)
at com.datadog.android.okhttp.trace.TracingInterceptor$sdkCoreReference$1.invoke(TracingInterceptor.kt:102)
at com.datadog.android.okhttp.trace.TracingInterceptor$sdkCoreReference$1.invoke(TracingInterceptor.kt:101)
at com.datadog.android.core.SdkReference.tryAcquire(SdkReference.kt:63)
at com.datadog.android.core.SdkReference.get(SdkReference.kt:42)
at com.datadog.android.okhttp.trace.TracingInterceptor.<init>(TracingInterceptor.kt:225)
at com.datadog.android.okhttp.DatadogInterceptor.<init>(DatadogInterceptor.kt:84)
at com.datadog.android.okhttp.DatadogInterceptorWithoutTracesTest.set up(DatadogInterceptorWithoutTracesTest.kt:162)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:569)
at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:727)
at org.junit.jupiter.engine.execution.MethodInvocation.proceed(MethodInvocation.java:60)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain$ValidatingInvocation.proceed(InvocationInterceptorChain.java:131)
at org.junit.jupiter.engine.extension.TimeoutExtension.intercept(TimeoutExtension.java:156)
at org.junit.jupiter.engine.extension.TimeoutExtension.interceptLifecycleMethod(TimeoutExtension.java:128)
at org.junit.jupiter.engine.extension.TimeoutExtension.interceptBeforeEachMethod(TimeoutExtension.java:78)
at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker$ReflectiveInterceptorCall.lambda$ofVoidMethod$0(InterceptingExecutableInvoker.java:103)
at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.lambda$invoke$0(InterceptingExecutableInvoker.java:93)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain$InterceptedInvocation.proceed(InvocationInterceptorChain.java:106)
at datadog.trace.instrumentation.junit5.BeforeAfterOperationsTracer.traceInvocation(BeforeAfterOperationsTracer.java:55)
at datadog.trace.instrumentation.junit5.BeforeAfterOperationsTracer.interceptBeforeEachMethod(BeforeAfterOperationsTracer.java:29)
at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker$ReflectiveInterceptorCall.lambda$ofVoidMethod$0(InterceptingExecutableInvoker.java:103)
at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.lambda$invoke$0(InterceptingExecutableInvoker.java:93)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain$InterceptedInvocation.proceed(InvocationInterceptorChain.java:106)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain.proceed(InvocationInterceptorChain.java:64)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain.chainAndInvoke(InvocationInterceptorChain.java:45)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain.invoke(InvocationInterceptorChain.java:37)
at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.invoke(InterceptingExecutableInvoker.java:92)
at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.invoke(InterceptingExecutableInvoker.java:86)
at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.invokeMethodInExtensionContext(ClassBasedTestDescriptor.java:520)
at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$synthesizeBeforeEachMethodAdapter$23(ClassBasedTestDescriptor.java:505)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeBeforeEachMethods$3(TestMethodTestDescriptor.java:174)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeBeforeMethodsOrCallbacksUntilExceptionOccurs$6(TestMethodTestDescriptor.java:202)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeBeforeMethodsOrCallbacksUntilExceptionOccurs(TestMethodTestDescriptor.java:202)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeBeforeEachMethods(TestMethodTestDescriptor.java:171)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:134)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:68)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:151)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141)
at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:41)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:155)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141)
at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:41)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:155)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141)
at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95)
at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:35)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:54)
at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:147)
at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:127)
at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:90)
at org.junit.platform.launcher.core.EngineExecutionOrchestrator.lambda$execute$0(EngineExecutionOrchestrator.java:55)
at org.junit.platform.launcher.core.EngineExecutionOrchestrator.withInterceptedStreams(EngineExecutionOrchestrator.java:102)
at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:54)
at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:114)
at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:86)
at org.junit.platform.launcher.core.DefaultLauncherSession$DelegatingLauncher.execute(DefaultLauncherSession.java:86)
at org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestClassProcessor$CollectAllTestClassesExecutor.processAllTestClasses(JUnitPlatformTestClassProcessor.java:124)
at org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestClassProcessor$CollectAllTestClassesExecutor.access$000(JUnitPlatformTestClassProcessor.java:99)
at org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestClassProcessor.stop(JUnitPlatformTestClassProcessor.java:94)
at org.gradle.api.internal.tasks.testing.SuiteTestClassProcessor.stop(SuiteTestClassProcessor.java:63)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:569)
at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:36)
at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
at org.gradle.internal.dispatch.ContextClassLoaderDispatch.dispatch(ContextClassLoaderDispatch.java:33)
at org.gradle.internal.dispatch.ProxyDispatchAdapter$DispatchingInvocationHandler.invoke(ProxyDispatchAdapter.java:92)
at jdk.proxy3/jdk.proxy3.$Proxy31.stop(Unknown Source)
at org.gradle.api.internal.tasks.testing.worker.TestWorker$3.run(TestWorker.java:200)
at org.gradle.api.internal.tasks.testing.worker.TestWorker.executeAndMaintainThreadName(TestWorker.java:132)
at org.gradle.api.internal.tasks.testing.worker.TestWorker.execute(TestWorker.java:103)
at org.gradle.api.internal.tasks.testing.worker.TestWorker.execute(TestWorker.java:63)
at org.gradle.process.internal.worker.child.ActionExecutionWorker.execute(ActionExecutionWorker.java:56)
at org.gradle.process.internal.worker.child.SystemApplicationClassLoaderWorker.call(SystemApplicationClassLoaderWorker.java:121)
at org.gradle.process.internal.worker.child.SystemApplicationClassLoaderWorker.call(SystemApplicationClassLoaderWorker.java:71)
at worker.org.gradle.process.internal.worker.GradleWorkerMain.run(GradleWorkerMain.java:69)
at worker.org.gradle.process.internal.worker.GradleWorkerMain.main(GradleWorkerMain.java:74)
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## develop #2563 +/- ##
===========================================
- Coverage 70.22% 70.11% -0.11%
===========================================
Files 803 803
Lines 30191 30144 -47
Branches 5058 5046 -12
===========================================
- Hits 21199 21133 -66
- Misses 7587 7605 +18
- Partials 1405 1406 +1
🚀 New features to boost your workflow:
|
| ) | ||
|
|
||
| init { | ||
| val sdkCore: FeatureSdkCore? = sdkCoreReference.get() as? FeatureSdkCore |
There was a problem hiding this comment.
very nit:
| val sdkCore: FeatureSdkCore? = sdkCoreReference.get() as? FeatureSdkCore | |
| val sdkCore = sdkCoreReference.get() as? FeatureSdkCore |
| localTracerFactory = DEFAULT_LOCAL_TRACER_FACTORY | ||
| ) | ||
|
|
||
| init { |
There was a problem hiding this comment.
alternatively it is possible to add a logic at the onSdkInstanceReady, but there it can be that we already sent telemetry configuration event (say interceptor is initialized 5s later than SDK core).
Anyway, I think both options work, because if we miss some data it is okay.
There was a problem hiding this comment.
as far as I understood onSdkInstanceReady is called when the first network request is made and can happen after the telemetry configuration event is sent
There was a problem hiding this comment.
yes, we also have a risk to miss the report.
| val samplingRate = traceSampler.getSampleRate() | ||
| it[OKHTTP_INTERCEPTOR_SAMPLE_RATE] = samplingRate |
There was a problem hiding this comment.
some naming inconsistency: samplingRate vs sampleRate, it will make text-based search a bit harder, because we need to search for both options.
There was a problem hiding this comment.
renamed everything I did to sampleRate
e507d80
e507d80 to
a604e75
Compare
| } | ||
|
|
||
| @Test | ||
| fun `M add traceSample rate to tracing feature context W constructor called`( |
There was a problem hiding this comment.
added another test to improve test coverage
| localTracerFactory = DEFAULT_LOCAL_TRACER_FACTORY | ||
| ) | ||
|
|
||
| init { |
There was a problem hiding this comment.
yes, we also have a risk to miss the report.
|
|
||
| @Test | ||
| fun `M add trace sample rate to tracing feature context W constructor called`( | ||
| @FloatForgery(min = 0f, max = 100f) sampleRate: Float |
There was a problem hiding this comment.
minor: usually in tests there fake values are called fakeXXX, e.g. here it will be fakeSampleRate.
But no need to update PR, it is good like that.
What does this PR do?
It adds a sampling rate from OkHttp interceptor to the telemetry configuration event. In the
initblock ofTracingInterceptorwe put the sampling rate into the feature context of the tracing feature. And then extract it inDatadogCore.sendCoreConfigurationTelemetryEventwhen we send the event to the Rum feature.Problems that remain unsolved:
TracingInterceptoris created beforeDatadogCorethen we will not send the sampling rate, because there is no way to submit the feature context without having an instance ofDatadogCore. It happened in the sample app. I fixed it by making necessary things lazy.TracingInterceptorin the app and we don't know which one's sampling rate will be sent.But for our purposes it looks like that these problems aren't blocking.
demo [internal]: link
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)