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:管理対象になりがち