SpringBoot Actuatorの動作確認メモ
背景
SpringBoot Acuatorについて動作確認したので、そのあたりのことをメモっときます。- 概要
- 簡単にいうと、Spring BootアプリケーションにAcuatorを適用すると、アプリケーションの設定や状態を取得できるWebAPI(JSON)が自動で追加され、それらの情報がWebAPI経由で取得できるようになります。
適用する
ビルドスクリプトに依存を追加する
- Spring Boot Actuatorプロジェクト
- Spring Boot Actuatorドキュメント
- 設定は任意。追加するとActuatorについてのドキュメント(/docs)がURLとして追加される。
- https://github.com/spring-projects/spring-boot/tree/master/spring-boot-actuator
- build.gradle
dependencies {
compile('org.springframework.boot:spring-boot-starter')
compile('org.springframework.boot:spring-boot-starter-web')
compile('org.springframework.boot:spring-boot-starter-actuator') // 追加
compile('org.springframework.boot:spring-boot-actuator-docs') // 追加
}ポートを設定する
- ActuatorというよりはSpring Bootの設定。
- SpringBootアプリケーションは同梱されるtomcatで起動されるのでそれに使うポートの設定。他で使っているポートと被らないようにする必要がある(デフォルト8080)。
- application.properties
server.port=8088
ポートを開放する
- 上記の利用するポートについては通常ポートが開いていないのため設定する。
- 現状AWSを利用しているのでセキュリティグループに設定する
| タイプ | プロトコル | ポート範囲 | 送信元 |
|---|---|---|---|
| カスタム TCP ルール | TCP | 8080 - 8089 | 0.0.0.0/0 |
実行する
Actuatorを取り込んだアプリケーションを実行する# java -jar springboot-1.0.0.jar
起動のログは以下で、メトリクス取得用フィルター(metricFilter)やURLマッピング(後述の一連のURL)が追加されているのが分かる。
# java -jar springboot-1.0.0.jar
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v1.3.2.RELEASE)
(中略)
[ main] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat initialized with port(s): 8088 (http)
[ main] o.apache.catalina.core.StandardService : Starting service Tomcat
[ main] org.apache.catalina.core.StandardEngine : Starting Servlet Engine: Apache Tomcat/8.0.30
[ost-startStop-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
[ost-startStop-1] o.s.web.context.ContextLoader : Root WebApplicationContext: initialization completed in 6715 ms
[ost-startStop-1] o.s.b.c.e.ServletRegistrationBean : Mapping servlet: 'dispatcherServlet' to [/]
[ost-startStop-1] o.s.b.c.embedded.FilterRegistrationBean : Mapping filter: 'metricFilter' to: [/*]
[ost-startStop-1] o.s.b.c.embedded.FilterRegistrationBean : Mapping filter: 'characterEncodingFilter' to: [/*]
[ost-startStop-1] o.s.b.c.embedded.FilterRegistrationBean : Mapping filter: 'hiddenHttpMethodFilter' to: [/*]
[ost-startStop-1] o.s.b.c.embedded.FilterRegistrationBean : Mapping filter: 'httpPutFormContentFilter' to: [/*]
[ost-startStop-1] o.s.b.c.embedded.FilterRegistrationBean : Mapping filter: 'requestContextFilter' to: [/*]
[ost-startStop-1] o.s.b.c.embedded.FilterRegistrationBean : Mapping filter: 'webRequestLoggingFilter' to: [/*]
[ost-startStop-1] o.s.b.c.embedded.FilterRegistrationBean : Mapping filter: 'applicationContextIdFilter' to: [/*]
[ main] s.w.s.m.m.a.RequestMappingHandlerAdapter : Looking for @ControllerAdvice: org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@4c8ae11d: startup date [Sun Jan 24 03:51:39 UTC 2016]; root of context hierarchy
[ main] s.w.s.m.m.a.RequestMappingHandlerAdapter : Detected ResponseBodyAdvice bean in org.springframework.boot.actuate.autoconfigure.EndpointWebMvcHypermediaManagementContextConfiguration$ActuatorEndpointLinksAdvice
[ main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/healthcheck]}" onto java.lang.String jp.co.namihira.prototype.springboot.web.controller.api.HealthcheckController.home()
[ main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error],produces=[text/html]}" onto public org.springframework.web.servlet.ModelAndView org.springframework.boot.autoconfigure.web.BasicErrorController.errorHtml(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse)
[ main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error]}" onto public org.springframework.http.ResponseEntity<java.util.Map<java.lang.String, java.lang.Object>> org.springframework.boot.autoconfigure.web.BasicErrorController.error(javax.servlet.http.HttpServletRequest)
[ main] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped URL path [/webjars/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
[ main] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped URL path [/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
[ main] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped URL path [/docs/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
[ main] .m.m.a.ExceptionHandlerExceptionResolver : Detected ResponseBodyAdvice implementation in org.springframework.boot.actuate.autoconfigure.EndpointWebMvcHypermediaManagementContextConfiguration$ActuatorEndpointLinksAdvice
[ main] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped URL path [/**/favicon.ico] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
[ main] o.s.b.a.e.mvc.EndpointHandlerMapping : Mapped "{[/actuator || /actuator.json],produces=[application/json]}" onto public org.springframework.hateoas.ResourceSupport org.springframework.boot.actuate.endpoint.mvc.HalJsonMvcEndpoint.links()
[ main] o.s.b.a.e.mvc.EndpointHandlerMapping : Mapped "{[/mappings || /mappings.json],methods=[GET],produces=[application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter.invoke()
[ main] o.s.b.a.e.mvc.EndpointHandlerMapping : Mapped "{[/configprops || /configprops.json],methods=[GET],produces=[application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter.invoke()
[ main] o.s.b.a.e.mvc.EndpointHandlerMapping : Mapped "{[/health || /health.json],produces=[application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.HealthMvcEndpoint.invoke(java.security.Principal)
[ main] o.s.b.a.e.mvc.EndpointHandlerMapping : Mapped "{[/env/{name:.*}],methods=[GET],produces=[application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.EnvironmentMvcEndpoint.value(java.lang.String)
[ main] o.s.b.a.e.mvc.EndpointHandlerMapping : Mapped "{[/env || /env.json],methods=[GET],produces=[application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter.invoke()
[ main] o.s.b.a.e.mvc.EndpointHandlerMapping : Mapped "{[/autoconfig || /autoconfig.json],methods=[GET],produces=[application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter.invoke()
[ main] o.s.b.a.e.mvc.EndpointHandlerMapping : Mapped "{[/metrics/{name:.*}],methods=[GET],produces=[application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.MetricsMvcEndpoint.value(java.lang.String)
[ main] o.s.b.a.e.mvc.EndpointHandlerMapping : Mapped "{[/metrics || /metrics.json],methods=[GET],produces=[application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter.invoke()
[ main] o.s.b.a.e.mvc.EndpointHandlerMapping : Mapped "{[/trace || /trace.json],methods=[GET],produces=[application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter.invoke()
[ main] o.s.b.a.e.mvc.EndpointHandlerMapping : Mapped "{[/dump || /dump.json],methods=[GET],produces=[application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter.invoke()
[ main] o.s.b.a.e.mvc.EndpointHandlerMapping : Mapped "{[/docs/],produces=[text/html]}" onto public java.lang.String org.springframework.boot.actuate.endpoint.mvc.DocsMvcEndpoint.browse()
[ main] o.s.b.a.e.mvc.EndpointHandlerMapping : Mapped "{[/docs],produces=[text/html]}" onto public java.lang.String org.springframework.boot.actuate.endpoint.mvc.DocsMvcEndpoint.redirect()
[ main] o.s.b.a.e.mvc.EndpointHandlerMapping : Mapped "{[/beans || /beans.json],methods=[GET],produces=[application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter.invoke()
[ main] o.s.b.a.e.mvc.EndpointHandlerMapping : Mapped "{[/info || /info.json],methods=[GET],produces=[application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter.invoke()
[ main] o.s.j.e.a.AnnotationMBeanExporter : Registering beans for JMX exposure on startup
[ main] o.s.c.support.DefaultLifecycleProcessor : Starting beans in phase 0
[ main] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat started on port(s): 8088 (http)アクセスしてみる
URLにアクセスしてみる。- SPring bootはコンテキストパスがないのでパスにアクセスする
- spring-boot-actuator-docsを追加しているなら以下で一覧や説明がみれる
- アクセスできるURLは以下を参照
- デフォルトはJSON形式で取れる。
- 監視ツールとかと連携しやすそう。
以下より取れた各情報のメモ(一部)
/docs
- 各パスについての情報が取れる
- 以下は画面キャプチャ

/beans
- springに登録されているbean一覧が見れる。
[{
context: "application:8088",
parent: null,
beans: [{
bean: "application",
scope: "singleton",
type: "jp.co.namihira.prototype.springboot.Application$$EnhancerBySpringCGLIB$$c11b16a2",
resource: "null",
dependencies: []
}, {
bean: "healthcheckController",
scope: "singleton",
type: "jp.co.namihira.prototype.springboot.web.controller.api.HealthcheckController",
resource: "URL [jar:file:/home/ec2-user/spring-boot/springboot-1.0.0.jar!/jp/co/namihira/prototype/springboot/web/controller/api/HealthcheckController.class]",
dependencies: []
}, {
・・・/dump
- アクセスしたときのスレッドバンプが取れる
[
{
threadName: "http-nio-8088-exec-10",
threadId: 29,
blockedTime: -1,
blockedCount: 0,
waitedTime: -1,
waitedCount: 6,
lockName: "java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject@4d23a891",
lockOwnerId: -1,
lockOwnerName: null,
inNative: false,
suspended: false,
threadState: "WAITING",
stackTrace: [{
methodName: "park",
fileName: "Unsafe.java",
lineNumber: -2,
className: "sun.misc.Unsafe",
nativeMethod: true
},
{
methodName: "park",
fileName: "LockSupport.java",
lineNumber: 175,
className: "java.util.concurrent.locks.LockSupport",
nativeMethod: false/env
- Springに登録されているプロパティ一覧(環境変数や*.propertiesで登録されたもの)が見れる。
{
profiles: [ ],
server.ports: {
local.server.port: 8088
},
servletContextInitParams: { },
systemProperties: {
java.runtime.name: "Java(TM) SE Runtime Environment",
java.protocol.handler.pkgs: "null|org.springframework.boot.loader",
sun.boot.library.path: "/usr/java/jdk1.8.0_25/jre/lib/amd64",
java.vm.version: "25.25-b02",
java.vm.vendor: "Oracle Corporation",
java.vendor.url: "http://java.oracle.com/",
path.separator: ":",
java.vm.name: "Java HotSpot(TM) 64-Bit Server VM",/health
- アプリケーションの状態用に用意されているパス
{
status: "UP",
diskSpace: {
status: "UP",
total: 8455118848,
free: 5770330112,
threshold: 10485760
}
}/info
- プロパティの内「info.*」で定義されているものを表示するパス
- いろいろ追加しやすそう
- デフォルトはなにもない
{}/mappings
- Springに登録されているRequestMapping情報一覧
- JSONだと見難いので、もっと見やすく一覧画面とかにしたほうがよさそう。
{
/webjars/**: {
bean: "resourceHandlerMapping"
},
/**: {
bean: "resourceHandlerMapping"
},
/docs/**: {
bean: "resourceHandlerMapping"
},
/**/favicon.ico: {
bean: "faviconHandlerMapping"
},
{[/healthcheck]}: {
bean: "requestMappingHandlerMapping",
method: "java.lang.String jp.co.namihira.prototype.springboot.web.controller.api.HealthcheckController.home()"
},
{[/error],produces=[text/html]}: {
bean: "requestMappingHandlerMapping",
method: "public org.springframework.web.servlet.ModelAndView org.springframework.boot.autoconfigure.web.BasicErrorController.errorHtml(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse)"
},
{[/error]}: {
bean: "requestMappingHandlerMapping",
method: "public org.springframework.http.ResponseEntity<java.util.Map<java.lang.String, java.lang.Object>> org.springframework.boot.autoconfigure.web.BasicErrorController.error(javax.servlet.http.HttpServletRequest)"
},
{[/actuator || /actuator.json],produces=[application/json]}: {
bean: "endpointHandlerMapping",
method: "public org.springframework.hateoas.ResourceSupport org.springframework.boot.actuate.endpoint.mvc.HalJsonMvcEndpoint.links()"
},
{[/mappings || /mappings.json],methods=[GET],produces=[application/json]}: {
bean: "endpointHandlerMapping",
method: "public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter.invoke()"
},
・・・/trace
- 直近10件のリクエスト情報がとれるパス
- サーバに入ってログ確認しなくて済むので便利そう。
[
{
timestamp: 1453615351832,
info: {
method: "GET",
path: "/mappings",
headers: {
request: {
host: "*.compute-1.amazonaws.com:8088",
connection: "keep-alive",
accept: "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
upgrade-insecure-requests: "1",
user-agent: "Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/47.0.2526.111 Safari/537.36",
accept-encoding: "gzip, deflate",
accept-language: "ja,en;q=0.8"
},
response: {
X-Application-Context: "application:8088",
Content-Type: "application/json;charset=UTF-8",
Transfer-Encoding: "chunked",
Date: "Sun, 24 Jan 2016 06:02:31 GMT",
status: "200"
}
}
}
},
{
timestamp: 1453615322537,
info: {
method: "GET",
path: "/favicon.ico",
headers: {
request: {
host: "*.compute-1.amazonaws.com:8088",
connection: "keep-alive",
pragma: "no-cache",
cache-control: "no-cache",
user-agent: "Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/47.0.2526.111 Safari/537.36",
accept: "*/*",
referer: "http://*.compute-1.amazonaws.com:8088/trace",
accept-encoding: "gzip, deflate",
accept-language: "ja,en;q=0.8"
},
response: {
X-Application-Context: "application:8088",
Last-Modified: "Sun, 24 Jan 2016 03:51:15 GMT",
Accept-Ranges: "bytes",
Content-Length: "946",
Date: "Sun, 24 Jan 2016 06:02:02 GMT",
status: "200"
}
}
}
},
・・・
/metrics
- アプリケーションのメトリクスがとれるパス
- メモリ情報やアクセス数の統計がデフォルトで取れる。
- 各要素の意味は以下参照
{
mem: 93268,
mem.free: 5850,
processors: 1,
instance.uptime: 5634497,
uptime: 5649567,
systemload.average: 0,
heap.committed: 43924,
heap.init: 10240,
heap.used: 38073,
heap: 148480,
nonheap.committed: 50496,
nonheap.init: 2496,
nonheap.used: 49344,
nonheap: 0,
threads.peak: 19,
threads.daemon: 17,
threads.totalStarted: 24,
threads: 19,
classes: 6041,
classes.loaded: 6041,
classes.unloaded: 0,
gc.copy.count: 111,
gc.copy.time: 1306,
gc.marksweepcompact.count: 3,
gc.marksweepcompact.time: 151,
httpsessions.max: -1,
httpsessions.active: 0,
gauge.response.mappings: 15,
gauge.response.beans: 575,
gauge.response.docs.star-star: 1341,
gauge.response.trace: 42,
gauge.response.autoconfig: 410,
gauge.response.dump: 122,
gauge.response.health: 37,
gauge.response.metrics: 14,
gauge.response.star-star: 44,
gauge.response.info: 8,
gauge.response.star-star.favicon.ico: 8,
gauge.response.env: 16,
gauge.response.docs: 3,
gauge.response.configprops: 345,
counter.status.200.mappings: 2,
counter.status.200.configprops: 1,
counter.status.404.star-star: 4,
counter.status.200.health: 3
counter.status.200.autoconfig: 1,
counter.status.200.env: 2,
counter.status.200.trace: 1,
counter.status.302.docs: 2,
counter.status.304.docs.star-star: 2,
counter.status.200.star-star.favicon.ico: 4,
counter.status.200.info: 2,
counter.status.200.beans: 1,
counter.status.200.docs.star-star: 6,
counter.status.200.metrics: 4,
counter.status.200.dump: 2
}まとめ
- 「アプリの設定や状態どうなってなってたっけ?(;´Д`)」というときは、「別管理されているファイル(エクセル?)」や「構成ツール(Chefとか)の設定」「サーバに入って確認する」をするよりかはAPIとして提供したほうが楽そうだし、正確。
- アプリのことはアプリに訊くという方針がよさそう。
- ドキュメントを作らなくて良いし(;´Д`)
- この機能を拡張(考え方を継承)して、利用しているOSS一覧*1とかもとれるようにしたら便利そう。
参考
*1:管理対象になりがち