Spring BootアプリのメトリクスをMicrometerで収集する #micrometer
Micrometer (micrometer-core)には、Javaでよく使われるライブラリ、ミドルウェア、JVM自身のメトリクスを収集するためのBinderと呼ばれるClassと、その設定を行うAutoConfigurationが含まれているので簡単にメトリクス収集を始める事ができます。
(ちなみに、MicrometerはJMXには依存してないので、JVMオプションでJMXを有効する必要はありません)
それらをいつくか紹介してみようと思います。
なお、この記事で使用しているバージョンは以下です。
(2018/05/23現在の最新)
- Spring Boot: 2.0.2 or 1.5.13
- Spring Boot
- spring-boot-starter-actuator
- Micrometer: 1.0.4
- micrometer-core
- micrometer-spring-legacy ※Spring Boot 1.5の場合は必須
- micrometer-registry-prometheus ※僕はモニタリングにPrometheusを使っています
Micrometerの標準Binderの紹介
まずは、Micrometerにどんな標準Binderが含まれているのかを紹介します。
Binderのソースを見てもらうとわかるように、Javaでよく使われるものがいくつかサポートされています。
例えば、
- ライブラリ(Logback, OkHttp3, GuavaCache, Caffeineなど)
- ミドルウェア(Tomcat, Jetty)
- JVM自身のメトリクス(GC, Thread, Heap Memory, Non-heap Memoryなど)
などなど。
そして、各Binderの設定を行うAutoConfigurationは、
Spring Boot 2.0の場合は、spring-boot-actuator-autoconfigure
の中にあります。
例えば、これら。
Spring Boot 1.5の場合は、micrometer-spring-legacy
の中です。
例えば、これら。
標準Binderが提供されていないライブラリやミドルウェアのメトリクスを収集したい場合は、標準BinderとAutoConfigurationのソースを参考にして自分で作ると良いでしょう。
実際にメトリクスを収集してみる
では次に、いつくかのメトリクスを実際に収集してみましょう。
※収集可能になるメトリクスを以下に例示していきますが、それらはPrometheusによってexportされるものです
ほとんどのメトリクスは spring-boot-starter-actuator
と micrometer-core
を追加するだけで収集可能になります。
(Spring Boot 1.5の場合は micrometer-spring-legacy
も)
また、他にも自分のモニタリングツールに合わせて micrometer-registry-prometheus
なども追加してください。
JVMのメトリクス
GCのメトリクス
- Minor GCの発生回数
- Minor GCの停止時間
- Major GCの発生回数
- Major GCの停止時間
などが収集されます。
# HELP jvm_gc_pause_seconds Time spent in GC pause
# TYPE jvm_gc_pause_seconds summary
jvm_gc_pause_seconds_count{action="end of minor GC",cause="Allocation Failure",} 14.0
jvm_gc_pause_seconds_sum{action="end of minor GC",cause="Allocation Failure",} 0.106
jvm_gc_pause_seconds_max{action="end of minor GC",cause="Allocation Failure",} 0.007
jvm_gc_pause_seconds_count{action="end of major GC",cause="System.gc()",} 4.0
jvm_gc_pause_seconds_sum{action="end of major GC",cause="System.gc()",} 0.481
jvm_gc_pause_seconds_max{action="end of major GC",cause="System.gc()",} 0.19
# HELP jvm_gc_concurrent_phase_time_seconds Time spent in concurrent phase
# TYPE jvm_gc_concurrent_phase_time_seconds summary
jvm_gc_concurrent_phase_time_seconds_count{action="end of major GC",cause="No GC",} 1.0
jvm_gc_concurrent_phase_time_seconds_sum{action="end of major GC",cause="No GC",} 0.337
jvm_gc_concurrent_phase_time_seconds_max{action="end of major GC",cause="No GC",} 0.0
Heap memoryのメトリクス
- Young Generation (Eden, Survivor)
- Old Generation
- Young Generation (Eden, Survivor)のMax
- Old GenerationのMax
などが収集されます。
# HELP jvm_memory_used_bytes The amount of used memory
# TYPE jvm_memory_used_bytes gauge
jvm_memory_used_bytes{area="heap",id="Par Eden Space",} 4177576.0
jvm_memory_used_bytes{area="heap",id="Par Survivor Space",} 87808.0
jvm_memory_used_bytes{area="heap",id="CMS Old Gen",} 3.3789184E7
# HELP jvm_memory_committed_bytes The amount of memory in bytes that is committed for the Java virtual machine to use
# TYPE jvm_memory_committed_bytes gauge
jvm_memory_committed_bytes{area="heap",id="Par Eden Space",} 8912896.0
jvm_memory_committed_bytes{area="heap",id="Par Survivor Space",} 1114112.0
jvm_memory_committed_bytes{area="heap",id="CMS Old Gen",} 5.6410112E7
# HELP jvm_memory_max_bytes The maximum amount of memory in bytes that can be used for memory management
# TYPE jvm_memory_max_bytes gauge
jvm_memory_max_bytes{area="heap",id="Par Eden Space",} 6.979584E7
jvm_memory_max_bytes{area="heap",id="Par Survivor Space",} 8716288.0
jvm_memory_max_bytes{area="heap",id="CMS Old Gen",} 4.39156736E8
Non-Heap memoryのメトリクス
- Code Cache
- Metaspace
- Compressed Class Space
- Code CacheのMax
- MetaspaceのMax
- Compressed Class SpaceのMax
などが収集されます。
# HELP jvm_memory_used_bytes The amount of used memory
# TYPE jvm_memory_used_bytes gauge
jvm_memory_used_bytes{area="nonheap",id="Code Cache",} 2.3145152E7
jvm_memory_used_bytes{area="nonheap",id="Metaspace",} 5.0919768E7
jvm_memory_used_bytes{area="nonheap",id="Compressed Class Space",} 6311672.0
# HELP jvm_memory_committed_bytes The amount of memory in bytes that is committed for the Java virtual machine to use
# TYPE jvm_memory_committed_bytes gauge
jvm_memory_committed_bytes{area="nonheap",id="Code Cache",} 2.3855104E7
jvm_memory_committed_bytes{area="nonheap",id="Metaspace",} 5.3735424E7
jvm_memory_committed_bytes{area="nonheap",id="Compressed Class Space",} 6811648.0
# HELP jvm_memory_max_bytes The maximum amount of memory in bytes that can be used for memory management
# TYPE jvm_memory_max_bytes gauge
jvm_memory_max_bytes{area="nonheap",id="Code Cache",} 2.5165824E8
jvm_memory_max_bytes{area="nonheap",id="Metaspace",} -1.0
jvm_memory_max_bytes{area="nonheap",id="Compressed Class Space",} 1.073741824E9
Threadのメトリクス
- Thread数
- Daemon Thread数
などが収集されます。
# HELP jvm_threads_live The current number of live threads including both daemon and non-daemon threads
# TYPE jvm_threads_live gauge
jvm_threads_live 33.0
# HELP jvm_threads_daemon The current number of live daemon threads
# TYPE jvm_threads_daemon gauge
jvm_threads_daemon 6.0
ClassLoaderのメトリクス
- loaded数
- unloaded数
# HELP jvm_classes_loaded The number of classes that are currently loaded in the Java virtual machine
# TYPE jvm_classes_loaded gauge
jvm_classes_loaded 9296.0
# HELP jvm_classes_unloaded_total The total number of classes unloaded since the Java virtual machine has started execution
# TYPE jvm_classes_unloaded_total counter
jvm_classes_unloaded_total 59.0
Tomcatのメトリクス
- ThreadPool busy
- ThreadPool current
- ThreadPool max
などが収集されます。
# HELP tomcat_threads_busy
# TYPE tomcat_threads_busy gauge
tomcat_threads_busy{name="http-nio-8080",} 0.0
# HELP tomcat_threads_current
# TYPE tomcat_threads_current gauge
tomcat_threads_current{name="http-nio-8080",} 10.0
# HELP tomcat_threads_config_max
# TYPE tomcat_threads_config_max gauge
tomcat_threads_config_max{name="http-nio-8080",} 200.0
Jettyのメトリクス
Requestのメトリクス
- 処理中のリクエスト
などが収集されます。
# HELP jetty_requests_active Number of requests currently active
# TYPE jetty_requests_active gauge
jetty_requests_active 0.0
Responseのメトリクス
- HTTP status毎のレスポンス数
# HELP jetty_responses_total Number of requests with response status
# TYPE jetty_responses_total counter
jetty_responses_total{status="1xx",} 0.0
jetty_responses_total{status="2xx",} 104.0
jetty_responses_total{status="3xx",} 0.0
jetty_responses_total{status="4xx",} 0.0
jetty_responses_total{status="5xx",} 0.0
Threadのメトリクス
JettyのThreadPoolのメトリクスを収集する標準Binderが無かったので(Micrometer 1.0.4現在)、僕の方で作成してPRしています。
まだマージされてませんが、ご参考までに。
https://github.com/micrometer-metrics/micrometer/pull/593
これにより、以下のメトリクスが収集できるようになります。
- ThreadPool busy
- ThreadPool current
- ThreadPool min
- ThreadPool max
# HELP jetty_threads_busy The current number of busy threads
# TYPE jetty_threads_busy gauge
jetty_threads_busy 2.0
# HELP jetty_threads_current The current number of current threads
# TYPE jetty_threads_current gauge
jetty_threads_current 4.0
# HELP jetty_threads_config_min The number of min threads
# TYPE jetty_threads_config_min gauge
jetty_threads_config_min 4.0
# HELP jetty_threads_config_max The number of max threads
# TYPE jetty_threads_config_max gauge
jetty_threads_config_max 100.0
HikariCPのメトリクス
- ConnectionPool Active
- ConnectionPool Pending
- ConnectionPool Idle
- ConnectionPool Total
- ConnectionPool Min
- ConnectionPool Max
などが収集されます。
# HELP hikaricp_connections_active Active connections
# TYPE hikaricp_connections_active gauge
hikaricp_connections_active{pool="HikariPool-1",} 0.0
# HELP hikaricp_connections_pending Pending threads
# TYPE hikaricp_connections_pending gauge
hikaricp_connections_pending{pool="HikariPool-1",} 0.0
# HELP hikaricp_connections_idle Idle connections
# TYPE hikaricp_connections_idle gauge
hikaricp_connections_idle{pool="HikariPool-1",} 10.0
# HELP hikaricp_connections Total connections
# TYPE hikaricp_connections gauge
hikaricp_connections{pool="HikariPool-1",} 10.0
# HELP hikaricp_connections_min Min connections
# TYPE hikaricp_connections_min gauge
hikaricp_connections_min{pool="HikariPool-1",} 10.0
# HELP hikaricp_connections_max Max connections
# TYPE hikaricp_connections_max gauge
hikaricp_connections_max{pool="HikariPool-1",} 10.0
余談ですが、HikariCPのメトリクス収集のロジックはHikariCP自身がMicrometerを使って実装しています。
ソースは以下。
Spring Boot 1.5の場合は話が少しややこしくて、micrometer-spring-legacy
も依存ライブラリに追加してもHikariCPのメトリクスは収集されません。
(Spring Boot 2.0を使っている方はここは読み飛ばしてください)
理由は、HikariCPがMicrometerを使ったメトリクス収集ロジックを実装したのが2.7.0
からで、Spring Boot 1.5は2.5.x
を使うようになっているからです。
そして、MicrometerでHikariCPのメトリクスが正しく収集出来るようになったのは2.7.9
からです。
FYI:
https://github.com/spring-projects/spring-boot/issues/12759
なので、pom.xml
やbuild.gradle
でHikariCPのバージョン(hikaricp.version
)を上書きして使うか(Spring Bootが指定したバージョンではないから問題が出るかも。自己責任で)、
以下のHikariCP設定をapplication.ymlに追加して 以下のようにHikariCPのJMX設定を行って、 *2018/05/24 修正
(設定内容が間違っていたので修正。application.ymlの設定例は紛らわしいので削除しました)
メトリクスをJMXで収集出来るようにすると良いでしょう。
僕が以前書いた記事も参照してください。
@Configuration
public class HikariCPConfig {
@Bean(destroyMethod = "close")
public DataSource dataSource(MBeanExporter mBeanExporter, DataSourceProperties properties) {
DataSource ds = DataSourceBuilder.create(properties.getClassLoader())
.type(HikariDataSource.class)
.driverClassName(properties.determineDriverClassName())
.url(properties.determineUrl())
.username(properties.determineUsername())
.password(properties.determinePassword())
.build();
// Configuration to monitor HikariCP metrics via JMX
HikariDataSource dataSource = (HikariDataSource) ds;
dataSource.setRegisterMbeans(true);
dataSource.setPoolName("dataSource");
mBeanExporter.addExcludedBean("dataSource");
return dataSource;
}
}
Prometheusを使っているのであれば、JMXでメトリクスを収集するのにjmx_exporterが使えます。
jmx_exporterのyamlファイルの例
lowercaseOutputName: true
whitelistObjectNames:
- com.zaxxer.hikari:*
rules:
- pattern: '^com.zaxxer.hikari<type=Pool \((\w+)\)><>ActiveConnections:'
name: hikaricp_pool_active_connections
labels:
source: $1
help: HikariCP Pool active connections $1
type: GAUGE
- pattern: '^com.zaxxer.hikari<type=Pool \((\w+)\)><>IdleConnections:'
name: hikaricp_pool_idle_connections
labels:
source: $1
help: HikariCP Pool idle connections $1
type: GAUGE
- pattern: '^com.zaxxer.hikari<type=Pool \((\w+)\)><>TotalConnections:'
name: hikaricp_pool_total_connections
labels:
source: $1
help: HikariCP Pool total connections $1
type: GAUGE
- pattern: '^com.zaxxer.hikari<type=Pool \((\w+)\)><>ThreadsAwaitingConnection:'
name: hikaricp_pool_threads_awaiting_connection
labels:
source: $1
help: HikariCP Pool threads awaiting connection(pending threads) $1
type: GAUGE
- pattern: '^com.zaxxer.hikari<type=PoolConfig \((\w+)\)><>MaximumPoolSize:'
name: hikaricp_pool_maximum_pool_size
labels:
source: $1
help: HikariCP PoolConfig maximum pool size $1
type: GAUGE
- pattern: '^com.zaxxer.hikari<type=PoolConfig \((\w+)\)><>MinimumIdle:'
name: hikaricp_pool_minimum_idle
labels:
source: $1
help: HikariCP PoolConfig minimum idle $1
type: GAUGE
jmx_exporter設定については公式ドキュメントを参照してください。
HTTPサーバとしてのレイテンシ
- アプリのURI毎のリクエスト処理回数
などが収集されます。
# HELP http_server_requests_seconds
# TYPE http_server_requests_seconds summary
http_server_requests_seconds_count{exception="None",method="GET",status="200",uri="/",} 4.0
http_server_requests_seconds_sum{exception="None",method="GET",status="200",uri="/",} 0.764760048
http_server_requests_seconds_count{exception="None",method="GET",status="200",uri="/**/favicon.ico",} 2.0
http_server_requests_seconds_sum{exception="None",method="GET",status="200",uri="/**/favicon.ico",} 0.022893644
# HELP http_server_requests_seconds_max
# TYPE http_server_requests_seconds_max gauge
http_server_requests_seconds_max{exception="None",method="GET",status="200",uri="/",} 0.620949991
http_server_requests_seconds_max{exception="None",method="GET",status="200",uri="/**/favicon.ico",} 0.021386207
さらに、以下のような設定をapplication.ymlに追加するとアプリのURI毎のレイテンシが取れます。
http.server.requests
の設定値はお好みでどうぞ。
application.yml
詳しくはmakingさんのブログを読んでください。
Spring Boot 1.5の場合は、僕が以前書いた記事も参照してください。
- Spring Boot 2とMicrometerでPrometheusを使うときの便利PromQLメモ
※僕は今のところ、management.metrics.distribution.percentiles
だけあれば十分だと思っているので、他は設定していません - Spring Boot 1.5.xでURI毎のレイテンシをモニタリングする設定
management:
metrics:
distribution:
percentiles:
http.server.requests: 0.5, 0.75, 0.95, 0.98, 0.99, 0.999, 1.0
アプリのURI毎のレイテンシのメトリクス
http_server_requests_seconds{exception="None",method="GET",status="200",uri="/",quantile="0.5",} 0.096468992
http_server_requests_seconds{exception="None",method="GET",status="200",uri="/",quantile="0.75",} 0.868220928
http_server_requests_seconds{exception="None",method="GET",status="200",uri="/",quantile="0.95",} 0.868220928
http_server_requests_seconds{exception="None",method="GET",status="200",uri="/",quantile="0.98",} 0.868220928
http_server_requests_seconds{exception="None",method="GET",status="200",uri="/",quantile="0.99",} 0.868220928
http_server_requests_seconds{exception="None",method="GET",status="200",uri="/",quantile="0.999",} 0.868220928
http_server_requests_seconds{exception="None",method="GET",status="200",uri="/",quantile="1.0",} 0.868220928
http_server_requests_seconds{exception="None",method="GET",status="200",uri="/**/favicon.ico",quantile="0.5",} 0.019922944
http_server_requests_seconds{exception="None",method="GET",status="200",uri="/**/favicon.ico",quantile="0.75",} 0.019922944
http_server_requests_seconds{exception="None",method="GET",status="200",uri="/**/favicon.ico",quantile="0.95",} 0.019922944
http_server_requests_seconds{exception="None",method="GET",status="200",uri="/**/favicon.ico",quantile="0.98",} 0.019922944
http_server_requests_seconds{exception="None",method="GET",status="200",uri="/**/favicon.ico",quantile="0.99",} 0.019922944
http_server_requests_seconds{exception="None",method="GET",status="200",uri="/**/favicon.ico",quantile="0.999",} 0.019922944
http_server_requests_seconds{exception="None",method="GET",status="200",uri="/**/favicon.ico",quantile="1.0",} 0.019922944
レイテンシに関する補足
上記のレイテンシはMicrometerのTimer
を使って計測されていて、Percentile設定は使う人がapplication.yml(もしくはapplication.properties)で自由に行えるようになっています。
公式ドキュメントは以下。
ソースで言うと、アプリケーションプロパティの設定値をTimerに設定しているのはPropertiesMeterFilter
というFilterです。
PropertiesMeterFilter
は全てのTimerに適用されます。
よって、自分がTimerを作る場合もPercentile設定はハードコーディングせずに、application.ymlで設定した方が良いと思います。
例
Timer.builder("my.timer")
// .publishPercentiles(...) ※ハードコーディングしない
.description("My Timer")
.register(registry);
management:
metrics:
distribution:
percentiles:
my.timer: 0.5, 0.75, 0.95, 0.98, 0.99, 0.999, 1.0 # my.timer は Timerの名前
ExecutorServiceのメトリクス
別の記事として追記しました
まとめ
- Micrometerには、Javaでよく使われるライブラリ、ミドルウェア、JVM自身のメトリクスを収集するためのBinderが含まれているので簡単にメトリクス収集を始める事ができる
- application.ymlに
management.metrics.distribution
の設定を行うと、アプリのURI毎のレイテンシが収集できる
以上です。
Micrometerがもっと色んなライブラリに対応したり、ミドルウェアに組み込まれたりして、モニタリングがもっと簡単に出来るようになったらいいなー。
あわせて読みたい
- Spring Boot Actuator 2.0 & Micrometer *2018/05/23 追記
- Spring Boot Actuator 2.0 & Micrometer (JJUG CCC 2018 Spring) *2018/05/28 追記