Tips
About 2 min
Tips 관련
Kotlin
Slf4j loggers in 3 ways
If you use SLF4J (and possibly Logback) for logging, you are probably familiar with the following code:
val logger = LoggerFactory.getLogger(MyClass::class.java)
We like short code, and we like DRY. So here are 3 other ways of getting a logger, to avoid repeating the tedious LoggerFactory
stuff:
1. Factory function
- Function definition is easy to understand, but usage requires the class name.
- Gives the correct logger class name in companions.
Usages
@tab:active Code
inline fun <reified T> logger(): Logger {
return LoggerFactory.getLogger(T::class.java)
}
@tab Usage 1
class LogWithFactoryFunction {
val logger = logger<LogWithFactoryFunction>()
fun doSomething() {
logger.info("Hey from a factory function!")
}
}
@tab Usage 2
class LogWithCompanionFactoryFunction {
companion object {
val logger = logger<LogWithFactoryFunction>()
}
fun doSomething() {
logger.info("Hey from a factory function!")
}
}
@tab Usage 3
class LogWithFactoryFunction {
val logger = logger(this)
fun doSomething() {
logger.info("Hey from a factory function!")
}
}
Alternatively, you can help kotlin figure out T
to avoid passing it in. However, this would cause Companion
to show up again:
@tab Usage 4
class LogWithFactoryFunction {
val logger = logger()
fun doSomething() {
logger.info("Hey from a factory function!")
}
}
Or even shorter, creating it as an extension function
2. Companion with inheritance
- No visible
logger
property in your code; it's available through the companion object - Logger gets
$Companion
in the logger name - Interface version asks for a logger each time, causing slf4j to check its initialization state
Usages
@tab:active Code
abstract class Log {
val logger: Logger = LoggerFactory.getLogger(this.javaClass)
}
// or
interface Log {
fun logger() = LoggerFactory.getLogger(this.javaClass)
}
@tab Usage 1
class LogWithCompanion {
companion object : Log() {}
fun doSomething() {
logger.info("Hey from a companion!")
}
}
@tab Usage 2
class LogWithInterfaceCompanion {
companion object : Log {}
fun doSomething() {
logger().info("Hey from a companion!")
}
}
3. Delegate property
- Harder to understand delegate source code
- Logger gets
$Companion
in the logger name if placed in a companion
Usage
@tab:active Code
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import kotlin.properties.ReadOnlyProperty
import kotlin.reflect.KProperty
class LoggerDelegate : ReadOnlyProperty<Any?, Logger> {
companion object {
private fun <T>createLogger(clazz: Class<T>) : Logger {
return LoggerFactory.getLogger(clazz)
}
}
private var logger: Logger? = null
override operator fun getValue(thisRef: Any?, property: KProperty<*>): Logger {
if (logger == null) {
logger = createLogger(thisRef!!.javaClass)
}
return logger!!
}
}
@tab Usage 1
class LogWithDelegate {
val logger by LoggerDelegate()
fun doSomething() {
logger.info("Hey from a delegate!")
}
}
😎4. Bonus
If you have access to the KClass
, this is an easy way to get rid of $Companion
:
inline fun <reified T> T.logger(): Logger {
if (T::class.isCompanion) {
return LoggerFactory.getLogger(T::class.java.enclosingClass)
}
return LoggerFactory.getLogger(T::class.java)
}