Getting Started¶
Add Scribe to commonMain¶
Use the library from shared code in your Kotlin Multiplatform module:
Create a Minimal Scribe¶
Create an object that extends Scribe, override its savers, then hire that
object's runtime with a Channel<Entry>.
object AppScribe : Scribe() {
override val shelves: List<Saver<*>> = listOf(NoteSaver { note ->
println("[${note.level}] ${note.tag}: ${note.message}")
})
}
AppScribe.hire(
channel = Channel(
capacity = 256,
onBufferOverflow = BufferOverflow.DROP_OLDEST,
),
)
Emit a Single Event¶
Use note(...) for standalone events:
With the saver above, the log output looks like this:
Track a Flow with Scroll¶
Scroll is a mutable map of JSON elements initialized by newScroll(...).
When sealing it, supply the Scribe runtime that should apply its footer
margin and deliver the event. Each seal(...) call emits a new
SealedScroll using a snapshot of the scroll data at that moment.
You can also merge other scrolls or nest them:
val base = AppScribe.newScroll()
base["gateway"] = JsonPrimitive("stripe")
val checkout = AppScribe.newScroll(id = "checkout-42")
checkout.extend(base) // copies missing keys from base
val meta = AppScribe.newScroll(id = "checkout-meta")
meta["items"] = JsonPrimitive(3)
checkout.append("meta", meta)
val scroll = AppScribe.newScroll(id = "checkout-42")
scroll["gateway"] = JsonPrimitive("stripe")
scroll["attempt"] = JsonPrimitive(1)
scroll["retry"] = JsonPrimitive(false)
scroll["cart"] = Json.encodeToJsonElement(
CheckoutMeta.serializer(),
CheckoutMeta(itemCount = 3, subtotalCents = 249_900, featureFlag = "wide-events"),
)
scroll.seal(AppScribe, success = true)
Use Multiple Runtimes¶
Each object is independent. A library may define its own object, or an application may supply a configured object to a component.
object PaymentsScribe : Scribe() {
override val shelves: List<Saver<*>> = listOf(EntrySaver { sendPaymentsRecord(it) })
}
object AnalyticsScribe : Scribe() {
override val shelves: List<Saver<*>> = listOf(EntrySaver { sendAnalyticsRecord(it) })
}
PaymentsScribe.hire(channel = Channel(256))
AnalyticsScribe.hire(channel = Channel(256))
Retiring PaymentsScribe does not stop AnalyticsScribe.
The emitted SealedScroll shape:
{
"success": true,
"data": {
"scroll_id": "checkout-42",
"gateway": "stripe",
"attempt": 1,
"retry": false,
"cart": {
"item_count": 3,
"subtotal_cents": 249900,
"feature_flag": "wide-events"
}
}
}
Choose the Right Saver¶
val noteSaver = NoteSaver { note -> println(note) }
val scrollSaver = ScrollSaver { scroll -> println(scroll) }
val entrySaver = EntrySaver { entry -> println(entry) }
NoteSaverhandles onlyNoteScrollSaverhandles onlySealedScrollEntrySaverhandles both
What to Read Next¶
- API Concepts for the core types and terminology
- Lifecycle and Delivery for channel behavior, margins, shutdown, and saver error callbacks