I got really tired of having this code in all of my classes and methods:
public class SomeClass {
private static final Logger LOGGER = Logger.getLogger(SomeClass.class);
//...
public int someMethod() {
final String methodName = "someMethod()";
LOGGER.debug("Entering " + methodName);
int returnValue = 0;
//...
LOGGER.debug("Exiting " + methodName + +", returning " + returnValue);
return returnValue;
}
}
... so I created the following utility class which would allow me to just call LoggingUtil.logMethodEntrance()
:
public class LoggingUtil {
private static final Logger LOGGER = Logger.getLogger(LoggingUtil.class);
private static final int GET_CALLING_METHOD_NAME_DEPTH = 3;
public static void logMethodEntrance() {
if(LOGGER.isDebugEnabled()) {
LOGGER.debug("Entering " + getCallingMethodName());
}
}
private static String getCallingMethodName() {
return Thread.currentThread().getStackTrace()[GET_CALLING_METHOD_NAME_DEPTH].getMethodName() + "()";
}
}
This was the first iteration. I put it here so you could understand the general concept. Before you mention that the isDebugEnabled()
call isn't necessary to prevent the logging, the reason it's there is because the getStackTrace()
method has notoriously poor performance and I don't want it called unless it's actually going to be used.
Now I wanted to add on to this basic framework and make it more powerful/dynamic by overloading the method and allowing the user to pass in a Priority
if they don't want it to be DEBUG
.
Normally, I would chain the methods like the following:
private static final Level DEFAULT_LOGGING_LEVEL = Level.DEBUG;
public static void logMethodEntrance() {
logMethodEntrance(DEFAULT_LOGGING_LEVEL);
}
public static void logMethodEntrance(Priority level) {
if(LOGGER.isEnabledFor(level)) {
LOGGER.log(level, "Entering " + getCallingMethodName());
}
}
The problem with this is that there's no way for the getCallingMethodName()
method to know how deep to search in the stack for the appropriate "calling method". If the calling code invoked logMethodEntrance()
, it will be one off from if they called logMethodEntrance(Level.INFO)
.
I thought about passing down an int
that described the stack depth to look for the method name, but it ended up boiling down to all of them passing a 1
down to the common method call. So this is what I wound up going with in my final implementation:
public class LoggingUtil {
private static final Logger LOGGER = Logger.getLogger(LoggingUtil.class);
private static final Object METHOD_SIGNATURE_BREAKER = new Object();
private static final Level DEFAULT_LOGGING_LEVEL = Level.DEBUG;
private static final int GET_CALLING_METHOD_NAME_DEPTH = 4;
public static void logMethodEntrance() {
logMethodEntrance(DEFAULT_LOGGING_LEVEL, METHOD_SIGNATURE_BREAKER);
}
public static void logMethodEntrance(Priority level) {
logMethodEntrance(level, METHOD_SIGNATURE_BREAKER);
}
private static void logMethodEntrance(Priority level, Object signature) {
if(LOGGER.isEnabledFor(level)) {
LOGGER.log(level, "Entering " + getCallingMethodName());
}
}
private static String getCallingMethodName() {
return Thread.currentThread().getStackTrace()[GET_CALLING_METHOD_NAME_DEPTH].getMethodName() + "()";
}
}
I'm not particularly happy with this solution, but it does work fine. The issue is even more noticeable in the logMethodExit()
method set, which I allow the calling code to either use with no arguments, use with Priority
, use with a T returnValue
, or supply both.
The METHOD_SIGNATURE_BREAKER
object only exists to allow a further method overload. What I mean is that there's no actual need for it, except that I have to have another parameter supplied to the common private
method in order for it to exist. (i.e., I can't have it as below):
public static void logMethodEntrance() {}
public static void logMethodEntrance(Priority level) {}
private static void logMethodEntrance(Priority level) {}
Passing down null
seemed even dirtier without the constant. I'm not willing to copy and paste code around or duplicate it. I fully believe that if you need to make a code change to fix a bug or something that you should only have to do it in one place. Ideally, I'd like to find a way to chain the methods downward, but I don't see how to do that well and preserve the getCallingMethodName()
's functionality. So, any great ideas?
getCallingMethodName()
method can dynamically get the right one and allow the methods to be chained together. It would make the code cleaner, I think, than relying on a separateprivate
version. \$\endgroup\$