5

I have create an extension function for logging:

import org.slf4j.LoggerFactory

fun Any.log(msg: String) {
    LoggerFactory.getLogger(javaClass.name).debug(msg)
}

But i'm not sure about will it be init any time when it will be called or not, because method LoggerFactory.getLogger invoking getILoggerFactory.

Mb someone already did something like that and can assure me that it's won't be any memory leaks :) ?

For now i using old fashion approach(of declaring logger field in a class):

companion object {
    private val logger = LoggerFactory.getLogger(LoggerTest::class.java.name)
}

but a simple unit test like this:

@Test
fun testLogger() {
    val start = System.currentTimeMillis()
    for (i in 0..999) {
        log("i=" + i)
    }
    val end = System.currentTimeMillis()
    val time = end - start
    println("*** TIME=" + time.toDouble() / 1000 + " sec")
}

shows same result as with old fashion option:

@Test
fun testLogger() {
    val start = System.currentTimeMillis()
    for (i in 0..999) {
        logger.debug("i=" + i)
    }
    val end = System.currentTimeMillis()
    val time = end - start
    println("*** TIME=" + time.toDouble() / 1000 + " sec")
}

companion object {
    private val logger = LoggerFactory.getLogger(LoggerTest::class.java.name)
}

~ *** TIME=0.02 sec

I'm using:

org.slf4j - 1.7.25
ch.qos.logback - 1.2.3
user2870934
  • 679
  • 1
  • 7
  • 22
  • 3
    It seems like `LoggerFactory.getLogger(...)` does not construct a new logger each times and is able to re-use them, but, as said in [this answer](https://stackoverflow.com/a/3924399/2196460), the calls are not free, so you should not do it that way. – hotkey Oct 25 '17 at 11:30
  • Maybe there is another option to make an extension kotlin function with logback? – user2870934 Oct 25 '17 at 14:32
  • If you use `Any.foo() { do sth with javaClass }` and call it like `String.foo()`, then `javaClass` should be `String` or `Any` (I'm not sure), but definitely not the calling class. However, compared to the old-fashioned way this doesn't give you the same result – msrd0 Oct 26 '17 at 06:14
  • Actually I'd probably go with `interface HasLogger { val logger : Logger }` and define `HasLogger.log()`. Although this still requires one logger per instance and not one logger per class – msrd0 Oct 26 '17 at 06:16

1 Answers1

3

For logging, I could recommend another solution.

First, add interface ILogging and class LoggingImpl:

interface ILogging {
    val log: Logger
}

class LoggingImp(loggerImpl: Logger) : ILogging {
    override val log: Logger = loggerImpl

    companion object {
        operator inline fun <reified T> invoke(): LoggingImp {
            return LoggingImp(LoggerFactory.getLogger(T::class.java))
        }
    }
}

And now you could add logger to any class using Kotlin delegation:

class TestClass : ILogging by LoggingImp<TestClass>() {
    fun test() {
        log.info("test")
    }
}

Logger will be created when create parent object. Example of usage:

fun main(args: Array<String>) {
    val testClass = TestClass()

    testClass.test()
}

Hope, this will help you.

kurt
  • 1,510
  • 2
  • 13
  • 23