Reputation: 1988
In order to initialize Log4j within a class, the programmer needs to provide the Class
instance (or a string name which represents it). The code will look like this:
public class MyClass {
private static Log logger = Logger.getLogger(MyClass.class);
[...]
}
Log4j will then provide output based on parameters given by the programmer. The output will look something like this:
[INFO] [MyClass:124] Foobar
My question: if Log4j knows what the line number of the logging statement is (124), then why does it need the Class name (MyClass)? It seems to me that it if can figure out the former, it should be able to figure out the latter.
Edit: This is not an idle problem either; most of us have seen code where a programmer accidentally copies and pastes the initialization code from another class, but forgets to change the class name in the initializer. This causes Log4j to provide confusing output.
(Granted, my question isn't stating a specific problem, but it is helpful for me to understand how Log4j and possibly Java reflection work).
Upvotes: 5
Views: 2698
Reputation: 36630
In order to initialize Log4j within a class, the programmer needs to provide the Class instance (or a string name which represents it).
No, it is not required to use a class name - this is by convention only, so that you can configure the logging by class and package name. You could also use
private static Log logger = Logger.getLogger("SomeUniqueName");
The class name and the line number retrieved at logging time is most likely retrieved through the StackTrace of the current thread, which is a performance bottleneck - see http://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/PatternLayout.html.
For example, for the L
pattern, it says
WARNING Generating caller location information is extremely slow and should be avoided unless execution speed is not an issue.
Leaving aside the performance of retrieving the stack trace, technically the following would also work to retrieve both static and instance loggers without an explicit reference to the class (probably requires some more sophisticated error handling, especially with the array access):
public class ClassLogger {
public static Logger getLogger() {
String className = Thread.currentThread().getStackTrace()[2].getClassName();
return Logger.getLogger(className);
}
}
This can then be used like
public class SomeClass {
private static Logger sl = ClassLogger.getLogger();
private Logger il = ClassLogger.getLogger();
...
This would also work in non-debug mode, since the class name is also available in that case (while the file name and the line number are not).
Upvotes: 3
Reputation: 5883
Generically speaking, a logger instance just needs a name.
Conventially, developers define one logger instance per class and use the class name as the logger name. To accommodate this common use, log4j added a convenience factory method that accepts a Class instance in lieu of a String.
java.util.logging
does not offer this convenience method (which may be part of the reason its usage is low despite being included in the JDK).
To avoid copy-paste errors, you could try using Lombok, which gives you an annotation that will do voodoo and inject a properly written logger static instance at compile time (and drive your static code analysis checkers nuts).
If you can break free from the Java language, Groovy offers similar AST transformation annotations.
Upvotes: 1
Reputation: 246
To solve your "copy and pasta" problem, try using the getClass() method in your code so when you copy and paste a logger declaration to a new class, it uses its own class name, like so:
private Logger log = Logger.getLogger(this.getClass());
Log4j will actually call .getName() on the class name so it has a string name to use for the logger. In fact, you can put any string in there, but the convention is to use the class name.
There may also be a use case where you want to use class A's logger inside class B. Maybe B is a subclass or part of an inner class and you want your logging output to look like it's coming from the same log name (class name). Ex:
public class MyClassB extends MyClassA {
private static Log logger = Logger.getLogger(MyClassB.class);
[...]
}
Upvotes: 1