Best practices for loggers

We do this...

class MyService {   companion object {   val log = Logger.getLogger(javaClass<MyService>())   }

  fun doStuff() {
  log.debug(“doing stuff”)
  }
}


It is functionally identical using a static variable in java.

So I gather there is no way to do this via an interface?

You could do something like this:

inline fun <reified T:Any> loggerFor() = LoggerFactory.getLogger(javaClass<T>())

public class Foo {
  companion object {
  private val LOG = loggerFor<Foo>()
  }
}

3 Likes

It seems like the "way of Java" would be to use annotations ("macros"), like @Slf4j, &c. I have been looking for resources on writing annotations in Kotlin but am not having much luck.

Are you referring to code generating libraries like Lombok when talking about `@Slf4J` annotations? And are searching for such a framework or are you building one?

By the way the kotlin language reference docs describe the annotations and how to write them.

Getting the package name of a Java class does not require the use of the Kotlin reflection library. This functionality is part of the JDK.

Where do I find that? It doesn't seem to be under System and, not being very familiar with the JDK, I'm not sure where else to look.

http://docs.oracle.com/javase/7/docs/api/java/lang/Class.html#getPackage%28%29

@udalov: Logback has logger cache, so it’s not really that “logger will be created for each instance of the class”, and I would assume other implementations do too, so I see no harm in using non-static val for it.

There is also a thing to keep in mind when you subclass a class with a logger. If using javaClass instead of specifying class statically, log statements from a base class will have logger corresponding to child class which might be misleading, e.g.:

open class A {
    val logger1 = LoggerFactory.getLogger(javaClass)
    val logger2 = LoggerFactory.getLogger(A::class.java)
    fun sayHello() {
        logger1.info("hello from logger 1")
        logger2.info("hello from logger 2")
    }
}
class B : A()

fun main(args: Array<String>) {
    B().sayHello()
}

will output

[main] INFO B - hello from logger 1
[main] INFO A - hello from logger 2

I used Mark’s solution in my own code. The inline reified function gives slightly nicer syntax, but that’s all it achieves here.

Here is the little utility file I use:

https://gist.github.com/mikehearn/2b6ac42d2e07cef70d2d

It reconfigures JDK logging to print one line per log entry, and gives a couple of utility methods to let you easily enable/disable logging for specific packages/classes. JDK logging is kind of awkward to configure I found, but the “big” logger frameworks tend to seem even more complicated. I use it in combination with SLF4J in case one day JDK logging is insufficient.

It’s Apache 2 licensed so go ahead and copy it into your own projects if you want it.

I created this library to help with logging in kotlin: https://github.com/MicroUtils/kotlin-logging

4 Likes

I use the companion method too:

fun <R : Any> R.logger(): Lazy<Logger> {
    return lazy { LoggerFactory.getLogger(this::class.java.name) }
}

and then

class MyType {

    companion object {
        val L by logger()
    }

}

My only issue is that access this logger costs a lot of performance in Kotlin, (see here: https://medium.com/@BladeCoder/exploring-kotlins-hidden-costs-part-1-fbb9935d9b62)

1 Like

I just use

class MyService {
	companion object {
		val log = Logger.getLogger(this::class.java.simpleName)
	}
}

I now arrived at this:

inline fun <reified R : Any> R.logger(): Logger =
    LoggerFactory.getLogger(this::class.java.name.substringBefore("\$Companion"))

and

companion object {
    val L = logger()
}
2 Likes

Just use Log4j 2 and don’t bother with the class name any longer.

import org.apache.logging.log4j.LogManager

class MyClass {

    companion object {
         private val logger = LogManager.getLogger()
    }
}

The getLogger method (without parameters) creates a logger with the name of the calling class.

1 Like

I came to this thread looking for how to approach logging with SLF4J in Kotlin. After a quick review and trying it out, Kotlin-Logging is actually very simple and nice. It’s nice enough that I’m going to re-plug it since oshai’s comment got a little buried in the middle.
Kotlin-Logging: https://github.com/MicroUtils/kotlin-logging
Kotlin Logging without the Fuss: https://realjenius.com/2017/08/31/logging-in-kotlin/

1 Like

Hm, I feel that something is wrong with logging and especially kotlin-logging:

[kotlin-logging is a] logging library wrapping slf4j with Kotlin extensions.

So, you would have

  • a wrapper (kotlin-logging) for
  • a facade (slf4j) which, in the case of Log4j 2, would use
  • Log4j API which in turn would use
  • Log4j implementation.

And all that without any real benefit over using Log4j API directly. For the case that you are not familiar with Log4j 2 API – it is the equivalent to slf4j, but more modern (uses lambdas for example). Other frameworks could adapt this API as well. See Go ahead: program to the log4j2 API instead of slf4j

1 Like

I started out trying to use kotlin-logging but found its multiplatform support lacking. There was a forked lib named KLogging which had multiplatform implemented, but I didn’t like the classname prefixes it was generating in the logs.

So, since the world doesn’t have enough of these, I created yet another logging framework wrapper: kmulti-logging. The API is targeted to my typical use case: creating a single, static logger instance per class or top-level file.

2 Likes

kotlin-logging now has multiplatform support: https://github.com/MicroUtils/kotlin-logging/wiki/Multiplatform-support

2 Likes
val logster: ReadOnlyProperty<Any, Logger> get() = LoggerDelegate()

class LoggerDelegate : ReadOnlyProperty<Any, Logger> {
    lateinit var logger: Logger

    override fun getValue(thisRef: Any, property: KProperty<*>): Logger {
        if (!::logger.isInitialized) logger = LoggerFactory.getLogger(thisRef.javaClass)
        return logger
    }

}

then
val logger by logster