I am trying to implement AOP based logging in Google - Guice. I have used MethodInterceptor for this but it doesn't work. I have used same in Spring by defining point-cuts. Everything is working fine there.
Spring Code for AOP based logging -
#Aspect
public class LoggingAspect {
private static Logger logger = LoggerFactory.getLogger(LoggingAspect.class);
#Around("requiredLog()")
public Object bentoBoxAround(ProceedingJoinPoint proceedingJoinPoint) {
Object returnValue = null;
try {
logger.info("Entered into the method -> " + proceedingJoinPoint.getSignature().toShortString()
+ " and input arguments are -> " + Arrays.asList(proceedingJoinPoint.getArgs()));
returnValue = proceedingJoinPoint.proceed();
logger.info("Method Execution over !! " + proceedingJoinPoint.getSignature().toShortString());
} catch (Throwable e) {
logger.error("Method has an exception " + e.getMessage());
}
return returnValue;
}
#Pointcut("within(org.cal.bento..*)")
public void allRequiredPakageLog() {
}
}
From above code we can log all the class and method executions inside the org.cal.bento.* package.
Guice code for AOP based logging -
public class GuiceLoggingInterceptor implements MethodInterceptor {
private static Logger logger = LoggerFactory
.getLogger(GuiceLoggingInterceptor.class);
#Override
public Object invoke(MethodInvocation invocation) throws Throwable {
Object returnValue = null;
try {
logger.info("GUICE - Entered into the method -> " + invocation.getMethod().getName()
+ " and input arguments are -> " + Arrays.asList(invocation.getArguments()));
returnValue = invocation.proceed();
logger.info("Method Execution over !! " + invocation.getMethod().getName());
} catch (Throwable e) {
logger.error("GUICE - Method has an exception " + e.getMessage());
}
return returnValue;
}
}
Binding Class -
public class GuiceAopModule extends AbstractModule {
#Override
protected void configure() {
bindInterceptor(Matchers.any(), Matchers.any(), new GuiceLoggingInterceptor());
}
}
Can we do similar in Guice for logging (by defining only one Aspect based class for whole logging system). I don't want to modify every class.
Refered Tutorial - https://schakrap.wordpress.com/2009/07/30/method-entry-exit-logging-in-guice-with-aop/
Any help would be highly appreciated.
Your issue appears to be that you are not using guice for creation. From the guice docs:
This approach imposes limits on what classes and methods can be
intercepted:
[...]
Instances must be created by Guice by an #Inject-annotated or
no-argument constructor It is not possible to use method interception
on instances that aren't constructed by Guice.
So this means, that because your instances are created by spring and likely added to guice, guice has no chance of proxying those classes for interception.
Source:
https://github.com/google/guice/wiki/AOP
Edit:
what you can do (as workaround) to be able to make this work would be:
Spring creates your instances.
Put them into guice
Create a delegate object that is created by Guice and inject the bean of (1) into the wrapper.
Use the wrapper instead of the object in 1 and then the methods will get intercepted.
Related
I have been wring Aspect for logging purpose. Now i am able to use before and after advice. But is it possible to call an advice after execution of certain line of business logic. Here is my current code and i want to substitute my code with advice. How to do that?
#ComponentScan
#EnableCaching
#EnableAutoConfiguration(exclude = {MetricFilterAutoConfiguration.class, MetricRepositoryAutoConfiguration.class})
public class Application {
private final Logger log = LoggerFactory.getLogger(Application.class);
#Inject
private Environment env;
#Inject
private AppConfig appConfig;
public void myBusinessLogicMethod(){
if (myVariable == 0) {
log.info("No Spring profile configured, running with default configuration");
//rest of the business logic here
} else {
log.info("Running with number profile(s) : {}",myVariable);
// //rest of the business logic here
}
My Aspect class
#Aspect
#Order(0)
public class LoggingAspect {
private final Logger log = LoggerFactory.getLogger(this.getClass());
#Inject
private HttpServletRequest request;
#Inject
private HttpServletResponse response;
#Inject
private Environment env;
#Pointcut("(within(com.repository.*) || within(com.service.*) || "
+ "within(com.web.rest.*)) && "
+ "!#annotation(com.aop.logging.NoLogging)")
public void loggingPoincut() {
}
#Before("within(com.web.rest.*) && "
+ "!#annotation(com.aop.logging.NoLogging)")
public void beforeRest(JoinPoint point) throws UnknownHostException {
String ipAddress = getIpAddress();
if (log.isDebugEnabled()) {
log.debug(">>>>>> From IP {}", isIpAvailble(ipAddress));
log.debug("Enter: {}.{}() with argument[s] = {}", point.getSignature().getDeclaringTypeName(),
point.getSignature().getName(), Arrays.toString(point.getArgs()));
}
}
#After("within(com.web.rest.*) && "
+ "!#annotation(com.aop.logging.NoLogging)")
public void afterRest(JoinPoint point) throws UnknownHostException {
if (log.isDebugEnabled()) {
log.debug("Exit: {}.{}()", point.getSignature().getDeclaringTypeName(), point.getSignature()
.getName());
log.debug("<<<<<< Rest Call Finished {} ", response.getStatus());
}
}
}
How can i replace tightly coupled log with an advice in my aspect class.
The simple answer is: With Spring AOP you can intercept method calls, not single lines of code. Even if you could, it would be a maintenance nightmare because code inside a method changes frequently. Even if you have a stable API of public methods, they are black boxes and you (or one of your colleagues) can change their internal implementation at any time.
But the solution is simple: Apply "Clean Code" principles (I hope you have read the book or know about the software craftsmanship movement from other sources already), i.e. use short methods, factoring out more complex code into smaller, re-useable, well named and maintainable pieces with low complexity. Factor out methods to the granularity level you need for logging, e.g.
public void myBusinessLogicMethod() {
if (myVariable == 0)
smallerBusinessLogicA(myVariable, someParameter);
else
smallerBusinessLogicB(myVariable);
}
Then target those factored out methods with your logging pointcut and log their names, parameters and/or results (whatever you need). In order for this to work with Spring AOP, you need to be careful to
either factor out the helper methods into other Spring component classes because Spring AOP does not support self-invocation interception
or self-inject the bean so you can call the helper methods using the injected bean (i.e. you use the AOP proxy and not the real implementation class underneath that does not know anything about AOP)
or switch from Spring AOP to full AspectJ which does not use proxies and works with self-invocation.
Another difference between Spring AOP and AspectJ is that the former can only target non-private methods, while AspectJ has no such limitation.
I am trying to log every method call. I am using spring annotations to create the beans instead of the normal xml file. I was wondering how to do this? I have already created the aspect logger and I tried using the tag #Aspect and #Component then scan for it and it still does not log the method calls.
The current aspect logger worked with the spring.xml file.
Here is the aspect
#Aspect
#Component
public class LoggingAspectService extends TestSetup
{
String infoFolderPath = null;
#Around("execution(* com.quality.pages..*(..))")
public Object stepLoggerAdvice(ProceedingJoinPoint proceedingJoinPoint) throws Throwable
{
String message = composeMessage(proceedingJoinPoint);
logger.step(message);
Object value = null;
try
{
value = proceedingJoinPoint.proceed();
}
catch (Throwable e)
{
logger.warn(message + " did not finish!");
storeFailedTestInfo(proceedingJoinPoint.getSignature().getName());
throw e;
}
return value;
}
// more methods
}
I am trying to log the execution time for methods annotated with custom interface.
I am using Spring AOP.
But this does not seems to work for inner methods.
I think it is the limitation in Spring AOP
#Aspect
public class BusinessProfiler {
private static Log log = LogFactory.getLog(BusinessProfiler.class);
#Around("execution(* *(..)) && #annotation(TimeLog)")
public Object profile(ProceedingJoinPoint point) throws Throwable {
long start = System.currentTimeMillis();
Object result = point.proceed();
String format =
String.format("%s#%s: took [%s msec]", point.getTarget().getClass().getSimpleName(),
MethodSignature.class.cast(point.getSignature()).getMethod().getName(),
System.currentTimeMillis() - start);
log.info(format);
return result;
}
}
Are there any alternatives than Spring AOP
If you think about the way AOP annotations are dealt with by Spring this will be clear:
Spring takes your class and wraps it in a proxy with the extra code generated on the fly by the AOP annotation added. So only code called via the proxy (i.e from outside your class will be included).
Example
#Service
public class Foo {
public void doSomething() {
doSomethingInternal();
}
public void doSomethingInternal() {
}
}
If from another Spring bean I do this:
#Service
public class Bar {
#Autowired
private Foo foo;
public void execute() {
foo.doSomething();
}
}
Only doSomething will be called via the proxy which wraps your class, not doSomethingInternal, that will be called by your class.
Here is the method:
public static boolean startModule(Module mod, ServletContext servletContext, boolean delayContextRefresh)
Here is the method call in a java file:
WebModuleUtil.startModule(module, getServletContext(), false);
I cannot make any changes to these files, but I want to intercept the method and add some of my code (I want access to the parameters as well)
Code I wrote in another java file but not successful:
public void main(String[] args) throws Exception {
Module module = null;
WebModuleUtil wmb = new WebModuleUtil();
ProxyFactory pf = new ProxyFactory(wmb);
pf.addAdvice(new MethodInterceptor() {
public Object invoke(MethodInvocation invocation) throws Throwable {
if (invocation.getMethod().getName().startsWith("start")) {
System.out.println("method " + invocation.getMethod() + " is called on " + invocation.getThis()
+ " with args " + invocation.getArguments());
System.out.println("********************");
Object ret = invocation.proceed();
System.out.println("method " + invocation.getMethod() + " returns " + ret);
return ret;
}
return null;
}
});
WebModuleUtil proxy = (WebModuleUtil) pf.getProxy();
proxy.startModule(module, getServletContext(), false);
}
private static ServletContext getServletContext() {
// TODO Auto-generated method stub
return null;
}
Use aop programming. For example try to read something about AspectJ.
Ex code:
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Service;
#Aspect
#Service
public class AspectDemo {
#Around("aop1()" )
public Object intercept(ProceedingJoinPoint joinPoint) throws Throwable {
for (Object obj : joinPoint.getArgs()) {
LOG.debug(obj);
}
}
#Pointcut("execution(*my.example.packcage.startModule.*(..))")
public void aop1() {
}
Aspect Oriented Programming (AOP) is made for this purpose, you can use AspectJ:
AspectJ enables clean modularization of crosscutting concerns, such as error checking and handling, synchronization, context-sensitive behavior, performance optimizations, monitoring and logging, debugging support, and multi-object protocols.
Or as you are using Spring you can use Spring 2.0 AOP:
Spring 2.0 introduces a simpler and more powerful way of writing custom aspects using either a schema-based approach or the #AspectJ annotation style. Both of these styles offer fully typed advice and use of the AspectJ pointcut language, while still using Spring AOP for weaving.
This is how to declare an Aspect:
#Aspect
public class NotVeryUsefulAspect {
}
And this is how to declare a pointCut:
#Pointcut("execution(* transfer(..))")// the pointcut expression
private void anyOldTransfer() {}// the pointcut signature
For further information you can refer these tutorials:
Spring AOP Example Tutorial – Aspect, Advice, Pointcut, JoinPoint,
Annotations, XML Configuration.
Spring AOP + AspectJ annotation example.
Have a look at byte code instrumentation. It allows you to intercept methods and change all kinds of things without necessarily having access to the source code (although it helps to have it for reference). Javassist is a great library to use along with the JVM Agent.
Cglib is another library that uses proxies to change the behaviour of targeted code, but I have found javassist easier to use.
This tutorial is a good start: http://rafaeloltra.com/developing-a-jvm-agent-for-bytecode-instrumentation-with-javassist-pt1/.
As well as the Javassist documentation: http://jboss-javassist.github.io/javassist/tutorial/tutorial.html
I have a Java class which is something like the following:
public class Foo{
public void doSomething(){
StageA a = new StageA();
StageB b = new StageB();
StageC c = new StageC();
a.execute();
b.execute();
c.execute();
}
}
Now, assuming I can't really edit this class itself, could I still use spring AOP to apply logging around the execute methods? (presumably without using aspect4j)
Well you can log method and it's required time(for performance) but I don't think you would be able to log what method is doing.
From Spring Docs :
Around advice: Advice that surrounds a join point such as a method invocation. This is the most powerful kind of advice. Around advice can perform custom behavior before and after the method invocation. It is also responsible for choosing whether to proceed to the join point or to shortcut the advised method execution by returning its own return value or throwing an exception.
If you are using log4j loggers internally in your methods you can log what method is doing by configuring log4j.
(presumably without using aspect4j)
--> Spring internally uses aspectJ
Check here for reference and example
EDITED:
I don't think that it is possible to log execution of the each "execute" method in your case without changing Foo or Stage classes. Because Stage... classes are not managed by container. You can only log when your doSomething method will start execution (if Foo class is managed by Spring container), you cannot control it's execution flow.
If your classes are to be managed by Spring container, then you can easy do this. You should simply write Spring AOP "around" aspect for Stage... classes, not for Foo class.
Here is an example of simple logging aspect:
#Component
#Aspect
#Order(value=2)
public class LoggingAspect {
#Around("execution(* com.blablabla.server..*.*(..))")
public Object logMethod(ProceedingJoinPoint joinPoint) throws Throwable{
final Logger logger = LoggerFactory.getLogger(joinPoint.getTarget().getClass().getName());
Object retVal = null;
try {
StringBuffer startMessageStringBuffer = new StringBuffer();
startMessageStringBuffer.append("Start method ");
startMessageStringBuffer.append(joinPoint.getSignature().getName());
startMessageStringBuffer.append("(");
Object[] args = joinPoint.getArgs();
for (int i = 0; i < args.length; i++) {
startMessageStringBuffer.append(args[i]).append(",");
}
if (args.length > 0) {
startMessageStringBuffer.deleteCharAt(startMessageStringBuffer.length() - 1);
}
startMessageStringBuffer.append(")");
logger.trace(startMessageStringBuffer.toString());
StopWatch stopWatch = new StopWatch();
stopWatch.start();
retVal = joinPoint.proceed();
stopWatch.stop();
StringBuffer endMessageStringBuffer = new StringBuffer();
endMessageStringBuffer.append("Finish method ");
endMessageStringBuffer.append(joinPoint.getSignature().getName());
endMessageStringBuffer.append("(..); execution time: ");
endMessageStringBuffer.append(stopWatch.getTotalTimeMillis());
endMessageStringBuffer.append(" ms;");
logger.trace(endMessageStringBuffer.toString());
} catch (Throwable ex) {
StringBuffer errorMessageStringBuffer = new StringBuffer();
// Create error message
logger.error(errorMessageStringBuffer.toString(), e)
throw ex;
}
return retVal;
}
}
Yes, you can write an #Around advice with execution pointcut targeting methods whose name is execute() as follows:
#Around("execution(* execute(..))")
public Object execute(ProceedingJoinPoint pjp) throws Throwable
{
// Log statements before the call;
Object obj = pjp.proceed();
// Log statements after the call;
return obj;
}