Monitoring Method Performance with Spring Boot 3 Observability

Introduction

Monitoring the performance of methods in Java applications is crucial for maintaining optimal performance and ensuring a smooth user experience. When methods perform slower than expected or throw more errors than usual, it can be challenging to isolate the problematic method, especially when dealing with a chain of method calls to complete a significant task. These performance issues can lead to degraded application performance, increased response times, and a poor user experience.

Traditional approaches to monitoring method performance, such as using Aspect-Oriented Programming (AOP) with around advice, can be effective but may require substantial effort to implement and maintain. Fortunately, with the release of Spring Boot 3, developers now have access to enhanced observability features that simplify the process of monitoring method or service class performance.

In this blog post, we will explore how to leverage Spring Boot 3's observability features to monitor the execution time of methods and track errors effectively. We will walk through setting up the necessary dependencies, enabling actuator endpoints, creating a custom performance tracker handler, registering the handler, and visualizing the collected metrics. By the end of this guide, you will have a comprehensive understanding of how to use Spring Boot 3 to gain valuable insights into the performance of your Java applications.

Setting Up Dependencies

In this section, we will guide you through the process of setting up the necessary dependencies to utilize Spring Boot 3 observability features. This involves adding the Spring Boot Starter Actuator and AOP dependencies to your pom.xml file.

Step 1: Add Spring Boot Starter Actuator Dependency

The Spring Boot Starter Actuator provides production-ready features to help monitor and manage your application. To add this dependency, include the following code snippet in your pom.xml file:

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

Step 2: Add Spring AOP Dependency

Spring AOP (Aspect-Oriented Programming) allows you to define cross-cutting concerns, such as performance monitoring, in a modular way. To add the AOP dependency, include the following code snippet in your pom.xml file:

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

Step 3: Update Your Application Properties

Once you have added the necessary dependencies, you need to enable the Actuator endpoints. This can be done by updating your application.properties file with the following keys:

management.endpoints.web.exposure.include=*
management.endpoint.health.show-details=always

These settings will expose all Actuator endpoints and provide detailed health information.

By following these steps, you will have successfully set up the dependencies required for using Spring Boot 3 observability features. Next, we will move on to Enabling Actuator Endpoints.

Enabling Actuator Endpoints

To enable Actuator endpoints in your Spring Boot application, you need to configure the application.properties file. This configuration will allow you to expose various Actuator endpoints and view detailed information about the application's health.

Step-by-Step Guide

  1. Open the application.properties File

    • Navigate to the src/main/resources directory in your Spring Boot project.
    • Open the application.properties file in your preferred text editor.
  2. Add Configuration Keys

    • To enable all Actuator endpoints, add the following key:
      management.endpoints.web.exposure.include=*
      
    • To always show health details, add the following key:
      management.endpoint.health.show-details=always
      
  3. Save and Close the File

    • After adding the necessary keys, save the application.properties file and close it.

Explanation

  • management.endpoints.web.exposure.include=* : This key enables all Actuator endpoints, allowing you to access a wide range of metrics and operational information.
  • management.endpoint.health.show-details=always : This key ensures that detailed health information is always displayed, providing deeper insights into the application's health status.

By following these steps, you will have successfully enabled Actuator endpoints in your Spring Boot application. This setup is crucial for monitoring and managing your application's performance and health.

Next, we will move on to Creating a Custom Performance Tracker Handler, where we will implement a custom handler to track method execution times.

Creating a Custom Performance Tracker Handler

In this section, we'll walk through the process of creating a custom performance tracker handler using the ObservationHandler interface from Spring Boot 3. This custom handler will log the execution start and stop times of methods, and handle errors, providing a clear insight into the performance of your methods.

Step 1: Create a New Package and Class

First, create a new package where you will define your custom handler. For this example, let's name the package aspect.

package com.example.aspect;

Next, create a new class in this package and name it PerformanceTrackerHandler.

public class PerformanceTrackerHandler implements ObservationHandler<Observation.Context> {
    // Class implementation will go here
}

Step 2: Implement the ObservationHandler Interface

The ObservationHandler interface requires you to implement several methods. For our purposes, we will focus on three: onStart, onStop, and onError.

Import Required Libraries

First, import the necessary libraries.

import io.micrometer.observation.Observation;
import io.micrometer.observation.ObservationHandler;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

Annotate the Class

Annotate the class with @Slf4j to enable logging and @Component to make it a Spring Bean.

@Slf4j
@Component
public class PerformanceTrackerHandler implements ObservationHandler<Observation.Context> {
    // Class implementation will go here
}

Step 3: Implement the onStart Method

The onStart method is called when the observation starts. We will log the start time here.

@Override
public void onStart(Observation.Context context) {
    log.info("Execution started: {}", context.getName());
    context.put("startTime", System.currentTimeMillis());
}

Step 4: Implement the onStop Method

The onStop method is called when the observation stops. We will calculate and log the execution time here.

@Override
public void onStop(Observation.Context context) {
    long startTime = context.getOrDefault("startTime", 0L);
    long duration = System.currentTimeMillis() - startTime;
    log.info("Execution stopped: {}, Duration: {} ms", context.getName(), duration);
}

Step 5: Implement the onError Method

The onError method is called when an error occurs. We will log the error message here.

@Override
public void onError(Observation.Context context) {
    log.error("Error occurred: {}", context.getError().getMessage());
}

Step 6: Register the Handler

Once your handler is implemented, you need to register it with the ObservationRegistry. This will allow Spring to use your custom handler for tracking method performance.

Navigate to the Registering the Handler section to complete this step.

Full Code Example

Here is the complete code for the PerformanceTrackerHandler class:

package com.example.aspect;

import io.micrometer.observation.Observation;
import io.micrometer.observation.ObservationHandler;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

@Slf4j
@Component
public class PerformanceTrackerHandler implements ObservationHandler<Observation.Context> {

    @Override
    public void onStart(Observation.Context context) {
        log.info("Execution started: {}", context.getName());
        context.put("startTime", System.currentTimeMillis());
    }

    @Override
    public void onStop(Observation.Context context) {
        long startTime = context.getOrDefault("startTime", 0L);
        long duration = System.currentTimeMillis() - startTime;
        log.info("Execution stopped: {}, Duration: {} ms", context.getName(), duration);
    }

    @Override
    public void onError(Observation.Context context) {
        log.error("Error occurred: {}", context.getError().getMessage());
    }
}

By following these steps, you will have a custom performance tracker handler that logs the start and stop times of method executions and handles errors effectively. Next, move on to the Registering the Handler section to integrate this handler into your application.

Registering the Handler

Once we have created our custom performance tracker handler, the next step is to register it with the ObservationRegistry so that it can start tracking the method execution times. Below are the steps to register the handler.

Step 1: Create a Configuration Class

First, create a new configuration class where we will register our custom handler. This class should be annotated with @Configuration to indicate that it contains Spring configuration settings.

import io.micrometer.observation.ObservationRegistry;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.aop.aspectj.annotation.AspectJProxyFactory;

@Configuration
public class ObservationConfig {

    @Bean
    public AspectJProxyFactory observedAspect(ObservationRegistry observationRegistry) {
        observationRegistry.observationConfig().observationHandler(new PerformanceTrackerHandler());
        return new AspectJProxyFactory(observationRegistry);
    }
}

Step 2: Annotate with @Configuration

Make sure to annotate the class with @Configuration. This tells Spring that this class contains beans that should be managed by the Spring container.

@Configuration
public class ObservationConfig {
    // Bean definitions go here
}

Step 3: Create a Bean for AspectJProxyFactory

Inside the configuration class, create a bean for AspectJProxyFactory and pass the ObservationRegistry to it. This is where we register our custom handler.

@Bean
public AspectJProxyFactory observedAspect(ObservationRegistry observationRegistry) {
    observationRegistry.observationConfig().observationHandler(new PerformanceTrackerHandler());
    return new AspectJProxyFactory(observationRegistry);
}

Step 4: Register the Custom Handler

Within the observedAspect method, use the observationRegistry.observationConfig().observationHandler() method to register the custom PerformanceTrackerHandler.

observationRegistry.observationConfig().observationHandler(new PerformanceTrackerHandler());

Step 5: Return the AspectJProxyFactory Bean

Finally, return the AspectJProxyFactory bean from the method.

return new AspectJProxyFactory(observationRegistry);

By following these steps, you will have successfully registered your custom performance tracker handler with the ObservationRegistry. This will enable you to track the execution time and other metrics for your methods, providing valuable insights into the performance of your application.

Next, we will explore how to track method execution time effectively. Head over to the Tracking Method Execution Time section to learn more.

Tracking Method Execution Time

In this section, we will explore how to track the execution time of specific methods using the @Observed annotation. This is particularly useful for monitoring the performance of your application and identifying any bottlenecks.

Step 1: Add the @Observed Annotation

To begin tracking the execution time of a method, simply add the @Observed annotation to the method you wish to monitor. This annotation is part of the Spring Boot Actuator library.

import io.micrometer.observation.annotation.Observed;

public class ProductService {

    @Observed
    public Product createProduct(Product product) {
        // Method implementation
    }

    @Observed
    public Product getProductById(Long id) {
        // Method implementation
    }

    @Observed
    public Product updateProduct(Long id, Product product) {
        // Method implementation
    }

    @Observed
    public void deleteProduct(Long id) {
        // Method implementation
    }
}

In the example above, the @Observed annotation is added to the CRUD methods of a ProductService class. This will allow us to track the execution time of each of these methods.

Step 2: Define Custom Metrics (Optional)

If you want to define custom metrics for more granular tracking, you can use the @Timed annotation from the Micrometer library. This allows you to specify custom metric names and tags.

import io.micrometer.core.annotation.Timed;

public class ProductService {

    @Timed(value = "product.create", description = "Time taken to create a product")
    public Product createProduct(Product product) {
        // Method implementation
    }

    @Timed(value = "product.get", description = "Time taken to get a product by ID")
    public Product getProductById(Long id) {
        // Method implementation
    }

    @Timed(value = "product.update", description = "Time taken to update a product")
    public Product updateProduct(Long id, Product product) {
        // Method implementation
    }

    @Timed(value = "product.delete", description = "Time taken to delete a product")
    public void deleteProduct(Long id) {
        // Method implementation
    }
}

In this example, we use the @Timed annotation to define custom metrics for each CRUD operation. The value attribute specifies the metric name, and the description attribute provides a brief description of the metric.

Step 3: Monitor the Metrics

Once you have added the @Observed or @Timed annotations to your methods, you can monitor the metrics using Spring Boot Actuator's /actuator/metrics endpoint. This endpoint provides a comprehensive view of all the metrics collected by your application.

For example, to view the metrics for the createProduct method, you can access the following URL:

http://localhost:8080/actuator/metrics/product.create

This will display the execution time and other relevant metrics for the createProduct method.

By following these steps, you can effectively track the execution time of specific methods in your Spring Boot application, allowing you to monitor performance and identify potential bottlenecks. For more information on visualizing these metrics, refer to the Visualizing Metrics with Actuator section.

Visualizing Metrics with Actuator

In this section, we will explore how to visualize the custom metrics using Spring Boot Actuator. By the end of this guide, you'll know how to access the Actuator endpoints and interpret the metrics data to monitor your application's performance effectively.

Step 1: Accessing Actuator Endpoints

To start visualizing metrics, you need to access the Actuator endpoints. These endpoints provide various metrics and monitoring information about your Spring Boot application.

  1. Ensure Actuator is Enabled: Make sure that Spring Boot Actuator is included in your project dependencies and properly configured. You can refer to the Setting Up Dependencies section for more details.

  2. Access the Endpoints: Open your web browser and navigate to the following URL:

    http://localhost:8080/actuator
    

    This will display a list of available Actuator endpoints.

Step 2: Viewing Custom Metrics

Once you have accessed the Actuator endpoints, you can view the custom metrics you have created.

  1. Navigate to the Metrics Endpoint: In the list of Actuator endpoints, find and click on the /metrics endpoint. This endpoint provides detailed information about various metrics collected by your application.

  2. Find Your Custom Metrics: Look for the custom metrics you have defined. For example, if you have created a custom performance tracker for method execution time, you should see an entry related to that metric.

  3. Interpret the Metrics Data: The metrics data is usually presented in JSON format. Here’s an example of how the data might look:

    {
      "name": "custom.performance.tracker",
      "measurements": [
        {
          "statistic": "count",
          "value": 10
        },
        {
          "statistic": "totalTime",
          "value": 5000.0
        }
      ],
      "availableTags": []
    }
    
    • name: The name of the metric.
    • measurements: An array containing the statistics for the metric.
      • statistic: The type of statistic (e.g., count, totalTime).
      • value: The value of the statistic.
    • availableTags: Any tags associated with the metric.

Step 3: Using Additional Tools for Visualization

While the Actuator endpoints provide raw metrics data, you might want to use additional tools for better visualization and monitoring. Here are a few options:

  1. Prometheus and Grafana: These tools can be integrated with Spring Boot Actuator to collect, store, and visualize metrics in a more user-friendly way.

  2. Spring Boot Admin: This is another tool that provides a web interface for monitoring and managing Spring Boot applications.

Conclusion

By following these steps, you can easily visualize and interpret custom metrics using Spring Boot Actuator. This is crucial for monitoring the performance and health of your application. For more information on setting up dependencies and enabling Actuator endpoints, refer to the Setting Up Dependencies and Enabling Actuator Endpoints sections.

Conclusion

In this blog post, we explored the robust observability features provided by Spring Boot 3, focusing on monitoring method performance and tracking custom metrics. Here are the key takeaways:

  1. Setting Up Dependencies: We began by setting up the necessary dependencies for our Spring Boot application, ensuring that we have all the tools needed for effective observability.

  2. Enabling Actuator Endpoints: We enabled various actuator endpoints that are crucial for monitoring and managing our application. These endpoints provide valuable insights into the application's health and performance.

  3. Creating a Custom Performance Tracker Handler: We created a custom performance tracker handler to monitor the execution time of methods. This handler is essential for identifying performance bottlenecks and optimizing code.

  4. Registering the Handler: We registered our custom handler within the Spring context, making it active and ready to track performance metrics.

  5. Tracking Method Execution Time: We demonstrated how to track the execution time of specific methods, providing a detailed view of where time is being spent within the application.

  6. Visualizing Metrics with Actuator: Finally, we visualized the collected metrics using Actuator, enabling us to make informed decisions based on real-time data.

By leveraging these features, developers can gain deep insights into their application's performance, quickly identify and address issues, and ultimately deliver a more reliable and efficient product. Spring Boot 3's observability tools are invaluable for maintaining high standards of performance and reliability in modern applications.

For more details, you can revisit the sections on Setting Up Dependencies, Enabling Actuator Endpoints, Creating a Custom Performance Tracker Handler, Registering the Handler, Tracking Method Execution Time, and Visualizing Metrics with Actuator.

Made with VideoToPage.com