I have a class with database calls, and I generally want to log every method called (with arguments) in this class with log4j:
logger.debug("foo(id="+id+") initiated");
Is it possible to do this automatically? Maybe by using some sort of annotation in start of each method instead of writing every single logger.debug?
Today I have to update my logging.debug every time I change arguments or method name.
Try @Loggable
annotation and an AspectJ aspect from jcabi-aspects (I'm a developer):
@Loggable(Loggable.INFO)
public String load(URL url) {
return url.openConnection().getContent();
}
All method calls are logged through SLF4J.
This blog post explains it step by step: Java Method Logging with AOP and Annotations
If you have interfaces declaring the methods you want to log calls to, you can use the standard Proxy API to achieve what you want.
The Proxy API would allow you to wrap your actual implementation in a new, proxy class, that would log the call, and the forward the call to implementation. You just have to implement one InvocationHandler
that does the logging and the forwarding.
For example,
interface Calculator {
int add(int a, int b);
}
class CalculatorImpl implements Calculator {
@Override public int add(int a, int b) { return a+b; }
}
class LoggingInvocationHandler implements InvocationHandler {
private final Object delegate;
public LoggingInvocationHandler(final Object delegate) {
this.delegate = delegate;
}
@Override invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("method: " + method + ", args: " + args);
return method.invoke(delegate, args);
}
}
class X {
public static void main(String... args) {
final Calculator calc = new CalculatorImpl();
final Calculator loggingCalc =
(Calculator) Proxy.newProxyInstance(X.class.getClassLoader(),
new Class[] {Calculator.class},
new LoggingInvocationHandler (calc));
loggingCalc.add(2, 3); // shall print to the screen
}
}
You can also easily log the return values and exceptions thrown by the methods, just by changing the code in the InvocationHandler
. Also, you could use any logging framework you like instead of System.out.println
as in the example.
To log return values and exceptions, you could do something like:
@Override invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("method: " + method + ", args: " + args);
try {
final Object ret = method.invoke(delegate, args);
System.out.println("return: " + ret);
return ret;
} catch (Throwable t) {
System.out.println("thrown: " + t);
throw t;
}
}
One possible solution would be to use aspectj. Idea would be to attach aspect to every method you wish to log, and perform logging is aspect instead of a method. One example of aspectj logging is right here in stackoverflow.