Java: Custom logger factory that automatically infers the class name

In this post I show how you can declare loggers like this:

public class MyService {
    private static final Logger LOG = MyLoggerFactory.getLogger();
}

There is no argument to the MyLoggerFactory::getLogger method. Contrast that to the normal way to declare a logger:

public class MyService {
    private static final Logger LOG = LoggerFactory.getLogger(MyService.class);
}

This is business as usual. But have you ever made that silly mistake where you copy/paste a class – and then, in the new class, forget to change the argument to that logger? It can be horribly misleading when reading log files afterwards.

The custom MyLoggerFactory::getLogger method is really super simple to implement: 3 lines of meat.

UPDATE: Alternative to the custom factory

On Reddit, while announcing this post, I was made aware of a simple alternative to the custom logger factory technique described in this post:

public class MyService {
    private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.Lookup.lookupClass());
}

That technique uses a plain standard Java SE API to get the class name – see [1]. Personally I think this is a clever technique as well!

I guess it is a matter of personal preferences to decide which logger declaration you want to use. If you still find the logger factory compelling – then read on…

Implementation

I have prepared an example on GitHub – consult that to see the code in its entirety and full context. But here it goes, using SLF4j/Logback:

package com.never.that.copy.paste.mistake.again;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class MyLoggerFactory {

    public static Logger getLogger() {

        StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();

        String callersClassName = stackTrace[2].getClassName();

        return LoggerFactory.getLogger(callersClassName);
    }
}

3 lines of meat. The magic number 2 represents the class calling MyLoggerFactory::getLogger. The first positions in the stackTrace array represents the invocation to the Thread::getStackTrace method as well as this MyLoggerFactory::getLogger method itself.

I chose to name the method getLogger here, so that it aligns with the underlying logging framework SLF4j/Logback.

Please note, that no code here is specific to SLF4j/Logback: So go ahead and implement a corresponding factory for your own favorite logging framework.

The example on GitHub has a very limited JUnit test that shows it works as expected:

package com.never.that.copy.paste.mistake.again;

import org.junit.Test;
import org.slf4j.Logger;

import static org.junit.Assert.assertEquals;

public class MyLoggerFactoryTests {

    @Test
    public void getLogger_whenGivenNothing_thenReturnsLoggerWithCallersClassName() {

        // Given
        // ( a little bit of magic ... )

        // When
        Logger loggerOne = MyLoggerFactory.getLogger();
        Logger loggerTwo = LoggerTester.LOG;

        // Then
        assertEquals(MyLoggerFactoryTests.class.getName(), loggerOne.getName());
        assertEquals(LoggerTester.class.getName(), loggerTwo.getName());
    }

    private static class LoggerTester {
        private static final Logger LOG = MyLoggerFactory.getLogger();
    }

}

That’s all there is to it.

References

[1] Java SE’s MethodHandles API:
https://docs.oracle.com/javase/8/docs/api/java/lang/invoke/MethodHandles.Lookup.html#lookupClass–

Tagged with: ,
Posted in Java SE
6 comments on “Java: Custom logger factory that automatically infers the class name
  1. […] It’s very easy to instantiate a logger for your class. But it’s also something that gets often copied from class to class. Sooner or later, someone forgets to update the logger category, and your log messages create more confusion then they provide helpful information about your application. Nicky Mølholm wrote a short blog post with two interesting approaches that can help you to avoid these issues: Java: Custom logger factory that automatically infers the class name. […]

  2. Robert says:

    Thanks for sharing this technique. It is valuable.

  3. Tom says:

    private final Logger log = LoggerFactory.getLogger(getClass());

    Simple and solid. No need to be static. You get a pooled Reference which does the same purpose.

  4. Stefano says:

    My only doubt is that the approach that you use can be expensive ’cause of exception stacktrace. In some my experiements i use also another technique [https://github.com/stefanofago73/E_B_P/blob/master/src/it/fago/ebp/Callers.java]. It seems that using MethodHandles can be a good compromise and you can use it in Java 7+

    • moelholm says:

      Fine point. I did mention MethodHandles above ;). Regarding expensiveness: In practice you won’t “feel” it. And that’s how I choose which performance battles I want to engage in.(remember it’s a one time cost per class that needs to log)

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: