How to log method calls using spring annotations? - java

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
}

Related

Modify value from class with #AfterReturning in Spring AOP

How to modify value with #AfterReturning advice, it works for any object except String. I know that String is Immutability. and how to modify the string without changing returning type of saveEverything() function in AccountDAO class?
here are code snippet:
#Component
public class AccountDAO {
public String saveEverything(){
String save = "save";
return save;
}
}
and aspect:
#Aspect
#Component
public class AfterAdviceAspect {
#AfterReturning(pointcut = "execution(* *.save*())", returning = "save")
public void afterReturn(JoinPoint joinPoint, Object save){
save = "0";
System.out.println("Done");
}
}
and main app:
public class Application {
public static void main(String[] args) {
AnnotationConfigApplicationContext context =
new AnnotationConfigApplicationContext(JavaConfiguration.class);
AccountDAO accountDAO = context.getBean("accountDAO", AccountDAO.class);
System.out.println(">"+accountDAO.saveEverything());;
context.close();
}
}
From the documentation :After Returning Advice
Please note that it is not possible to return a totally different
reference when using after returning advice.
As anavaras lamurep rightly pointed out in the comments , #Around advice can be used to achieve your requirement. An example aspect would be as follows
#Aspect
#Component
public class ExampleAspect {
#Around("execution(* com.package..*.save*()) && within(com.package..*)")
public String around(ProceedingJoinPoint pjp) throws Throwable {
String rtnValue = null;
try {
// get the return value;
rtnValue = (String) pjp.proceed();
} catch(Exception e) {
// log or re-throw the exception
}
// modify the return value
rtnValue = "0";
return rtnValue;
}
}
Please note that the pointcut expression given in the question is global . This expression will match call to any spring bean method starting with save and returning an Object. This might have undesired outcome. It is recommended to limit the scope of classes to advice.
--- Update ---
As pointed out by #kriegaex , for better readability and maintainability the pointcut expression may be rewritten as either
execution(* com.package..*.save*())
or
execution(* save*()) && within(com.package..*)

How to wrap a method with try-catch by annotation?

If an exception should be ignored inside a method call, one would write eg the following:
public void addEntryIfPresent(String key, Dto dto) {
try {
Map<String, Object> row = database.queryForMap(key);
dto.entry.put(row);
} catch (EmptyResultDataAccessException e) {}
}
I'm trying to write eg a custom spring annotation that has the same effect, but could just be applied to the method header. That could look similar to the following:
#IgnoreException(EmptyResultDataAccessException.class) //this annotation does not exist
public void addEntryIfPresent(String key, Dto dto) {
Map<String, Object> row = database.queryForMap(key);
dto.entry.put(row);
}
How could such an annotation be created?
Here is a way of doing it with AspectJ.
First of all, define a method level annotation.
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
public #interface IgnoreRuntimeException{
}
Then, define an around aspect for the annotation.
#Component
#Aspect
public class ExceptionHandlingAdvice {
#Around("#annotation(com.yourpackage.IgnoreRuntimeException) && execution(* *(..))")
public Object aroundAdvice(ProceedingJoinPoint joinPoint) throws Throwable {
Object returnObject = null;
// do something before the invocation
try {
// invoke method
returnObject = joinPoint.proceed();
// do something with the returned object
return returnObject;
} catch (Exception e) {
// do something with the exception
}
}
}
You can then apply the annotation on top of your methods to ignore exceptions.
#IgnoreRuntimeException
public void addEntryIfPresent(String key, Dto dto) {
// ...
}
You can also check the parameters of the annotation using the api of ProceedingJoinPoint to ignore only the exceptions that you'd like to ignore.

Rollback Annotation in Java

I have service calls in my application that make remote network calls to other services as well as DB calls. Spring Boot has good support for rolling back bad transactions with #Transactional, but I wanted to know if I could define a custom rollback procedure using an annotation.
I would need to rollback the data on the other services as well as the database.
In code, I could do it like this:
#Transactional
public void doSomethingComplicated() {
try {
srvcOne.makeRemoteNetworkCall();
srvcTwo.makeDatabaseCall();
} catch(Exception e) {
srvcOne.rollBackNetworkCall();
}
}
but I was hoping I could do something like this:
#Transactional
#MyCustomRollbackListener(handler = MyCustomRollBackHandler.class)
public void doSomethingComplicated() {
srvcOne.makeRemoteNetworkCall();
srvcTwo.makeDatabaseCall();
}
and in the handler:
public class MyCustomRollBackHandler {
public void handleRollback() {
srvcOne.rollBackNetworkCall();
}
}
I implemented a global exception listener and I am able to get the class the exception came from, but I have no way to get the method and to retrieve any annotations on it. Here is my initial attempt:
#ControllerAdvice
public class RollbackExceptionListener{
private static final Logger logger = LoggerFactory.getLogger(RollbackExceptionListener.class);
#ExceptionHandler(Exception.class)
public void lookForAnnotationClassForException(final Exception exception) {
logger.error("Exception thrown", exception);
final StackTraceElement topElement = exception.getStackTrace()[0];
final Class callingClass = topElement.getClass();
final String methodName = topElement.getMethodName();
try {
// Can't get the method with just the name, need to
// know the params as well.
final Method method = callingClass.getMethod(methodName);
// Retrieve the annotation on the method and call the handler
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
}
}
Is there anyway to do something like this?
Arguments are not part of the Stacktrace. If the method is unique, i.e. not overloaded, you can probably find it with getMethods()? Something else that comes to mind, maybe you can look at Aspects to wrap the method in some handler before it is executed. Can be done either at compile time or runtime.
The aspect can do the rollback itself, it can enrich the exception with the information you need, or it can set some ThreadLocal variable with the handler class that was defined in the method before re-throwing the exception. You can then get this value from the ThreadLocal at the point where you catch the exception.

In Junit, how to prevent from printing expected exception which is thrown intentionally and caught already in log?

I guess this is a junit and Logback problem. In my project, Logging is done through slf4j. The logging implementation is Logback.
So I have a class:
#Component
#Slf4j
public class A {
private final ObjectMapper objectMapper = new ObjectMapper();
private static final String DEFAULT_REPLY = "just continue...";
public String doSomething(Object value) {
try {
return objectMapper.methodAbc(value);
} catch (JPException e) {
log.error("Exception while processing value", e);
return DEFAULT_REPLY;
}
}
}
and its test class
#RunWith(MockitoJUnitRunner.class)
public class ATest {
#Before
public void init() {
processor = new A();
}
#Test
public void test() throws Exception {
ObjectMapper mockMapper = mock(ObjectMapper.class);
JP mockJp = mock(JP.class);
Exception thrownException = new JPException(mockJp, null);
when(mockMapper.methodAbc(any())).thenThrow(thrownException);
String result = processor.doSomething("abc");
assertTrue(result.equals("just continue..."));
}
}
I don't have any problem with the test itself. Just as you can see, in the test, the JPException will be printing out on the log, because it's intentionally thrown.
When I debug through logs, there're too many this kind of expected exceptions, I'm just wondering is there a way to remove them from logs? And of course still print other exceptions which is not expected.
Logback has the functionality to support filters and evaluations based on certain logic.
This link is probably what you could be looking for :
https://logback.qos.ch/manual/layouts.html#Evaluators
You can configure your logback to do or not do certain action if it is an instance of any exceptio - in your case JPException
Try this:
Create a mock for the log.
Inject the mock into the class being tested.
Assert that the error method on the mocked log object was called.

AOP based logging in Guice

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.

Categories

Resources