Actuator in Spring boot 2.0

In a previous article, I wrote a small introduction to Actuator in Spring boot 1. With Spring boot 2 now released, Actuator got a new boost with many new features and updates. While it has been redesigned and new edpoints have been added, Actuator kept its fundamental intent, but simplifies its model, extends its capabilities and incorporate better defaults.

Also, if you're considering upgrading to 2.x Actuator, it’s important to keep in mind that some changes are breaking. The Spring team put together a super neat migration guide.

Micrometer

Spring Boot 2.0 no longer ships with its own metrics APIs.
The in-house metrics were replaced with Micrometer support. This means that Micrometer is now part of Actuator’s dependencies. Hence, you should be good to go as long as the Actuator dependency is in the classpath.

Furthermore, Metrics now can be exported to a wide range of systems and out-of-the box Spring Boot 2.0 provides support for Atlas, Datadog, Ganglia, Graphite, Influx, JMX, New Relic, Prometheus, SignalFx, StatsD and Wavefront.

/metrics endpoint

If you've used Actuator in Spring boot 1.x, the /metrics endpoint traditionally return some metrics. Now in 2.x Actuator, there are no actual metrics, instead it displays a list of available meter names:

{
"names": [
"jvm.buffer.memory.used",
"jvm.memory.used",
"jvm.gc.memory.allocated",
"jvm.memory.committed",
"tomcat.sessions.created",
"tomcat.sessions.expired",
"tomcat.global.request.max",
"tomcat.global.error",
...
  ]
}

In order to get the actual value of a specific metric, we have to drill down to view information about a particular meter by providing its name. For example, we should hit /actuator/metrics/jvm.memory.max to get a detailed response:

{
   "name":"jvm.memory.max",
   "measurements":[
      {
         "statistic":"VALUE",
         "value":5597298687
      }
   ],
   "availableTags":[
      {
         "tag":"area",
         "values":[
            "heap",
            "nonheap"
         ]
      },
      {
         "tag":"id",
         "values":[
            "Compressed Class Space",
            "PS Survivor Space",
            "PS Old Gen",
            "Metaspace",
            "PS Eden Space",
            "Code Cache"
         ]
      }
   ]
}

This offer much more highlights and details! love it!

Actuator Endpoints

As in version 1.x, Spring Boot Actuator 2.x includes a number of built-in endpoints and lets you add your own. One big difference you'll notice is, in order make an actuator endpoint available via HTTP and/or JMX, it needs to be both enabled and exposed.

Below is a list of predefined endpoints in 2.x Actuator:

  • auditevents: Exposes audit events information for the current application.
  • beans: Displays a complete list of all the Spring beans in your application.
  • conditions: Shows the conditions that were evaluated on configuration and auto-configuration classes and the reasons why they did or did not match.
  • configprops: Displays a collated list of all @ConfigurationProperties.
  • env: Exposes properties from Spring’s ConfigurableEnvironment.
  • flyway: Shows any Flyway database migrations that have been applied.
  • health: Shows application health information.
  • httptrace: Displays HTTP trace information (by default, the last 100 HTTP request-response exchanges).
  • info: Displays arbitrary application info.
  • loggers: Shows and modifies the configuration of loggers in the application.
  • liquibase: Shows any Liquibase database migrations that have been applied.
  • metrics: Shows metrics information for the current application.
  • mappings: Displays a collated list of all @RequestMapping paths.
  • scheduledtasks: Displays the scheduled tasks in your application.
  • sessions: Allows retrieval and deletion of user sessions from a Spring Session-backed session store. Not available when using Spring Session’s support for reactive web applications.
  • shutdown: Lets the application be gracefully shutdown. It's the only endpoint that's not enable by default.
  • threaddump: Performs a thread dump.

As you might have noticed, some endpoints have been added, some removed and some have been restructured.

Enhancing Endpoint Security

As mentioned above, Spring Boot 2.0 takes a slightly different approach to ensure web endpoints security. The majority of Web endpoints are now disabled by default (Only the /health and /info endpoints are exposed) and the management.security.enabled property has been removed. There is no longer a separate security auto-configuration for the Actuator, individual endpoints may be enabled/disabled and/or exposed via configuration in the application.properties file. For example:

# disable beans endpoint  
management.endpoints.beans.enabled=false
# expose all endpoints:
management.endpoints.web.exposure.include=*

Custom Endpoints

In 1.x we had the ability to create our very own custom endpoints. Spring Boot Actuator 2.x has redesigned the way to achieve this by introducing @Enpoint annotation, which significantly simplified the process of creating user-defined endpoints.

The example below add a active-users endpoint to track active users:

@Component
@Endpoint(id="active-users")
public class ActiveUsersEndpoint {

    private final Map<String, User> users = new HashMap();

    ActiveUsersEndpoint() {
        this.users.put("Fero", new User("Kebbour Benani Smiras"));
        this.users.put("Weld", new User("Chaabia Mert Kebour"));
        this.users.put("Hero", new User("Habib Saheb Kebbour"));
    }

    @ReadOperation
    public List getAll() {
        return new ArrayList(this.users.values());
    }

    @ReadOperation
    public User getActiveUser(@Selector String user) {
        return this.users.get(user);
    }

    @WriteOperation
    public void updateActiveUser(@Selector String name, String user) {
        this.users.put(name, new User(user));
    }

    @DeleteOperation
    public void deleteActiveUser(@Selector String name) {
        this.users.remove(name);
    }

    public static class User {
        private String name;

        User(String name) {
            this.name = name;
        }

        public String getName() {
            return this.name;
        }

        public void setName(String name) {
            this.name = name;
        }
    }
}

This endpoint exposes 4 methods defined with the @ReadOperation, @WriteOperation and @DeleteOperation annotations, which will map to GET, POST and DELETE HTTP methods respectively.
Note that the path of our endpoint is determined by the id parameter of @Endpoint, which in our case will route requests to /actuator/active-users. Like any other 2.x Actuator endpoint, it'll be enabled and exposed on JMX by default, but not on HTTP. We can also enable or disable the endpoint using @Endpoint(id = “active-users”, enableByDefault = false).

[
   {
      "name":"Habib Saheb Kebbour"
   },
   {
      "name":"Kebbour Benani Smiras"
   },
   {
      "name":"Chaabia Mert Kebour"
   }
]

See, No more dependencies with MVC nor extending an interface anymore! We can also write technology-specific endpoints by using @JmxEndpoint or @WebEndpoint.

Jersey and WebFlux Support

In addition to Spring MVC and JMX support, 2.x Actuator can now access actuator endpoints when developing pure Jersey or WebFlux applications.

Extending Endpoints

We can also extend existing Actuator endpoints by using @EndpointWebExtension or @EndpointJmxExtension and write technology specific enhancements. For example the HealthEndpointWebExtension class overrides the main ReadOperation and change the HTTP responses of the /health endpoint based on the computed Health.

@Component
@EndpointWebExtension(endpoint = HealthEndpoint.class)
public class HealthEndpointWebExtension {

    private HealthEndpoint healthEndpoint;
    private HealthStatusHttpMapper statusHttpMapper;

// Constructor

    @ReadOperation
    public WebEndpointResponse<Health> health() {
        Health health = this.healthEndpoint.health();
        Integer status = getStatus(health);
        return new WebEndpointResponse<>(health, status);
    }

    private Integer getStatus(Health health) {
        return this.statusHttpMapper.mapStatus(health.getStatus());
    }
}