Every program manager loves metrics. In fact, a popular company (Netflix) is so well known in this arena that people describe it as a metrics-gathering company that happens to stream video.
When it comes to Spring Boot, metrics are a prime piece of Spring Boot Actuator functionality. If we visit /application/metrics, we can see a list of metrics:
{
"names": [
"jvm.buffer.memory.used",
"jvm.memory.used",
"jvm.buffer.count",
"logback.events",
"process.uptime",
"jvm.memory.committed",
"http.server.requests",
"jvm.buffer.total.capacity",
"jvm.memory.max",
"process.starttime"
]
}
This lists all sorts of stuff--memory, garbage collection, heap versus nonheap, threads, and more. That's nice, but what's usually needed is the ability to create our own metrics.
Spring Boot provides an interface to register our own metrics and have them appear on the same page. Supplied immediately is the ability to grab a MeterRegistry.
To make use of this three meter registry, we need to inject it into ImageService we built in Chapter 3, Reactive Data Access with Spring Boot:
@Service public class ImageService { ... private final MeterRegistry meterRegistry; public ImageService(ResourceLoader resourceLoader, ImageRepository imageRepository, MeterRegistry meterRegistry) { this.resourceLoader = resourceLoader; this.imageRepository = imageRepository; this.meterRegistry = meterRegistry; } ...
This code shows the following:
- Three metric services, CounterService, GaugeService, and InMemoryMetricRepository declared as final attributes
- These three fields are populated by constructor injection, ensuring they are supplied when the service is created
With that in place, further down inside createImage, we can define custom metrics:
public Mono<Void> createImage(Flux<FilePart> files) { return files .log("createImage-files") .flatMap(file -> { Mono<Image> saveDatabaseImage = imageRepository.save( new Image( UUID.randomUUID().toString(), file.filename())) .log("createImage-save"); Mono<Void> copyFile = Mono.just(Paths.get(UPLOAD_ROOT,
file.filename()).toFile()) .log("createImage-picktarget") .map(destFile -> { try { destFile.createNewFile(); return destFile; } catch (IOException e) { throw new RuntimeException(e); } }) .log("createImage-newfile") .flatMap(file::transferTo) .log("createImage-copy"); Mono<Void> countFile = Mono.fromRunnable(() -> { meterRegistry
.summary("files.uploaded.bytes")
.record(Paths.get(UPLOAD_ROOT,
file.filename()).toFile().length()) }); return Mono.when(saveDatabaseImage, copyFile, countFile) .log("createImage-when"); }) .log("createImage-flatMap") .then() .log("createImage-done"); }
The first part of the code where a new image is created is the same, but following that is meterRegistry.summary("files.uploaded.bytes").record(… ), which creates a new distribution summary named files.uploaded.bytes. A distribution summary includes both a name, optional tags, and a value. What is registered is both a value and an occurrence. Each time a meter is added, it counts it, and the running total is tabulated.
With these adjustments, we can refresh the application, wait for it to reload, and then upload a few images, as shown here:
After uploading these images, if we revisit /application/metrics, we can see our new metric at the bottom of the list:
{ "names": [ "jvm.buffer.memory.used", "jvm.memory.used", "jvm.buffer.count", "logback.events", "process.uptime", "jvm.memory.committed", "http.server.requests", "jvm.buffer.total.capacity", "jvm.memory.max", "process.starttime", "files.uploaded.bytes" ] }
If we navigate to http://localhost:8080/application/metrics/files.uploaded.bytes, we can view it:
{ "name": "files.uploaded.bytes", "measurements": [ { "statistic": "Count", "value": 3.0 }, { "statistic": "Total", "value": 208020.0 } ], "availableTags": [ ] }
This JSON shows that three measurements have been registered with files.uploaded.bytes, totaling 208020 bytes. What's not immediately shown is also the time when these metrics were posted. It's possible to calculate upload trends using the new Micrometer module (http://micrometer.io).
This is but a sampling of the possible metrics that can be defined. Feel free to dig in and experiment with the data.