Android
This guide shows you how to easily integrate the Coralogix RUM SDK into your native Android apps.
Coralogix Android SDK supports Android 7.0+ (API level 24) and above.
Features
In addition to capturing errors, including ANRs, this SDK can intercept network calls, send log messages with customized severity levels, report errors, and track your customers' page transitions.
Installation
Add the Maven Central Repository dependency to your module's build.gradle:
dependencies {
implementation "com.coralogix:android-sdk:2.21.0"
}
The SDK pulls in androidx.work transitively — you do not need to declare it yourself.
If you use on-demand WorkManager initialization and remove the default androidx.startup provider, the automatic flush-on-background backstop job is skipped. The immediate flush still runs on backgrounding and nothing else is affected.
If you encounter a dependency that requires Java 8+ APIs, see Troubleshooting for next steps.
Initialization
Call CoralogixRum.initialize() once in your Application.onCreate() method. Calling it a second time is a no-op.
import com.coralogix.android.sdk.CoralogixRum
import com.coralogix.android.sdk.model.CoralogixDomain
import com.coralogix.android.sdk.model.CoralogixOptions
class MyApp : Application() {
override fun onCreate() {
super.onCreate()
val options = CoralogixOptions(
applicationName = "MyApp",
coralogixDomain = CoralogixDomain.EU2,
publicKey = "<YOUR_PUBLIC_KEY>",
environment = "production",
version = BuildConfig.VERSION_NAME
)
CoralogixRum.initialize(this, options)
}
}
Check whether the SDK initialized successfully:
if (CoralogixRum.isInitialized()) {
Log.d("TAG", "Coralogix SDK is initialized.")
}
Refer to the following fields for CoralogixOptions.
| Property | Type | Description | Required | Example |
|---|---|---|---|---|
applicationName | String | Name of the application | Yes | "MyApp" |
coralogixDomain | CoralogixDomain | The region associated with your Coralogix domain | Yes | EU1, EU2, US1, US2, US3, AP1, AP2, AP3 |
publicKey | String | Coralogix token, publicly visible public_key value | Yes | "my_public_key" |
labels | Map<String,String> | An optional set of labels that are added to every span for enrichment purposes | No | labels = mapOf("payment" to "visa") |
environment | String | Specify the environment, such as development, staging, or production | Yes | "production" |
version | String | Version of the application | No | "v.1.0" |
userContext | UserContext | User context information, such as user ID, name, email, and additional metadata | No | UserContext(userId = "123", username = "User", email = "[email protected]", metadata = mapOf("role" to "Admin")) |
viewContext | ViewContext | Description of current activity | No | "MyActivityName" |
instrumentations | Map<Instrumentation, Boolean> | Map to turn on/off specific instrumentation. Defaults to all true. | No | instrumentations = mapOf( Instrumentation.Network to false) |
ignoreUrls | List<String> | A list of URLs to ignore for logging and monitoring purposes. Supports strings and regular expressions for matching. | No | ignoreUrls = listOf("https://jsonplaceholder\\.typicode\\.com/.*", ".*\\.svg", ".*\\.ico") |
ignoreErrors | List<String> | List of error patterns to be ignored for logging and monitoring. Supports strings and regular expressions for matching. | No | ignoreErrors = listOf("^IllegalArgumentException$", "IOException", "ANR") |
collectIPData | Boolean | Allow the option to send IP address to identify session country. Defaults to true. | No | true |
sessionSampleRate | Number | Percentage of overall sessions being tracked. Defaults to 100%. | No | 100% |
traceParentInHeader | TraceParentInHeaderConfig | Controls W3C traceparent header injection in network requests across service boundaries. | No | TraceParentInHeaderConfig() |
fpsSamplingSeconds | Number | Interval (in seconds) for collecting and logging frame rate data for performance tracking. Defaults to 300 sec. | No | 10 |
beforeSend | (EditableCxRum) -> EditableCxRum? | Intercepts and modifies each event before sending. Return null to discard the event. | No | beforeSend = { event -> if (event.userContext?.email?.endsWith("@example.com", ignoreCase = true) == true) { event.copy(userContext = event.userContext?.copy(email = "redacted")) } else { event } } |
beforeSendCallback | (List<Map<String, Any?>>) -> Unit | Callback for hybrid frameworks (React Native, Flutter). No-op on native Android. | No | beforeSendCallback = { events -> /* forward to host */ } |
tracesExporter | (CoralogixTraceExporterData) -> Unit | Optional callback that receives the raw OTLP span payload alongside the standard RUM export, enabling distributed-tracing integration. See Trace Exporter. | No | tracesExporter = { data -> /* forward OTLP payload */ } |
excludeFromSampling | List<ExcludableInstrumentation> | Instrumentation categories always exported at 100% regardless of sessionSampleRate. See Decoupling session sampling. | No | listOf(ExcludableInstrumentation.Errors) |
userInteractionOptions | UserInteractionOptions | Configuration for user-interaction instrumentation. | No | UserInteractionOptions() |
debug | Boolean | Enables verbose SDK logging. Defaults to false. | No | true |
proxyUrl | String? | Optional proxy endpoint for data routing. The ingestion URL is appended as a cxforward query parameter. | No | "https://your.proxy.com/" |
Configuration
Instrumentation
Enable or disable instrumentation modules using the instrumentations map. All modules are enabled by default.
instrumentations = mapOf(
Instrumentation.Network to false // disables network instrumentation
)
| Module | Constant | Description |
|---|---|---|
Error | Instrumentation.Error | Captures handled and unhandled exceptions. |
Network | Instrumentation.Network | Reports network requests and responses. |
Custom | Instrumentation.Custom | Allows sending custom logs and events. |
MobileVitals | Instrumentation.MobileVitals | Tracks CPU, memory, FPS, cold start, and warm start. |
Anr | Instrumentation.Anr | Detects Application Not Responding (ANR) events. |
UserInteraction | Instrumentation.UserInteraction | Automatically tracks taps, scrolls, and swipes. |
Lifecycle | Instrumentation.Lifecycle | Captures app and fragment lifecycle events. |
Navigation | Instrumentation.Navigation | Reports navigation events between screens. |
Mobile Vitals
Set which Mobile Vitals detectors to run. All detectors are enabled by default.
val config = CoralogixOptions(
// ...
mobileVitalsOptions = mapOf(
MobileVitalType.CpuUsage to true,
MobileVitalType.MemoryUsage to false // disables memory detector
)
)
| Detector | Description |
|---|---|
MobileVitalType.CpuUsage | CPU utilization and main-thread time. |
MobileVitalType.MemoryUsage | Memory footprint and utilization. |
MobileVitalType.Fps | Frame rate (FPS). |
MobileVitalType.ColdStartTime | App cold start duration. |
MobileVitalType.WarmStartTime | App warm start duration. |
MobileVitalType.SlowFrozenFrames | Slow and frozen frame counts. |
Learn more in Mobile Vitals.
Decoupling session sampling
By default, sessionSampleRate is an all-or-nothing gate: when a session is sampled out, the entire SDK shuts down and nothing is sent. Use excludeFromSampling to keep specific instrumentation categories flowing even when a session is sampled out.
This is useful when you want a low sample rate to reduce data volume but still need every error or custom log to reach Coralogix.
val options = CoralogixOptions(
// ...
sessionSampleRate = 10,
excludeFromSampling = listOf(
ExcludableInstrumentation.Logs,
ExcludableInstrumentation.Errors
)
)
| Value | Exported event type |
|---|---|
Errors | error |
Logs | log |
Network | network-request |
UserInteractions | user-interaction |
MobileVitals | mobile-vitals |
CustomSpan | custom-span |
CustomMeasurement | custom-measurement |
Navigation | navigation |
Lifecycle | life-cycle |
Inside a beforeSend hook, EditableCxRum.isSessionSampledIn tells you whether the event came from a sampled-in session or was sent only because its category appears in excludeFromSampling. Use this to filter excluded categories further — for example, keep only ANR errors from sampled-out sessions:
beforeSend = { event ->
val isError = event.errorContext != null
when {
!isError -> event
event.isSessionSampledIn == true -> event
event.errorContext?.type == "ANR" -> event
else -> null
}
}
Ignoring errors and URLs
- Ignore errors: Use
ignoreErrorsto exclude errors matching specific patterns. Supports strings and regular expressions.
ignoreErrors = listOf(
"^IllegalArgumentException$",
"IOException",
"ANR"
)
- Ignore URLs: Use
ignoreUrlsto exclude URLs matching specific patterns from network tracing.
ignoreUrls = listOf(
"https://jsonplaceholder\\.typicode\\.com/.*",
".*\\.svg",
".*\\.ico"
)
Network interception
To enable RUM to intercept network events, add CoralogixOkHttpInterceptor to your network client.
OkHttp
val okHttpClient = OkHttpClient.Builder()
.addInterceptor(CoralogixOkHttpInterceptor())
.build()
val request = Request.Builder()
.url("https://api.example.com/data")
.build()
val response = okHttpClient.newCall(request).execute()
println(response.body?.string())
Retrofit
val okHttpClient = OkHttpClient.Builder()
.addInterceptor(CoralogixOkHttpInterceptor())
.build()
val retrofit = Retrofit.Builder()
.baseUrl("https://api.example.com/")
.client(okHttpClient)
.addConverterFactory(GsonConverterFactory.create())
.build()
interface ApiService {
@GET("data")
suspend fun getData(): Response<DataModel>
}
val apiService = retrofit.create(ApiService::class.java)
val response = apiService.getData()
println(response.body())
Ktor
Only the OkHttp engine is supported.
val httpClient = HttpClient(OkHttp) {
engine {
preconfigured = OkHttpClient.Builder()
.addInterceptor(CoralogixOkHttpInterceptor())
.build()
}
}
val response = httpClient.get("https://api.example.com/data")
println(response.bodyAsText())
Network capture rules
By default, the SDK captures request URLs, status codes, and timings. Use networkCaptureConfig to also capture specific request and response headers or payloads on a per-URL basis. Rules are evaluated in order — the first match wins.
val config = CoralogixOptions(
// ...
networkCaptureConfig = listOf(
NetworkCaptureRule(
urlPattern = Regex(".*api\\.example\\.com.*"),
reqHeaders = listOf("Accept", "Content-Type"),
resHeaders = listOf("Content-Type", "X-Request-Id"),
collectReqPayload = true,
collectResPayload = false
)
)
)
NetworkCaptureRule field | Type | Description |
|---|---|---|
url | String? | Exact URL string to match. |
urlPattern | Regex? | Regex pattern matched against the full request URL. Provide url or urlPattern, not both. |
reqHeaders | List<String>? | Request header names to capture (case-insensitive match, captured using your casing). |
resHeaders | List<String>? | Response header names to capture (case-insensitive match, captured using your casing). |
collectReqPayload | Boolean | Capture the request body. Only text-based payloads ≤ 1024 characters are captured. |
collectResPayload | Boolean | Capture the response body. Only text-based payloads ≤ 1024 characters are captured. |
Payloads exceeding 1024 characters are dropped entirely, not truncated. Only Content-Type values of application/json, text/*, application/javascript, and application/xml are captured.
Only headers explicitly listed in reqHeaders or resHeaders are captured. Avoid adding security-sensitive headers such as Authorization or Cookie to your capture rules in production.
Trace exporter
Forward span data to your own collector or backend by setting a tracesExporter callback in CoralogixOptions. The callback receives each batch of spans the SDK produces. Spans continue to flow to Coralogix when this callback is set.
val config = CoralogixOptions(
// ...
tracesExporter = { data ->
val json = data.toJson() // full OTLP JSON string
val spans = data.resourceSpans // structured List<OtlpResourceSpans>
forwardToCustomCollector(json)
}
)
CoralogixTraceExporterData fields:
| Field | Type | Description |
|---|---|---|
resourceSpans | List<OtlpResourceSpans> | Structured OTLP resource-spans tree. |
spanCount | Int | Total number of spans in this batch. |
Call toJson() to get the full OTLP JSON encoding ready to POST.
For payload format and forwarding considerations, see Trace exporter.
Modify events with beforeSend
Use beforeSend to inspect, transform, or discard events before they reach Coralogix. Return the modified event to send it, or return null to discard it.
val config = CoralogixOptions(
// ...
beforeSend = { event ->
if (event.userContext?.email?.endsWith("@coralogix.com", ignoreCase = true) == true) {
event.copy(userContext = event.userContext?.copy(email = "redacted"))
} else {
event
}
}
)
The event parameter is an EditableCxRum object that exposes all event fields you can read or copy-and-modify:
data class EditableCxRum(
val eventContext: EventContext?,
val labels: Map<String, Any?>?,
val spanId: String?,
val traceId: String?,
val environment: String?,
val viewContext: ViewContext?,
val errorContext: EditableErrorContext?,
val logContext: LogContext?,
val networkRequestContext: NetworkRequestContext?,
val userContext: UserContext?,
val lifecycleContext: LifecycleContext?,
val customMeasurementContext: CustomMeasurementContext?,
val interactionContext: InteractionContext?,
val isSessionSampledIn: Boolean?
)
interactionContext.isMaskedElement is true when the interaction targeted masked content — use it in beforeSend to drop or redact masked interactions. See Privacy and masking for recipes.
isSessionSampledIn is read-only. It is false when the event reached beforeSend only because its category appears in excludeFromSampling.
Proxy URL
Route data through a proxy endpoint by setting proxyUrl in CoralogixOptions. The SDK appends the Coralogix ingestion URL as a cxforward query parameter:
https://your.proxy.com/?cxforward=ingestion.url.com
Your proxy is responsible for forwarding the data to the Coralogix ingestion endpoint.
Custom spans
Use custom spans to instrument business-critical operations. Spans appear in Coralogix as standalone records or grouped under a parent global span.
What you need
W3C traceparent header injection must be enabled:
val options = CoralogixOptions(
// ...
traceParentInHeader = TraceParentInHeaderConfig(enabled = true)
)
getCustomTracer() returns null and logs a warning if this is not set.
Get the tracer
Acquire a tracer from CoralogixRum. Only one tracer instance is issued per SDK lifecycle — store and reuse the returned instance.
val tracer = CoralogixRum.getCustomTracer() ?: return
Start a global span
A global span is the root of a custom trace. Only one global span can be active at a time.
val globalSpan = tracer.startGlobalSpan(
name = "checkout-flow",
labels = mapOf("cart.item_count" to 3)
) ?: return
startGlobalSpan returns null if a global span is already active. The global span exposes traceId and spanId (W3C hex strings) so you can correlate spans with your own backend.
Always call endSpan() when the operation is complete:
globalSpan.endSpan()
Add child spans
Within a global span's context, create child spans for sub-operations:
val childSpan = globalSpan.startCustomSpan(
name = "payment-step",
labels = mapOf("payment.method" to "credit_card")
)
// ... do work ...
childSpan.endSpan()
Child spans inherit the global span's traceId and set the global span as their parentSpanId.
Set attributes
CoralogixCustomSpan supports four attribute types:
childSpan.setAttribute("http.method", "GET") // String
childSpan.setAttribute("http.status", 200) // Int
childSpan.setAttribute("duration.ms", 3.14) // Double
childSpan.setAttribute("from.cache", true) // Boolean
Add events
Add timestamped events to a span to record notable moments:
childSpan.addEvent("retry-attempted")
childSpan.addEvent(
name = "cache-miss",
attributes = mapOf("key" to "user-profile", "size_bytes" to 1024)
)
childSpan.addEvent(
name = "request-sent",
timestamp = System.currentTimeMillis() - 500,
unit = TimeUnit.MILLISECONDS
)
Supported attribute value types: String, Int, Long, Double, Float, Boolean.
Set span status
childSpan.setStatus(StatusCode.OK)
childSpan.setStatus(StatusCode.ERROR)
Cross-thread context propagation
If you start a child span from a different thread than the global span, use withContext to make the global span the active OTel context on that thread:
globalSpan.withContext {
val childSpan = globalSpan.startCustomSpan("background-task")
// ... do work ...
childSpan.endSpan()
}
Ignored instruments
Pass ignoredInstruments to getCustomTracer() to suppress specific automatic instrumentation while a global span is active:
val tracer = CoralogixRum.getCustomTracer(
ignoredInstruments = setOf(
CoralogixIgnoredInstrument.NETWORK_REQUESTS,
CoralogixIgnoredInstrument.USER_INTERACTIONS,
CoralogixIgnoredInstrument.ERRORS
)
) ?: return
| Value | Description |
|---|---|
NETWORK_REQUESTS | Suppresses automatic network event reporting. |
USER_INTERACTIONS | Suppresses automatic user-interaction event reporting. |
ERRORS | Suppresses automatic error/crash event reporting. |
Complete example
val tracer = CoralogixRum.getCustomTracer() ?: return
val globalSpan = tracer.startGlobalSpan(
name = "checkout-flow",
labels = mapOf("cart.item_count" to 3)
) ?: return
val paymentSpan = globalSpan.startCustomSpan(
name = "payment-step",
labels = mapOf("payment.method" to "credit_card")
)
paymentSpan.addEvent("payment-initiated")
try {
processPayment()
paymentSpan.setAttribute("payment.success", true)
paymentSpan.setStatus(StatusCode.OK)
paymentSpan.addEvent("payment-complete")
} catch (e: Exception) {
paymentSpan.setAttribute("error.message", e.message ?: "unknown")
paymentSpan.setStatus(StatusCode.ERROR)
} finally {
paymentSpan.endSpan()
globalSpan.endSpan()
}
For details about prerequisites, use cases, and limitations, see Custom spans.
Integration functions
Set user context
Provide user context dynamically as user metadata becomes available.
CoralogixRum.setUserContext(
UserContext(
userId = "123",
username = "User User",
metadata = mapOf("role" to "Admin")
)
)
val context: UserContext = CoralogixRum.getUserContext()
getUserContext() returns a UserContext with empty values if the SDK is not initialized.
Set labels
Update global labels during runtime.
CoralogixRum.setLabels(
mapOf(
"environment" to "production",
"version" to "1.0.2"
)
)
val labels: Map<String, Any?> = CoralogixRum.getLabels()
getLabels() returns an empty map if the SDK is not initialized.
Set application context
Update the application name and version dynamically.
CoralogixRum.setApplicationContext(
appName = "MyApp",
appVersion = "1.0.0"
)
Custom measurements
Send a numeric key-value measurement to Coralogix.
CoralogixRum.sendCustomMeasurement("image_upload_time_ms", 1480L)
Custom time measurement
Measure the duration of any operation by wrapping it with startTimeMeasure and endTimeMeasure. The SDK records the elapsed time and emits a measurement event with the duration in milliseconds.
CoralogixRum.startTimeMeasure("checkout-flow", mapOf("cart.items" to 3))
// ... perform the operation ...
CoralogixRum.endTimeMeasure("checkout-flow")
- If
startTimeMeasureis called a second time with the same name beforeendTimeMeasure, the second call is a no-op. - If the session goes idle between start and end, the measurement is silently dropped.
- Calling
endTimeMeasurewithout a priorstartTimeMeasureis a no-op.
Set view context
When using the SDK, ViewContext is set to the activity or fragment name by default. Update it manually when needed.
CoralogixRum.setViewContext(
viewName = "CustomViewName"
)
Log messages
Send log messages with customized severity levels. data and labels are optional and apply only to this event.
CoralogixRum.log(
severity = CoralogixLogSeverity.Error,
message = "Custom log message error",
data = mapOf("userId" to "12345"), // optional
labels = mapOf("environment" to "staging") // optional
)
CoralogixLogSeverity defines severity levels:
| Case | Severity level |
|---|---|
Debug | Debug |
Verbose | Verbose |
Info | Informational |
Warn | Warning |
Error | Error |
Critical | Critical |
Report errors
Report a handled error to Coralogix. Reported errors appear with level 5 (error) severity.
// Basic — report the exception.
try {
riskyOperation()
} catch (t: Throwable) {
CoralogixRum.reportError(
throwable = t,
data = mapOf("cart_size" to 3, "reason" to "timeout"),
labels = mapOf("team" to "payments")
)
}
The data and labels parameters are optional. data attaches arbitrary key-value pairs to the error event; labels adds searchable metadata.
Custom spans
try {
riskyOperation()
} catch (t: Throwable) {
CoralogixRum.reportError(
throwable = t,
data = mapOf("orderId" to "ORD-123", "amount" to 99.99),
labels = mapOf("team" to "payments")
)
}
data is emitted under error_context.error_custom_data. labels are merged into the event's labels. Provide JSON-serializable values; values that cannot be serialized may be dropped.
For richer error reporting, pass a CoralogixErrorDecorator with customAttributes:
val decorator = CoralogixErrorDecorator(
throwable = t,
isCrash = false
).copy(
customAttributes = mapOf(
"userId" to "12345",
"screen" to "checkout"
)
)
CoralogixRum.reportError(decorator)
To filter or redact error data before it leaves the device, edit the event in the beforeSend callback.
Create new session
Force-start a fresh RUM session on demand — for example, on user logout — without re-initializing the SDK. A new session ID is issued and all per-session state resets.
CoralogixRum.createNewSession()
Pair this with setUserContext when logging in a new user.
Get session ID
Retrieve the current session ID.
val sessionId: String = CoralogixRum.getSessionId()
Returns an empty string if the SDK is not initialized.
Flush events
Flush any events still held in the SDK's batch buffer. The SDK automatically flushes when the app backgrounds; use this to flush on demand.
CoralogixRum.flush()
// React once the export completes:
CoralogixRum.flush {
Log.d("MyApp", "buffered events exported")
}
Shut down
Gracefully shut down the SDK.
CoralogixRum.shutdown()
Check initialization state
Before calling SDK methods, verify the SDK is initialized.
Example
if (CoralogixRum.isInitialized()) {
CoralogixRum.log(CoralogixLogSeverity.Info, "SDK is ready")
}
Get session ID
Retrieve the current session ID. Returns an empty string if the SDK is not initialized.
Example
val sessionId = CoralogixRum.getSessionId()
Get user context
Read back the user context that was set with setUserContext.
Example
val userContext = CoralogixRum.getUserContext()
Get labels
Read back the labels currently attached to events. Returns an empty map if the SDK is not initialized.
Example
val labels = CoralogixRum.getLabels()
Set application context
Attach or update the application name and version at runtime.
Example
CoralogixRum.setApplicationContext(
applicationName = "MyApp",
applicationVersion = "2.1.0"
)
Create new session
Force a fresh session, for example on user logout. Resets the session ID and all session state.
Example
CoralogixRum.createNewSession()
Measure operation duration
Bracket an operation with startTimeMeasure / endTimeMeasure to track how long it takes. Elapsed time appears as a custom measurement in Coralogix.
Example
CoralogixRum.startTimeMeasure("checkout", mapOf("step" to "payment"))
// ... perform checkout ...
CoralogixRum.endTimeMeasure("checkout")
Shut down the SDK
Flush pending data and release resources. Call this when you no longer need the SDK, for example during graceful app termination.
Example
CoralogixRum.shutdown()
Decoupling session sampling
By default, the sessionSampleRate controls which instrumentation categories are exported. Use excludeFromSampling to always export specific categories at 100%, regardless of the sample rate — for example, to ensure errors are never dropped even on low-traffic samples.
Example
val config = CoralogixOptions(
// ...
sessionSampleRate = 20,
excludeFromSampling = listOf(
ExcludableInstrumentation.Errors
)
)
ExcludableInstrumentation values:
| Value | Description |
|---|---|
Errors | Error events |
Logs | Log messages |
Network | Network requests |
UserInteractions | Click and long-click events |
MobileVitals | CPU, memory, FPS, and startup metrics |
CustomSpan | Custom OpenTelemetry spans |
CustomMeasurement | Custom numeric measurements |
Navigation | Navigation events |
Lifecycle | Activity and fragment lifecycle events |
Troubleshooting
If you encounter a dependency that requires Java 8+ APIs, such as enabling core library desugaring to ensure compatibility across Android versions, do the following:
Update compileOptions by adding the following to your build.gradle file.
android {
compileOptions {
isCoreLibraryDesugaringEnabled = true
}
}
Add the desugaring dependency.
dependencies {
coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:2.0.4")
}
This enables modern Java features on older Android versions.