Interesting trick to finding Java caller 17

Posted by Warner Onstine on March 27, 2008

Have you ever wondered how to find the Class that has just called your class? Well, now you can (or rather you always could) by using this little trick that I got from my friend and former co-worker Leo.

Class.forName(new Throwable().getStackTrace()[2].getClassName()));

This will retrieve the caller name from the stacktrace and then you can get the actual class using Class.forName(). He uses this in an interesting way for logging.

private static final Log getLog() {
    try {
        return LogFactory.getLog(Class.forName(new Throwable().getStackTrace()[2].getClassName()));
    }
    catch (Exception e) {
        return LogFactory.getLog(FormattedLogger.class);
    }
}

Which he then imports as static methods so that he can just do:

info("Some intereting INFO logging going on here");

Pretty slick and an interesting use of the Java libraries.

Share and Enjoy:
  • Print
  • Digg
  • Reddit
  • del.icio.us
  • Twitter
  • Facebook
  • Google Bookmarks
  • DZone
Related Posts:
Trackbacks

Trackbacks are closed.

  • Another JVM specific way to do with is

    sun.reflect.Reflection.getCallerClass(2)
  • Leo
    I looked at the J2SE sourcecode for Throwable, and I'm going to state an opinion. I don't think getStackTrace() is very heavy weight. I just did a quick test with 2 milliseconds margin of error from reporting the start and stop time to verify my assumption. The test took 4 milliseconds consistently, so getStackTrace() is negligible.

    I found that getStackTraceElement() used in Throwable is a native call. getStackTraceDepth() is also native. getStackTrace() loops calls to getStackTraceElement() to create StackTraceElement[]. This is the intersting part though. Before returning, getStacktrace() does stacktrace.clone().

    This is interesting because there can be two reason for doing this:
    1. StackTraceElement is extremely volatile and a copy is made not to mess with the original native reference.
    2. StackTraceElement has within it direct references on the stack.

    If #2 is true, then a StackTraceElement[100] is no more difficult to create than simply looping from 0 to 99. It is the clone that is the heaviest part. I think this is probably really efficient from the test that I did.

    I'm going to side with new Throwable().getStackTrace() as being pretty lean in contrast to what people think. I also found that if you're really concerned about performance, some can be gained by not using Thread.getStackTrace() because it does checks to verify the currentThread. Since new Throwable().getStackTrace() uses thread-safe calls, I don't see anything to gain from Thread.getStackTrace() since it calls new Throwable().getStackTrace().
  • Leo
    I got this from J2SE 6.0 Javadocs on the Thread class.

    >On some platforms, specifying a higher value for the stackSize parameter may allow a thread to achieve greater recursion depth before throwing a StackOverflowError. Similarly, specifying a lower value may allow a greater number of threads to exist concurrently without throwing an OutOfMemoryError (or other internal error). The details of the relationship between the value of the stackSize parameter and the maximum recursion depth and concurrency level are platform-dependent. On some platforms, the value of the stackSize parameter may have no effect whatsoever.
    >
    >The virtual machine is free to treat the stackSize parameter as a suggestion. If the specified value is unreasonably low for the platform, the virtual machine may instead use some platform-specific minimum value; if the specified value is unreasonably high, the virtual machine may instead use some platform-specific maximum. Likewise, the virtual machine is free to round the specified value up or down as it sees fit (or to ignore it completely). "
  • Leo
    Sorry. In my previous comment, #2 might have been a silly suggestion. After thinking about it, decreasing the size of your stack could have really bad side-effects, and a smaller stack doesn't necessarily mean smaller StackTraceElement[] because StackTraceElement[] isn't reporting on the entire stack.
  • Leo
    I did some digging and I found that the StackTraceElement[] creation is so so heavyweight. Keep in mind StackTraceElement is pretty much a bean and the array is created from a stack dump. This means there are 2 ways to tune this, so the overhead is not so great.
    1. Turn off debug. The -debug option in the compiler will add extra information the the stack and as a result the StackTraceElement. Line numbers for example. Basically, when you deploy your application into production, you're going to build with debug off and rest assured that this getStackTrace() becomes more efficient.
    2. Configure the size of the stack at runtime. -Xss option lets you set a size for your stack. This is the size of the stack maintained during runtime. The smaller this is, the smaller StackTraceElement[] is going to be. I'm not sure how this effects other aspects of the VM, but it will make it so getStackTrace() runs lightweight depending on what value you set.
  • Leo
    @Andy: You're right! You can just do Thread.getStackTrace()!

    I thought that getStackTrace() was an instance method, so I assumed you had to get the currentThread first. It's not though. It's static, so this makes things a little easier.
  • @Warner: sorry, but there is previous art. This is an old javaspecialists column:
    http://www.javaspecialists.co.za/archive/newsle...

    Lots of logging frameworks, like log5j, are already doing this.

    @Notafan: it is already being used in my production code, and works like a charm (of course, I just use it for logging, but anyways). It works fine with aspects, but needs some tweaking to work with -javaagent (JavaRebel, for example).
  • Notafan
    You've really got to be asking yourself why you are writing ugly hacks like this. It's expensive and clumsy. What if you want to use AOP to intercept calls?

    Seems ok for test code but if it makes it to your production code you've got to question your design.
  • @Gagan it must be pretty costly also I never mesure the performance it. It would probably be usefull to cache the Logger in a way or another.

    I use the Exception trick to store when a connection was open to detect not close exception years ago - the state was capture in the finalizer, which print the stack trace to indicate where the leak came from -.
  • Gagan
    Stack trace creation is a heavy operation isn't it, i wonder about the performance aspects of this?
  • Casper
    I've used this numerous times as a means to achieve IOC during DBUnit tests, inject test data into the database by tracing the class and test method and load data from a file named the same.
  • @anjun
    Here's how `info()` works

    public static final void info(String pattern, Object ... objs) {
    getLog().debug(getMessage(pattern, objs));
    }

    Which you can do a static import on:

    import static FormattedLogging.info

    and then directly reference in your class. This way you no longer have to do `LogFactory.getLog(MyClass.class)`, this will do it for you automatically.

    @Andy
    The credit belongs all to Leo :-).
  • hi there,

    I didn't understand how getLog() gets used in info() call. Sorry for being dense -- can you explain with lil more details -- probably there are others who might not get it.

    I've been using the new Throwable() trick for a long time but this one I don't grok.

    BR,
    ~A
  • Andy
    It used to be that Thread.getStackTrace accompished its task by doing the throw too. I suspect things haven't changed.

    Nicely done.
  • Leo
    It's a Throwable, but it's also probably getting the stack trace from the current Thread, so that's a good idea.

    Also attainable from the StackTraceElement is the method name in case you want to know who's calling you. It's useful if you want supergeneric recursion or callback by convention.
  • James
    This is cool. Now...how do you get the calling Object?
  • Brian
    You can also just ask the current thread for its stack trace rather than creating the exception.
blog comments powered by Disqus

Easy AdSense by Unreal