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
-
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.
- Navigate to the
-
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
- To enable all Actuator endpoints, add the following key:
-
Save and Close the File
- After adding the necessary keys, save the
application.properties
file and close it.
- After adding the necessary keys, save the
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.
-
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.
-
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.
-
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. -
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.
-
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:
-
Prometheus and Grafana: These tools can be integrated with Spring Boot Actuator to collect, store, and visualize metrics in a more user-friendly way.
-
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:
-
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.
-
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.
-
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.
-
Registering the Handler: We registered our custom handler within the Spring context, making it active and ready to track performance metrics.
-
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.
-
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.