Spring Boot Default Indicators

Learn more about Spring Boot default metrics and their sources.

Have you noticed all the default metrics generated by Spring Boot and Mirometer for your application?If not - you can add an actuator dependency to your project and click on the / actuator / metrics endpoint, where you'll find useful information about JVM, processes, Tomcat, traffic, and so on.Then add some caches, data sources, or JPA dependencies, and even more metrics.This article is intended for you if you want to know how they ended and where we can find explanations for the parameters they describe.

Display Indicators

To keep it organized, let's start by showing metrics in the Spring Boot application.If you already know, you can skip this part.

Indicators in Spring Boot are handled by micrometer.io.However, if you use an actuator, you do not need to add a micrometer dependency to the project, because the actuator already depends on it.Even if you're not interested in the endpoints it provides, you'd also like to use actuator, as this is a module that registers many metrics through its AutoConfigurations.We'll discuss it in more detail later.

Therefore, first, simply add the executor dependency to the project (here build.gradle.kts)

dependencies {
    implementation("org.springframework.boot:spring-boot-starter-actuator")
}

And display the indicator name in the executor endpoint, Click http://localhost:8080/actuator/metrics.

{
  "names": [
    "jvm.threads.states",
    "process.files.max",
    "jvm.memory.used",
    "jvm.gc.memory.promoted",
    "jvm.memory.max",
    "system.load.average.1m",
    ...
  ]
}

Then, to see the details, add the indicator name to the URL path, for example: http://localhost:8080/actuator/metrics/system.cpu.count.

{
  "name": "system.cpu.count",
  "description": "The number of processors available to the Java virtual machine",
  "baseUnit": null,
  "measurements": [
    {
      "statistic": "VALUE",
      "value": 8
    }
  ],
  "availableTags": [
  ]
}

By providing a specific gauge registry, these metrics can be sent periodically to the indicator system of your choice (Prometheus, New Relic, CloudWatch, Graphite, and so on).Let's do it with the simplest registry, LoggingMeterRegistry, which just periodically records all the metrics.

@Configuration
class MetricsConfig {
    @Bean
    LoggingMeterRegistry loggingMeterRegistry() {
        return new LoggingMeterRegistry();
    }
}

Indicators are now also shown in the log:

2019-07-17 11:07:09.406  INFO 91283 --- [trics-publisher] i.m.c.i.logging.LoggingMeterRegistry     : jvm.buffer.count{id=direct} value=0 buffers
2019-07-17 11:07:09.406  INFO 91283 --- [trics-publisher] i.m.c.i.logging.LoggingMeterRegistry     : jvm.buffer.count{id=mapped} value=0 buffers
2019-07-17 11:07:09.406  INFO 91283 --- [trics-publisher] i.m.c.i.logging.LoggingMeterRegistry     : jvm.buffer.memory.used{id=direct} value=0 B
2019-07-17 11:07:09.406  INFO 91283 --- [trics-publisher] i.m.c.i.logging.LoggingMeterRegistry     : jvm.buffer.memory.used{id=mapped} value=0 B
2019-07-17 11:07:09.408  INFO 91283 --- [trics-publisher] i.m.c.i.logging.LoggingMeterRegistry     : jvm.classes.loaded{} value=8530 classes
2019-07-17 11:07:09.408  INFO 91283 --- [trics-publisher] i.m.c.i.logging.LoggingMeterRegistry     : jvm.gc.live.data.size{} value=0 B
2019-07-17 11:07:09.408  INFO 91283 --- [trics-publisher] i.m.c.i.logging.LoggingMeterRegistry     : jvm.gc.max.data.size{} value=0 B
2019-07-17 11:07:09.410  INFO 91283 --- [trics-publisher] i.m.c.i.logging.LoggingMeterRegistry     : jvm.memory.committed{area=nonheap,id=Compressed Class Space} value=6.25 MiB
2019-07-17 11:07:09.410  INFO 91283 --- [trics-publisher] i.m.c.i.logging.LoggingMeterRegistry     : jvm.memory.committed{area=heap,id=G1 Eden Space} value=168 MiB
...

Indicator Supply

So how do you provide these metrics?One example might be WebMvcMetricsFilter, which adds performance metrics (http.server.requests metric) to all Spring Web MVC endpoints.

But the example is simple.When all requests are processed by the Spring framework, it is not necessary to add call generation metrics internally (just check the WebMvcMetricsFilter.record() method).

But what happens if you use pure ehcache or hibernate or other data sources and generate metrics?

So what about the cache. * metric, even if I @Autowired net.sf.ehcache.Cache?

So what about the hibernate. * metric, even if I @Autowired just org.hibernate.SessionFactory?

Then, how can jvm. *, process. *, tomcat. * and so on be generated automatically?

It seems simpler than people think because these statistics are provided by the monitored components themselves.Sometimes it will be provided directly, such as cache.getStatistics() providing StatisticsGateway *, * or sessionFactory.getStatistics() providing Statistics for Hibernate SessionFactory, and so on.

Sometimes this can be done in other ways, such as managed bean s.For example, use RuntimeMXBean for JVM process. * metrics and Tomcat mbeans for tomcat. * metrics (such as GlobalRequestProcessor, Servlet, etc.)

To access these statistics and translate them into specific metrics, Micrometer introduced the concept of MeterBinder.

Check the MeterBinder implementation hierarchy to learn more about the available indicator groups.

Micrometer MeterBinders

You can also check directly on the micrometer repo.

Open, for example, EhCache2Metrics, and you'll find how and what the Ehcache statistics map to a specific Micrometer metric.

cache.size -> StatisticsGateway:getSize cache.gets{result=miss} -> StatisticsGateway:cacheMissCount cache.gets{result=hit} -> StatisticsGateway:cacheHitCount cache.puts -> StatisticsGateway:cachePutCount cache.evictions -> StatisticsGateway:cacheEvictedCount cache.remoteSize -> StatisticsGateway::getRemoteSize cache.removals -> StatisticsGateway::cacheRemoveCount cache.puts.added{result=added} -> StatisticsGateway::cachePutAddedCount cache.puts.added{result=updated} -> StatisticsGateway::cachePutAddedCount cache.misses{reason=expired} -> StatisticsGateway::cacheMissExpiredCount) cache.misses{reason=notFound} -> StatisticsGateway::cacheMissNotFoundCount) cache.xa.commits{result=readOnly} -> StatisticsGateway::xaCommitReadOnlyCount cache.xa.commits{result=exception} -> StatisticsGateway::xaCommitExceptionCount cache.xa.commits{result=committed} -> StatisticsGateway::xaCommitCommittedCount cache.xa.rollbacks{result=exception} -> StatisticsGateway::xaRollbackExceptionCount cache.xa.rollbacks{result=success} -> StatisticsGateway::xaRollbackSuccessCount cache.xa.recoveries{result=nothing} -> StatisticsGateway::xaRecoveryNothingCount cache.xa.recoveries{result=success} -> StatisticsGateway::xaRecoveryRecoveredCount cache.local.offheap.size -> StatisticsGateway::getLocalOffHeapSize) cache.local.heap.size -> StatisticsGateway::getLocalHeapSizeInBytes cache.local.disk.size -> StatisticsGateway::getLocalDiskSizeInBytes

Registering MeterBinders is very simple, and examples can be found in the micrometer documentation.

Remember, you can do it manually:

new ClassLoaderMetrics().bindTo(registry);
new JvmMemoryMetrics().bindTo(registry);
new EhCache2Metrics(cache, Tags.of("name", cache.getName())).bindTo(registry)
new TomcatMetrics(manager, tags).bindTo(registry)
...

Or you can use Spring Boot, which does this for you under the engine.

As I mentioned earlier, actuator will provide many AutoConfigurations and Metrics Binders, and it will register MeterBinders whenever a given dependency is added.

For example, TomcatMetrics Binder will register TomcatMetrics as your embedded container.MeterRegistryConfigurer will register the JVM, run time, and other system metrics.

Now, suppose you want to use Ehcache in your application.You can add two dependencies:

    implementation("org.springframework.boot:spring-boot-starter-cache")
    implementation("net.sf.ehcache:ehcache")

Then register the cache (you can also do this with ehcache.xml)

  @Bean
    Cache playCache(EhCacheCacheManager cacheManager) {
        CacheConfiguration cacheConfiguration = new CacheConfiguration()
            .name(CACHE_NAME)
            .maxEntriesLocalHeap(MAX_ELEMENTS_IN_MEMORY);
        Cache cache = new Cache(cacheConfiguration);
        cacheManager.getCacheManager().addCache(cache);
        cacheManager.initializeCaches();
        return cache;
    }

CacheMetrics RegistrarConfiguration will now register EhCache2Metrics for each cache management via the Spring Cache Manager.

If you don't want to use Spring cache management, you can also register EhCache2Metrics yourself.

Now, start the application and you'll see the other ehcache metrics.

2019-07-17 13:08:45.113  INFO 93052 --- [trics-publisher] i.m.c.i.logging.LoggingMeterRegistry     : cache.gets{cache=playCache,cacheManager=cacheManager,name=playCache,result=hit} throughput=12.95/s
2019-07-17 13:08:45.124  INFO 93052 --- [       Thread-4] i.m.c.i.logging.LoggingMeterRegistry     : cache.misses{cache=playCache,cacheManager=cacheManager,name=playCache,reason=notFound} throughput=3.7/s
2019-07-17 13:08:45.124  INFO 93052 --- [trics-publisher] i.m.c.i.logging.LoggingMeterRegistry     : cache.gets{cache=playCache,cacheManager=cacheManager,name=playCache,result=miss} throughput=3.7/s
2019-07-17 13:08:48.840  INFO 93052 --- [       Thread-4] i.m.c.i.logging.LoggingMeterRegistry     : cache.puts{cache=playCache,cacheManager=cacheManager,name=playCache} throughput=16.65/s
2019-07-17 13:08:48.840  INFO 93052 --- [trics-publisher] i.m.c.i.logging.LoggingMeterRegistry     : cache.misses{cache=playCache,cacheManager=cacheManager,name=playCache,reason=notFound} throughput=3.7/s
2019-07-17 13:08:48.841  INFO 93052 --- [trics-publisher] i.m.c.i.logging.LoggingMeterRegistry     : cache.puts{cache=playCache,cacheManager=cacheManager,name=playCache} throughput=16.65/s
2019-07-17 13:08:48.841  INFO 93052 --- [       Thread-4] i.m.c.i.logging.LoggingMeterRegistry     : cache.puts.added{cache=playCache,cacheManager=cacheManager,name=playCache,result=updated} throughput=0.116667/s
2019-07-17 13:08:48.841  INFO 93052 --- [trics-publisher] i.m.c.i.logging.LoggingMeterRegistry     : cache.puts.added{cache=playCache,cacheManager=cacheManager,name=playCache,result=updated} throughput=0.116667/s
2019-07-17 13:08:48.841  INFO 93052 --- [       Thread-4] i.m.c.i.logging.LoggingMeterRegistry     : cache.puts.added{cache=playCache,cacheManager=cacheManager,name=playCache,result=added} throughput=0.116667/s
2019-07-17 13:08:48.842  INFO 93052 --- [trics-publisher] i.m.c.i.logging.LoggingMeterRegistry     : cache.puts.added{cache=playCache,cacheManager=cacheManager,name=playCache,result=added} throughput=0.116667/s
2019-07-17 13:08:48.847  INFO 93052 --- [trics-publisher] i.m.c.i.logging.LoggingMeterRegistry     : cache.local.disk.size{cache=playCache,cacheManager=cacheManager,name=playCache} value=0 B
2019-07-17 13:08:48.847  INFO 93052 --- [       Thread-4] i.m.c.i.logging.LoggingMeterRegistry     : cache.local.disk.size{cache=playCache,cacheManager=cacheManager,name=playCache} value=0 B
2019-07-17 13:08:48.908  INFO 93052 --- [       Thread-4] i.m.c.i.logging.LoggingMeterRegistry     : cache.local.heap.size{cache=playCache,cacheManager=cacheManager,name=playCache} value=1.039062 KiB
2019-07-17 13:08:48.908  INFO 93052 --- [trics-publisher] i.m.c.i.logging.LoggingMeterRegistry     : cache.local.heap.size{cache=playCache,cacheManager=cacheManager,name=playCache} value=1.039062 KiB
2019-07-17 13:08:48.909  INFO 93052 --- [trics-publisher] i.m.c.i.logging.LoggingMeterRegistry     : cache.local.offheap.size{cache=playCache,cacheManager=cacheManager,name=playCache} value=0 B
2019-07-17 13:08:48.909  INFO 93052 --- [       Thread-4] i.m.c.i.logging.LoggingMeterRegistry     : cache.local.offheap.size{cache=playCache,cacheManager=cacheManager,name=playCache} value=0 B
2019-07-17 13:08:48.909  INFO 93052 --- [       Thread-4] i.m.c.i.logging.LoggingMeterRegistry     : cache.remoteSize{} value=0
2019-07-17 13:08:48.909  INFO 93052 --- [trics-publisher] i.m.c.i.logging.LoggingMeterRegistry     : cache.remoteSize{} value=0
2019-07-17 13:08:48.909  INFO 93052 --- [       Thread-4] i.m.c.i.logging.LoggingMeterRegistry     : cache.size{cache=playCache,cacheManager=cacheManager,name=playCache} value=7
2019-07-17 13:08:48.909  INFO 93052 --- [trics-publisher] i.m.c.i.logging.LoggingMeterRegistry     : cache.size{cache=playCache,cacheManager=cacheManager,name=playCache} value=7

In this case, the responsibilities of each component in the indicator context can be summarized as follows:

Ehcache Indicator Architecture

You can see all these concepts in the sample applications provided here.

Spring Essential Series (2)-AOP

Type Passing Problem in Java

Simple implementation of Java blocking queues

Essential Series of Spring (1) --Dependent Injection

java craftsmanship - elegant handling of null values

Security Base of REST API

Deep shallow CAS

Spring Boot Devtools Hot Deployment

Spring Boot AOP records user action logs

Spring Boot Integration Mongo DB

[Explanation of illustrations] You must be able to understand the HTTPS principle analysis!

For basic interviews, why do interviewers always like to ask String?

Spring Boot Admin 2.2.0 released to support the latest Spring Boot/Cloud in addition to the new Chinese display!

You should know the @ConfigurationProperties note usage posture, this is enough

This article is published by blog OpenWrite Release!

Tags: Programming Spring jvm Ehcache Tomcat

Posted on Sat, 04 Jan 2020 07:19:16 -0800 by sp0rk