Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ import java.net.Proxy
* @param bundleLogsWithTraces Enables Traces correlation with logs.
* @param trackNonFatalAnrs Enables tracking of non-fatal ANRs on Android.
* @param batchProcessingLevel The preferred number of batches of data that will be sent in a single upload (can be 'LOW', 'MEDIUM' (default), 'HIGH')
* @param initialResourceThreshold "The amount of time after a view starts where a Resource should be considered when calculating Time to Network-Settled (TNS)"
*/
data class DdSdkConfiguration(
val clientToken: String,
Expand Down Expand Up @@ -71,7 +72,8 @@ data class DdSdkConfiguration(
val bundleLogsWithRum: Boolean? = null,
val bundleLogsWithTraces: Boolean? = null,
val trackNonFatalAnrs: Boolean? = null,
val batchProcessingLevel: String? = null
val batchProcessingLevel: String? = null,
val initialResourceThreshold: Double? = null
)

internal data class JSONConfigurationFile(
Expand Down Expand Up @@ -104,7 +106,8 @@ internal data class JSONDdSdkConfiguration(
val bundleLogsWithRum: Boolean? = null,
val bundleLogsWithTraces: Boolean? = null,
val trackNonFatalAnrs: Boolean? = null,
val batchProcessingLevel: String? = null
val batchProcessingLevel: String? = null,
val initialResourceThreshold: Double? = null
)

internal data class JSONProxyConfiguration(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,8 @@ internal fun ReadableMap.asDdSdkConfiguration(): DdSdkConfiguration {
bundleLogsWithRum = getBoolean("bundleLogsWithRum"),
bundleLogsWithTraces = getBoolean("bundleLogsWithTraces"),
trackNonFatalAnrs = getBooleanOrNull("trackNonFatalAnrs"),
batchProcessingLevel = getString("batchProcessingLevel")
batchProcessingLevel = getString("batchProcessingLevel"),
initialResourceThreshold = getDouble("initialResourceThreshold")
)
}

Expand Down Expand Up @@ -138,6 +139,7 @@ internal object DefaultConfiguration {
const val trackBackgroundEvents = false
const val bundleLogsWithRum = true
const val bundleLogsWithTraces = true
const val initialResourceThreshold = 0.1
}

@Suppress("ComplexMethod")
Expand Down Expand Up @@ -172,7 +174,9 @@ internal fun JSONDdSdkConfiguration.asDdSdkConfiguration(): DdSdkConfiguration {
this.firstPartyHosts?.asFirstPartyHosts(),
this.bundleLogsWithRum ?: DefaultConfiguration.bundleLogsWithRum,
this.bundleLogsWithTraces ?: DefaultConfiguration.bundleLogsWithTraces,
this.trackNonFatalAnrs
this.trackNonFatalAnrs,
this.batchProcessingLevel,
this.initialResourceThreshold ?: DefaultConfiguration.initialResourceThreshold
)
}

Expand Down Expand Up @@ -236,6 +240,7 @@ internal fun DdSdkConfiguration.toReadableMap(): ReadableMap {
trackBackgroundEvents?.let { map.putBoolean("trackBackgroundEvents", it) }
trackNonFatalAnrs?.let { map.putBoolean("trackNonFatalAnrs", it) }
additionalConfig?.let { map.putMap("additionalConfig", it.toWritableMap()) }
initialResourceThreshold?.let { map.putDouble("initialResourceThreshold", it)}
return map
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import com.datadog.android.rum.RumConfiguration
import com.datadog.android.rum.RumSessionListener
import com.datadog.android.rum._RumInternalProxy
import com.datadog.android.rum.configuration.VitalsUpdateFrequency
import com.datadog.android.rum.metric.networksettled.TimeBasedInitialResourceIdentifier
import com.datadog.android.rum.model.ActionEvent
import com.datadog.android.rum.model.ResourceEvent
import com.datadog.android.rum.tracking.ActivityViewTrackingStrategy
Expand All @@ -31,6 +32,7 @@ import com.facebook.react.bridge.WritableNativeMap
import com.facebook.react.modules.core.DeviceEventManagerModule
import com.google.gson.Gson
import java.util.Locale
import kotlin.time.Duration.Companion.seconds

/**
* Initializes the Android Datadog SDK.
Expand Down Expand Up @@ -215,6 +217,11 @@ class DdSdkNativeInitialization internal constructor(
}
)

configuration.initialResourceThreshold?.let {
val milliseconds = it.seconds.inWholeMilliseconds
configBuilder.setInitialResourceIdentifier(TimeBasedInitialResourceIdentifier(milliseconds))
}

return configBuilder.build()
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ internal class DdSdkNativeInitializationTest {
assertThat(configuration.firstPartyHosts?.get("example.com").toString()).isEqualTo(
"[B3MULTI, TRACECONTEXT]"
)
assertThat(configuration.initialResourceThreshold).isEqualTo(0.5)
}

@Test
Expand Down Expand Up @@ -161,6 +162,7 @@ internal class DdSdkNativeInitializationTest {
assertThat(configuration.serviceName).isNull()
assertThat(configuration.proxyConfig).isNull()
assertThat(configuration.firstPartyHosts).isNull()
assertThat(configuration.initialResourceThreshold).isEqualTo(0.1)
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import com.datadog.android.rum.RumConfiguration
import com.datadog.android.rum.RumPerformanceMetric
import com.datadog.android.rum._RumInternalProxy
import com.datadog.android.rum.configuration.VitalsUpdateFrequency
import com.datadog.android.rum.metric.networksettled.TimeBasedInitialResourceIdentifier
import com.datadog.android.rum.model.ActionEvent
import com.datadog.android.rum.model.ResourceEvent
import com.datadog.android.rum.tracking.ActivityViewTrackingStrategy
Expand All @@ -42,6 +43,7 @@ import com.facebook.react.bridge.ReadableMap
import fr.xgouchet.elmyr.Forge
import fr.xgouchet.elmyr.annotation.AdvancedForgery
import fr.xgouchet.elmyr.annotation.BoolForgery
import fr.xgouchet.elmyr.annotation.DoubleForgery
import fr.xgouchet.elmyr.annotation.Forgery
import fr.xgouchet.elmyr.annotation.IntForgery
import fr.xgouchet.elmyr.annotation.LongForgery
Expand All @@ -52,6 +54,7 @@ import fr.xgouchet.elmyr.junit5.ForgeConfiguration
import fr.xgouchet.elmyr.junit5.ForgeExtension
import java.util.Locale
import java.util.stream.Stream
import kotlin.time.Duration.Companion.seconds
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.AfterEach
import org.junit.jupiter.api.BeforeEach
Expand Down Expand Up @@ -1785,6 +1788,80 @@ internal class DdSdkTest {

// endregion

// region initial resource threshold TNS

@Test
fun `𝕄 initialize W initialize() { initialResourceThreshold is null }`(
@Forgery configuration: DdSdkConfiguration
) {
// Given
val sdkConfigCaptor = argumentCaptor<Configuration>()
val rumConfigCaptor = argumentCaptor<RumConfiguration>()
val logsConfigCaptor = argumentCaptor<LogsConfiguration>()
val traceConfigCaptor = argumentCaptor<TraceConfiguration>()
val defaultTimeBasedIdentifier = TimeBasedInitialResourceIdentifier(100)

// When
testedBridgeSdk.initialize(configuration.toReadableJavaOnlyMap(), mockPromise)

// Then
inOrder(mockDatadog) {
verify(mockDatadog).initialize(
same(mockContext),
sdkConfigCaptor.capture(),
any()
)
verify(mockDatadog).enableRum(rumConfigCaptor.capture())
verify(mockDatadog).enableTrace(traceConfigCaptor.capture())
verify(mockDatadog).enableLogs(logsConfigCaptor.capture())
}

assertThat(rumConfigCaptor.firstValue)
.hasField("featureConfiguration") {
it.hasFieldEqualTo("initialResourceIdentifier", defaultTimeBasedIdentifier)
}
}

@Test
fun `𝕄 initialize W initialize() { initialResourceThreshold is not null}`(
@DoubleForgery(min = 0.1, max = 5.0) thresholdInSeconds: Double,
@Forgery configuration: DdSdkConfiguration
) {
// Given
val bridgeConfiguration = configuration.copy(
initialResourceThreshold = thresholdInSeconds
)
val sdkConfigCaptor = argumentCaptor<Configuration>()
val rumConfigCaptor = argumentCaptor<RumConfiguration>()
val logsConfigCaptor = argumentCaptor<LogsConfiguration>()
val traceConfigCaptor = argumentCaptor<TraceConfiguration>()
val timeBasedIdentifier = TimeBasedInitialResourceIdentifier(
thresholdInSeconds.seconds.inWholeMilliseconds
)

// When
testedBridgeSdk.initialize(bridgeConfiguration.toReadableJavaOnlyMap(), mockPromise)

// Then
inOrder(mockDatadog) {
verify(mockDatadog).initialize(
same(mockContext),
sdkConfigCaptor.capture(),
any()
)
verify(mockDatadog).enableRum(rumConfigCaptor.capture())
verify(mockDatadog).enableTrace(traceConfigCaptor.capture())
verify(mockDatadog).enableLogs(logsConfigCaptor.capture())
}

assertThat(rumConfigCaptor.firstValue)
.hasField("featureConfiguration") {
it.hasFieldEqualTo("initialResourceIdentifier", timeBasedIdentifier)
}
}

// endregion

// region version suffix

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,12 @@ fun DdSdkConfiguration.toReadableJavaOnlyMap(): ReadableMap {
map["batchProcessingLevel"] = BatchProcessingLevel.MEDIUM.toString()
}

if (initialResourceThreshold != null) {
map.put("initialResourceThreshold", initialResourceThreshold)
} else {
map.put("initialResourceThreshold", 0.1f)
}

return map.toReadableMap()
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
"trackFrustrations": false,
"uploadFrequency": "FREQUENT",
"version": "2.3.1",
"vitalsUpdateFrequency": "NEVER"
"vitalsUpdateFrequency": "NEVER",
"initialResourceThreshold": 0.5
}
}
4 changes: 4 additions & 0 deletions packages/core/datadog-configuration.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,10 @@
"trackNonFatalAnrs": {
"description": "Enables tracking of non-fatal ANRs on Android.",
"type": "boolean"
},
"initialResourceThreshold" : {
"description": "The amount of time after a view starts where a Resource should be considered when calculating Time to Network-Settled (TNS)",
"type": "number"
}
},
"required": [
Expand Down
6 changes: 5 additions & 1 deletion packages/core/ios/Sources/DdSdkConfiguration.swift
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ import DatadogRUM
- appHangThreshold: The threshold for non-fatal app hangs reporting in seconds.
- trackWatchdogTerminations: Whether the SDK should track application termination by the watchdog
- batchProcessingLevel: Maximum number of batches processed sequentially without a delay
- initialResourceThreshold: The amount of time after a view starts where a Resource should be considered when calculating Time to Network-Settled (TNS)
*/
@objc(DdSdkConfiguration)
public class DdSdkConfiguration: NSObject {
Expand Down Expand Up @@ -74,6 +75,7 @@ public class DdSdkConfiguration: NSObject {
public var appHangThreshold: Double? = nil
public var trackWatchdogTerminations: Bool
public var batchProcessingLevel: Datadog.Configuration.BatchProcessingLevel
public var initialResourceThreshold: Double? = nil

public init(
clientToken: String,
Expand Down Expand Up @@ -105,7 +107,8 @@ public class DdSdkConfiguration: NSObject {
bundleLogsWithTraces: Bool,
appHangThreshold: Double?,
trackWatchdogTerminations: Bool,
batchProcessingLevel: Datadog.Configuration.BatchProcessingLevel
batchProcessingLevel: Datadog.Configuration.BatchProcessingLevel,
initialResourceThreshold: Double?
) {
self.clientToken = clientToken
self.env = env
Expand Down Expand Up @@ -137,6 +140,7 @@ public class DdSdkConfiguration: NSObject {
self.appHangThreshold = appHangThreshold
self.trackWatchdogTerminations = trackWatchdogTerminations
self.batchProcessingLevel = batchProcessingLevel
self.initialResourceThreshold = initialResourceThreshold
}
}

Expand Down
6 changes: 6 additions & 0 deletions packages/core/ios/Sources/DdSdkNativeInitialization.swift
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,11 @@ public class DdSdkNativeInitialization: NSObject {
eventEmitter?.sendEvent(withName: "RumSessionStarted", body: body)
}

var networkSettledResourcePredicate: TimeBasedTNSResourcePredicate? = nil
if let initialResourceThreshold = configuration.initialResourceThreshold as TimeInterval? {
networkSettledResourcePredicate = TimeBasedTNSResourcePredicate(threshold: initialResourceThreshold)
}

return RUM.Configuration(
applicationID: configuration.applicationId,
sessionSampleRate: (configuration.sampleRate as? NSNumber)?.floatValue ?? Float(DefaultConfiguration.sessionSamplingRate),
Expand All @@ -167,6 +172,7 @@ public class DdSdkNativeInitialization: NSObject {
appHangThreshold: configuration.appHangThreshold,
trackWatchdogTerminations: configuration.trackWatchdogTerminations,
vitalsUpdateFrequency: configuration.vitalsUpdateFrequency,
networkSettledResourcePredicate: networkSettledResourcePredicate ?? TimeBasedTNSResourcePredicate(),
resourceEventMapper: { resourceEvent in
if resourceEvent.context?.contextInfo[InternalConfigurationAttributes.dropResource] != nil {
return nil
Expand Down
10 changes: 7 additions & 3 deletions packages/core/ios/Sources/RNDdSdkConfiguration.swift
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ extension NSDictionary {
let appHangThreshold = object(forKey: "appHangThreshold") as? Double
let trackWatchdogTerminations = object(forKey: "trackWatchdogTerminations") as? Bool
let batchProcessingLevel = object(forKey: "batchProcessingLevel") as? NSString
let initialResourceThreshold = object(forKey: "initialResourceThreshold") as? Double

return DdSdkConfiguration(
clientToken: (clientToken != nil) ? clientToken! : String(),
Expand Down Expand Up @@ -73,7 +74,8 @@ extension NSDictionary {
bundleLogsWithTraces: bundleLogsWithTraces ?? DefaultConfiguration.bundleLogsWithTraces,
appHangThreshold: appHangThreshold,
trackWatchdogTerminations: trackWatchdogTerminations ?? DefaultConfiguration.trackWatchdogTerminations,
batchProcessingLevel: batchProcessingLevel.asBatchProcessingLevel()
batchProcessingLevel: batchProcessingLevel.asBatchProcessingLevel(),
initialResourceThreshold: initialResourceThreshold
)
}

Expand Down Expand Up @@ -241,7 +243,8 @@ extension Dictionary where Key == String, Value == AnyObject {
let appHangThreshold = configuration["appHangThreshold"] as? Double
let trackWatchdogTerminations = configuration["trackWatchdogTerminations"] as? Bool
let batchProcessingLevel = configuration["batchProcessingLevel"] as? NSString

let initialResourceThreshold = configuration["initialResourceThreshold"] as? Double

return DdSdkConfiguration(
clientToken: clientToken ?? String(),
env: env ?? String(),
Expand Down Expand Up @@ -275,7 +278,8 @@ extension Dictionary where Key == String, Value == AnyObject {
bundleLogsWithTraces: bundleLogsWithTraces ?? DefaultConfiguration.bundleLogsWithTraces,
appHangThreshold: appHangThreshold,
trackWatchdogTerminations: trackWatchdogTerminations ?? DefaultConfiguration.trackWatchdogTerminations,
batchProcessingLevel: batchProcessingLevel.asBatchProcessingLevel()
batchProcessingLevel: batchProcessingLevel.asBatchProcessingLevel(),
initialResourceThreshold: initialResourceThreshold
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ class DdSdkNativeInitializationTests: XCTestCase {
XCTAssertEqual(configuration?.proxyConfig?[kCFProxyPasswordKey] as? String, "proxypassword")
let expectedFirstPartyHosts: [String: Set<TracingHeaderType>]? = ["example.com": [.b3multi, .tracecontext]]
XCTAssertEqual(configuration?.firstPartyHosts, expectedFirstPartyHosts)
XCTAssertEqual(configuration?.initialResourceThreshold, 0.5)
}

func testReturnsConfigurationWithMinimalData() {
Expand Down Expand Up @@ -93,6 +94,7 @@ class DdSdkNativeInitializationTests: XCTestCase {
XCTAssertNil(configuration?.proxyConfig)
let expectedFirstPartyHosts: [String: Set<TracingHeaderType>]? = [:]
XCTAssertEqual(configuration?.firstPartyHosts, expectedFirstPartyHosts)
XCTAssertEqual(configuration?.initialResourceThreshold, nil)
}

func testPrintsMessageWithIncorrectFile() {
Expand Down
Loading