Kotlin-Logging
Lightweight yet powerful Kotlin logging library based on SLF4J. Supports lazy-evaluated strings using lambda expressions, reducing boilerplate code for class names and logger names. Modern logging solution optimized for Kotlin idioms.
Library
Kotlin-Logging
Overview
Kotlin-Logging is a lightweight yet powerful Kotlin logging library based on SLF4J that supports lazy-evaluated strings using lambda expressions, reducing boilerplate code for class names and logger names. As a modern logging solution optimized for Kotlin idioms, it has established itself as the most popular choice for Kotlin applications in 2025. Through Kotlin-like syntax and performance optimization, it provides advantages over traditional Java logging frameworks and is widely adopted in both Android development and server-side Kotlin.
Details
Kotlin-Logging 2025 edition has established a solid position as a lightweight multiplatform logging framework. It functions as a thin wrapper over existing logging frameworks (SLF4J on JVM, native logging for JavaScript, WebAssembly, and Native environments), providing Kotlin-like APIs and performance optimization through lazy evaluation. Starting from version 5, the Maven group ID has been changed to io.github.oshai and the package structure has been unified to io.github.oshai.kotlinlogging. Structured logging capabilities have been added through KLoggingEventBuilder using the at method group, enabling the creation of rich log events that include messages, exceptions, and payload (structured metadata).
Key Features
- Lightweight Facade: Built on top of existing logging frameworks, leveraging their capabilities
- Lazy Evaluation: Performance improvement through lambda expressions that evaluate messages only when log level is enabled
- Kotlin Idioms: Provides more natural APIs for Kotlin developers
- Multiplatform Support: Consistent behavior across JVM, JavaScript, WebAssembly, and Native environments
- Structured Logging: Rich log event creation through advanced log management capabilities
- Boilerplate Code Reduction: No need for logger definitions or class name specifications
Pros and Cons
Pros
- Overwhelming adoption rate in Kotlin ecosystem with abundant learning resources
- Performance optimization and memory-efficient execution through lazy evaluation with lambda expressions
- Significant development efficiency improvement through substantial reduction of boilerplate code
- Easy integration with existing systems through complete compatibility with existing SLF4J backends
- Consistent log API provision through multiplatform support
- Advanced log analysis and debugging capabilities through structured logging
Cons
- Necessity of SLF4J dependency and additional runtime implementation in JVM environments
- Backward compatibility challenges from version 5 onwards (package name and group ID changes)
- Increased learning cost for structured logging features
- Limitations in advanced use cases requiring complex configurations
- Stack trace tracking during debugging becomes slightly more complex due to lambda expressions
- Consistency challenges due to functional differences between platforms
Reference Pages
Code Examples
Installation and Basic Setup
// Add dependencies to build.gradle.kts
dependencies {
implementation("io.github.oshai:kotlin-logging-jvm:7.0.0")
implementation("ch.qos.logback:logback-classic:1.4.14") // SLF4J implementation
}
// Basic logger definition
import io.github.oshai.kotlinlogging.KotlinLogging
private val logger = KotlinLogging.logger {} // Automatic logger creation with class name
class FooWithLogging {
val message = "world"
fun bar() {
logger.debug { "hello $message" } // Lazy-evaluated logging
}
}
// Custom logger name specification
private val customLogger = KotlinLogging.logger("MyCustomLogger")
fun main() {
logger.info { "Application has started" }
customLogger.warn { "Warning from custom logger" }
}
Log Levels and Basic Messages
import io.github.oshai.kotlinlogging.KotlinLogging
private val logger = KotlinLogging.logger {}
fun main() {
// Basic log levels
logger.trace { "This is a trace message" }
logger.debug { "This is a debug message" }
logger.info { "This is an info message" }
logger.warn { "This is a warning message" }
logger.error { "This is an error message" }
// Lazy-evaluated messages with variables
val userName = "John Doe"
val userId = 123
logger.info { "User $userName (ID: $userId) has logged in" }
// Conditional logging (performance optimization)
logger.debug {
// This processing is executed only when DEBUG level is enabled
val expensiveCalculation = performComplexCalculation()
"Calculation result: $expensiveCalculation"
}
}
fun performComplexCalculation(): String {
// Simulation of heavy processing
Thread.sleep(10)
return "Result of complex calculation"
}
Exception Handling and Error Logging
import io.github.oshai.kotlinlogging.KotlinLogging
private val logger = KotlinLogging.logger {}
fun processUserData(userData: Map<String, Any>) {
try {
val userId = userData["id"] as? Int ?: throw IllegalArgumentException("User ID is required")
val userName = userData["name"] as? String ?: throw IllegalArgumentException("User name is required")
logger.info { "User data processing started: $userName (ID: $userId)" }
// Business logic
validateUserData(userData)
saveUserData(userData)
logger.info { "User data processing completed: $userName" }
} catch (e: IllegalArgumentException) {
logger.error(e) { "User data validation error: ${e.message}" }
throw e
} catch (e: Exception) {
logger.error(e) { "Unexpected error occurred during user data processing" }
throw RuntimeException("Failed to process user data", e)
}
}
fun validateUserData(userData: Map<String, Any>) {
val email = userData["email"] as? String
if (email?.contains("@") != true) {
throw IllegalArgumentException("Invalid email address: $email")
}
}
fun saveUserData(userData: Map<String, Any>) {
// Database save processing
logger.debug { "Saving user data to database..." }
// Actual save processing
}
Structured Logging (KLoggingEventBuilder)
import io.github.oshai.kotlinlogging.KotlinLogging
private val logger = KotlinLogging.logger {}
data class User(val id: Int, val name: String, val email: String)
data class Order(val id: String, val userId: Int, val amount: Double, val status: String)
fun processOrder(user: User, order: Order) {
// Creating structured log events
logger.atInfo {
message = "Order processing started"
payload = mapOf(
"userId" to user.id,
"userName" to user.name,
"orderId" to order.id,
"orderAmount" to order.amount,
"timestamp" to System.currentTimeMillis()
)
}
try {
// Order processing logic
validateOrder(order)
processPayment(order)
updateInventory(order)
logger.atInfo {
message = "Order processing completed successfully"
payload = mapOf(
"orderId" to order.id,
"finalStatus" to "completed",
"processingTime" to measureProcessingTime()
)
}
} catch (e: InsufficientFundsException) {
logger.atWarn {
message = "Insufficient funds error occurred during payment processing"
cause = e
payload = mapOf(
"orderId" to order.id,
"userId" to user.id,
"requestedAmount" to order.amount,
"availableBalance" to e.availableBalance,
"errorType" to "insufficient_funds"
)
}
} catch (e: Exception) {
logger.atError {
message = "Unexpected error occurred during order processing"
cause = e
payload = mapOf(
"orderId" to order.id,
"userId" to user.id,
"errorType" to e.javaClass.simpleName,
"errorMessage" to e.message
)
}
throw e
}
}
class InsufficientFundsException(val availableBalance: Double) : Exception("Insufficient funds")
fun validateOrder(order: Order) { /* validation logic */ }
fun processPayment(order: Order) { /* payment logic */ }
fun updateInventory(order: Order) { /* inventory logic */ }
fun measureProcessingTime(): Long = 150L // milliseconds
Spring Boot Integration
// build.gradle.kts
dependencies {
implementation("org.springframework.boot:spring-boot-starter-web")
implementation("io.github.oshai:kotlin-logging-jvm:7.0.0")
implementation("ch.qos.logback:logback-classic")
}
// application.yml
/*
logging:
level:
com.example: DEBUG
io.github.oshai.kotlinlogging: INFO
pattern:
console: "%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n"
*/
// UserController.kt
import io.github.oshai.kotlinlogging.KotlinLogging
import org.springframework.web.bind.annotation.*
@RestController
@RequestMapping("/api/users")
class UserController(private val userService: UserService) {
companion object {
private val logger = KotlinLogging.logger {}
}
@GetMapping("/{id}")
fun getUser(@PathVariable id: Int): User {
logger.info { "User retrieval request: ID=$id" }
return try {
val user = userService.findById(id)
logger.info { "User retrieval successful: ${user.name} (ID: ${user.id})" }
user
} catch (e: UserNotFoundException) {
logger.warn { "User not found: ID=$id" }
throw e
} catch (e: Exception) {
logger.error(e) { "Error occurred during user retrieval: ID=$id" }
throw e
}
}
@PostMapping
fun createUser(@RequestBody userRequest: CreateUserRequest): User {
logger.atInfo {
message = "User creation request"
payload = mapOf(
"requestedName" to userRequest.name,
"requestedEmail" to userRequest.email
)
}
return userService.create(userRequest)
}
}
Android Integration
// build.gradle.kts (app module)
dependencies {
implementation("io.github.oshai:kotlin-logging-android:7.0.0")
implementation("org.slf4j:slf4j-android:1.7.36")
}
// UserRepository.kt
import io.github.oshai.kotlinlogging.KotlinLogging
import android.content.Context
class UserRepository(private val context: Context) {
companion object {
private val logger = KotlinLogging.logger {}
}
suspend fun fetchUserProfile(userId: String): UserProfile {
logger.info { "User profile retrieval started: $userId" }
return try {
val profile = apiService.getUserProfile(userId)
logger.atInfo {
message = "User profile retrieval successful"
payload = mapOf(
"userId" to userId,
"profileId" to profile.id,
"responseTime" to measureApiResponseTime()
)
}
profile
} catch (e: NetworkException) {
logger.atError {
message = "User profile retrieval failed due to network error"
cause = e
payload = mapOf(
"userId" to userId,
"networkState" to getNetworkState(),
"retryCount" to e.retryCount
)
}
throw e
}
}
private fun getNetworkState(): String {
// Network state retrieval
return "connected"
}
private fun measureApiResponseTime(): Long = 250L
}
Coroutines Integration
import io.github.oshai.kotlinlogging.KotlinLogging
import kotlinx.coroutines.*
private val logger = KotlinLogging.logger {}
class AsyncTaskProcessor {
suspend fun processTasksConcurrently(tasks: List<Task>) = coroutineScope {
logger.info { "Concurrent task processing started: ${tasks.size} tasks" }
val results = tasks.map { task ->
async {
logger.debug { "Task processing started: ${task.id}" }
try {
val result = processTask(task)
logger.atInfo {
message = "Task processing completed"
payload = mapOf(
"taskId" to task.id,
"taskType" to task.type,
"processingTime" to result.processingTimeMs,
"status" to "completed"
)
}
result
} catch (e: Exception) {
logger.atError {
message = "Error occurred during task processing"
cause = e
payload = mapOf(
"taskId" to task.id,
"taskType" to task.type,
"errorType" to e.javaClass.simpleName
)
}
throw e
}
}
}
val completedResults = results.awaitAll()
logger.info { "All task processing completed: ${completedResults.size} results obtained" }
completedResults
}
private suspend fun processTask(task: Task): TaskResult {
delay(100) // Simulation of asynchronous processing
return TaskResult(task.id, 100L)
}
}
data class Task(val id: String, val type: String)
data class TaskResult(val taskId: String, val processingTimeMs: Long)