
Lightweight logging API offering lazy-evaluated message blocks, automatic call-site tag inference, configurable format strategies, disk logging with rotation/cleanup, and multiple concurrent loggers.
π A lightweight Kotlin Multiplatform (KMP) logging API with lazy evaluation, configurable formatting, disk logging, and tag inference.
logcat { } is lazy: message blocks run only when a logger is installed and loggable.AndroidLogcatFormatStrategy / IosLogcatFormatStrategy: platform-style console output with configurable fields.PrettyFormatStrategy: bordered output with thread info and call stack.NonFormatStrategy: no formatting; forwards raw messages to the underlying LogStrategy.Throwable.asLog() for readable stack traces.Add the dependency in commonMain:
kotlin {
sourceSets {
commonMain.dependencies {
implementation("com.airsaid:logcat:$version")
}
}
}Make sure you have Maven Central:
repositories {
mavenCentral()
}Android (e.g., Application.onCreate):
val formatStrategy = AndroidLogcatFormatStrategy.Builder<AndroidLogcatLogStrategy>()
.logStrategy(AndroidLogcatLogStrategy())
.timeStampPattern(
pattern = "uuuu-MM-dd HH:mm:ss.SSS",
timeZone = TimeZone.currentSystemDefault(),
)
.build()
AndroidLogcatLogger.install(
minPriority = LogPriority.DEBUG,
formatStrategy = formatStrategy,
)iOS (app startup):
val formatStrategy = IosLogcatFormatStrategy.Builder<IosLogcatLogStrategy>()
.logStrategy(IosLogcatLogStrategy())
.timeStampPattern(
pattern = "uuuu-MM-dd HH:mm:ss.SSS",
timeZone = TimeZone.currentSystemDefault(),
)
.build()
IosLogcatLogger.install(
minPriority = LogPriority.DEBUG,
formatStrategy = formatStrategy,
)class Foo {
fun bar() {
logcat { "Default log" }
logcat(LogPriority.INFO) { "Info log" }
logcat(tag = "CustomTag") { "Custom tag log" }
}
}
logcat("StandaloneTag") { "Log in a top-level function" }
try {
error("boom")
} catch (t: Throwable) {
logcat { t.asLog() }
}Note: logcat { } is an Any extension. For top-level functions (no this), use the
logcat(tag) { } overload.
Formats output similar to platform consoles. You can toggle fields:
showTimeStamp(Boolean): include/exclude the timestamp.timeStampPattern(String, TimeZone): customize timestamp output with a Unicode pattern and timezone.timeStampFormatter((Instant) -> String): fully custom timestamp formatter.showProcessId(Boolean): include/exclude the process id.showThreadInfo(Boolean): include/exclude thread name and id.showTag(Boolean): include/exclude the log tag.showLevel(Boolean): include/exclude the log priority.By default, timestamp uses Instant.toString() (ISO-8601 UTC).
Adds borders, thread info, and call stack lines:
showThreadInfo(Boolean): include/exclude thread info.methodCount(Int): number of call stack lines to print.methodOffset(Int): offset into the call stack.Bypasses formatting and delegates directly to the underlying LogStrategy.
Disk logging is provided by DiskLogStrategy + DiskLogger and supports writing logs to disk:
val diskStrategy = DiskLogStrategy.Builder()
.logFileDirectory(logDirectory)
.logFileGenerator(DefaultLogFileGenerator())
.logFileMaxSize(1024L * 1024L * 100L) // 100MB
.logFileMaxTime(7L * 24L * 60L * 60L * 1000L) // 7 days
.logFileMaxSizeResolver(AvailableSpaceLogFileMaxSizeResolver())
.logBufferMaxSize(10 * 1024) // 10K chars
.build()
val formatStrategy = NonFormatStrategy(diskStrategy)
DiskLogger.installOnApp(LogPriority.WARN, formatStrategy)Builder options:
logFileDirectory(String)
logFileGenerator(LogFileGenerator)
DefaultLogFileGenerator (daily folder + date files).logFileMaxSize(Long)
logFileMaxTime(Long)
logFileMaxSizeResolver(LogFileMaxSizeResolver)
logBufferMaxSize(Int)
Manual flush:
val diskLogger = DiskLogger(LogPriority.WARN, NonFormatStrategy(diskStrategy))
LogcatLogger.install(diskLogger)
// ...
diskLogger.flush()val androidLogger = AndroidLogcatLogger(
minPriority = LogPriority.DEBUG,
formatStrategy = AndroidLogcatFormatStrategy.Builder<AndroidLogcatLogStrategy>()
.logStrategy(AndroidLogcatLogStrategy())
.build()
)
val diskLogger = DiskLogger(
minPriority = LogPriority.WARN,
formatStrategy = NonFormatStrategy(diskStrategy)
)
LogcatLogger.install(androidLogger, diskLogger)Apache-2.0. See LICENSE.
π A lightweight Kotlin Multiplatform (KMP) logging API with lazy evaluation, configurable formatting, disk logging, and tag inference.
logcat { } is lazy: message blocks run only when a logger is installed and loggable.AndroidLogcatFormatStrategy / IosLogcatFormatStrategy: platform-style console output with configurable fields.PrettyFormatStrategy: bordered output with thread info and call stack.NonFormatStrategy: no formatting; forwards raw messages to the underlying LogStrategy.Throwable.asLog() for readable stack traces.Add the dependency in commonMain:
kotlin {
sourceSets {
commonMain.dependencies {
implementation("com.airsaid:logcat:$version")
}
}
}Make sure you have Maven Central:
repositories {
mavenCentral()
}Android (e.g., Application.onCreate):
val formatStrategy = AndroidLogcatFormatStrategy.Builder<AndroidLogcatLogStrategy>()
.logStrategy(AndroidLogcatLogStrategy())
.timeStampPattern(
pattern = "uuuu-MM-dd HH:mm:ss.SSS",
timeZone = TimeZone.currentSystemDefault(),
)
.build()
AndroidLogcatLogger.install(
minPriority = LogPriority.DEBUG,
formatStrategy = formatStrategy,
)iOS (app startup):
val formatStrategy = IosLogcatFormatStrategy.Builder<IosLogcatLogStrategy>()
.logStrategy(IosLogcatLogStrategy())
.timeStampPattern(
pattern = "uuuu-MM-dd HH:mm:ss.SSS",
timeZone = TimeZone.currentSystemDefault(),
)
.build()
IosLogcatLogger.install(
minPriority = LogPriority.DEBUG,
formatStrategy = formatStrategy,
)class Foo {
fun bar() {
logcat { "Default log" }
logcat(LogPriority.INFO) { "Info log" }
logcat(tag = "CustomTag") { "Custom tag log" }
}
}
logcat("StandaloneTag") { "Log in a top-level function" }
try {
error("boom")
} catch (t: Throwable) {
logcat { t.asLog() }
}Note: logcat { } is an Any extension. For top-level functions (no this), use the
logcat(tag) { } overload.
Formats output similar to platform consoles. You can toggle fields:
showTimeStamp(Boolean): include/exclude the timestamp.timeStampPattern(String, TimeZone): customize timestamp output with a Unicode pattern and timezone.timeStampFormatter((Instant) -> String): fully custom timestamp formatter.showProcessId(Boolean): include/exclude the process id.showThreadInfo(Boolean): include/exclude thread name and id.showTag(Boolean): include/exclude the log tag.showLevel(Boolean): include/exclude the log priority.By default, timestamp uses Instant.toString() (ISO-8601 UTC).
Adds borders, thread info, and call stack lines:
showThreadInfo(Boolean): include/exclude thread info.methodCount(Int): number of call stack lines to print.methodOffset(Int): offset into the call stack.Bypasses formatting and delegates directly to the underlying LogStrategy.
Disk logging is provided by DiskLogStrategy + DiskLogger and supports writing logs to disk:
val diskStrategy = DiskLogStrategy.Builder()
.logFileDirectory(logDirectory)
.logFileGenerator(DefaultLogFileGenerator())
.logFileMaxSize(1024L * 1024L * 100L) // 100MB
.logFileMaxTime(7L * 24L * 60L * 60L * 1000L) // 7 days
.logFileMaxSizeResolver(AvailableSpaceLogFileMaxSizeResolver())
.logBufferMaxSize(10 * 1024) // 10K chars
.build()
val formatStrategy = NonFormatStrategy(diskStrategy)
DiskLogger.installOnApp(LogPriority.WARN, formatStrategy)Builder options:
logFileDirectory(String)
logFileGenerator(LogFileGenerator)
DefaultLogFileGenerator (daily folder + date files).logFileMaxSize(Long)
logFileMaxTime(Long)
logFileMaxSizeResolver(LogFileMaxSizeResolver)
logBufferMaxSize(Int)
Manual flush:
val diskLogger = DiskLogger(LogPriority.WARN, NonFormatStrategy(diskStrategy))
LogcatLogger.install(diskLogger)
// ...
diskLogger.flush()val androidLogger = AndroidLogcatLogger(
minPriority = LogPriority.DEBUG,
formatStrategy = AndroidLogcatFormatStrategy.Builder<AndroidLogcatLogStrategy>()
.logStrategy(AndroidLogcatLogStrategy())
.build()
)
val diskLogger = DiskLogger(
minPriority = LogPriority.WARN,
formatStrategy = NonFormatStrategy(diskStrategy)
)
LogcatLogger.install(androidLogger, diskLogger)Apache-2.0. See LICENSE.