Skip to content

Commit

Permalink
Better handling of event write errors in RUM
Browse files Browse the repository at this point in the history
  • Loading branch information
0xnm committed Dec 14, 2023
1 parent b628685 commit 3501939
Show file tree
Hide file tree
Showing 5 changed files with 680 additions and 591 deletions.
1 change: 1 addition & 0 deletions detekt_custom.yml
Original file line number Diff line number Diff line change
Expand Up @@ -845,6 +845,7 @@ datadog:
- "kotlin.collections.MutableCollection.forEach(kotlin.Function1)"
- "kotlin.collections.MutableCollection.toList()"
- "kotlin.collections.MutableIterator.hasNext()"
- "kotlin.collections.MutableList.add(com.datadog.android.api.InternalLogger.Target)"
- "kotlin.collections.MutableList.add(com.datadog.android.core.internal.persistence.Batch)"
- "kotlin.collections.MutableList.add(com.datadog.android.plugin.DatadogPlugin)"
- "kotlin.collections.MutableList.add(com.datadog.android.rum.internal.domain.scope.RumScope)"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,16 @@
package com.datadog.android.rum.internal.domain.scope

import androidx.annotation.WorkerThread
import com.datadog.android.api.feature.Feature
import com.datadog.android.api.storage.DataWriter
import com.datadog.android.core.InternalSdkCore
import com.datadog.android.rum.GlobalRumMonitor
import com.datadog.android.rum.RumActionType
import com.datadog.android.rum.internal.FeaturesContextResolver
import com.datadog.android.rum.internal.domain.RumContext
import com.datadog.android.rum.internal.domain.Time
import com.datadog.android.rum.internal.monitor.StorageEvent
import com.datadog.android.rum.model.ActionEvent
import com.datadog.android.rum.utils.createRumEventWriteOperation
import com.datadog.android.rum.utils.hasUserData
import java.lang.ref.WeakReference
import java.util.UUID
Expand Down Expand Up @@ -220,90 +221,90 @@ internal class RumActionScope(
} else {
ActionEvent.ActionEventSessionType.SYNTHETICS
}
val frustrations = mutableListOf<ActionEvent.Type>()
if (trackFrustrations && eventErrorCount > 0 && actualType == RumActionType.TAP) {
frustrations.add(ActionEvent.Type.ERROR_TAP)
}

sdkCore.getFeature(Feature.RUM_FEATURE_NAME)
?.withWriteContext { datadogContext, eventBatchWriter ->
val user = datadogContext.userInfo
val hasReplay = featuresContextResolver.resolveViewHasReplay(
datadogContext,
rumContext.viewId.orEmpty()
)
val frustrations = mutableListOf<ActionEvent.Type>()
if (trackFrustrations && eventErrorCount > 0 && actualType == RumActionType.TAP) {
frustrations.add(ActionEvent.Type.ERROR_TAP)
}

val actionEvent = ActionEvent(
date = eventTimestamp,
action = ActionEvent.ActionEventAction(
type = actualType.toSchemaType(),
id = actionId,
target = ActionEvent.ActionEventActionTarget(eventName),
error = ActionEvent.Error(eventErrorCount),
crash = ActionEvent.Crash(eventCrashCount),
longTask = ActionEvent.LongTask(eventLongTaskCount),
resource = ActionEvent.Resource(eventResourceCount),
loadingTime = max(endNanos - startedNanos, 1L),
frustration = if (frustrations.isNotEmpty()) {
ActionEvent.Frustration(frustrations)
} else {
null
}
),
view = ActionEvent.View(
id = rumContext.viewId.orEmpty(),
name = rumContext.viewName,
url = rumContext.viewUrl.orEmpty()
),
application = ActionEvent.Application(rumContext.applicationId),
session = ActionEvent.ActionEventSession(
id = rumContext.sessionId,
type = sessionType,
hasReplay = hasReplay
),
synthetics = syntheticsAttribute,
source = ActionEvent.ActionEventSource.tryFromSource(
datadogContext.source,
sdkCore.internalLogger
),
usr = if (user.hasUserData()) {
ActionEvent.Usr(
id = user.id,
name = user.name,
email = user.email,
additionalProperties = user.additionalProperties.toMutableMap()
)
sdkCore.createRumEventWriteOperation(writer) { datadogContext ->
val user = datadogContext.userInfo
val hasReplay = featuresContextResolver.resolveViewHasReplay(
datadogContext,
rumContext.viewId.orEmpty()
)

ActionEvent(
date = eventTimestamp,
action = ActionEvent.ActionEventAction(
type = actualType.toSchemaType(),
id = actionId,
target = ActionEvent.ActionEventActionTarget(eventName),
error = ActionEvent.Error(eventErrorCount),
crash = ActionEvent.Crash(eventCrashCount),
longTask = ActionEvent.LongTask(eventLongTaskCount),
resource = ActionEvent.Resource(eventResourceCount),
loadingTime = max(endNanos - startedNanos, 1L),
frustration = if (frustrations.isNotEmpty()) {
ActionEvent.Frustration(frustrations)
} else {
null
},
os = ActionEvent.Os(
name = datadogContext.deviceInfo.osName,
version = datadogContext.deviceInfo.osVersion,
versionMajor = datadogContext.deviceInfo.osMajorVersion
),
device = ActionEvent.Device(
type = datadogContext.deviceInfo.deviceType.toActionSchemaType(),
name = datadogContext.deviceInfo.deviceName,
model = datadogContext.deviceInfo.deviceModel,
brand = datadogContext.deviceInfo.deviceBrand,
architecture = datadogContext.deviceInfo.architecture
),
context = ActionEvent.Context(additionalProperties = attributes),
dd = ActionEvent.Dd(
session = ActionEvent.DdSession(
plan = ActionEvent.Plan.PLAN_1,
sessionPrecondition = rumContext.sessionStartReason.toActionSessionPrecondition()
),
configuration = ActionEvent.Configuration(sessionSampleRate = sampleRate)
}
),
view = ActionEvent.View(
id = rumContext.viewId.orEmpty(),
name = rumContext.viewName,
url = rumContext.viewUrl.orEmpty()
),
application = ActionEvent.Application(rumContext.applicationId),
session = ActionEvent.ActionEventSession(
id = rumContext.sessionId,
type = sessionType,
hasReplay = hasReplay
),
synthetics = syntheticsAttribute,
source = ActionEvent.ActionEventSource.tryFromSource(
datadogContext.source,
sdkCore.internalLogger
),
usr = if (user.hasUserData()) {
ActionEvent.Usr(
id = user.id,
name = user.name,
email = user.email,
additionalProperties = user.additionalProperties.toMutableMap()
)
} else {
null
},
os = ActionEvent.Os(
name = datadogContext.deviceInfo.osName,
version = datadogContext.deviceInfo.osVersion,
versionMajor = datadogContext.deviceInfo.osMajorVersion
),
device = ActionEvent.Device(
type = datadogContext.deviceInfo.deviceType.toActionSchemaType(),
name = datadogContext.deviceInfo.deviceName,
model = datadogContext.deviceInfo.deviceModel,
brand = datadogContext.deviceInfo.deviceBrand,
architecture = datadogContext.deviceInfo.architecture
),
context = ActionEvent.Context(additionalProperties = attributes),
dd = ActionEvent.Dd(
session = ActionEvent.DdSession(
plan = ActionEvent.Plan.PLAN_1,
sessionPrecondition = rumContext.sessionStartReason.toActionSessionPrecondition()
),
connectivity = networkInfo.toActionConnectivity(),
service = datadogContext.service,
version = datadogContext.version
)

@Suppress("ThreadSafety") // called in a worker thread context
writer.write(eventBatchWriter, actionEvent)
configuration = ActionEvent.Configuration(sessionSampleRate = sampleRate)
),
connectivity = networkInfo.toActionConnectivity(),
service = datadogContext.service,
version = datadogContext.version
)
}
.onError {
it.eventDropped(rumContext.viewId.orEmpty(), StorageEvent.Action(frustrations.size))
}
.submit()

sent = true
}
Expand Down
Loading

0 comments on commit 3501939

Please sign in to comment.