Activity Trails & Structured Logging in KMP
An Activity Trail is a chronological sequence of breadcrumb events captured in the application leading up to an issue, crash, or goal completion.
Appxiom automatically records screen lifecycle transitions and button taps (Modifier.axTrackClick). Custom activity trails allow developers to add domain breadcrumbs across shared repositories, background sync workers, state machines, and third-party SDK calls.
1. Custom Activity Markers (Ax.setActivityMarkerAt)
Record a discrete, high-value domain event marker:
import tech.appxiom.kmp.Ax
// Syntax: Ax.setActivityMarkerAt(where: String, marker: String)
Ax.setActivityMarkerAt("AuthService", "User requested biometric authentication")
Ax.setActivityMarkerAt("CartRepository", "Applied promo code SUMMER2026")
where: Name of the class, ViewModel, or service emitting the marker.marker: Descriptive representation of the action or state change.
2. Structured Logging Helpers
Appxiom provides built-in logging helpers in common code:
import tech.appxiom.kmp.axLog
import tech.appxiom.kmp.axPrint
// Outputs to console and attaches as an activity trail breadcrumb
axLog(tag = "SyncWorker", message = "Database sync started for 48 records")
axPrint("User tapped refresh button")
3. Integrating with Third-Party KMP Logging Libraries
Kermit Integration
Create a custom Kermit LogWriter to forward log statements to Appxiom:
import co.touchlab.kermit.LogWriter
import co.touchlab.kermit.Severity
import tech.appxiom.kmp.Ax
class AppxiomKermitWriter : LogWriter() {
override fun log(severity: Severity, message: String, tag: String, throwable: Throwable?) {
if (severity >= Severity.Info) {
val fullMessage = if (throwable != null) {
"$message\n${throwable.stackTraceToString()}"
} else {
message
}
Ax.setActivityMarkerAt(tag, fullMessage)
}
}
}
Add the writer during logger initialization:
import co.touchlab.kermit.Logger
import co.touchlab.kermit.platformLogWriter
val logger = Logger.withTag("MyApp").apply {
setLogWriters(platformLogWriter(), AppxiomKermitWriter())
}
Napier Integration
Create a custom Napier Antilog:
import io.github.aakira.napier.Antilog
import io.github.aakira.napier.LogLevel
import tech.appxiom.kmp.Ax
class AppxiomAntilog : Antilog() {
override fun performLog(
priority: LogLevel,
tag: String?,
throwable: Throwable?,
message: String?
) {
if (priority.ordinal >= LogLevel.INFO.ordinal) {
val where = tag ?: "App"
val text = message ?: ""
Ax.setActivityMarkerAt(where, text)
}
}
}
Next Steps
Learn how to measure critical business funnels in Goal Tracking & GFI.