《Spring Boot in Action》【7. Actuator】

Keywords: Spring Java MongoDB Attribute

7. Actuator

To enable Actuator, you just add in build.gradle:

compile 'org.springframework.boot:spring-boot-starter-actuator'

Spring Boot Actuator provides a series of RESTful interfaces:

HTTP method Route describe
GET /beans All bean s in Spring application context and their dependencies
GET /autoconfig Automatic configuration report, which automatic configuration is valid or invalid
GET /env Various environmental attributes
GET /env/{name} Retrieving environment attributes by name
GET /configprops Various configuration attributes
GET /mappings Mapping URI Path to Controller
GET /metrics Various indicators
GET /metrics/{name} Name-based index retrieval
GET /trace Recent 100 HTTP request tracking (data in memory)
GET /dump Snapshot of thread activity
GET /health Application of health indicators, provided by the implementation of the Health Indicator interface
POST /shutdown Close application
GET /info Provide custom information about applications

These information can be divided into three categories: configuration, metrics and miscellaneous. For security, any name or last part of a name is shown as an attribute of "password", "secret" or "key".

7.1 Configuration Information

Including / beans, / autoconfig, / env, / env/{name}, / configprops, / mappings

7.2 Indicator Information

The / metrics interface returns various metrics, categorized by prefix:

classification prefix describe
garbage collector gc.* mark-sweep and scavenge garbage collectors garbage collection count and duration (from java.lang.management.Garbage Collector MXBean)
Memory mem.* Memory allocated to applications and free memory (from java.lang.Runtime)
heap heap.* Current memory usage (from java.lang.management.MemoryUsage)
classloader classes.* Number of classes loaded and unloaded by the JVM class loader (from java.lang.management.ClassLoading MXBean)
system processors,uptime,instance.uptime,systemload.average Number of processors (from java.lang.Runtime), run time (from java. lang. management. Runtime MXBean), average system load (from java. lang. management. Operating System MXBean)
Thread pool threads.* Threads, number of daemon threads, and peak threads (from java.lang.management.ThreadMXBean) since JVM startup
data source datasource.* Number of data source connections (from the data source's metadata, only if there is a data source bean)
HTTP session httpsessions.* Active and maximum number of sessions (from the embedded Tomcat bean, only if there is a built-in server)
HTTP counter.status.,gauge.response. Various measures and statistics of HTTP requests

The/health interface returns various health indicators:

Health indicator Key Reports
ApplicationHealthIndicator none Always "UP"
DataSourceHealthIndicator db "UP" and database type if the database can be contacted; "DOWN" status otherwise
DiskSpaceHealthIndicator diskSpace "UP" and available disk space, and "UP" if available space is above a threshold; "DOWN" if there isn't enough disk space
JmsHealthIndicator jms "UP" and JMS provider name if the message broker can be contacted; "DOWN" otherwise
MailHealthIndicator mail "UP" and the mail server host and port if the mail server can be contacted; "DOWN" otherwise
MongoHealthIndicator mongo "UP" and the MongoDB server version; "DOWN" otherwise
RabbitHealthIndicator rabbit "UP" and the RabbitMQ broker version; "DOWN" otherwise
RedisHealthIndicator redis "UP" and the Redis server version; "DOWN" otherwise
SolrHealthIndicator solr "UP" if the Solr server can be contacted; "DOWN" otherwise

Suppose you need to shut down your application, such as in a microservice architecture, and you need to gracefully shut down one of the application instances, you can do this:

curl -X POST http://localhost:8080/shutdown

Obviously, closing an application is a dangerous behavior, so / shutdown is not enabled by default. You need to set endpoints.shutdown.enabled to true to open, and you should pay attention to the permissions of the / shutdown interface.

The / Info interface returns information about the application and the empty JSON object ({}) is returned by default. You add more returns by configuring the properties of the info header:

info:
  contact:
    email: support@myreadinglist.com
    phone: 1-888-555-1971

7.3 Remote Shell

Spring Boot integrates CRaSH (a shell built into Java applications) and provides a series of commands, starting with dependencies:

compile("org.springframework.boot:spring-boot-starter-remote-shell")

Then, when applied, you can use SSH to connect to it (port 2000). The password is the random password printed in the log.

ssh user@localhost -p 2000

You can use the commands provided by Spring Boot: autoconfig, beans, metrics (you can see dynamic updates, Ctrl-C exits), but not every Web interface has a corresponding command, at this time you need the endpoint command:

endpoint list

This returns the endpoint list, but not url s, but their bean name s:

requestMappingEndpoint
environmentEndpoint
healthEndpoint
beansEndpoint
infoEndpoint
metricsEndpoint
traceEndpoint
dumpEndpoint
autoConfigurationReportEndpoint
configurationPropertiesReportEndpoint

Then use the endpoint invoke command (remove the "Endpoint" suffix):

endpoint invoke health

7.4 Customization

7.4.1 Modify endpoint IDs

Each Actuator endpoint has an ID to determine the path. For example, the default ID of / beans is beans. You can modify the path by modifying the endpoints.endpoint-id.id attribute, such as / shutdown to / kill:

endpoints:
  shutdown:
    id: kill

7.4.2 Disable (Enable) endpoints

You can also disable some endpoint s:

endpoints:
  metrics:
    enabled: false

You can disable all and enable some:

endpoints:
  enabled: false
  metrics:
    enabled: true

7.4.3 Add custom metrics and gauges

For example, we need to record how many times the user saved the book to the reading list, and how long it last saved, and expose it through / metrics. Actuator automatically configures a CounterService instance for us to help complete the counting function:

package org.springframework.boot.actuate.metrics;

public interface CounterService {
  void increment(String metricName);
  void decrement(String metricName);
  void reset(String metricName);
}

Another example of GaugeService helps set a value for an indicator:

package org.springframework.boot.actuate.metrics;

public interface GaugeService {
  void submit(String metricName, double value);
}

You just need to inject the two bean s and call the method directly:

@RequestMapping(method = RequestMethod.POST)
public String addToReadingList(Reader reader, Book book) {

  book.setReader(reader);
  readingListRepository.save(book);

  counterService.increment("books.saved");
  gaugeService.submit("books.last.saved", System.currentTimeMillis());

  return "redirect:/";
}

For a slightly more complex metrics, the two services above are not enough. At this point, you can implement the PublicMetrics interface:

package org.springframework.boot.actuate.endpoint;

public interface PublicMetrics {
  Collection<Metric<?>> metrics();
}

For example, we need to know when application context starts, the number of beans, the number of beans defined, and the number of beans annotated @Controller:

package readinglist;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.actuate.endpoint.PublicMetrics;
import org.springframework.boot.actuate.metrics.Metric;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;

@Component
public class ApplicationContextMetrics implements PublicMetrics {

  @Autowired
  private ApplicationContext context;

  @Override
  public Collection<Metric<?>> metrics() {
    List<Metric<?>> metrics = new ArrayList<Metric<?>>();
    metrics.add(new Metric<Long>("spring.context.startup-date", context.getStartupDate()));
    metrics.add(new Metric<Integer>("spring.beans.definitions", context.getBeanDefinitionCount()));
    metrics.add(new Metric<Integer>("spring.beans", context.getBeanNamesForType(Object.class).length));
    metrics.add(new Metric<Integer>("spring.controllers", context.getBeanNamesForAnnotation(Controller.class).length));
    return metrics;
  }
}

Then access / metrics and display our custom metrics:

{
  ...
  spring.context.startup-date: 1429398980443,
  spring.beans.definitions: 261,
  spring.beans: 272,
  spring.controllers: 2,
  books.count: 1,
  gauge.books.save.time: 1429399793260,
  ...
}

spring.controllers are 2 because the Basic Error Controller provided by ReadingListController and Spring Book is counted.

7.4.4 Add custom trace repository

By default, the / trace interface uses memory repository with a capacity of 100. We can declare the InMemoryTraceRepository bean ourselves, such as setting the capacity to 1000:

package readinglist;

import org.springframework.boot.actuate.trace.InMemoryTraceRepository;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class ActuatorConfig {

  @Bean
  public InMemoryTraceRepository traceRepository() {
    InMemoryTraceRepository traceRepo = new InMemoryTraceRepository();
    traceRepo.setCapacity(1000);
    return traceRepo;
  }

}

Although the capacity has increased, it still consumes memory and has no persistence, so we can implement Spring Boot's TraceRepository interface ourselves:

package org.springframework.boot.actuate.trace;

import java.util.List;
import java.util.Map;

public interface TraceRepository {
  List<Trace> findAll();
  void add(Map<String, Object> traceInfo);
}

For example, the implementation saves to MongoDB:

package readinglist;
import java.util.Date;
import java.util.List;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.actuate.trace.Trace;
import org.springframework.boot.actuate.trace.TraceRepository;
import org.springframework.data.mongodb.core.MongoOperations;
import org.springframework.stereotype.Service;

@Service
public class MongoTraceRepository implements TraceRepository {

  @Autowired
  private MongoOperations mongoOps;

  @Override
  public List<Trace> findAll() {
    return mongoOps.findAll(Trace.class);
  }

  @Override
  public void add(Map<String, Object> traceInfo) {
    mongoOps.save(new Trace(new Date(), traceInfo));
  }
}

As long as you join the starter of MongoDB, you will automatically configure the MongoOperations bean:

compile("org.springframework.boot:spring-boot-starter-data-mongodb")

7.4.5 Custom Health Indicators

For example, we need to add a health indicator to test whether Amazon can access:

package readinglist;
import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;

@Component
public class AmazonHealth implements HealthIndicator {

  @Override
  public Health health() {
    try {
      RestTemplate rest = new RestTemplate();
      rest.getForObject("http://www.amazon.com", String.class);
      return Health.up().build();
    } catch (Exception e) {
      return Health.down().build();
    }
  }
}

If you need more detailed information, you can call the withDetail() method, such as changing the code in catch to:

return Health.down().withDetail("reason", e.getMessage()).build();

7.5 Actuator Interface Security

The weighting limit for Actuator paths is the same as that for general paths, but it is obviously troublesome to list all interfaces once with antMatchers, so a common prefix can be configured:

management:
  context-path: /mgmt

Then add in the configure() method in SecurityConfig.java:

.antMatchers("/mgmt/**").access("hasRole('ADMIN')")

Posted by jasondavis on Mon, 17 Dec 2018 13:03:03 -0800