A flavored Kotlin Multiplatform logging library
Scribe is a Kotlin Multiplatform logging library built around the ideas from loggingsucks.com, so structured logs can model both single events and longer contextual flows.
| Documentation Page |
- Story-driven logging primitives instead of flat logger calls
- Contextual logging with
newScroll(...)and immediate-seal one-shot scrolls - Delivery hooks through
Archivistinstances receivingEntrysnapshots - Scroll lifecycle enrichment through
Margin - Independent
Scribeobjects for applications and imported libraries - A JVM SLF4J 2.x provider backed by the same structured logging pipeline
Add Scribe to your commonMain dependencies:
kotlin {
sourceSets {
commonMain.dependencies {
implementation("com.rafambn:scribe:0.7.0")
}
}
}Create a Scribe object, start processing its private buffer, and emit a scroll:
object AppScribe : Scribe() {
override val archivists: List<Archivist> = listOf(
Archivist { entry ->
println(entry)
}
)
}
AppScribe.hire()
val scroll = AppScribe.newScroll()
scroll["tag"] = JsonPrimitive("payments")
scroll["message"] = JsonPrimitive("starting checkout")
scroll["level"] = JsonPrimitive("INFO")
scroll.seal(AppScribe)Use a scroll when you need shared context for a longer flow:
object BillingScribe : Scribe() {
override val archivists: List<Archivist> = listOf(
Archivist { entry -> println(entry) }
)
override val imprint = mapOf(
"service" to JsonPrimitive("billing"),
"environment" to JsonPrimitive("production"),
)
}
BillingScribe.hire()
val scroll = BillingScribe.newScroll(id = "checkout-42")
scroll["gateway"] = JsonPrimitive("stripe")
scroll["attempt"] = JsonPrimitive(1)
scroll["retry"] = JsonPrimitive(false)
scroll.seal(BillingScribe)Each Scribe object has independent configuration and delivery lifecycle. A Scroll is a mutable JSON-element map initialized by newScroll(...); pass the runtime that should enrich and deliver it to scroll.seal(scribe). Each seal(...) call emits a separate snapshot of the scroll data.
Version 0.7.0 publishes the core scribe module for exactly these 22 targets:
- JVM:
jvm,android - Web:
js,wasmJs,wasmWasi - Android Native:
androidNativeArm32,androidNativeArm64,androidNativeX86,androidNativeX64 - Apple:
iosArm64,iosSimulatorArm64,iosX64,macosArm64,tvosArm64,tvosSimulatorArm64,watchosArm32,watchosArm64,watchosDeviceArm64,watchosSimulatorArm64 - Other Native:
linuxArm64,linuxX64,mingwX64
The js and wasmJs targets support both browser and Node.js execution; wasmWasi is configured for Node.js. The project inherits Kotlin 2.4.10, kotlinx.serialization 1.11.0, and kotlinx.coroutines 1.11.0 runtime requirements. The scribe-slf4j module remains JVM-only.
Both artifacts are consumed from Maven Central. Scribe does not publish an npm package; Kotlin/JS and Kotlin/Wasm consumers use the Gradle Multiplatform dependency above.
For JVM applications, add the SLF4J provider:
dependencies {
implementation("com.rafambn:scribe-slf4j:0.7.0")
}Select exactly one application-wide backend with @ScribeBackend:
@ScribeBackend
object AppScribe : Slf4jScribe() {
override val bufferCapacity = 1_024
override val bufferOverflow = BufferOverflow.DROP_OLDEST
override val onArchiveFailure: ((Archivist, Entry, Throwable) -> Unit)? = null
override val archivists = listOf(
Archivist { entry -> println(entry) },
)
override fun isEnabled(
loggerName: String,
level: Level,
marker: Marker?,
): Boolean = level.toInt() >= Level.INFO.toInt()
}The provider discovers the annotated backend once on the first SLF4J access and registers a JVM shutdown hook. Intake starts open, so early calls accumulate in the private buffer; the application calls AppScribe.hire() when processing should begin. The shutdown hook retires the Scribe and drains accepted entries automatically. Initialization fails with a descriptive error when no backend is present, multiple backends are annotated, or the annotation is not placed on a Kotlin object extending Slf4jScribe.
scribe-slf4j is a standalone SLF4J provider. Do not include another provider such as logback-classic in the same runtime classpath.
See the full documentation for lifecycle controls, overflow behavior, margins, and SLF4J field mapping.
Scribe is designed for high-throughput and thread-safe concurrent logging.
The repository includes JVM throughput tests for concurrent in-memory ingestion and serialized file writing. Results depend on the machine, runtime, buffer configuration, and archivist implementation; run the tests in your target environment before using them for capacity planning.