I often need to access the current class, for example for logging purposes:
public class Foo { private Logger logger = LoggerFactory.getLogger(getClass()); }
However, this won’t work in a static context, since you don’t have any this object to call getClass() on:
public class Foo { static private Logger logger = LoggerFactory.getLogger(Foo.class); }
It’s always bothered me to have to copy/paste this line and then remember to replace the name of the class, so here is one way to make this snippet more generic:
public class ClassUtil extends SecurityManager { public static Class getCurrentClass() { return getClassContext()[1]; } }
Which you use as follows:
public class Foo { private static Logger logger = LoggerFactory.getLogger( new ClassUtil().getCurrentClass()); }
A somewhat less hacky and cheaper approach is to instantiate an anonymous class in order to materialize a this object:
private static Class thisClass = new Object() { }.getClass().getEnclosingClass(); private static Logger logger = LoggerFactory.getLogger(thisClass);
Can you think of any other way?
#1 by Jonathan Halterman on July 15, 2011 - 8:36 am
Another option:
return new Object() { }.getClass().getEnclosingClass();
You can also grab a stacktrace, though this wouldn’t be my preferred approach:
Thread.currentThread().getStackTrace()[1].getClassName();
#2 by Cedric on July 15, 2011 - 8:39 am
Jonathan: isn’t the first snippet of code exactly the same as the one I wrote above?
#3 by Christian Schlichtherle on July 15, 2011 - 8:40 am
I wouldn’t mind writing Foo.class in your first example. Everything else appears overengineered to me.
#4 by Jonathan Halterman on July 15, 2011 - 8:43 am
Cedric – it is. For some reason I didn’t catch your last example.
#5 by Andrey on July 15, 2011 - 9:07 am
I agree with Christian: for logging purposes that appears overengineered. I think it’s easier to use templates/shortcuts of your favourite IDE. You can construct them by hand using variables for class names.
#6 by Tetsuo on July 15, 2011 - 9:18 am
The same, a little different 🙂
private enum _ {}
static Logger log = LoggerFactory.getLogger(_.class.getEnclosingClass());
#7 by Sumit on July 15, 2011 - 10:15 am
My submission:
static Logger log = Logger.getLogger( new Object() {{}}.getClass().getEnclosingClass() );
But I see this as a concern only if you insist on copy-paste-modify rather than IDE template.
#8 by Bradley Schaefer on July 15, 2011 - 10:36 am
I don’t consider the latter approach less hacky – it somewhat offends my sensibilities to pollute the classloader with unnecessary anonymous classes (in practice I doubt it matters, but even so). I prefer the ClassUtils approach – hell, LoggerFactory ought to have a getClassLogger() method that does just that.
I will give you that it’s an interesting thought experiment, and I don’t have better suggestions.
#9 by Ramesh Mandaleeka on July 15, 2011 - 3:40 pm
How about:
new Exception().getStackTrace()[2].getClassName();
#10 by Ramesh Mandaleeka on July 15, 2011 - 4:01 pm
Yet another approach using Sub internal API.
import sun.reflect.Reflection;
public class ClassUtil {
static Class getCurrentClass() {
return Reflection.getCallerClass(2);
}
}
#11 by Ben on July 15, 2011 - 7:20 pm
Would this get the right class?
class LogUtil extends SecurityManager {
static Logger get() {
Class cls = Log().getClassContext()[1]
println cls
return Logger.getLogger(cls.name)
}
}
#12 by Daniel Zimmerman on July 15, 2011 - 10:31 pm
While it’s interesting to see all the different hoops one can jump through to accomplish this, I’m confused as to why would you want to make this particular bit of code more generic. Part of my difficulty arises from your initial statement: “I often need to access the current class, for example for logging purposes.”
When you’re doing this in a static context, you don’t want “the current class” in the sense of “the class in which this code is currently running”; instead, you want “the class this code was written in” (you called it Foo, above). getClass(), as in your original bit of code, is going to give you the Class object corresponding to the object that’s actually instantiated at runtime, not necessarily the Class object corresponding to Foo. If you want a logger for Foo, specifically (as opposed to child classes of Foo, etc.), you can’t use getClass() even in a non-static context (unless Foo is final).
In a static context, you always know which Class object you want at the time you’re writing the code. The “.class” construct exists in Java precisely to allow you to statically obtain a reference to a Class object… using it not only avoids instantiating unnecessary objects (and creating unnecessary classes), but also seems, to me at least, to be much more readable and understandable (in terms of determining programmer intent) than the other options presented. It is almost certainly also faster, but that’s probably negligible.
#13 by Stefan on July 15, 2011 - 11:35 pm
A good article on this issue can be found here: http://www.javaspecialists.co.za/archive/newsletter.do?issue=137
#14 by Cosmin Marginean on July 15, 2011 - 11:53 pm
I personally don’t see a problem with the construct:
private static Logger log = org.apache.log4j.Logger.getLogger(ThisClass.class);
This is as refactoring-resistent as the other constructs as a rename on the class will take care of this too (if you are using a decent IDE that is, but even a blind replace would do it). I can hardly see the value in having a generic version of this.
I personally use IntelliJ’s “Live templates” where something like this:
private static Logger log = org.apache.log4j.Logger.getLogger($clsName$.class);
Is easily expanded in:
private static Logger log = org.apache.log4j.Logger.getLogger(TheCurrentClassIAmEditing.class);
by typing log or whatever abreviation/shortcut you set for this. It handles the log4j package import too, and removes the fully qualified path in front of Logger.getLogger automatically. I am pretty sure Eclipse should have this stuff too.
#15 by Reinier Zwitserloot on July 16, 2011 - 12:58 pm
For logging specifically, you can use @Log / @Slf4j / @CommonsLog / @Log4j of projectlombok which does just that (creates a static final variable named ‘log’ using your own class as log bucket).
#16 by Prakash G.R. on July 16, 2011 - 9:41 pm
An Eclipse template is the simplest way. You can add a template and use ctrl+space to fill the whole line.
#17 by Hamlet D'Arcy on July 17, 2011 - 9:51 pm
Lombok _and_ Groovy both have the @Log, @Slf4j, @Commons, and @Log4j type annotations to clean this up. Also both Java and Groovy have static analysis tools that catch this sort of error for you where you have the wrong class name (FindBugs/PMD/CodeNarc).
I know it doesn’t answer your orginal question though. There is a general problem here that LiveTemplates/annotations isn’t going to solve in a general way.
#18 by Rafael Naufal on July 18, 2011 - 11:04 am
If you use Eclipse as your IDE, you can define a code template to fill a Logger with the current class and use it with ctrl+space.
Go to Window > Preferences > Java > Editor > Templates and create a new template, giving it a name which you want to code complete. Add the following code:
private static final Logger logger = Logger.getLogger(${enclosing_type}.class);
And you are ready to use it!
#19 by Micha? Gruca on July 18, 2011 - 9:48 pm
First of all why the hell so many plp works with static context? I hope for you all that you just treat this as a challenge to come with something smarter than Cedric did
Secondly it is easier to write myClass.class If you are not programming in notepad then it will resist any future changes and it simply just won’t compile if you are. Even netbeans should have refactoring for that.
#20 by Thomas Mueller on July 18, 2011 - 9:53 pm
This is only a tiny bit shorter: LoggerFactory.getEnclosingLogger(new Object(){});
This was also discussed at SLF4J: http://bugzilla.slf4j.org/show_bug.cgi?id=163
Another interesting idea is using an annotation processor, see Project Lombok.
#21 by Cedric on July 18, 2011 - 9:56 pm
Wow, Thomas, that’s quite a discussion on this bug! Glad to see I’m not the only one who identified this need.
#22 by Thomas Mueller on July 18, 2011 - 10:11 pm
There seems to be a typo in your SecurityManager example; getCurrentClass can’t be static. What about:
public class Test {
private static Logger logger = LoggerFactory.getCallerLogger();
public static void main(String… args) {
logger.log(“Hello”);
}
}
public class LoggerFactory {
public static Logger getCallerLogger() {
return new Logger() {{
name = new SecurityManager() {
public Class getClass(int level) {
return getClassContext()[level];
}
}.getClass(3).getName();
}};
}
}
public class Logger {
String name;
public void log(String s) {
System.out.println(name + “: ” + s);
}
}
#23 by Thomas Mueller on July 18, 2011 - 10:18 pm
I have submitted my previous comment to SLF4J, where it’s readable: http://bugzilla.slf4j.org/show_bug.cgi?id=163#c40 – I hope you don’t mind stealing your idea…
#24 by Thomas Mueller on July 18, 2011 - 10:25 pm
Well, it turns out this idea is relatively old: 2006: http://web.archiveorange.com/archive/v/NUYyjKKmz6YnLKAMeZDc – but it seems to have been ignored.
#25 by Dave Shah on July 19, 2011 - 3:50 am
I haven’t totally thought this one through, but this would be my quickest alternative 😉
public class ClassUtil{
public static String getCurrentClass(){
StackTraceElement[] elements = Thread.currentThread().getStackTrace();
return elements[2].getClassName();
}
}
#26 by Jon H on July 20, 2011 - 3:47 am
I’m intrigued by your statement that you often need to access the current class – what are the other use cases besides logging?
I find it overkill to make a logger per class – surely the only reason to do this is so that you can adjust the logging level per class – but in real life how often do you do this? Outside of debugging it’s rare, and inside debugging it matters little if you raise the logging level of lots of classes at once – there are plenty of tools to help you filter the output. So I find this whole discussion a bit academic. (OK, I’m interested in academic issues!)
Also, looking at the discussion, doesn’t anybody but me prefer to make their loggers final? Is there a use case when it might change after construction??
#27 by Matt B on July 20, 2011 - 6:15 am
Template for IDEA:
private static final org.apache.log4j.Logger log = org.apache.log4j.Logger.getLogger($CLASS$.class);
An interesting discussion, but anyone who actually used any of these solutions in real code to declare a logger should get smacked in the code review 🙂
#28 by Ji?Ã Pejchal on July 20, 2011 - 10:19 am
Template for netbeans:
private static final ${LOG_TYPE type=”org.slf4j.Logger” default=”Logger” editable=false} logger = ${LOG_FACT type=”org.slf4j.LoggerFactory” default=”LoggerFactory” editable=false}.getLogger(${classVar editable=”false” currClassName default=”getClass()”}.class);
I use abbreviation ‘logs’ for this template.
#29 by Thomas Mueller on July 23, 2011 - 3:16 am
> anyone who actually used any of these solutions
> should get smacked in the code review
Anyone who relies on the IDE to *generate* source code should get smacked in the code review hehe.