- 필수 기능
- 시작하기
- Glossary
- 표준 속성
- Guides
- Agent
- 통합
- 개방형텔레메트리
- 개발자
- Administrator's Guide
- API
- Datadog Mobile App
- CoScreen
- Cloudcraft
- 앱 내
- 서비스 관리
- 인프라스트럭처
- 애플리케이션 성능
- APM
- Continuous Profiler
- 스팬 시각화
- 데이터 스트림 모니터링
- 데이터 작업 모니터링
- 디지털 경험
- 소프트웨어 제공
- 보안
- AI Observability
- 로그 관리
- 관리
If you have not set up the SDK yet, follow the in-app setup instructions or refer to the Android RUM setup documentation.
Android RUM automatically tracks attributes such as user activity, screens, errors, and network requests. See the RUM Data Collection documentation to learn about the RUM events and default attributes. You can further enrich user session information and gain finer control over the attributes collected by tracking custom events.
In addition to tracking views automatically, you can also track specific distinct views (such as activities and fragments) when they become visible and interactive in the onResume()
lifecycle. Stop tracking when the view is no longer visible. Most often, this method should be called in the frontmost Activity
or Fragment
:
fun onResume() {
GlobalRumMonitor.get().startView(viewKey, viewName, viewAttributes)
}
fun onPause() {
GlobalRumMonitor.get().stopView(viewKey, viewAttributes)
}
public void onResume() {
GlobalRumMonitor.get().startView(viewKey, viewName, viewAttributes);
}
public void onPause() {
GlobalRumMonitor.get().stopView(viewKey, viewAttributes);
}
iOS RUM tracks the time it takes for your view to load. To notify the SDK that your view has finished loading, call the addViewLoadingTime(override=)
method
through the GlobalRumMonitor
instance. Call this method when your view is fully loaded and displayed to the user:
@OptIn(ExperimentalRumApi::class)
fun onViewLoaded() {
GlobalRumMonitor.get().addViewLoadingTime(override = false)
}
@OptIn(markerClass = ExperimentalRumApi.class)
public void onViewLoaded() {
GlobalRumMonitor.get().addViewLoadingTime(override);
}
Use the override
option to replace the previously calculated loading time for the current view.
After the loading time is sent, it is accessible as @view.loading_time
and is visible in the RUM UI.
Please note that this API is still experimental and might change in the future.
In addition to RUM’s default attributes, you can measure where your application is spending its time by using the addTiming
API. The timing measure is relative to the start of the current RUM view. For example, you can time how long it takes for your hero image to appear:
fun onHeroImageLoaded() {
GlobalRumMonitor.get().addTiming("hero_image")
}
public void onHeroImageLoaded() {
GlobalRumMonitor.get().addTiming("hero_image");
}
Once the timing is sent, the timing is accessible as @view.custom_timings.<timing_name>
. For example: @view.custom_timings.hero_image
. You must create a measure before graphing it in RUM analytics or in dashboards.
In addition to tracking actions automatically, you can also track specific custom user actions (such as taps, clicks, and scrolls) with RumMonitor#addAction
. For continuous action tracking (for example, tracking a user scrolling a list), use RumMonitor#startAction
and RumMonitor#stopAction
.
Note the action type should be one of the following: “custom”, “click”, “tap”, “scroll”, “swipe”, “back”.
fun onUserInteraction() {
GlobalRumMonitor.get().addAction(actionType, name, actionAttributes)
}
public void onUserInteraction() {
GlobalRumMonitor.get().addAction(actionType, name, actionAttributes);
}
When tracking resources automatically, provide a custom RumResourceAttributesProvider
instance to add custom attributes to each tracked network request. For example, if you want to track a network request’s headers, create an implementation as follows, and pass it in the constructor of the DatadogInterceptor
.
class CustomRumResourceAttributesProvider : RumResourceAttributesProvider {
override fun onProvideAttributes(
request: Request,
response: Response?,
throwable: Throwable?
): Map<String, Any?> {
val headers = request.headers
return headers.names().associate {
"headers.${it.lowercase(Locale.US)}" to headers.values(it).first()
}
}
}
public class CustomRumResourceAttributesProvider implements RumResourceAttributesProvider {
@NonNull
@Override
public Map<String, Object> onProvideAttributes(
@NonNull Request request,
@Nullable Response response,
@Nullable Throwable throwable
) {
Map<String, Object> result = new HashMap<>();
Headers headers = request.headers();
for (String key : headers.names()) {
String attrName = "headers." + key.toLowerCase(Locale.US);
result.put(attrName, headers.values(key).get(0));
}
return result;
}
}
In addition to tracking resources automatically, you can also track specific custom resources (such as network requests and third-party provider APIs) with methods (such as GET
and POST
) while loading the resource with RumMonitor#startResource
. Stop tracking with RumMonitor#stopResource
when it is fully loaded, or RumMonitor#stopResourceWithError
if an error occurs while loading the resource.
fun loadResource() {
GlobalRumMonitor.get().startResource(resourceKey, method, url, resourceAttributes)
try {
// do load the resource
GlobalRumMonitor.get().stopResource(resourceKey, resourceKind, additionalAttributes)
} catch (e: Exception) {
GlobalRumMonitor.get().stopResourceWithError(resourceKey, message, origin, e)
}
}
public void loadResource() {
GlobalRumMonitor.get().startResource(resourceKey, method, url, resourceAttributes);
try {
// do load the resource
GlobalRumMonitor.get().stopResource(resourceKey, resourceKind, additionalAttributes);
} catch (Exception e) {
GlobalRumMonitor.get().stopResourceWithError(resourceKey, message, origin, e);
}
}
To track specific errors, notify the monitor when an error occurs with the message, source, exception, and additional attributes. Refer to the Error Attributes documentation.
GlobalRumMonitor.get().addError(message, source, throwable, attributes)
You can use the addUserProperties
API to append extra user properties to previously set properties.
fun addUserProperties(extraInfo: Map<String, Any?>, sdkCore: SdkCore = getInstance()) {
sdkCore.addUserProperties(extraInfo)
}
The Android SDK first stores events and only uploads events when the intake specifications conditions are met.
You have the option of deleting all unsent data stored by the SDK with the clearAllData
API.
fun clearAllData(sdkCore: SdkCore = getInstance()) {
sdkCore.clearAllData()
}
You can use the StopInstance
API to stop the SDK instance assigned to the given name (or the default instance if the name is null) from collecting and uploading data further.
fun stopInstance(instanceName: String? = null) {
synchronized(registry) {
val instance = registry.unregister(instanceName)
(instance as? DatadogCore)?.stop()
}
}
Many operations, such as data processing and event input/output, are queued in background threads to handle edge cases where the queue has grown so much that there could be delayed processing, high memory usage, or Application Not Responding (ANR) errors.
You can control the buildup of events on the SDK with the setBackpressureStrategy
API. This API ignores new tasks if a queue reaches 1024 items.
fun setBackpressureStrategy(backpressureStrategy: BackPressureStrategy): Builder {
coreConfig = coreConfig.copy(backpressureStrategy = backpressureStrategy)
return this
}
See an example of this API being used.
You can define the minimum log level (priority) to send events to Datadog in a logger instance. If the log priority is below the one you set at this threshold, it does not get sent. The default value is -1 (allow all).
fun setRemoteLogThreshold(minLogThreshold: Int): Builder {
minDatadogLogsPriority = minLogThreshold
return this
}
In addition to the default RUM attributes captured by the RUM Android SDK automatically, you can choose to add additional contextual information, such as custom attributes, to your RUM events to enrich your observability within Datadog. Custom attributes allow you to filter and group information about observed user behavior (such as cart value, merchant tier, or ad campaign) with code-level information (such as backend services, session timeline, error logs, and network health).
Adding user information to your RUM sessions makes it easy to:
The following attributes are optional, you should provide at least one of them:
Attribute | Type | Description |
---|---|---|
usr.id | String | Unique user identifier. |
usr.name | String | User friendly name, displayed by default in the RUM UI. |
usr.email | String | User email, displayed in the RUM UI if the user name is not present. It is also used to fetch Gravatars. |
To identify user sessions, use the setUserInfo
API, for example:
Datadog.setUserInfo('1234', 'John Doe', 'john@doe.com')
// Adds an attribute to all future RUM events
GlobalRumMonitor.get().addAttribute(key, value)
// Removes an attribute to all future RUM events
GlobalRumMonitor.get().removeAttribute(key)
Widgets are not automatically tracked with the SDK. To send UI interactions from your widgets manually, call the Datadog API. See example.
You can use the following methods in Configuration.Builder
when creating the Datadog configuration to initialize the library:
setFirstPartyHosts()
first-party
. Note: If you define custom tracing header types in the Datadog configuration and are using a tracer registered with GlobalTracer
, make sure the same tracing header types are set for the tracer in use.useSite(DatadogSite)
setFirstPartyHostsWithHeaderType
setBatchSize([SMALL|MEDIUM|LARGE])
setUploadFrequency([FREQUENT|AVERAGE|RARE])
setBatchProcessingLevel(LOW|MEDIUM|HIGH)
setAdditionalConfiguration
setProxy
setEncryption(Encryption)
setPersistenceStrategyFactory
setCrashReportsEnabled(Boolean)
true
.setBackpressureStrategy(BackPressureStrategy)
You can use the following methods in RumConfiguration.Builder
when creating the RUM configuration to enable RUM features:
trackUserInteractions(Array<ViewAttributesProvider>)
disableUserInteractionTracking
useViewTrackingStrategy(strategy)
trackLongTasks(durationThreshold)
durationThreshold
on the main thread as long tasks in Datadog. See Automatically track long tasks for more information.trackNonFatalAnrs(Boolean)
setVitalsUpdateFrequency([FREQUENT|AVERAGE|RARE|NEVER])
setSessionSampleRate(<sampleRate>)
setSessionListener(RumSessionListener)
setTelemetrySampleRate
0
and 100
. By default, this is set to 20
.setViewEventMapper
setResourceEventMapper
setActionEventMapper
setErrorEventMapper
setLongTaskEventMapper
trackBackgroundEvents
trackFrustrations
useCustomEndpoint
To automatically track your views (such as activities and fragments), provide a tracking strategy at initialization. Depending on your application’s architecture, you can choose one of the following strategies:
ActivityViewTrackingStrategy
FragmentViewTrackingStrategy
MixedViewTrackingStrategy
NavigationViewTrackingStrategy
For instance, to set each fragment as a distinct view, use the following configuration in your setup:
val rumConfig = RumConfiguration.Builder(applicationId)
.useViewTrackingStrategy(FragmentViewTrackingStrategy(...))
.build()
RumConfiguration rumConfig = new RumConfiguration.Builder(applicationId)
.useViewTrackingStrategy(new FragmentViewTrackingStrategy(...))
.build();
For ActivityViewTrackingStrategy
, FragmentViewTrackingStrategy
, or MixedViewTrackingStrategy
, you can filter which Fragment
or Activity
is tracked as a RUM View by providing a ComponentPredicate
implementation in the constructor:
val rumConfig = RumConfiguration.Builder(applicationId)
.useViewTrackingStrategy(
ActivityViewTrackingStrategy(
trackExtras = true,
componentPredicate = object : ComponentPredicate<Activity> {
override fun accept(component: Activity): Boolean {
return true
}
override fun getViewName(component: Activity): String? = null
})
)
.build()
RumConfiguration rumConfig = new RumConfiguration.Builder(applicationId)
.useViewTrackingStrategy(new ActivityViewTrackingStrategy(
true,
new ComponentPredicate<Activity>() {
@Override
public boolean accept(Activity component) {
return true;
}
@Override
public String getViewName(Activity component) {
return null;
}
}
))
.build();
Note: By default, the library is using ActivityViewTrackingStrategy
. If you decide not to provide a view tracking strategy, you must manually send the views by calling the startView
and stopView
methods yourself.
To get timing information in resources (such as third-party providers, network requests) such as time to first byte or DNS resolution, customize the OkHttpClient
to add the EventListener factory:
Add the Gradle dependency to the dd-sdk-android-okhttp
library in the module-level build.gradle
file:
dependencies {
implementation "com.datadoghq:dd-sdk-android-okhttp:x.x.x"
}
Add add the EventListener factory:
val okHttpClient = OkHttpClient.Builder()
.addInterceptor(DatadogInterceptor())
.eventListenerFactory(DatadogEventListener.Factory())
.build()
OkHttpClient okHttpClient = new OkHttpClient.Builder()
.addInterceptor(new DatadogInterceptor())
.eventListenerFactory(new DatadogEventListener.Factory())
.build();
Long running operations performed on the main thread can impact the visual performance and reactivity of your application. To track these operations, define the duration threshold above which a task is considered too long.
val rumConfig = RumConfiguration.Builder(applicationId)
// …
.trackLongTasks(durationThreshold)
.build()
For example, to replace the default 100 ms
duration, set a custom threshold in your configuration.
val rumConfig = RumConfiguration.Builder(applicationId)
// …
.trackLongTasks(250L) // track tasks longer than 250ms as long tasks
.build()
RumConfiguration rumConfig = new RumConfiguration.Builder(applicationId)
// …
.trackLongTasks(durationThreshold)
.build();
For example, to replace the default 100 ms
duration, set a custom threshold in your configuration.
RumConfiguration rumConfig = new RumConfiguration.Builder(applicationId)
// …
.trackLongTasks(250L) // track tasks longer than 250ms as long tasks
.build();
To modify some attributes in your RUM events, or to drop some of the events entirely before batching, provide an implementation of EventMapper<T>
when initializing the RUM Android SDK:
val rumConfig = RumConfiguration.Builder(applicationId)
// ...
.setErrorEventMapper(rumErrorEventMapper)
.setActionEventMapper(rumActionEventMapper)
.setResourceEventMapper(rumResourceEventMapper)
.setViewEventMapper(rumViewEventMapper)
.setLongTaskEventMapper(rumLongTaskEventMapper)
.build()
RumConfiguration rumConfig = new RumConfiguration.Builder(applicationId)
// ...
.setErrorEventMapper(rumErrorEventMapper)
.setActionEventMapper(rumActionEventMapper)
.setResourceEventMapper(rumResourceEventMapper)
.setViewEventMapper(rumViewEventMapper)
.setLongTaskEventMapper(rumLongTaskEventMapper)
.build();
When implementing the EventMapper<T>
interface, only some attributes are modifiable for each event type:
Event type | Attribute key | Description |
---|---|---|
ViewEvent | view.referrer | URL that linked to the initial view of the page. |
view.url | URL of the view. | |
view.name | Name of the view. | |
ActionEvent | ||
action.target.name | Target name. | |
view.referrer | URL that linked to the initial view of the page. | |
view.url | URL of the view. | |
view.name | Name of the view. | |
ErrorEvent | ||
error.message | Error message. | |
error.stack | Stacktrace of the error. | |
error.resource.url | URL of the resource. | |
view.referrer | URL that linked to the initial view of the page. | |
view.url | URL of the view. | |
view.name | Name of the view. | |
ResourceEvent | ||
resource.url | URL of the resource. | |
view.referrer | URL that linked to the initial view of the page. | |
view.url | URL of the view. | |
view.name | Name of the view. | |
LongTaskEvent | ||
view.referrer | URL that linked to the initial view of the page. | |
view.url | URL of the view. | |
view.name | Name of the view. |
Note: If you return null from the EventMapper<T>
implementation, the event is dropped.
Retrieving the RUM session ID can be helpful for troubleshooting. For example, you can attach the session ID to support requests, emails, or bug reports so that your support team can later find the user session in Datadog.
You can access the RUM session ID at runtime without waiting for the sessionStarted
event:
GlobalRumMonitor.get().getCurrentSessionId { sessionId ->
currentSessionId = sessionId
}
추가 유용한 문서, 링크 및 기사: