The Atatus mobile SDK lets you collect traces from your native Android and Android TV applications. You can automatically trace network requests, create custom spans for any operation in your code, and connect those traces to your back-end services when you also run Atatus APM. The tracer implements the OpenTelemetry standard.

Prerequisites

  • The Atatus SDK is initialized in your application. See Android (Kotlin) setup for the initialization steps.
  • A RUM application created in Atatus, which gives you a License Key.
Note: If your project's minSdkVersion is below 26, enable Java 8+ library desugaring in your Gradle configuration. The tracer relies on APIs that require desugaring on older Android versions.

Setup

1. Add the Trace Dependencies

Add the Atatus trace and OkHttp libraries to your application module's build.gradle:

copy
icon/buttons/copy
dependencies {
    implementation("com.atatus.android:atatus-android-trace:1.+")
    implementation("com.atatus.android:atatus-android-okhttp:1.+")
}

2. Initialize the SDK

Initialize the SDK in your Application class, as described in Android (Kotlin) setup:

copy
icon/buttons/copy
import android.app.Application
import com.atatus.android.Atatus
import com.atatus.android.core.configuration.Configuration
import com.atatus.android.privacy.TrackingConsent

class SampleApplication : Application() {

    override fun onCreate() {
        super.onCreate()

        val configuration = Configuration.Builder(
            licenseKey = "<LICENSE_KEY>",
            env = "<ENV_NAME>",
            variant = "<APP_VARIANT_NAME>"
        )
            .build()

        Atatus.initialize(this, configuration, TrackingConsent.GRANTED)
    }
}

3. Enable the Trace Feature

After the SDK is initialized, enable the Trace feature and register a global tracer:

copy
icon/buttons/copy
import com.atatus.android.trace.AtatusTracing
import com.atatus.android.trace.GlobalAtatusTracer
import com.atatus.android.trace.Trace
import com.atatus.android.trace.TraceConfiguration

val traceConfiguration = TraceConfiguration.Builder().build()
Trace.enable(traceConfiguration)

GlobalAtatusTracer.registerIfAbsent(
    AtatusTracing.newTracerBuilder().build()
)

4. Set the Sample Rate

Configure the trace sample rate, service name, and partial flush threshold when you build the tracer. The sample rate is a percentage between 0f and 100f:

copy
icon/buttons/copy
val tracer = AtatusTracing.newTracerBuilder()
    .withPartialFlushMinSpans(10)
    .withSampleRate(80f)
    .withServiceName("<SERVICE_NAME>")
    .build()

GlobalAtatusTracer.registerIfAbsent(tracer)

5. Instrument Network Requests

To trace network requests made with OkHttp, add the AtatusInterceptor and list the hosts whose requests you want to trace end to end:

copy
icon/buttons/copy
import com.atatus.android.okhttp.AtatusInterceptor
import okhttp3.OkHttpClient

val tracedHosts = listOf("example.com", "example.eu")

val okHttpClient = OkHttpClient.Builder()
    .addInterceptor(
        AtatusInterceptor.Builder(tracedHosts)
            .setTraceSampleRate(20f)
            .build()
    )
    .build()

For detailed network-level timing, also add a network interceptor:

copy
icon/buttons/copy
import com.atatus.android.okhttp.AtatusInterceptor
import com.atatus.android.okhttp.TracingInterceptor
import okhttp3.OkHttpClient

val okHttpClient = OkHttpClient.Builder()
    .addInterceptor(
        AtatusInterceptor.Builder(tracedHosts)
            .setTraceSampleRate(20f)
            .build()
    )
    .addNetworkInterceptor(
        TracingInterceptor.Builder(tracedHosts)
            .setTraceSampleRate(100f)
            .build()
    )
    .build()

Each traced request is reported as a span. When the host is in tracedHosts and you also run Atatus APM, the request is connected to its back-end trace.

6. Create Custom Spans

Retrieve the global tracer and wrap any operation in a span:

copy
icon/buttons/copy
import com.atatus.android.trace.GlobalAtatusTracer

val tracer = GlobalAtatusTracer.get()

val span = tracer.buildSpan("<SPAN_NAME>").start()
// Do something
span.finish()

7. Activate Spans and Create Nested Spans

Activate a span to make it the active span for the current scope, so any spans created inside it become children automatically:

copy
icon/buttons/copy
val tracer = GlobalAtatusTracer.get()

val span = tracer.buildSpan("parent_operation").start()
try {
    tracer.activateSpan(span).use {
        // Work that belongs to the parent span

        val childSpan = tracer.buildSpan("child_operation").start()
        try {
            tracer.activateSpan(childSpan).use {
                // Work that belongs to the child span
            }
        } catch (e: Throwable) {
            childSpan.logThrowable(e)
        } finally {
            childSpan.finish()
        }
    }
} catch (e: Throwable) {
    span.logThrowable(e)
} finally {
    span.finish()
}

8. Add Tags and Errors to Spans

Attach tags to give spans more context, and record errors on a span:

copy
icon/buttons/copy
span.setTag("http.url", url)
span.logThrowable(throwable)
span.logErrorMessage("Something went wrong")

To correlate spans with the RUM view they happened in, enable RUM bundling on the tracer:

copy
icon/buttons/copy
val tracer = AtatusTracing.newTracerBuilder()
    .setBundleWithRumEnabled(true)
    .build()

GlobalAtatusTracer.registerIfAbsent(tracer)

Next Steps