Abrova
☰ Docs menu · Android SDK

Android SDK

Native Kotlin SDK for Android with automatic crash detection, ANR detection, OkHttp integration, and performance monitoring.

Android 0.2.3API 19+Kotlin / Java 8+Last updated: 27 Oct 2026
Native Kotlin SDK

Built with Kotlin, supports API 19+ (Android 4.4+)

Automatic Crash Detection

Catches uncaught exceptions, plus native crashes on Android 11+

ANR Detection

Detects Application Not Responding issues

Breadcrumbs & Navigation

Track user journey before errors

OkHttp Interceptor

Automatic HTTP request tracking

Performance Monitoring

Track API latency with OkHttp interceptors

Rich Device Context

Automatic device, OS, and app info collection

Installation#

The SDK comes from Abrova Center. Two steps:

1. Add the Abrova repository in settings.gradle.kts (or settings.gradle):

kotlin
dependencyResolutionManagement {
    repositories {
        google()
        mavenCentral()
        maven { url = uri("https://center.abrova.ir/api/packages/abrova/maven") }
    }
}

Groovy: maven { url "https://center.abrova.ir/api/packages/abrova/maven" }

2. Add the dependency to your app's build.gradle.kts:

kotlin
dependencies {
    implementation("ir.abrova.trace:abrova-trace-android-sdk:0.2.3")
}

Then sync your project with Gradle files. Requirements: Android 4.4 (API 19) or later; native crash reports need Android 11 (API 30) or later.

Quick Start#

Initialize AbrovaTrace in your Application class:

kotlin
import android.app.Application
import ir.abrova.trace.sdk.AbrovaTrace
import ir.abrova.trace.sdk.AbrovaTraceConfig

class MyApp : Application() {

    override fun onCreate() {
        super.onCreate()

        val config = AbrovaTraceConfig.Builder("ab_live_YOUR_API_KEY")
            .environment(if (BuildConfig.DEBUG) "development" else "production")
            .release(BuildConfig.VERSION_NAME)
            .debug(BuildConfig.DEBUG)
            .captureUncaughtExceptions(true)
            .captureSignalCrashes(true)
            .captureAnr(true)
            .anrTimeoutMs(5000L)
            .build()

        AbrovaTrace.init(this, config)
    }

    override fun onTerminate() {
        AbrovaTrace.close()
        super.onTerminate()
    }
}
Graceful Shutdown.

onTerminate() is never called on real devices, so also call close() in your main Activity. close() flushes buffered logs, stops the ANR watchdog and shuts down the network client. The API key must start with ab_live_ — the config throws otherwise.

kotlin
class MainActivity : AppCompatActivity() {
    override fun onDestroy() {
        if (isFinishing) {
            AbrovaTrace.close()  // Flush logs, stop the ANR watchdog
        }
        super.onDestroy()
    }
}

Capture Errors#

Capture Exceptions

kotlin
try {
    someRiskyOperation()
} catch (e: Exception) {
    AbrovaTrace.captureException(e)
}

Capture with Extra Context

kotlin
AbrovaTrace.captureException(
    throwable = exception,
    message = "Failed to load user profile",
    extra = mapOf("user_id" to userId, "screen" to "ProfileActivity"),
    tags = mapOf("feature" to "user_profile")
)

Capture Messages

kotlin
import ir.abrova.trace.sdk.models.MessageLevel

// Info message
AbrovaTrace.captureMessage("User completed onboarding", MessageLevel.INFO)

// Warning message
AbrovaTrace.captureMessage("API rate limit approaching", MessageLevel.WARNING)

// Error message with extra data
AbrovaTrace.captureMessage(
    message = "Payment failed",
    level = MessageLevel.ERROR,
    extra = mapOf("amount" to 99.99)
)
Where messages show up.

Messages appear in the Console under Abrova Trace → Messages, with their level, platform, extra data and breadcrumbs. They never create an issue. Levels: MessageLevel.DEBUG, INFO (default), WARNING, ERROR. FATAL is accepted and stored as error. Use messages for things your app reports on purpose ("checkout started", "sync finished"); for high-volume diagnostics use Logs.

Breadcrumbs help you understand what happened before an error occurred. They are automatically added for Activity navigation, app foreground/background, and system events.

kotlin
import ir.abrova.trace.sdk.models.BreadcrumbType

// User action
AbrovaTrace.addUserBreadcrumb("Added item to cart", mapOf("product_id" to "abc"))

// Navigation
AbrovaTrace.addNavigationBreadcrumb(from = "MainActivity", to = "ProductDetailActivity")

// HTTP request
AbrovaTrace.addHttpBreadcrumb(
    method = "POST",
    url = "https://api.example.com/orders",
    statusCode = 201,
    duration = 342L
)

// Custom breadcrumb
AbrovaTrace.addBreadcrumb("Order placed", BreadcrumbType.INFO, mapOf(
    "order_id" to "12345",
    "total" to 99.99
))

OkHttp Integration#

Automatically track HTTP requests as breadcrumbs and capture HTTP errors:

Basic Setup

kotlin
import ir.abrova.trace.sdk.network.AbrovaTraceInterceptor
import okhttp3.OkHttpClient

val client = OkHttpClient.Builder()
    .addInterceptor(AbrovaTraceInterceptor())
    .build()

With Error Capturing

kotlin
import ir.abrova.trace.sdk.network.AbrovaTraceInterceptor
import ir.abrova.trace.sdk.network.AbrovaTraceErrorInterceptor
import okhttp3.OkHttpClient

val client = OkHttpClient.Builder()
    .addInterceptor(AbrovaTraceInterceptor())       // Adds breadcrumbs
    .addInterceptor(AbrovaTraceErrorInterceptor())  // Captures HTTP errors
    .build()

AbrovaTraceErrorInterceptor sends each 5xx response as a message at error level (Abrova Trace → Messages). 4xx responses are off by default — turn them on with AbrovaTraceErrorInterceptor(captureClientErrors = true) (sent at warning). Network failures (no response) are captured as exceptions.

Crash Detection#

AbrovaTrace detects crashes in-process and, on Android 11+, from the system's own exit records.

  • Java/Kotlin Exceptions — Captured in real-time by an uncaught exception handler (captureUncaughtExceptions). The report is sent before the app closes, on any thread including the main thread; if the server does not accept it within 2 seconds it is kept on the device and sent on the next launch. Requires version 0.2.2 or newer.
  • ANR Events — A watchdog thread detects the main thread being blocked longer than anrTimeoutMs (captureAnr)
  • Native Crashes — SIGSEGV, SIGABRT, etc., reported on the next launch (Android 11 / API 30+ only, captureSignalCrashes)

How It Works

  1. On each launch (API 30+), AbrovaTrace reads Android's ApplicationExitInfo records for your app
  2. Exits since the last launch with reason JVM crash, native crash or ANR are converted into crash reports, including the tombstone for native crashes
  3. The timestamp of the last sent record is saved, so each crash is reported once; a failed send is retried on the next launch. A Java/Kotlin crash the in-process handler already reported is skipped here
  4. On Android 10 and below this step does nothing — JVM crashes are still captured by the in-process handler

Performance Monitoring (APM)#

Track API latency and HTTP request performance automatically.

Automatic HTTP Performance Tracking with OkHttp

kotlin
import ir.abrova.trace.sdk.network.AbrovaTraceInterceptor
import ir.abrova.trace.sdk.network.AbrovaTracePerformanceInterceptor
import okhttp3.OkHttpClient
import okhttp3.Request

val client = OkHttpClient.Builder()
    .addInterceptor(AbrovaTraceInterceptor())              // Breadcrumbs
    .addInterceptor(AbrovaTracePerformanceInterceptor())   // APM tracking
    .build()

// All API calls are automatically tracked!
val request = Request.Builder()
    .url("https://api.example.com/users")
    .build()

client.newCall(request).execute().use { response ->
    // Duration, status code, and errors are automatically reported
    val users = response.body?.string()
}

Using with Retrofit

kotlin
import ir.abrova.trace.sdk.network.AbrovaTraceInterceptor
import ir.abrova.trace.sdk.network.AbrovaTracePerformanceInterceptor
import okhttp3.OkHttpClient
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory

val okHttpClient = OkHttpClient.Builder()
    .addInterceptor(AbrovaTraceInterceptor())
    .addInterceptor(AbrovaTracePerformanceInterceptor())
    .build()

val retrofit = Retrofit.Builder()
    .baseUrl("https://api.example.com/")
    .client(okHttpClient)
    .addConverterFactory(GsonConverterFactory.create())
    .build()

val api = retrofit.create(MyApiService::class.java)
// All API calls through this Retrofit instance are automatically tracked

Track Custom Operations

kotlin
import ir.abrova.trace.sdk.AbrovaTrace

val result = AbrovaTrace.trackOperation("load_user_profile") {
    val user = fetchUserFromApi()
    cacheUserLocally(user)
    user
}

Manual Span Reporting

kotlin
import ir.abrova.trace.sdk.models.PerformanceSpan

// HTTP request span
AbrovaTrace.reportPerformanceSpan(
    PerformanceSpan.fromHttpRequest(
        method = "GET",
        url = "https://api.example.com/products",
        statusCode = 200,
        durationMs = 245L,
        startTime = System.currentTimeMillis() - 245
    )
)

// Database query span
AbrovaTrace.reportPerformanceSpan(
    PerformanceSpan.forDbQuery(
        queryType = "SELECT",
        tableName = "products",
        durationMs = 15L,
        startTime = System.currentTimeMillis() - 15,
        rowsAffected = 50
    )
)

// Custom operation span
AbrovaTrace.reportPerformanceSpan(
    PerformanceSpan.custom(
        operation = "image_processing",
        durationMs = 1250L,
        startTime = System.currentTimeMillis() - 1250,
        tags = mapOf("format" to "png")
    )
)

Logging#

Structured log ingestion with automatic batching and retry. Enable it once, then use the level helpers from anywhere in your app:

kotlin
// Enable logging after AbrovaTrace.init()
AbrovaTrace.enableLogging(sourceId = "my-android-app")

AbrovaTrace.info("User login successful", mapOf("user_id" to "user_123"))

See Android SDK Logging for the full pipeline — batching, log levels, lifecycle handling, manual flush, and configuration.

Configuration Options#

kotlin
val config = AbrovaTraceConfig.Builder("ab_live_YOUR_API_KEY")
    .environment("production")        // Environment name
    .release("1.0.0")                 // App version
    .debug(false)                     // Enable debug logging
    .enabled(true)                    // Enable/disable SDK
    .sampleRate(1.0f)                 // Error sample rate (0.0 - 1.0)
    .maxBreadcrumbs(20)               // Max breadcrumbs to keep
    .httpTimeout(30)                  // HTTP request timeout (seconds)
    .enableOfflineStorage(true)       // Keep unsent errors on the device while offline
    .captureUncaughtExceptions(true)  // Catch uncaught exceptions
    .captureSignalCrashes(true)       // Detect native signal crashes
    .captureAnr(true)                 // Detect ANR issues
    .anrTimeoutMs(5000L)              // ANR detection timeout
    .apiUrl("https://trace.abrova.ir") // Server URL (self-hosted override)
    .build()
OptionTypeRequiredDescription
apiKeyStringYesYour project API Key (must start with ab_live_)
environmentStringNoEnvironment name (default: "production")
releaseStringNoApp version identifier
debugBooleanNoEnable debug logging (default: false)
enabledBooleanNoEnable/disable SDK (default: true)
sampleRateFloatNoSample rate 0.0-1.0 for captureException (default: 1.0)
maxBreadcrumbsIntNoMax breadcrumbs to keep (default: 20)
httpTimeoutIntNoHTTP request timeout in seconds (default: 30)
enableOfflineStorageBooleanNoKeep errors that could not be sent while the device is offline (up to 100) and send them later (default: true)
captureUncaughtExceptionsBooleanNoCatch uncaught exceptions (default: true)
captureSignalCrashesBooleanNoReport native crashes, and other crashes and ANRs recorded by the system, on next launch, API 30+ (default: true)
captureAnrBooleanNoDetect ANR events (default: true)
anrTimeoutMsLongNoANR detection timeout in ms (default: 5000)
apiUrlStringNoTrace server URL (default: "https://trace.abrova.ir")
Offline storage.

When an error cannot be sent because the device has no connection, the SDK keeps it in the app's private storage and sends it later: on the next launch, and as soon as another error is sent successfully. Up to 100 errors are kept; when the limit is reached, the oldest are dropped. This covers errors only — captureException, uncaught exceptions and ANR reports. Messages, logs and performance data are not stored. Set enableOfflineStorage(false) to keep nothing on the device.

ProGuard / R8#

The SDK includes ProGuard rules automatically. If you need custom rules, add to proguard-rules.pro:

text
-keep class ir.abrova.trace.sdk.** { *; }
-keepattributes SourceFile,LineNumberTable
-keepattributes *Annotation*

Device Compatibility#

  • Android 4.4 KitKat — API 19
  • Android 5.0 Lollipop — API 21
  • Android 8.0 Oreo — API 26
  • Android 10 — API 29
  • Android 12 — API 31
  • Android 14 — API 34

Context & Tags#

Attach persistent metadata to every error and message captured by the SDK (tags are also added to performance spans). Useful for filtering by team, region, feature flag, or experiment cohort in the Abrova Console. ANR reports and crash reports read from the system on the next launch do not carry them.

Global extras

Extras are arbitrary key/value pairs (string keys, any serializable value) automatically merged into every event:

kotlin
// Set individual extras
AbrovaTrace.setExtra("organizationId", "org-123")
AbrovaTrace.setExtra("feature", "checkout-v2")

// Set multiple at once
AbrovaTrace.setExtras(mapOf(
    "organizationId" to "org-123",
    "feature" to "checkout-v2",
    "experiment" to "new-flow"
))

// Clear when no longer needed (e.g. on logout)
AbrovaTrace.clearExtras()

Tags

Tags are string-only key/value pairs that are attached to every error and message:

kotlin
AbrovaTrace.setTag("team", "payments")
AbrovaTrace.setTag("region", "us-east")

AbrovaTrace.setTags(mapOf("team" to "payments", "version" to "2.1"))

AbrovaTrace.clearTags()
Tags vs Extras.

Use tags for low-cardinality categorical filters (team, region, build flavor). Use extras for richer per-event context (user IDs, request bodies, computed values).

API Reference#

Full method surface of AbrovaTrace and PerformanceSpan. Method signatures use the Kotlin convention; optional parameters are marked with ?.

AbrovaTrace — Initialization & Lifecycle

OptionTypeRequiredDescription
init(context, config)--Initialize SDK with a full AbrovaTraceConfig
init(context, apiKey)--Initialize SDK with just an API key (uses defaults)
isInitialized()--Returns true if the SDK is initialized
close()--Flush logs, stop the ANR watchdog and shut down the network client

Error capture

OptionTypeRequiredDescription
captureException(throwable, message?, extra?, tags?, callback?)--Capture an exception with optional metadata
captureMessage(message, level?, extra?, tags?, callback?)--Send a message to Abrova Trace → Messages at a given level (not an issue)

User & session

OptionTypeRequiredDescription
setUserId(id)--Set the current user identifier
getUserId()--Get the current user identifier

Global context (extras)

OptionTypeRequiredDescription
setExtra(key, value)--Set a single extra
setExtras(extras)--Set multiple extras at once
clearExtras()--Clear all extras

Tags

OptionTypeRequiredDescription
setTag(key, value)--Set a single tag
setTags(tags)--Set multiple tags at once
clearTags()--Clear all tags

Breadcrumbs

OptionTypeRequiredDescription
addBreadcrumb(message, type?, data?)--Add a generic breadcrumb
addNavigationBreadcrumb(from, to)--Add a navigation breadcrumb
addUserBreadcrumb(action, data?)--Add a user-action breadcrumb
addHttpBreadcrumb(method, url, statusCode?, duration?)--Add an HTTP breadcrumb
clearBreadcrumbs()--Clear all breadcrumbs

Performance monitoring

OptionTypeRequiredDescription
reportPerformanceSpan(method, url, statusCode, durationMs, startTime, errorMessage?, tags?)--Report a single HTTP span
reportPerformanceSpan(span, callback?)--Report a constructed PerformanceSpan
reportPerformanceSpanBatch(spans, callback?)--Report multiple spans at once
trackOperation(operation, operationType?, tags?, block)--Time a block and report it as a span
performanceInterceptor(minDurationMs?)--Build an OkHttp interceptor for HTTP timings

Logging

OptionTypeRequiredDescription
enableLogging(sourceId?, sourceName?, batchSize?, flushIntervalMs?)--Turn on the log pipeline
log(message, level?, metadata?)--Log at an explicit level
trace(message, metadata?)--Log at trace level
logDebug(message, metadata?)--Log at debug level
info(message, metadata?)--Log at info level
warn(message, metadata?)--Log at warn level
logError(message, metadata?)--Log at error level
fatal(message, metadata?)--Log at fatal level
flushLogs(callback?)--Flush buffered logs immediately
getPendingLogCount()--Number of logs still in the buffer

PerformanceSpan factories

OptionTypeRequiredDescription
PerformanceSpan.fromHttpRequest(...)--Construct a span from an HTTP request/response
PerformanceSpan.forDbQuery(...)--Construct a span for a database query
PerformanceSpan.custom(...)--Construct a fully custom span
PerformanceSpan.generateTraceId()--Generate a random trace ID
PerformanceSpan.generateSpanId()--Generate a random span ID

Complete Example#

kotlin
import android.app.Application
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import ir.abrova.trace.sdk.AbrovaTrace
import ir.abrova.trace.sdk.AbrovaTraceConfig
import ir.abrova.trace.sdk.models.MessageLevel
import ir.abrova.trace.sdk.network.AbrovaTraceInterceptor
import okhttp3.OkHttpClient

class MyApp : Application() {
    override fun onCreate() {
        super.onCreate()

        val config = AbrovaTraceConfig.Builder("ab_live_YOUR_API_KEY")
            .environment(if (BuildConfig.DEBUG) "development" else "production")
            .release(BuildConfig.VERSION_NAME)
            .debug(BuildConfig.DEBUG)
            .captureUncaughtExceptions(true)
            .captureSignalCrashes(true)
            .captureAnr(true)
            .anrTimeoutMs(5000L)
            .maxBreadcrumbs(20)
            .enableOfflineStorage(true)
            .build()

        AbrovaTrace.init(this, config)
    }

    override fun onTerminate() {
        AbrovaTrace.close()
        super.onTerminate()
    }
}

class MainActivity : AppCompatActivity() {

    private val httpClient = OkHttpClient.Builder()
        .addInterceptor(AbrovaTraceInterceptor())
        .build()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        AbrovaTrace.setUserId("user_123")
        AbrovaTrace.addUserBreadcrumb("Viewed home screen")
    }

    private fun handlePurchase() {
        try {
            processPurchase()
            AbrovaTrace.captureMessage(
                message = "Purchase successful",
                level = MessageLevel.INFO,
                extra = mapOf("amount" to 99.99)
            )
        } catch (e: Exception) {
            AbrovaTrace.captureException(
                throwable = e,
                message = "Purchase failed",
                extra = mapOf("user_id" to "user_123", "screen" to "MainActivity")
            )
        }
    }

    override fun onDestroy() {
        if (isFinishing) {
            AbrovaTrace.close()
        }
        super.onDestroy()
    }

    private fun processPurchase() { /* ... */ }
}

Best Practices#

Initialize in Application Class

Initialize AbrovaTrace in your Application class onCreate() to catch all errors from app start

Call close() on App Exit

Flushes buffered logs and stops the ANR watchdog

Add Context to Errors

Include user IDs, screen names, and relevant data in the extra parameter

Use Different Environments

Separate production, staging, and development errors using the environment config

Enable Debug Mode in Development

Set debug(true) during development to see SDK logs and verify integration

Use OkHttp Interceptors

Add AbrovaTraceInterceptor for HTTP breadcrumbs and AbrovaTraceErrorInterceptor for HTTP error reporting

Enable Logging Early

Call enableLogging() right after init() to capture all application events

Was this page helpful?
Report a problem