In this post I show how you can declare loggers like this:
1
2
3
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:
1
2
3
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:
1
2
3
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:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
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.