Sunday, April 6, 2008

How to get the calling method information in Java

I was wondering around in a huge code base trying to figure out whats going on.
I wouldn't say it was a bad code base, but it was done by a single geeky programmer who never cared about the readability because I guess he never thought anyone else would ever try to read it. Anyway, I could see all these meaningful log messages coming out from it but I didn't know from where. I think he never heard of log4j or anything like that but luckily he used a class like this:

public class LogHelper{
public static void log(String log){

System.out.println(log);
}
}

and used it like this:
 1 public class X
2 {
3 public void doA()

4 {
5 Logger.log("doing A");
6

7 //..
8 //..
9 Logger.log("done doing A");

10 }
11
12 public void doB()
13 {

14 Logger.log("doing B");
15
16 //..
17 //..

18 Logger.log("done doing B");
19 }
20 }

So he called this static log method from all over the code base to do the logging.
And the log looked like this:

doing A
doing B
done doing B
done doing A

I desperately wanted to know which class/method/line was calling this method like I am used to in log4j.

Then I thought the calling Thread has this information. The question is how can I get it! After playing a bit with stack information, I came up with this:
public static String getCallingMethodInfo() {
StackTraceElement[] stackTrace ;

try{
throw new Exception();
}

catch (Exception e) {
stackTrace = e.getStackTrace();

}

if(stackTrace != null && stackTrace.length >= 2) {

StackTraceElement s = stackTrace[2];
if(s != null) {

return s.getClassName() + ".(" + s.getMethodName() +"):["+ s.getLineNumber() + "] -:";

}
}

return null;
}
So finally the log method looked like this:
public class LogHelper {
public static void log(String log){

String caller = getCallingMethodInfo();
System.out.println(caller + ": " + log);

}
}

So, finally the logs looked like this:

X.(doA):[5]-: doing A
X.(doB):[14]-: doing B
X.(doA):[9]-: done doing A
X.(doB):[18]-: done doing B
In jdk 1.5 you can actually get the stack information like this:
Thread.currentThread().getStackTrace()
but unfortunately I was working with a 1.4 codebase. But Throwing an exception and catching it back did the trick :)

Having said all that, It will be an insane idea to put this code to production. But can be very helpful when you are trying to understand the code.

1 comment:

greg said...

You don't need to throw and catch your exception. This works just as well:

final Throwable fakeException = new Throwable();
final StackTraceElement[] elements = fakeException.getStackTrace();