Spring Boot Actuators: Expose Operational Info
Tags: spring, java, springboot
Published: 2022-07-10 12:08:38

Spring boot has gained a lot of popularity with time, because of its provided features. and now this framework offers solutions for a wide range of enterprise problems and patterns and fits best into every need, It is bundled with a large number of auto configurable libraries/modules to reduce the boiler plate and allow you to focus on your business logic.

Spring boot Actuator is one of the popular feature of Spring Boot for creating production ready applications. Actuator exposes set of endpoints to monitor your application health, view metrics, secure them with spring security and many other features that are necessary for you to declare your application production ready.

###Features of Spring Boot Actuator

1. Spring Boot Actuator module allows you to monitor your applications in 2 ways; either by leveraging HTTP endpoints or [JMX](https://docs.spring.io/spring-boot/docs/2.1.1.RELEASE/reference/html/production-ready-jmx.html) (Java Management Extension). Most of the applications leverage HTTP endpoints.
2. **Endpoints:** Actuator module is bundled with a rich set of HTTP endpoints to monitor and interact with the application. You can even create custom endpoints to fulfil your needs, and also leverage spring-security to secure actuator endpoints.
Few examples are
   - info
   - health
   - metrics
   - beans
   - caches
   - loggers
   - mappings
3. **Metrics:** Spring Boot Actuator provides support for a instrumentation library, called **Micrometer**, which is a **vendor neutral application metrics facade** that captures and exposes application metrics to different monitoring solutions such as Prometheus, Dynatrace, New Relic, DataDog and many more... It provides interfaces for **timers, gauges, counters, distribution summaries, and long task timers** with a dimensional data model.
4. Actuator module is highly (yet simply) configurable and you can utilise your application.[properties|yaml] file to meet most of your customised needs.

Let's build a spring boot application and witness the magic of spring-boot-actuator in action...

###Pre-Requisites:
1. Java installed on your system (JDK 17 recommended), Try [SDKMAN](https://sdkman.io/install) to manage your JDK versions.
2. Spring project with starter-web dependency.
```{bash}
$ curl -G https://start.spring.io/starter.zip \
    -d type=gradle-project \
    -d language=java \
    -d packaging=jar \
    -d javaVersion=17 \
    -d dependencies=web \
    -d name=spring-boot-actuator-example \
    -d baseDir=spring-boot-actuator-example \
    -o starter.zip
$ # You can specify more options using -d option; like groupId, artifactId, packageName
$ unzip starter.zip
$ # Open project in your preferred IDE
```

###Adding Spring Boot Actuator to your application
Add 'spring-boot-starter-actuator' dependency in your build configuration file.
For maven users:
```xml
<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
```
For gradle users:
```gradle
implementation 'org.springframework.boot:spring-boot-starter-actuator'
```

As soon as you add starter-actuator dependency in your classpath and restart the application (after rebuilding), you can see actuator endpoints are enabled by spring boot. By default, **'/actuator'** is a base-path for your all actuator endpoints.
```log
INFO 9143 --- [           main] i.c.s.SpringbootActuatorsApplication     : No active profile set, falling back to 1 default profile: "default"
INFO 9143 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8080 (http)
INFO 9143 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
INFO 9143 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet engine: [Apache Tomcat/9.0.64]
INFO 9143 --- [           main] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
INFO 9143 --- [           main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 648 ms
INFO 9143 --- [           main] o.s.b.a.e.web.EndpointLinksResolver      : Exposing 1 endpoint(s) beneath base path '/actuator'
INFO 9143 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8080 (http) with context path ''
INFO 9143 --- [           main] i.c.s.SpringbootActuatorsApplication     : Started SpringbootActuatorsApplication in 1.271 seconds (JVM running for 1.818)
INFO 9143 --- [on(1)-127.0.0.1] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring DispatcherServlet 'dispatcherServlet'
INFO 9143 --- [on(1)-127.0.0.1] o.s.web.servlet.DispatcherServlet        : Initializing Servlet 'dispatcherServlet'
INFO 9143 --- [on(1)-127.0.0.1] o.s.web.servlet.DispatcherServlet        : Completed initialization in 1 ms
```

The application is started on port 8080, Let's hit **'/actuator'** endpoint to see all the available actuator endpoints...
![SpringBoot Actuator Endpoint](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/v32emkedz9mwisjoiypd.png)
 
As you can observe there is only **'/actuator/health'** endpoint is exposed, By default all the other endpoints are disabled, because of security reasons, Since some of the actuator endpoints can expose sensitive data, So be careful...

Let's enable all the endpoints...Update your 'application.properties' file by setting 'management.endpoints.web.exposure.include' property
```properties
management.endpoints.web.exposure.include=*
```
**'*'** pattern will enable all the endpoints exposed by actuator module. But it is recommended to expose a specific set of endpoints (comma separated list) which is necessary for your operational requirement to avoid any security issues. 
NOTE: 'shutdown' endpoint will still be disabled since it is highly critical for your application. It is used to gracefully shutdown your application.
Let's try by hitting 'http://localhost:8080/actuator'
```json
{
   "_links" : {
      "beans" : {
         "href" : "http://localhost:8080/actuator/beans",
         "templated" : false
      },
      "caches" : {
         "href" : "http://localhost:8080/actuator/caches",
         "templated" : false
      },
      "caches-cache" : {
         "href" : "http://localhost:8080/actuator/caches/{cache}",
         "templated" : true
      },
      "conditions" : {
         "href" : "http://localhost:8080/actuator/conditions",
         "templated" : false
      },
      "configprops" : {
         "href" : "http://localhost:8080/actuator/configprops",
         "templated" : false
      },
      "configprops-prefix" : {
         "href" : "http://localhost:8080/actuator/configprops/{prefix}",
         "templated" : true
      },
      "env" : {
         "href" : "http://localhost:8080/actuator/env",
         "templated" : false
      },
      "env-toMatch" : {
         "href" : "http://localhost:8080/actuator/env/{toMatch}",
         "templated" : true
      },
      "health" : {
         "href" : "http://localhost:8080/actuator/health",
         "templated" : false
      },
      "health-path" : {
         "href" : "http://localhost:8080/actuator/health/{*path}",
         "templated" : true
      },
      "heapdump" : {
         "href" : "http://localhost:8080/actuator/heapdump",
         "templated" : false
      },
      "info" : {
         "href" : "http://localhost:8080/actuator/info",
         "templated" : false
      },
      "loggers" : {
         "href" : "http://localhost:8080/actuator/loggers",
         "templated" : false
      },
      "loggers-name" : {
         "href" : "http://localhost:8080/actuator/loggers/{name}",
         "templated" : true
      },
      "mappings" : {
         "href" : "http://localhost:8080/actuator/mappings",
         "templated" : false
      },
      "metrics" : {
         "href" : "http://localhost:8080/actuator/metrics",
         "templated" : false
      },
      "metrics-requiredMetricName" : {
         "href" : "http://localhost:8080/actuator/metrics/{requiredMetricName}",
         "templated" : true
      },
      "scheduledtasks" : {
         "href" : "http://localhost:8080/actuator/scheduledtasks",
         "templated" : false
      },
      "self" : {
         "href" : "http://localhost:8080/actuator",
         "templated" : false
      },
      "threaddump" : {
         "href" : "http://localhost:8080/actuator/threaddump",
         "templated" : false
      }
   }
}
```
Take a look on this [spring documentation](https://docs.spring.io/spring-boot/docs/current/reference/html/actuator.html#actuator.endpoints) for detailed explanation of each endpoint.

*Some more configuration in your application.properties file:
```properties
#1. Update default port for actuator endpoints
management.server.port=2121

#2. Update base-path to use **'/manage'** instead of **'/actuator'**
management.endpoints.web.base-path=/manage

#3. Following will ensure include all actuator endpoints except 'mappings' and 'heapdump'
management.endpoints.web.exposure.include=*
management.endpoints.web.exposure.exclude=mappings,heapdump
```

###Health Endpoint
Health endpoint exposes your application's health to outside world (with useful data), This endpoint becomes more important in distributed systems, where liveness of your one service is dependent on another service like databases, queues or other applications.
Now, Let's try **'/actuator/health'** endpoint, and see the response
![/actuator/health](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ipr5q0ehf1knva4w8l1b.png)
 
Right now the only information being returned by health endpoint is whether the application is up or not, by default health endpoint does not show all of the details. This can be enabled (or securely enabled) by setting value of health.show-details attribute.
```properties
management.endpoint.health.show-details=always
# Possible values for health.show-details can be one of the following
# never|always|when_authorized
# by default it is set to 'never', 
# 'always' will display all the details,
# 'when_autorized' will display details when requesting user is authorized.
```
Output (health endpoint with status and diskspace):
![Health Endpoint with show-details](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2z5k0fddyefkeifcpnk1.png)

_Please note Spring Boot Actuator module support configuration update for each endpoint individually by using following pattern._
```properties
# Pattern: management.endpoint.{endpoint-name}.{property}=value
# property attribute can vary according to endpoint

management.endpoint.configprops.enabled=true
management.endpoint.info.enabled=false

management.endpoint.beans.cache.time-to-live=10s

management.endpoint.logfile.external-file=~/app.log
```

###Info Endpoint
Let's try **'/actuator/info'** endpoint now, and see what it returns.
![/actuator/info](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/s8cwuwsxu15psltmjiyw.png)

So, by default **info** endpoint does not return any useful information, You can customise the response of info endpoint by updating build configurations, Let's walk through how you can configure the info endpoint in build.gradle file.
Maven users can also follow this official [spring doc](https://docs.spring.io/spring-boot/docs/1.2.0.RC1/reference/html/production-ready-endpoints.html#production-ready-application-info-automatic-expansion-maven)

For gradle users, let's first update the build.gradle file, Add buildInfo task, and update version value.
```gradle
description = 'Spring Boot Actuator Configuration App'
version = '2.0.0'

springBoot {
    buildInfo() {
        properties {
            additional = [
                    'lastRelease': '1.0.0',
                    'description': rootProject.description
            ]
        }
    }
}
```
Apart from build info, You can also set additional information in application.properties file:
```properties
management.info.env.enabled=true

app.cusomInfo=This is Custom Info
info.app.cusomInfo=${app.cusomInfo}
info.app.java-version=${java.version}
info.app.java-vendor=${java.vendor}
info.app.excluded-actuator-enpoints=${management.endpoints.web.exposure.exclude}
```
Let's now see output of **'/actuator/info'** endpoint:
![/actuator/info additional info](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/grvz0sdq6inxcgvb3yuk.png)

You can observe all the configured information in response. The info endpoint can be extended further to expose git related info (like commit id, branch).

###Final Words:
As we can observe just by leveraging the application.[properties|yaml] file, we can fully customise and configure our actuator endpoints to expose useful information. In subsequent posts we will see how we can create custom endpoints, and connect to different monitoring tools using micrometer.

Source Code: [manojshr/springboot-actuators](https://github.com/manojshr/springboot-actuators)

Please share your thoughts in the comments!