DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Related

  • Java, Spring Boot, and MongoDB: Performance Analysis and Improvements
  • Spring Boot Pet Clinic App: A Performance Study
  • Improving Backend Performance Part 1/3: Lazy Loading in Vaadin Apps
  • Custom Annotations To Validate Input Requests in Spring Boot - Part I

Trending

  • Optimizing High-Volume REST APIs Using Redis Caching and Spring Boot (With Load Testing Code)
  • How SaaS Architectures Break at Scale — and the Engineering Decisions That Prevent It
  • Build a GitHub Slack Bot With AWS Bedrock and MCP, Part 2
  • Testing AI-Infused Apps: A Dual-Layer Framework for AI Quality Assurance
  1. DZone
  2. Coding
  3. Frameworks
  4. Auto Logging in Class and Method Level Using Custom Annotations in Spring Boot App

Auto Logging in Class and Method Level Using Custom Annotations in Spring Boot App

Learn how to automate logging in class and method level using custom annotations in the SpringBoot application, benefitting aspect-oriented programming.

By 
Artun Kutluk user avatar
Artun Kutluk
·
Dec. 17, 21 · Tutorial
Likes (6)
Comment
Save
Tweet
Share
15.2K Views

Join the DZone community and get the full member experience.

Join For Free

It doesn't matter which type or size of software application we are developing, we all come to a point where we want to do a process over and over again but also don't want to re-implement or make a recall every time.

This situation is not new nor it is valid for one case. There are many different variations for this situation and as a result many solutions. I would like to stage one of these solutions in a process common for all applications: Logging. I will be creating a spring boot application and will benefit from AOP.

Let's first list what we want to achieve:

  • Able to define class so every method in it will be logged when called
  • Able to exclude a method in the class from being logged
  • Able to define methods easily as loggable when we don't want the whole class to be loggable

Now lets first create a spring boot application named "logger" and add the following dependencies:

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


Next, let's create our custom annotations to manage the loggability of our classes and methods. Let's have 3 different annotations.

  1. An annotation to define the classes as loggable
  2. An annotation to define the methods as loggable
  3. An annotation to define the methods as not loggable

1. Annotation Loggable Class

We define our loggable class annotation as follows:

Java
 
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface LoggableClass {}

@Target indicates the contexts in which an annotation type is applicable. ElementType.TYPE is used to indicate that our annotation is for classes.

@Retention indicates how long annotations with the annotated type are to be retained. Since we want them to be retained in run time, we pass RetentionPolicy.RUNTIME to it.

2. Annotation Loggable Method

We define our loggable method annotation just like our loggable class annotation but with just one difference. Instead of passing ElementType.TYPE to @Target, we pass ElementType.METHOD.

Java
 
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface LoggableMethod {}

3. Annotation Not Loggable Method

This is the same as the loggable method.

Java
 
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface NotLoggableMethod {}

Now we have our annotations. It is time to make these annotations meaningful. We must define what will be done when a class or method has these annotations. 

Let's create a class named Logger:

Java
 
@Aspect
@Component
public class Logger {
  
}

Here we add @Aspect to make the class to be detected automatically by spring and make the necessary configuration for AOP. We also add @Component for spring to detect our bean.

Now let's define which executions we are interested in. 

As we mentioned before, our first interest is all method executions in a class with annotated @LoggableClass. We define this as the following:

Java
 
@Pointcut("@within(com.kutluk.logger.annotations.LoggableClass)")
public void loggableClass() {}

The @Pointcut annotation has two parts. The first part is the first line which is the expression part. The expression covers everything within the class with LoggableClass annotation. The second part is the second line which is the signature. It is a sort of a shortcut for the expression.

Our second and third interests are similar. Let's now define them.

Java
 
@Pointcut("@annotation(com.kutluk.logger.annotations.LoggableMethod)")
public void loggableMethod() {}

@Pointcut("@annotation(com.kutluk.logger.annotations.NotLoggableMethod)")
public void notLoggableMethod() {}

 For these two pointcuts, we defined our join points using "@annotation" instead of "@within" in the expression part because we only want to cover methods that have our annotation. We don't need more.

Since we have our pointcuts ready, now let's define the "advice" which is what to do when the execution comes to our join points.

There are many types of "advice" which we can use but we will use the following three:

  1. @Before - The advice that is executed before the join point
  2. @After - The advice that is executed after the join point
  3. @AfterThrowing - The advice that is executed after an exception is thrown at the join point

  Let's create our "advice" which just prints out what they are called.

Java
 
@Before("(loggableClass() ||  loggableMethod()) && !notLoggableMethod()")
public void adviceBefore(JoinPoint jp)  {
    System.out.println("@Before called");
}

@After("(loggableClass() ||  loggableMethod()) && !notLoggableMethod()")
public void adviceAfter(JoinPoint jp)  {
    System.out.println("@After called");
}

@AfterThrowing("(loggableClass() ||  loggableMethod()) && !notLoggableMethod()")
public void adviceAfterThrowing(JoinPoint jp){
    System.out.println("@AfterThrowing called");
}

All three "advices" take a logical expression using the pointcut signatures. In our case; for all of them, we gave the same expression: if the joining point is coming from a loggable class or from a loggable method, but at the same time not from a not loggable method.

All three "advices" also have the same parameter: JoinPoint jp. This is optional. It contains information about the joining point which caused advice to be triggered which is very useful but we won't benefit from it in our case.

Now our structure is ready. What we need is now is some dummy code with the annotations we just created and a code enabling us to call the dummy code. For this let's create:

  1. A dummy class that has @LoggableClass annotation and multiple methods where some have @NotLoggableMethod annotation
  2. A dummy class with multiple methods with @LoggableMethod annotation
  3. A dummy rest service that calls all the methods inside the two dummy classes mentioned above

1. Dummy Class With @LoggableClass Annotation

Java
 
@LoggableClass
@Service
public class ClassWhichIsLoggable {

    public void methodOne(){
        System.out.println("ClassWhichIsLoggable -> method with no annotation called");
    }

    @LoggableMethod
    public void methodTwo(){
        System.out.println("ClassWhichIsLoggable -> method with @LoggableMethod annotation called");
    }

    @NotLoggableMethod
    public void methodThree(){
        System.out.println("ClassWhichIsLoggable -> method with @NotLoggableMethod annotation called");
    }

    public void methodFour() throws Exception{
        System.out.println("ClassWhichIsLoggable -> method throwing exception called");
        throw new Exception("custom exception");
    }
}

2. Dummy Class With @LoggableMethod Annotation

Java
 
@Service
public class ClassWithLoggableMethods {

    @LoggableMethod
    public void methodOne(){
        System.out.println("ClassWithLoggableMethods -> method with @LoggableMethod annoation called");
    }

    @LoggableMethod
    public void methodTwo() throws Exception{
        System.out.println("ClassWithLoggableMethods -> method with @LoggableMethod annoation throwing exception called");
        throw new Exception("custom exception");
    }

    public void methodThree(){
        System.out.println("ClassWithLoggableMethods -> method without @LoggableMethod annoation called");
    }
}

3. Dummy Rest Service That Calls All Methods

Java
 
@RestController
@RequestMapping("/logRest")
public class LogRestController {

    @Autowired
    ClassWithLoggableMethods classWithLoggableMethods;

    @Autowired
    ClassWhichIsLoggable classWhichIsLoggable;

    @RequestMapping(value = "/serviceWithLog", method = RequestMethod.POST)
    @ResponseBody
    public String serviceWithLog() {

        System.out.println("\n\n\n------------------------------------------------------------------------------");
        System.out.println("                       ClassWhichIsLoggable");
        System.out.println("------------------------------------------------------------------------------");

        printSeparator();
        classWhichIsLoggable.methodOne();
        printSeparator();
        classWhichIsLoggable.methodTwo();
        printSeparator();
        classWhichIsLoggable.methodThree();
        printSeparator();
        try {
            classWhichIsLoggable.methodFour();
        }catch(Exception e){
            System.out.println("Custom exception caught");
        }


        System.out.println("\n\n\n------------------------------------------------------------------------------");
        System.out.println("                     ClassWithLoggableMethods");
        System.out.println("------------------------------------------------------------------------------");

        printSeparator();
        classWithLoggableMethods.methodOne();
        printSeparator();
        try {
            classWithLoggableMethods.methodTwo();
        }catch(Exception e){
            System.out.println("Custom exception caught");
        }
        printSeparator();
        classWithLoggableMethods.methodThree();
        printSeparator();

        return "done";
    }

    private void printSeparator(){
        System.out.println("\n---------------------------------------\n");
    }
    
}

Let's run our application and call the dummy rest service and see the result:

Results of dummy

You can find the source code for the application at the following repository: https://github.com/artunkutluk/AopLogging.git

Spring Framework Annotation Spring Boot app

Opinions expressed by DZone contributors are their own.

Related

  • Java, Spring Boot, and MongoDB: Performance Analysis and Improvements
  • Spring Boot Pet Clinic App: A Performance Study
  • Improving Backend Performance Part 1/3: Lazy Loading in Vaadin Apps
  • Custom Annotations To Validate Input Requests in Spring Boot - Part I

Partner Resources

×

Comments

The likes didn't load as expected. Please refresh the page and try again.

  • RSS
  • X
  • Facebook

ABOUT US

  • About DZone
  • Support and feedback
  • Community research

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 215
  • Nashville, TN 37211
  • [email protected]

Let's be friends:

  • RSS
  • X
  • Facebook