Supported instrumentations
Supported Instrumentations
| Instrumentation | Description |
|---|---|
| Logs | Structured logging with severity and labels (called Custom) |
| Errors | Uncaught & handled exceptions |
| Network | Request/response performance data |
| ANR | ANR tracking |
| Mobile Vitals | CPU, FPS, memory, and cold/warm start times. |
| Custom Metrics | User-defined numeric metrics |
| Lifecycle | Report lifecycle events. |
| User Interaction | Report user interactions in the application. |
| Navigation | Report navigation events |
Logs (Custom)
Send a simple log with optional data and labels.
Example
CoralogixRum.log(
severity = CoralogixLogSeverity.Info,
message = "User logged in successfully",
data = mapOf("userId" to "12345"), // optional
labels = mapOf("environment" to "staging") // optional
)
Disabling this feature
You can disable this instrumentation by passing false in the instrumentations map in CoralogixOptions. Any call to log() would be ignored if the instrumentation in disabled:
val options = CoralogixOptions(
// ... other options ...
instrumentations = mapOf(
Instrumentation.Custom to false
)
)
The passed labels map will be added to other labels set in the SDK for this event only.
Error Reporting
The Coralogix SDK automatically captures unhandled crashes and exceptions through its built-in Error Instrumentation module.
Automatic Crash Tracking
Once the SDK is initialized and the error instrumentation is enabled, all unhandled exceptions and app crashes are automatically detected and sent to Coralogix.
Manual Error Reporting
You can manually report handled exceptions when you want visibility into recoverable or expected failures, such as API errors or business logic exceptions.
Example
try {
riskyOperation()
} catch (t: Throwable) {
CoralogixRum.reportError(t)
}
Attaching custom attributes
For richer error reporting, pass a CoralogixErrorDecorator with customAttributes. The attributes are emitted on the resulting RUM error event under the data field of errorContext (matching the iOS SDK).
val decorator = CoralogixErrorDecorator(
throwable = t,
isCrash = false
).copy(
customAttributes = mapOf(
"userId" to "12345",
"screen" to "checkout"
)
)
CoralogixRum.reportError(decorator)
Bundling a message, data, and labels
reportError(throwable, ...) also accepts optional data and labels, so you can attach context in a single call instead of a separate log(). data is emitted under error_context.error_custom_data and labels are merged into the event's labels.
try {
riskyOperation()
} catch (t: Throwable) {
CoralogixRum.reportError(
throwable = t,
data = mapOf("cart_size" to 3, "reason" to "timeout"),
labels = mapOf("team" to "payments")
)
}
Disabling this feature
You can disable this instrumentation by passing false in the instrumentations map in CoralogixOptions. Any call to reportError() would be ignored if the instrumentation in disabled:
val options = CoralogixOptions(
// ... other options ...
instrumentations = mapOf(
Instrumentation.Error to false
)
)
Network Events Reporting
To enable RUM to intercept network events, add CoralogixOkHttpInterceptor to your network client (or use the Coralogix Gradle Plugin).
Examples
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
val httpClient = HttpClient(OkHttp) {
engine {
preconfigured = OkHttpClient.Builder()
.addInterceptor(CoralogixOkHttpInterceptor())
.build()
}
}
val response = httpClient.get("https://api.example.com/data")
println(response.bodyAsText())
Disabling this feature
You can disable this instrumentation by passing false in the instrumentations map in CoralogixOptions:
val options = CoralogixOptions(
// ... other options ...
instrumentations = mapOf(
Instrumentation.Network to false
)
)
Only OkHttp clients are currently supported.
ANR Events Reporting
The Coralogix SDK automatically captures and report ANR events, ANR events are reported as an error.
Disabling this feature
You can disable this instrumentation by passing false in the instrumentations map in CoralogixOptions:
val options = CoralogixOptions(
// ... other options ...
instrumentations = mapOf(
Instrumentation.Anr to false
)
)
Mobile Vitals Reporting
The Mobile Vitals instrumentation continuously monitors your app’s performance and responsiveness, providing real-time visibility into user experience quality. It automatically tracks key system metrics, periodic snapshots, and one-time events that represent critical app behaviors like cold starts or frame drops.
What We Measure
| Category | Metric | Description |
|---|---|---|
| Frame Rendering | FPS (Frames Per Second) | Measures how smoothly your app renders frames. Low FPS indicates UI stutters or heavy rendering work. |
| Frame Quality | Slow Frames / Frozen Frames | Tracks frames that exceeded normal rendering budgets (slow) or were completely stuck (frozen). |
| CPU Usage | Total / Main Thread CPU | Captures overall and main-thread CPU usage percentage and timing, helping identify CPU-heavy operations. |
| Memory Usage | Resident / Heap / Java / Native Memory | Tracks memory utilization from both Java and native layers to identify leaks or memory pressure. |
| Startup Performance | Cold Start / Warm Start Time | Measures how long it takes for the app to become ready when launched cold or resumed from background. |
Aggregation Reports
The SDK continuously aggregates performance data and periodically sends it to Coralogix as Mobile Vitals Aggregations. These reports summarize metrics like average CPU load, memory usage, and FPS over time. Reports are meant to be tied to a specific view context, to implement this idea a report is being sent on one of two scenarios:
- The view context has changed (navigation has occurred)
- No mobile vitals event was reported for at least 15 seconds (no navigation)
One Shot Events
Alongside periodic summaries, the SDK reports important one-time measurements that occur during specific lifecycle moments:
| Event | Description |
|---|---|
| Cold Start | Time taken from process creation until the first activity is visible. |
| Warm Start | Time taken when resuming the app from background. |
These one-shot events are sent as soon as they occur, giving you instant visibility into startup performance and user flow responsiveness.
Disabling this feature
You can disable this instrumentation by passing false in the instrumentations map in CoralogixOptions:
val options = CoralogixOptions(
// ... other options ...
instrumentations = mapOf(
Instrumentation.MobileVitals to false
)
)
You can also disable specific metric collection by passing false for a specific detector in the mobileVitalsOptions map in CoralogixOptions
For example to disable cpu usage:
val options = CoralogixOptions(
// ... other options ...
mobileVitalsOptions = mapOf(
MobileVitalType.CpuUsage to false // for example
)
)
Custom Metrics
Send arbitrary key-value pairs.
Example
CoralogixRum.sendCustomMeasurement("image_upload_time_ms", 1480L)
Custom Time Measurement
Measure the elapsed time of any operation.
Example
CoralogixRum.startTimeMeasure("image-load", mapOf("source" to "network"))
loadImage()
CoralogixRum.endTimeMeasure("image-load")
See the Custom Time Measurement section in the API Reference for full behaviour details.
Lifecycle Events Reporting
Captures app lifecycle activity and fragment events that occur during runtime, providing insights into the application's behavior and performance.
-
Activity lifecycle events:
- onActivityCreated - Triggered when the activity is first created, initializing the activity and setting up the UI.
- onActivityStarted - Called when the activity becomes visible to the user, but may not yet be interactive.
- onActivityResumed - Invoked when the activity enters the resumed state, making it fully interactive and allowing user input.
- onActivityPaused - Called when the activity is partially obscured, such as when another activity or a dialog appears, but the activity is still partially visible.
- onActivityStopped - Triggered when the activity is no longer visible to the user and enters the stopped state.
- onActivitySaveInstanceState - Called to save the current state of the activity (e.g., UI data, user input) so it can be restored if the activity is restarted.
- onActivityDestroyed - Called just before the activity is destroyed, allowing for final cleanup of resources and references.
-
Fragment lifecycle events:
- onFragmentAttached - Called when the fragment is attached to its host activity. This is the point where the fragment is associated with the activity lifecycle.
- onFragmentCreated - Called when the fragment is created. This occurs only once per fragment instance, even if it is attached and detached multiple times.
- onFragmentViewCreated - Called after the fragment’s view is created and fully inflated. At this point, the fragment's UI is ready.
- onFragmentStarted - Triggered when the fragment enters the started phase and becomes visible to the user.
- onFragmentResumed - Called when the fragment enters the resumed phase, meaning the fragment is now interactive and the user can interact with it.
- onFragmentPaused - Called when the fragment is partially obscured by another UI element (e.g., another activity, dialog box), but still visible.
- onFragmentStopped - Called when the fragment is no longer visible to the user and has entered the stopped state.
- onFragmentSaveInstanceState - Called to save the fragment's state (e.g., UI data, input) when the system needs to save its state (during pauses, backgrounding, or configuration changes), so it can be restored later.
- onFragmentViewDestroyed - Called when the fragment’s view is destroyed and is no longer available, typically when the fragment is removed or replaced.
- onFragmentDestroyed - Called when the fragment instance is fully destroyed, releasing any remaining resources.
- onFragmentDetached - Called when the fragment is detached from its host activity, ending the association between the fragment and its host.
Disabling this feature
You can disable this instrumentation by passing false in the instrumentations map in CoralogixOptions:
val options = CoralogixOptions(
// ... other options ...
instrumentations = mapOf(
Instrumentation.Lifecycle to false
)
)
Automatic flush on backgrounding
When the app moves to the background, the SDK immediately exports any events still held in its batch buffer, so telemetry from the end of a session is not lost if the process is killed while backgrounded. A WorkManager job is enqueued alongside it as a backstop, covering the case where the export does not finish before the system suspends the process.
This tracks the process-level background transition, so it fires once when the app actually leaves the foreground — not on activity-to-activity navigation within the app.
No configuration is required, and this is not tied to any instrumentation — it stays on even if you disable Lifecycle Events Reporting. You can also flush on demand at any point:
CoralogixRum.flush()
// Or, to react once the export completes:
CoralogixRum.flush {
Log.d("MyApp", "buffered events exported")
}
User Interaction Reporting
Automatically tracks user interactions, including taps, scrolls, and swipes. Each event includes the target view, element class, and direction (where applicable).
Disabling this feature
You can disable this instrumentation by passing false in the instrumentations map in CoralogixOptions:
val options = CoralogixOptions(
// ... other options ...
instrumentations = mapOf(
Instrumentation.UserInteraction to false
)
)
Navigation Events Reporting
Automatically report any navigation event.
Custom Spans
Create arbitrary OpenTelemetry spans that appear in the Coralogix RUM trace view. Spans are automatically linked to the current session and view context. Child spans inherit the parent's trace ID and carry a parentSpanId.
Prerequisites
Custom spans require W3C traceparent header injection to be enabled:
val options = CoralogixOptions(
// ...
traceParentInHeader = TraceParentInHeaderConfig(enabled = true)
)
getCustomTracer() returns null and logs a warning if this is not set.
Getting the tracer
val tracer = CoralogixRum.getCustomTracer() ?: return
- Returns
nullif the SDK is not initialized ortraceParentInHeaderis disabled. - Only one tracer instance is issued per SDK lifecycle — subsequent calls return
null. Store the returned instance and reuse it. - Accepts an optional
ignoredInstrumentsset to suppress specific automatic instrumentation while a global span is active (see Ignored instruments).
Starting 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
- Returns
nullif a global span is already active. labelsare optional. They are merged with the SDK-level labels and forwarded with every child span started from this global span.- Exposes
traceIdandspanIdproperties (W3C hex strings) so you can correlate spans with your own backend:
val traceId: String = globalSpan.traceId // 32 lowercase hex chars
val spanId: String = globalSpan.spanId // 16 lowercase hex chars
Always call endSpan() when the operation is complete:
globalSpan.endSpan()
Starting child spans
val childSpan = globalSpan.startCustomSpan(
name = "payment-step",
labels = mapOf("payment.method" to "credit_card")
)
- Child spans inherit the global span's trace ID and set the global span as their
parentSpanId. labelsare optional and are merged with SDK labels and the global span's labels.- Always call
endSpan()when the child operation is complete:
childSpan.endSpan()
Setting 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
Adding events
Add timestamped events to a span to record notable moments during its lifetime.
// Event with no extra data
childSpan.addEvent("retry-attempted")
// Event with attributes
childSpan.addEvent(
name = "cache-miss",
attributes = mapOf("key" to "user-profile", "size_bytes" to 1024)
)
// Event at a specific time (defaults to milliseconds)
childSpan.addEvent(
name = "request-sent",
timestamp = System.currentTimeMillis() - 500,
unit = TimeUnit.MILLISECONDS
)
// Event with both attributes and a custom timestamp
childSpan.addEvent(
name = "response-received",
attributes = mapOf("status" to "200"),
timestamp = System.currentTimeMillis(),
unit = TimeUnit.MILLISECONDS
)
Supported attribute value types: String, Int, Long, Double, Float, Boolean. Any other type (including null) is silently ignored.
Setting 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()
}
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()
}
Ignored instruments
Pass ignoredInstruments to getCustomTracer() to prevent specific automatic instrumentation from firing 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. |
Receiving raw OTLP span data (tracesExporter)
Supply a tracesExporter callback in CoralogixOptions to receive the raw OTLP payload for each batch of exported spans — useful for forwarding to a backend trace collector:
val options = CoralogixOptions(
// ...
tracesExporter = { data: CoralogixTraceExporterData ->
val spanCount = data.spanCount // total spans in this batch
val json = data.toJson() // full OTLP JSON string
val spans = data.resourceSpans // structured List<OtlpResourceSpans>
forwardToMyCollector(json)
}
)
CoralogixTraceExporterData fields:
| Field | Type | Description |
|---|---|---|
resourceSpans | List<OtlpResourceSpans> | Structured OTLP resource-spans tree. |
spanCount | Int | Total number of spans across all resource-spans. |
CoralogixTraceExporterData exposes toJson() for convenience, returns the full OTLP JSON encoding of this batch (ready to POST).
When tracesExporter is set, instrumentation_data (network request details) is stripped from the standard RUM payload to avoid double-reporting.