How it works...

A few words about Spring Boot Admin Web before we delve into the details of creating and emitting metrics. It is a simple web GUI that, in the backend, uses the same endpoints exposed by Spring Boot Actuator, which we learned about in the previous recipe. The data is extracted from the application as we click on the various links in Admin Web and displayed in a nice graphical way—no magic!

We only had to configure a few properties in addition to adding the client library dependency in order to get our application to connect and register with Admin Web:

  • spring.application.name=BookPub Catalog Application: This configures the name of the application that we have chosen to use. It is also possible to take the value from the description property defined in gradle.properties using the resource processing task in Gradle. Admin Web uses this value when displaying the application list.
  • spring.boot.admin.client.url=http://localhost:8090: This configures the location of the Admin Web application so that our application knows where to go in order to register itself. As we are running on port 8080, we chose to configure Admin Web to listen on port 8090, but any port can be chosen as desired. You can see more configuration options by visiting https://codecentric.github.io/spring-boot-admin/current/.

If we also want to enable the logging level control through the UI, we will need to add a Jolokia JMX library to our compile("org.jolokia:jolokia-core:+") build dependency as well as a logback.xml file in the src/main/resources directory in the root of the project with the following content:

<configuration> 
  <include 
resource="org/springframework/boot/logging/logback/base.xml"/>
<jmxConfigurator/> </configuration>

The metrics facility in Spring Boot is very powerful and extendable, offering a number of different approaches for emitting and consuming metrics. Starting with Spring Boot 2.0, the Micrometer.io library is being used under the hood to provide a very comprehensive monitoring solution. Out of the box, Spring Boot already configures a number of data metrics that monitor the system resources, such as heap memory, thread counts, system uptime, and many others as well as the database usage and HTTP session counts. The MVC endpoints are also instrumented to gauge the request latency, which is measured in milliseconds, as well as a counter for each endpoint request status.

Various metrics, such as gauges, counters, timers, and so on, are emitted via the MeterRegistry implementation that is provided by Spring Boot at runtime. The registry can be easily autowired into any Spring-managed object and be used to emit metrics.

For example, we can easily count the number of times a particular method gets invoked. All we need to do is to autowire an instance of MeterRegistry into our object during creation, and place the following line at the beginning of the method:

meterRegistry.counter("objectName.methodName.invoked").increment();

Each time the method gets called, the particular metric count will be incremented.

This approach will give us the counts that we can increment, but if we want to measure latency or any other arbitrary value, we will need to use Gauge to submit our metrics. To measure how long it will take for our method to execute, we can use MeterRegistry and at the beginning of the method, record the time:

long start = System.currentTimeMillis();

We will then place our code and before the return, capture the time again:

long end = System.currentTimeMillis();.

Then, we will emit the metric meterRegistry.gauge("objectName.methodName.latency", end - start);, which will update the last. The use of gauge for timing purposes is very rudimentary and MeterRegistry actually provides a specialized type of meter—Timer. The Timer meter, for example, provides the ability to wrap runnable or callable lambdas and automatically time the execution. Another benefit of using a Timer instead of Gauge is that a Timer meter keeps both the event counts as well as the latency it took to execute each occurrence.

The MeterRegistry implementation covers most of the simple use cases and is very handy when we operate in our own code and have the flexibility to add them where we need to. However, it is not always the case, and in these cases, we will need to resort to wrapping whatever it is we want to monitor by creating a custom implementation of MeterBinder. In our case, we will use it to expose the counts for each of the repositories in the database as we can't insert any monitoring code into the CrudRepository proxy implementations.

Whenever the MeterRegistry implementation does not provide enough flexibility, for example, when there is a need to wrap an object in a meter like Gauge, most meter implementations provide fluid builders to gain more flexibility. In our example, to wrap the repository metrics, we used a Gauge fluid builder to construct Gauge:

Gauge.builder(metricName, repository, CrudRepository::count)

The main builder method takes the following three arguments:

  • metricName: This specifies the name to use to uniquely identify this metric
  • repository: This provides an object on which we invoke the method that should return a numeric value that gauge will report
  • CrudRepository::count: This is the method that should be called on the repository object to get the current count of entries

This enables us to build flexible wrappers because all we have to do is provide an object that would expose the necessary numeric value and a function reference to a function that should be called on the instance to get that value during the gauge evaluation.

The MeterBinder interface, used to export the Meter, has only one method defined,
void bindTo(MeterRegistry);, which the implementer needs to code with the definition of what exactly is being monitored. The implementation class needs to be exposed as @Bean, and it will automatically be picked up and processed during the application initialization. Assuming that one actually registered the created Meter instance with the provided MeterRegistry implementation, typically by terminating the fluid builder's chain by calling .builder(...).register(registry), the metrics will be exposed via MetricsEndpoint, which will expose all the meters registered with the registry every time the /metrics actuator is called.

It is important to mention that we have created the MeterBinder and HealthIndicator beans inside the main application context and not in the management one. The reason being that even though the data is being exposed via the management endpoints, the endpoint beans, such as MetricsEndpoint, get defined in the main application context, and thus expect all the other autowired dependencies to be defined there as well.

This approach is safe because in order to get access to the information, one needs to go through the WebMvcEndpointHandlerMapping implementation facade, which is created in the management context and use the delegate endpoint from the main application context. Take a look at the MetricsEndpoint class and the corresponding @Endpoint annotation to see the details.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.139.82.23