Skip to content

Commit

Permalink
Config params for tracking non-fatal ANRs and app hangs
Browse files Browse the repository at this point in the history
  • Loading branch information
marco-saia-datadog committed Aug 6, 2024
1 parent 5630e33 commit a25759b
Show file tree
Hide file tree
Showing 16 changed files with 119 additions and 12 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -215,3 +215,15 @@ internal fun ReadableArray.toList(): List<*> {

return list
}

/**
* Returns the boolean for the given key, or null if the entry is
* not in the map.
*/
internal fun ReadableMap.getBooleanOrNull(key: String): Boolean? {
return if (hasKey(key)) {
getBoolean(key)
} else {
null
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import java.net.Proxy
* @param firstPartyHosts List of backend hosts to enable tracing with.
* @param bundleLogsWithRum Enables RUM correlation with logs.
* @param bundleLogsWithTraces Enables Traces correlation with logs.
* @param trackNonFatalAnrs Enables tracking of non-fatal ANRs on Android.
*/
data class DdSdkConfiguration(
val clientToken: String,
Expand Down Expand Up @@ -64,7 +65,8 @@ data class DdSdkConfiguration(
val serviceName: String? = null,
val firstPartyHosts: Map<String, Set<TracingHeaderType>>? = null,
val bundleLogsWithRum: Boolean? = null,
val bundleLogsWithTraces: Boolean? = null
val bundleLogsWithTraces: Boolean? = null,
val trackNonFatalAnrs: Boolean? = null
)

internal data class JSONConfigurationFile(
Expand Down Expand Up @@ -95,7 +97,8 @@ internal data class JSONDdSdkConfiguration(
val serviceName: String? = null,
val firstPartyHosts: List<JSONFirstPartyHost>? = null,
val bundleLogsWithRum: Boolean? = null,
val bundleLogsWithTraces: Boolean? = null
val bundleLogsWithTraces: Boolean? = null,
val trackNonFatalAnrs: Boolean? = null
)

internal data class JSONProxyConfiguration(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,8 @@ internal fun ReadableMap.asDdSdkConfiguration(): DdSdkConfiguration {
serviceName = getString("serviceName"),
firstPartyHosts = getArray("firstPartyHosts")?.asFirstPartyHosts(),
bundleLogsWithRum = getBoolean("bundleLogsWithRum"),
bundleLogsWithTraces = getBoolean("bundleLogsWithTraces")
bundleLogsWithTraces = getBoolean("bundleLogsWithTraces"),
trackNonFatalAnrs = getBooleanOrNull("trackNonFatalAnrs")
)
}

Expand Down Expand Up @@ -169,7 +170,8 @@ internal fun JSONDdSdkConfiguration.asDdSdkConfiguration(): DdSdkConfiguration {
this.serviceName,
this.firstPartyHosts?.asFirstPartyHosts(),
this.bundleLogsWithRum ?: DefaultConfiguration.bundleLogsWithRum,
this.bundleLogsWithTraces ?: DefaultConfiguration.bundleLogsWithTraces
this.bundleLogsWithTraces ?: DefaultConfiguration.bundleLogsWithTraces,
this.trackNonFatalAnrs
)
}

Expand Down Expand Up @@ -231,6 +233,7 @@ internal fun DdSdkConfiguration.toReadableMap(): ReadableMap {
uploadFrequency?.let { map.putString("uploadFrequency", it) }
batchSize?.let { map.putString("batchSize", it) }
trackBackgroundEvents?.let { map.putBoolean("trackBackgroundEvents", it) }
trackNonFatalAnrs?.let { map.putBoolean("trackNonFatalAnrs", it) }
additionalConfig?.let { map.putMap("additionalConfig", it.toWritableMap()) }
return map
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,10 @@ class DdSdkNativeInitialization internal constructor(
configBuilder.useCustomEndpoint(it)
}

configuration.trackNonFatalAnrs?.let {
configBuilder.trackNonFatalAnrs(it)
}

return configBuilder.build()
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,34 @@ internal class DdSdkBridgeExtTest {
assertThat(list).isEmpty()
}

@Test
fun `M returns a boolean W getBooleanOrNull { entry in the map }`() {
// Given
val readableMap = mapOf(
"testKey" to true
).toReadableMap()

// When
val value = readableMap.getBooleanOrNull("testKey")

// Then
assertThat(value).isTrue()
}

@Test
fun `M returns null W getBooleanOrNull { entry not in the map }`() {
// Given
val readableMap = mapOf(
"dummy" to false
).toReadableMap()

// When
val value = readableMap.getBooleanOrNull("testKey")

// Then
assertThat(value).isNull()
}

private fun getTestMap(): MutableMap<String, Any?> = mutableMapOf(
"null" to null,
"int" to 1,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,8 @@ fun DdSdkConfiguration.toReadableJavaOnlyMap(): ReadableMap {
map.put("bundleLogsWithRum", bundleLogsWithRum)
map.put("bundleLogsWithTraces", bundleLogsWithTraces)

trackNonFatalAnrs?.let { map.put("trackNonFatalAnrs", it) }

return map.toReadableMap()
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,8 @@ class DdSdkConfigurationForgeryFactory : ForgeryFactory<DdSdkConfiguration> {
serviceName = forge.aNullable { forge.anAlphabeticalString() },
firstPartyHosts = null,
bundleLogsWithRum = forge.aBool(),
bundleLogsWithTraces = forge.aBool()
bundleLogsWithTraces = forge.aBool(),
trackNonFatalAnrs = forge.aBool()
)
}
}
8 changes: 8 additions & 0 deletions packages/core/datadog-configuration.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,14 @@
"bundleLogsWithTraces": {
"description": "Enables Traces correlation with logs.",
"type": "boolean"
},
"appHangThreshold": {
"description": "The app hang threshold in milliseconds for non-fatal app hangs on iOS.",
"type": "number"
},
"trackNonFatalAnrs": {
"description": "Enables tracking of non-fatal ANRs on Android.",
"type": "boolean"
}
},
"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 @@ -38,6 +38,7 @@ import DatadogRUM
- firstPartyHosts: List of backend hosts to enable tracing with.
- bundleLogsWithRum: Correlates logs with RUM.
- bundleLogsWithTraces: Correlates logs with traces.
- appHangThreshold: The threshold for non-fatal app hangs reporting in seconds.
*/
@objc(DdSdkConfiguration)
public class DdSdkConfiguration: NSObject {
Expand Down Expand Up @@ -68,6 +69,7 @@ public class DdSdkConfiguration: NSObject {
public var resourceTracingSamplingRate: Double? = nil
public var bundleLogsWithRum: Bool
public var bundleLogsWithTraces: Bool
public var appHangThreshold: Double? = nil

public init(
clientToken: String,
Expand Down Expand Up @@ -96,7 +98,8 @@ public class DdSdkConfiguration: NSObject {
firstPartyHosts: [String: Set<TracingHeaderType>]?,
resourceTracingSamplingRate: Double?,
bundleLogsWithRum: Bool,
bundleLogsWithTraces: Bool
bundleLogsWithTraces: Bool,
appHangThreshold: Double?
) {
self.clientToken = clientToken
self.env = env
Expand Down Expand Up @@ -125,6 +128,7 @@ public class DdSdkConfiguration: NSObject {
self.resourceTracingSamplingRate = resourceTracingSamplingRate
self.bundleLogsWithRum = bundleLogsWithRum
self.bundleLogsWithTraces = bundleLogsWithTraces
self.appHangThreshold = appHangThreshold
}
}

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 @@ -150,6 +150,11 @@ public class DdSdkNativeInitialization: NSObject {
}
}

var appHangThreshold: Double? = nil
if let customAppHangThreshold = configuration.appHangThreshold {
appHangThreshold = customAppHangThreshold
}

return RUM.Configuration(
applicationID: configuration.applicationId,
sessionSampleRate: (configuration.sampleRate as? NSNumber)?.floatValue ?? Float(DefaultConfiguration.sessionSamplingRate),
Expand All @@ -159,6 +164,7 @@ public class DdSdkNativeInitialization: NSObject {
trackFrustrations: configuration.trackFrustrations ?? true,
trackBackgroundEvents: configuration.trackBackgroundEvents ?? false,
longTaskThreshold: longTaskThreshold,
appHangThreshold: appHangThreshold,
vitalsUpdateFrequency: configuration.vitalsUpdateFrequency,
resourceEventMapper: { resourceEvent in
if resourceEvent.context?.contextInfo[InternalConfigurationAttributes.dropResource] != nil {
Expand Down
8 changes: 6 additions & 2 deletions packages/core/ios/Sources/RNDdSdkConfiguration.swift
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ extension NSDictionary {
let resourceTracingSamplingRate = object(forKey: "resourceTracingSamplingRate") as? Double
let bundleLogsWithRum = object(forKey: "bundleLogsWithRum") as? Bool
let bundleLogsWithTraces = object(forKey: "bundleLogsWithTraces") as? Bool
let appHangThreshold = object(forKey: "appHangThreshold") as? Double

return DdSdkConfiguration(
clientToken: (clientToken != nil) ? clientToken! : String(),
Expand Down Expand Up @@ -67,7 +68,8 @@ extension NSDictionary {
firstPartyHosts: firstPartyHosts?.asFirstPartyHosts(),
resourceTracingSamplingRate: resourceTracingSamplingRate,
bundleLogsWithRum: bundleLogsWithRum ?? DefaultConfiguration.bundleLogsWithRum,
bundleLogsWithTraces: bundleLogsWithTraces ?? DefaultConfiguration.bundleLogsWithTraces
bundleLogsWithTraces: bundleLogsWithTraces ?? DefaultConfiguration.bundleLogsWithTraces,
appHangThreshold: appHangThreshold
)
}

Expand Down Expand Up @@ -231,6 +233,7 @@ extension Dictionary where Key == String, Value == AnyObject {
let resourceTracingSamplingRate = configuration["resourceTracingSamplingRate"] as? Double
let bundleLogsWithRum = configuration["bundleLogsWithRum"] as? Bool
let bundleLogsWithTraces = configuration["bundleLogsWithTraces"] as? Bool
let appHangThreshold = configuration["appHangThreshold"] as? Double

return DdSdkConfiguration(
clientToken: clientToken ?? String(),
Expand Down Expand Up @@ -262,7 +265,8 @@ extension Dictionary where Key == String, Value == AnyObject {
firstPartyHosts: firstPartyHosts?.asFirstPartyHosts() ?? DefaultConfiguration.firstPartyHosts,
resourceTracingSamplingRate: resourceTracingSamplingRate ?? DefaultConfiguration.resourceTracingSamplingRate,
bundleLogsWithRum: bundleLogsWithRum ?? DefaultConfiguration.bundleLogsWithRum,
bundleLogsWithTraces: bundleLogsWithTraces ?? DefaultConfiguration.bundleLogsWithTraces
bundleLogsWithTraces: bundleLogsWithTraces ?? DefaultConfiguration.bundleLogsWithTraces,
appHangThreshold: appHangThreshold
)
}
}
Expand Down
6 changes: 4 additions & 2 deletions packages/core/ios/Tests/DdSdkTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -984,7 +984,8 @@ extension DdSdkConfiguration {
firstPartyHosts: [String: Set<TracingHeaderType>]? = nil,
resourceTracingSamplingRate: Double? = nil,
bundleLogsWithRum: Bool = true,
bundleLogsWithTraces: Bool = true
bundleLogsWithTraces: Bool = true,
appHangThreshold: Double? = nil
) -> DdSdkConfiguration {
DdSdkConfiguration(
clientToken: clientToken as String,
Expand Down Expand Up @@ -1013,7 +1014,8 @@ extension DdSdkConfiguration {
firstPartyHosts: firstPartyHosts,
resourceTracingSamplingRate: resourceTracingSamplingRate,
bundleLogsWithRum: bundleLogsWithRum,
bundleLogsWithTraces: bundleLogsWithTraces
bundleLogsWithTraces: bundleLogsWithTraces,
appHangThreshold: appHangThreshold
)
}
}
Expand Down
4 changes: 3 additions & 1 deletion packages/core/src/DdSdkReactNative.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -279,7 +279,9 @@ export class DdSdkReactNative {
configuration.serviceName,
formatFirstPartyHosts(configuration.firstPartyHosts),
configuration.bundleLogsWithRum,
configuration.bundleLogsWithTraces
configuration.bundleLogsWithTraces,
configuration.trackNonFatalAnrs,
configuration.appHangThreshold
);
};

Expand Down
24 changes: 24 additions & 0 deletions packages/core/src/DdSdkReactNativeConfiguration.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,30 @@ export class DdSdkReactNativeConfiguration {
*/
public bundleLogsWithTraces: boolean = DEFAULTS.bundleLogsWithTraces;

/**
* Enables tracking of non-fatal ANRs on Android.
* By default, the reporting of non-fatal ANRs on Android 30+ is disabled because it would
* create too much noise over fatal ANRs. On Android 29 and below, however,
* the reporting of non-fatal ANRs is enabled by default,
* as fatal ANRs cannot be reported on those versions.
*/
public trackNonFatalAnrs?: boolean;

/**
* The app hang threshold in milliseconds for non-fatal app hangs on iOS.
*
* App hangs are an iOS-specific type of error that happens when the application is unresponsive for too long.
* By default, app hangs reporting is disabled, but you can enable it and set your
* own threshold to monitor app hangs that last more than a specified
* duration by using the this parameter.
*
* Set the appHangThreshold parameter to the minimal duration you want
* app hangs to be reported. For example, enter 0.25 to report hangs lasting at least 250 ms.
* See [Configure the app hang threshold](https://docs.datadoghq.com/real_user_monitoring/error_tracking/mobile/ios/?tab=cocoapods#configure-the-app-hang-threshold)
* for more guidance on what to set this value to.
*/
public appHangThreshold?: number;

/**
* Specifies a custom prop to name RUM actions on elements having an `onPress` prop.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ describe('DatadogProvider', () => {
"additionalConfiguration": {
"_dd.source": "react-native",
},
"appHangThreshold": undefined,
"applicationId": "fakeApplicationId",
"batchSize": "MEDIUM",
"bundleLogsWithRum": true,
Expand Down Expand Up @@ -94,6 +95,7 @@ describe('DatadogProvider', () => {
"telemetrySampleRate": 20,
"trackBackgroundEvents": false,
"trackFrustrations": true,
"trackNonFatalAnrs": undefined,
"trackingConsent": "granted",
"uploadFrequency": "AVERAGE",
"verbosity": undefined,
Expand Down
4 changes: 3 additions & 1 deletion packages/core/src/types.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,9 @@ export class DdSdkConfiguration {
propagatorTypes: string[];
}[],
readonly bundleLogsWithRum: boolean,
readonly bundleLogsWithTraces: boolean
readonly bundleLogsWithTraces: boolean,
readonly trackNonFatalAnrs: boolean | undefined,
readonly appHangThreshold: number | undefined
) {}
}

Expand Down

0 comments on commit a25759b

Please sign in to comment.