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
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

The software you build is only as secure as the code that powers it. Learn how malicious code creeps into your software supply chain.

Apache Cassandra combines the benefits of major NoSQL databases to support data management needs not covered by traditional RDBMS vendors.

Generative AI has transformed nearly every industry. How can you leverage GenAI to improve your productivity and efficiency?

Modernize your data layer. Learn how to design cloud-native database architectures to meet the evolving demands of AI and GenAI workloads.

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

  • Building Resilient Networks: Limiting the Risk and Scope of Cyber Attacks
  • Mastering Advanced Traffic Management in Multi-Cloud Kubernetes: Scaling With Multiple Istio Ingress Gateways
  • Assessing Bias in AI Chatbot Responses
  • Customer 360: Fraud Detection in Fintech With PySpark and ML
  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
14.3K 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
Oops! Something Went Wrong

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

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

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 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!