Best practices for loggers

Hi,

What are best practices for creating loggers?

I’ve seen:

val log = LoggingFactory.getLogger("com.example.app");

but it would be nice to do something without the potential for mistyping.

My usage:

fun <T> loggerFor(clazz: Class<T>) = LoggerFactory.getLogger(clazz)

public class Foo{
   private val LOG = loggerFor(javaClass)
}
3 Likes

This seems like a good start. I'm a little confused as to whether or not getting the package name (for package level loggers) would involve "reflection", so-called, or not.

 

class MyClass {
 
  val logger = LoggerFactory.getLogger(javaClass<MyClass>().getSimpleName())

}

Please beware that this way the logger will be created for each instance of the class. You probably want the declaration to be placed inside the companion object or outside of the class instead.

4 Likes

@B7W - Awesome, exactly what I was looking for.

@Alexander - “… the logger will be created for each instance of the class.”
This is a good point to make.  For me loggers tend to be on service classes that are usually singleton scoped via dependency injection so B7W’s loggerFor is perfectly fine a lot of the time.

Cheers, Rob.

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.

2 Likes

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.

1 Like

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

1 Like

@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:

BriefLogFormatter · GitHub

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: GitHub - MicroUtils/kotlin-logging: Lightweight logging framework for Kotlin. A convenient and performant logging library wrapping slf4j with Kotlin extensions

3 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: Exploring Kotlin’s hidden costs — Part 1 | by Christophe Beyls | Medium)

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