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–

Latest Comments

  1. Robert says:

    Thanks for sharing this technique. It is valuable.

  2. 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.

  3. 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