Logging methods with custom annotation in Spring AOP

Kaan Çetin
Garanti BBVA Teknoloji
4 min readDec 21, 2021

In this article, we’ll learn Spring AOP and create custom annotation for some practical use cases to log our methods for auditing.

Introduction

Aspect-Oriented Programming (AOP) complements Object-Oriented Programming (OOP) by providing another way of thinking about program structure. The key unit of modularity in OOP is the class, whereas in AOP the unit of modularity is the aspect. Aspects enable the modularization of concerns such as logging, caching, auditing, transaction management that cut across multiple types and objects.

To quickly summarize, it is a way for adding behavior to existing code without modifying that code.

Let’s Code

Dependency

We need Spring AOP dependency in our project.

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>

Custom Annotation

This annotation will be used to log our method message and viewed data for auditing. We are using two parameters for this annotation.

The value parameter allows our fields to be displayed with the names we want in the log. Readability is important in audit logs

and showData is for security. We may not want show the data returned from the request to be logged. In this case only the name of the field in the request is shown.

Aspect

Now that we have our annotation, let’s create our aspect. This is just the module that will encapsulate our cross-cutting concern, which is, in our case, logging of method message and returned fields for auditing.

In Spring AOP, aspects are implemented by using regular classes (the schema-based approach) or regular classes annotated with the @Aspect annotation (the @AspectJ style). We’ve also included the @Component annotation, as our class also needs to be a Spring bean to be detected.

@Aspect
@Component
public class Log {
}

Pointcut and Advice

To understand pointcut and advice, we must first understand join point.

Join point: a point during the execution of a program, such as the execution of a method or the handling of an exception. In Spring AOP, a join point always represents a method execution.

Advice: action taken by an aspect at a particular join point. Different types of advice include “around,” “before” and “after” advice. Many AOP frameworks, including Spring, model an advice as an interceptor, maintaining a chain of interceptors around the join point.

Pointcut: a predicate that matches join points. Advice is associated with a pointcut expression and runs at any join point matched by the pointcut (for example, the execution of a method with a certain name). The concept of join points as matched by pointcut expressions is central to AOP, and Spring uses the AspectJ pointcut expression language by default.

There are types of advices;

  • Before advice: executes before a join point
  • After returning advice: executed after a join point completes normally
  • After throwing advice: executed if a method exits by throwing an exception.
  • After (finally) advice: executed regardless of the means by which a join point exits (normal or exceptional return).
  • Around advice: 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.

In this case we use @AfterReturning as per our needs.

@AfterReturning(pointcut = @annotation(Logger)", returning = "object")
private static void log(JoinPoint joinPoint, Object object) throws IllegalAccessException {
}

Now we have enough knowledge, all we need to do is add some extra logic to our advice. This will be what logs the method message and returned data.

Let’s some code.

We’ve not done anything complicated here. We’ve just get method message and check returned data via reflection, then printed as an info log to the console. The reason we use reflection here is to control the fields of the object to be returned and understand how it will be logged. Since these logs are thrown for auditing, we need to specify which fields the user can see and which field’s data can or cannot be seen.

Now, let’s try annotating a method with @Log, and then executing it to see what happens.

Annotation Usage

We are creating a simple Employee controller and CRUD methods. We put our @Log annotation above the methods for enable logging and log message.

And we are creating our Employee entity. We also put our @Log annotation above the fields for logging returned data.

Let’s try the code.

$ curl --location --request GET 'http://localhost:8080/employees/1'
console output

The logging statements are printed in the console.

Conclusion

In this article, we’ve used Spring Boot AOP to create our custom annotation and developed audit log infrastructure without changing our existing codes. As Garanti BBVA, we made improvements for audit logs with a similar logic, using Spring AOP without changing our existing codes.

We kept our code simple to understand the logic of AOP. In cases where there is an entity in an entity, we can improve our code by using it by recursive.

--

--