A small logging library for Kotlin/Native. Console output by default, with optional daily-rolling file logging and optional asynchronous (off-thread) dispatch.
Available on Maven Central. Add the dependency to your Kotlin/Native source set:
repositories {
mavenCentral()
}
kotlin {
sourceSets {
nativeMain {
dependencies {
implementation("io.github.kyyana:kogger:1.0.1")
}
}
}
}Supported targets: linuxX64 and linuxArm64.
class Example {
private val logger = logger() // inferred from the enclosing class
fun run() {
logger.info { "Starting..." }
logger.debug { "address=$address" }
logger.error(throwable) { "Failed to start" }
}
}Messages are lambdas (() -> String), so the string is only built when the level is
actually enabled.
Configure once at startup, before logging:
Kogger.verifyArgs(args) // --trace, --debug, --no-colors
Kogger.enableFileLogging("logs", 30) // optional
Kogger.enableAsyncLogging(1024, BufferOverflow.DROP_OLDEST) // optionalOn shutdown, drain and flush pending logs:
Kogger.shutdown()
shutdown()blocks until the async worker has drained the queue and the file is closed. Call it from a normal execution path (e.g. after the main loop returns), not from inside a POSIX signal handler — coroutine code is not async-signal-safe and will deadlock there. In a signal handler, only flip a flag / stop the loop.
TRACE < DEBUG < INFO < WARN < ERROR < FATAL. Messages below minLevel
(default INFO) are dropped before the message lambda runs.
Each level has its own ANSI color for console output (when colors is enabled):
| Level | Color |
|---|---|
TRACE |
gray |
DEBUG |
cyan |
INFO |
green |
WARN |
yellow |
ERROR |
red |
FATAL |
dark red |
| Property | Default | Meaning |
|---|---|---|
minLevel |
INFO |
Minimum level that gets logged. |
colors |
true |
ANSI colors in console output. |
timeFormatter |
HH:mm:ss.SSS |
Timestamp format. |
loggerProvider |
console (+ file) | The sink that receives every event passing the filter. |
verifyArgs lowers minLevel to TRACE on --trace or DEBUG on --debug
(--trace takes precedence), and disables colors on --no-colors.
The default provider emits one line per event:
[12:34:56.789] [Example/INFO]: Example started
The timestamp follows timeFormatter, Example is the source class, and INFO is the
level. When colors is enabled, the line is wrapped in the level's ANSI color. A custom
provider defines its own format.
loggerProvider is a plain var, so you can replace the default sink with your own —
e.g. to emit JSON, ship logs over the network, or route to another logging backend.
It is a fun interface, so a lambda works:
Kogger.loggerProvider = LoggerProvider { clazz, logType, throwable, instant, message ->
// format and emit however you like
}enableAsyncLogging wraps whatever provider is currently set, so assign your custom
provider before calling it if you want it dispatched off-thread. To reuse the default
file output from a custom provider, write to Kogger.fileWriter (populated by
enableFileLogging):
Kogger.fileWriter?.write(line)RollingFileWriter is not thread-safe. A custom provider that writes to
Kogger.fileWriter from multiple threads must either serialize the writes itself or sit
behind enableAsyncLogging, whose single worker is then the only writer.
logger() -> KotlinLogger -> Kogger (config + provider) -> console (+ file)
Kogger— the global singleton that holds all configuration (minLevel,colors,timeFormatter) and the activeLoggerProvider. Configure it once at startup; every logger shares it.KotlinLogger— the user-facing API, tagged with its source class. Filters byKogger.minLevel, then forwards the event toKogger.loggerProvider.LoggerProvider— afun interfacethat receives a formatted log event. The default provider formats the line, writes it to the file (if enabled), and prints it to the console.AsyncLoggerProvider— an optional decorator that wraps another provider and runs it off-thread.RollingFileWriter— handles file output and rotation.
- Writes to
<directory>/latest.log. - Rotates per day and at startup: when the date changes, or whenever the process
starts and finds a leftover
latest.log(even one from the same day), the current file is gzip-archived as<directory>/yyyy-MM-dd-N.log.gz(Nincrements for multiple files on the same day, e.g.2026-07-02-1.log.gz,2026-07-02-2.log.gz, ...). - Keeps only the most recent
maxArchivedFilesarchives, deleting the oldest. - Single-writer only — not thread-safe. It is meant to run behind
AsyncLoggerProvider, whose single worker is the only writer.
Decouples the calling thread from slow log I/O using a classic producer/consumer:
log()wraps the work in aRunnableandtrySends it to aChannel— non-blocking, so callers never wait on disk.- A single coroutine on
Dispatchers.Defaultconsumes the channel and runs each task, delegating to the wrapped (downstream) provider. BufferOverflowdecides what happens when the channel is full.DROP_OLDESTprioritizes the program's responsiveness over preserving every log line.shutdown()closes the channel (so the worker drains remaining entries) and joins the worker viarunBlocking.
enableAsyncLogging must be called after a base provider exists (i.e. after the
default provider is set, optionally with file logging enabled); it wraps the current one.
Licensed under the Apache License, Version 2.0.