Search This Blog

Thursday, July 11, 2019

Spring Boot-Actuators


Introduction

Spring Boot ships with a module called actuator which enables things like metrics and statistics about your application. For example, we can collect logs, view metrics, perform thread dumps,
show environment variables, understand garbage collection, and show what beans are configured in the BeanFactory. You can expose this information via HTTP, JMX, or you can even log in directly to the process via SSH.

Features

  • Endpoints Actuator endpoints allow you to monitor and interact with your application. Spring Boot includes a number of built-in endpoints and you can also add your own. For example the health endpoint provides basic application health information. Run up a basic application and look at /actuator/health.
  • Metrics Spring Boot Actuator provides dimensional metrics by integrating with Micrometer
  • Audit Spring Boot Actuator has a flexible audit framework that will publish events to an AuditEventRepository. Once Spring Security is in play it automatically publishes authentication events by default. This can be very useful for reporting, and also to implement a lock-out policy based on authentication failures.
To add the actuator to a Maven based project, add the following ‘Starter’ dependency:
<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

For Gradle, use the following declaration:
dependencies {
    compile("org.springframework.boot:spring-boot-starter-actuator")
}
That’s it? Yes! You have now added the actuator to your existing application. When you restart your application, endpoints are enabled for the user access.

Actuator Endpoints


Spring Boot includes a number of built-in endpoints and lets you add your own. For example, the health endpoint provides basic application health information.
Each individual endpoint can be enabled or disabled. This controls whether or not the endpoint is created and its bean exists in the application context. To be remotely accessible an endpoint also has to be exposed via JMX or HTTP. Most applications choose HTTP, where the ID of the endpoint along with a prefix of /actuator is mapped to a URL. For example, by default, the health endpoint is mapped to /actuator/health.
Here are some of the most common endpoints Boot provides out of the box:
/health – Shows application health information (a simple ‘status’ when accessed over an
unauthenticated connection or full message details when authenticated). It is not sensitive by
default.
/info – Displays arbitrary application info. Not sensitive by default.
/metrics – Shows ‘metrics’ information for the current application. It is also sensitive by default.
/trace – Displays trace information (by default the last few HTTP requests). You can find the
full list of existing endpoints over on the official docs.

Enabling Endpoint

By default, all endpoints except for shutdown are enabled. To configure the enablement of an endpoint, use its management.endpoint.<id>.enabled property. The following example enables the shutdown endpoint:
management.endpoint.shutdown.enabled=true
If you prefer endpoint enablement to be opt-in rather than opt-out, set the management.endpoints.enabled-by-default property to false and use individual endpoint enabled properties to opt back in.
management.endpoints.enabled-by-default=false
management.endpoint.info.enabled=true
Since Endpoints may contain sensitive information so we should be very careful while enabling the endpoints. To change which endpoints are exposed, use the following technology-specific include and exclude properties:
                Property                                    Default
management.endpoints.jmx.exposure.exclude
management.endpoints.jmx.exposure.include                       *
management.endpoints.web.exposure.exclude
management.endpoints.web.exposure.include                 info, health

Customizing Endpoint

Each endpoint can be customized with properties in application.properties file , using following format:
endpoints. [endpoint name].[property to customize]

Three properties are available:
id – by which this endpoint will be accessed over HTTP
enabled – if true then it can be accessed otherwise not
sensitive – if true then need the authorization to show crucial information over HTTP.

For example, add the following properties will customize the /beans endpoint:
endpoints.beans.id=springbeans
endpoints.beans.sensitive=false
endpoints.beans.enabled=true

Customizing Health Endpoint : 

The /health endpoint is used to check the health/status of the running application. It’s usually used by basic monitoring software to alert you if the production goes down.
By default only health information is shown to unauthorized access over HTTP:
{
"status" : "UP"
}
This health information is collected from all the beans implementing HealthIndicator interface configured in your application context. Some information returned by HealthIndicator is sensitive in nature – but you can configure endpoints.health.sensitive=false to expose the other information like disk space, data source etc.
You can also roll your own custom health indicator. One can extend the HealthIndicator interface and provide their own implementation. CustomHealthCheck is implementing the method health() which is declared in the interface HealthIndicator. When you create a custom class of this type and override the method health(), the default functionality will be overwritten by your custom logic.
@Component
public class HealthCheckEndpoint implements HealthIndicator {
  
     
    /* (non-Javadoc)
     * @see org.springframework.boot.actuate.health.HealthIndicator#health()
     */
    @Override
    public Health health() {
        Health.Builder builder = new Health.Builder();
        Health health = null;
        if(isRemoteServiceUp()){
            health = builder.up().withDetail("MyApp-Services""online").build();
        }else {
            health = builder.down().withDetail("MyApp-Services""offline").build();
        }
        return health;
    }
  
    /**
     * Checks if is remote service up.
     *
     * @return true, if is remote service up
     */
    private boolean isRemoteServiceUp(){ 
        ApplicationHealthIndicator applicationHealthIndicator = new ApplicationHealthIndicator();
        // perform call out to remote service to check if its up
        DataSourceHealthIndicator dataSourceHealthIndicator = new DataSourceHealthIndicator();
        return Status.UP.equals(applicationHealthIndicator.health().getStatus()) &&
                Status.UP.equals(dataSourceHealthIndicator.health().getStatus());
    }
}

Customizing Application Info Endpoint:

We can also configure information about Spring boot application which is being developed. It needs few configurations -
info.app.name=@project.name@
info.app.description=@project.description@
info.app.version=@project.version@
info.app.encoding=@project.build.sourceEncoding@
info.app.java.version=@java.version@

And we call this endpoint using below url which will produce the output like mentioned in code block.
http://<url:port>/actuator/info
{
    "app": {
        "name""MyApp",
        "description""MyApp SERVICE",
        "version""1.0.2",
        "encoding""UTF-8",
        "java": {
            "version""1.8.0_201"
        }
    }
}

Creating Custom Endpoint:

Sometimes we need to get few specific metrics which are not provided by Spring boot actuators as default metrics. So for this purpose we can develop it using annotation @Endpoint . 
Its really simple to create metrics for your application as your business needs. The detail syntax to create your own metrics is 
@Component
@Endpoint(id = "service.count")
public class ApplicationMetricsEndpoint {
    /** The counter. */
    private Counter counter;
     
    /**
     * Instantiates a new application metrics endpoint.
     *
     * @param registry the registry
     */
    public ApplicationMetricsEndpoint(MeterRegistry registry) {
        this.counter = registry.counter("service.count");
    }
     
    /**
     * Count service.
     *
     * @return the string
     * @throws JSONException
     */
    @ReadOperation
    public String countService() throws JSONException {
        counter.increment();
        JSONObject metricData = new JSONObject();
        metricData.put(Constants.TOTAL_COUNT, counter.count());
        return metricData.toString();
    }
     
}

Integration with Prometheus

With Spring boot 2.0, adding Prometheus support to Spring boot became a lot easier thanks to the integration of Micrometer. Micrometer can be compared to what slf4j does for logging, but for monitoring in stead. It provides a clean API that can be accessed, and has a bridge to many monitoring platforms, including Prometheus.
To be able to monitor our application within Spring boot using third party tool, we need to add the following dependencies:
<dependency>
   <groupId>io.micrometer</groupId>
   <artifactId>micrometer-registry-prometheus</artifactId>
</dependency>

Now you can configure Actuator to expose the Prometheus endpoint by configuring the following property within application.properties:

management.endpoints.web.exposure.include=prometheus

If you run your application now, and you visit - http://<host>:<port no>/actuator
{
    "_links": {
        "self": {
            "href""http://localhost:8081/actuator",
            "templated"false
        },
        "service.count": {
            "href""http://localhost:8081/actuator/service.count",
            "templated"false
        },
        "service.count.clients": {
            "href""http://localhost:8081/actuator/service.count.clients",
            "templated"false
        },
        "service.count.queries": {
            "href""http://localhost:8081/actuator/service.count.queries",
            "templated"false
        },
        "service.count.grants": {
            "href""http://localhost:8081/actuator/service.count.grants",
            "templated"false
        },
        "service.count.requests": {
            "href""http://localhost:8081/actuator/service.count.requests",
            "templated"false
        },
        "service.count.requests.timebased": {
            "templated"false
        },
        "health": {
            "href""http://localhost:8081/actuator/health",
            "templated"false
        },
        "shutdown": {
            "href""http://localhost:8081/actuator/shutdown",
            "templated"false
        },
        "flyway": {
            "href""http://localhost:8081/actuator/flyway",
            "templated"false
        },
        "info": {
            "href""http://localhost:8081/actuator/info",
            "templated"false
        },
        "prometheus": {
            "href""http://localhost:8081/actuator/prometheus",
            "templated"false
        },
        "metrics-requiredMetricName": {
            "templated"true
        },
        "metrics": {
            "href""http://localhost:8081/actuator/metrics",
            "templated"false
        },
        "httptrace": {
            "href""http://localhost:8081/actuator/httptrace",
            "templated"false
        }
    }
}

Prometheus has to be configured separately, by creating a prometheus.yml file. In this case, I’m going to set up Prometheus so that it will scan the following locations:
● Our Spring boot application
● Prometheus itself
● The Grafana web application
Just we need to add prometheus.yml file into our boot project in resources with following configurations
prometheus.yml
# my global config
global:
  scrape_interval:     15s # Set the scrape interval to every 15 seconds. Default is every 1 minute.
  evaluation_interval: 15s # Evaluate rules every 15 seconds. The default is every 1 minute.
  # scrape_timeout is set to the global default (10s).
# Load rules once and periodically evaluate them according to the global 'evaluation_interval'.
rule_files:
  # - "first_rules.yml"
  # - "second_rules.yml"
# A scrape configuration containing exactly one endpoint to scrape:
# Here it's Prometheus itself.
scrape_configs:
  # The job name is added as a label `job=<job_name>` to any timeseries scraped from this config.
  - job_name: 'prometheus'
    # metrics_path defaults to '/metrics'
    # scheme defaults to 'http'.
    static_configs:
    - targets: ['127.0.0.1:9090']
  - job_name: 'MyApp-actuator'
    metrics_path: '/actuator/prometheus'
    scrape_interval: 5s
    static_configs:
    - targets: ['127.0.0.1:8081']
Once we restart our boot application after these changes , the metrics which has been configured in the application , will be published to prometheus . Prometheus usually pulls the data on some scrape interval basis. 

Grafana dashboard

We can also display these metrics on Grafana, just we need to configure datasource and add the job name which we configured for prometheus.