🏠Spring BootStartup Actuator Endpoint in Spring Boot

Startup Actuator Endpoint in Spring Boot

This post will learn more about the new Startup Actuator endpoint and how we can use it to optimize the startup time of Spring Boot applications.

Spring Boot startup sequence is a complex process. We can speed the Spring Boot application startup by tracking the application startup events. Till now we didn’t have an easier way to do this. However, Spring Boot version 2.4.0 adds support to instrument startup events.

Introduction to Startup Actuator

Before getting into this post. I want to make sure that you need to have an understanding of Spring Boot Actuators. A typical application startup contains three major parts.

  1. The lifecycle of the application context itself. (reading config, classes etc)
  2. Bean creation, evaluation and the post processing.
  3. Event processing from the application itself.

You can find the list of startup steps in this official spring documentation. By tracking these steps using Startup Actuator Endpoints, we can pinpoint which part of the application is dragging down the application start up time. We can even identify which beans take time and probably fix them. Spring Boot achieves this functionality via the newly introduced ApplicationStartup implementations.

The ApplicationStartup interface comes with three implementation variants.

Class NameDescription
DefaultApplicationStartupno-op implementation that is configured by default
BufferingApplicationStartupAn im-memory buffered implementation for capturing startup steps
FlightRecorderApplicationStartupAn implementation that forwards captured steps as events to the java flight recorder

Using any of the above except the default implementation would require few lines of code. We will see the examples later in this post.

We don’t have anything to speak about default implementation. Because of this implementation there just tas a placeholder. The Buffering and FlightRecorder implementations have their space. Let’s discuss each of them in detail.

Configure Startup Actuator endpoint

Remember that the application startup is made of a set of steps. When these events are fired, the Application startup can track them and provide the metric in an easily understandable format. A buffering implementation is a straightforward approach for that. This implementation is an in-memory solution that keeps a configured number of events in memory. This information is drawn out using the startup actuator endpoint or can be read and processed within the application. Here is a simple example.

@SpringBootApplication public class ActuatorStartupExampleApplication { public static void main(String[] args) { SpringApplication app = new SpringApplication(ActuatorStartupExampleApplication.class); app.setApplicationStartup(new BufferingApplicationStartup(2048)); app.run(args); } }
Code language: JavaScript (javascript)

Along with the above change, you need to enable the management endpoint for the startup to see the results. To do this add the following configuration to your property file.

Code language: PHP (php)

If you do this right, sending a POST request to http://localhost:8080/actuator/startup will result in the event information.

Spring Boot Actuator for Startup trace information

Understanding the event information from the response takes a little effort, but it is not difficult. Every event entry has the following format.

{ "startupStep": { "name": "spring.beans.instantiate", "id": 23, "parentId": 6, "tags": [ { "key": "beanName", "value": "org.springframework.context.annotation.internalCommonAnnotationProcessor" }, { "key": "beanType", "value": "interface org.springframework.beans.factory.config.BeanPostProcessor" } ] }, "startTime": "2020-11-14T07:28:51.245122900Z", "endTime": "2020-11-14T07:28:51.247944100Z", "duration": "PT0.0028212S" }
Code language: JSON / JSON with Comments (json)

It has timing details and how the events arrange themselves in a tree structure using parentId and id. The tag information tells additional information about the startup step.

IMPORTANT: The Actuator Endpoint is only available for use if it is enabled along with BufferingApplicationStartup. Otherwise, the StartupEndpointAutoConfiguration will not configure a StartupEndpoint.

Collect stats with flight-recorder

In my opinion, BufferingApplicationStartup is more than enough to test things locally. But understanding these events can get a little frustrating on our own. Thus, Spring Boot provides yet another implementation for startup tracking. This implementation uses Java Flight Recorder event logging as its storage.

This ApplicationStartup tracking implementation forwards all the event details in the form of JFR event logs. If we run our application with Flight Recorder profiling enabled, we can see the events starting to show up under the events category. Let’s test this ourself with by modifying one line in the above example.

app.setApplicationStartup(new FlightRecorderApplicationStartup());
Code language: JavaScript (javascript)

Also, note that this implementation requires at least Oracle JDK 9 or OpenJDK 8.u262. So make sure your JDK setup is right for this. Once all the above are set, you can call the application using flight recorder profiling as shown below.

$ java -XX:+FlightRecorder -XX:StartFlightRecording:filename=recording.jfr,duration=10s -jar actuator-startup-example-0.0.1-SNAPSHOT.jar

Once you run the application, there will be a recording.jfr in your current working directory. You can use the Java Mission Control. Here is a sample screenshot.

Flight recorder screen with Application startup information

The Event Browser can show appropriate data for the startup steps. In Event Types Tree, navigate to Spring Application > Startup Step. This would give the same information as the previous BufferingApplicationStartup implementation.

Things to consider

Here are some important points to consider.

The startup endpoint is not for all

Using BufferingApplicationStartup will activate a StartupEndpoint. If you are planning to use Default or FlightRecorder implementations, then the StartupEndpointAutoConfiguration will not activate. If you are planning to use a custom implementation then extend the BufferingApplicationStartup.

FlightRecorderApplicationStartup and JDK version

The FlightRecorderApplicationStartup is directly dependent on the jdk.jfr package. Oracle JDK has had this package only since java 9. Java 8 has had JFR support only since Update 262. So don’t be afraid if you are getting compile errors for jdk.jfr packages.

Filtering Events

BufferingApplicationStartup has a fixed capacity for events and these events are stored in memory. This means a large number of events are not healthy for the application’s performance. So filtering out the events you are interested in might be a good idea. For this reason, BufferingApplicationStartup comes with an addFilter(Predicate<StartupStep> filter) method that takes a predicate to match which steps to record.

For example, If you only need to see the events for bean creation, the setup would look like below.

@SpringBootApplication public class ActuatorStartupExampleApplication { public static void main(String[] args) { SpringApplication app = new SpringApplication(ActuatorStartupExampleApplication.class); BufferingApplicationStartup applicationStartup = new BufferingApplicationStartup(2048); applicationStartup.addFilter(startupStep -> startupStep.getName().startsWith("spring.beans.instantiate")); app.setApplicationStartup(applicationStartup); app.run(args); } }
Code language: JavaScript (javascript)

The above filtering would make sure that only the steps with the name spring.beans.instantiate. The same can be confirmed by doing a POST request to /actuator/startup endpoint. You can further add more predicates to narrow down the list of steps. Also, you have to note that this implementation is currently available only for BufferingApplicationStartupFlightRecorderApplicationStartup forwards any step data to the .JFR files so the application is not affected in any way.

Instrumenting in production doesn’t make sense

Let’s be honest. If you are a developer working for a large corporation, you may have an instrumentation system like Dynatrace or Wily introscope. So it is not worth using another library just for startup performance data. So it is a good idea to only implement them for test environments.

Even if you choose to instrument your application, there is a problem. The implementation would require code changes to the application. Because we cannot cheat and make two builds for testing and production. In this scenario, Using a command-line argument or a system property to conditionally load the appropriate startup type would make sense. Here is my version of this implementation.

@SpringBootApplication public class ActuatorStartupExampleApplication { public static void main(String[] args) { SpringApplication app = new SpringApplication(ActuatorStartupExampleApplication.class); String startupType = System.getProperty("app.startup.implementation.type", ""); if ("BUFFERING".equals(startupType)) { BufferingApplicationStartup applicationStartup = new BufferingApplicationStartup(2048); applicationStartup.addFilter(startupStep -> startupStep.getName().startsWith("spring.beans.instantiate")); app.setApplicationStartup(applicationStartup); } else if ("JFR".equals(startupType)) { app.setApplicationStartup(new FlightRecorderApplicationStartup()); } else { app.setApplicationStartup(ApplicationStartup.DEFAULT); } app.run(args); } }
Code language: JavaScript (javascript)

With the above setup, you can use the same spring boot build with different recording implementations.


Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *