概述
- 引入Actuator
- 使用 Actuator endpoints
- 自定义 Actuator endpoints
- Securing Actuator
引入Actuator
1. 概述
Actuator
可以实现对程序内部运行情况监控,比如监控状况,Bean加载情况,环境变量,日志信息,线程信息等- 通过
Actuator
公开的端点,我们可以获取到Spring Boot应用运行时的一些状态信息- 应用环境中哪些配置属性可用
- 应用中各个包的日志环境级别
- 应用正在消耗多少内存
- HTTP端点被请求了多少次
- 应用与其协调的外部服务的运行状况如何
2. 开始
*依赖: * pom.xml中添加
Actuator
的Maven依赖<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency>
端点: **
Actuator
一旦引入,应用将会提供几个端点以供查询应用信息(默认base-path为/actuator,即端点需要加此前缀使用**,如使用/actuator/health
),大多端点默认都是关闭的,需要通过配置开启,以下除了/heapdump
之外,其他所有HTTP端点也作为JMX MBeans
公开请求方式 path(端点) 描述 默认是否开启 GET /auditevents 生成任何已经触发的audit events的报告 No GET /beans 显示Spring应用上下文中的所有bean No GET /conditions 生成通过和失败的autoconfiguration conditions,
并指向应用上下文创建的beanNo GET /configprops 显示所有配置属性及其当前的值 No GET,POST
,DELETE/env 生成应用可用的所有属性源及其属性报告 No GET /env/{toMatch} 显示指定的单个环境变量的值 No GET /health 返回应用程序及其外部依赖应用的总的运行状况 Yes GET /heapdump 下载heap dump No GET /httptrace 生成最近100个请求的信息 No GET /info 返回应用的任何developer-defined信息 Yes GET /loggers 生成包的列表及其对应的配置的日志级别 No GET,POST /loggers/{name} 返回给定的日志记录器的已配置且有效的日志级别,
可以通过POST请求设置有效日志级别No GET /mappings 生成报告,包含所有HTTP映射及其相应的处理方法 No GET /metrics 返回所有metrics(度量)类别的列表 No GET /metrics/{name} 返回给定的metrics的多维set值集 No GET /scheduledtasks 列出所有计划任务(scheduled tasks) No GET /threaddump 返回所有应用线程的报告 No
3. 配置Actuator endpoints
3.1 base-path配置
*base-path: * 默认情况下所有端点的根路径为
/actuator
,如/health
端点应使用/actuator/health
,如果想自定义(如/management
)则需要以下配置management: endpoints: web: base-path: /management
3.2 启用或关闭端点
通过2的表格可以发现,除了
/health
和/info
两个端点默认开启之外,其他的(由于涉及到敏感信息)默认都是关闭的,如果要开启这些端点则需要配置,考虑安全需求,可以配合Spring Security
使用*开启指定端点: *
management: endpoints: web: exposure: include: health,info,beans,conditions
*通配符()开启所有端点: **
management: endpoints: web: exposure: include: '*'
*排除指定端点: *
management: endpoints: web: exposure: include: '*' exclude: threaddump,heapdump #排除此端点
使用Actuator endpoints
*/actuator * 通过
http://localhost:8762/actuator
获取到以下信息,可以查看目前开启的HTTP端点{ "_links": { "self": { "href": "http://localhost:8762/actuator", "templated": false }, "health": { "href": "http://localhost:8762/actuator/health", "templated": false }, "health-path": { "href": "http://localhost:8762/actuator/health/{*path}", "templated": true }, "info": { "href": "http://localhost:8762/actuator/info", "templated": false }, "refresh": { "href": "http://localhost:8762/actuator/refresh", "templated": false } } }
/info 通过
http://localhost:8762/actuator/info
可以获取到application.yml
中配置的info信息(或者实现了InfoContributor
接口定义的info信息)application.yml
配置以下info信息info: contact: email: [email protected] phone: 123
通过
/info
获取{"contact":{"email":"[email protected]","phone":123}}
/health 通过
http://localhost:8762/actuator/health
可以获取到系统的状态一共有4种状态
状态 描述 UP 外部系统启动并可访问,所有都满足时为此状态 DOWN 外部系统已关闭或无法访问,存在一个即为此状态 UNKNOWN 外部系统状态不清楚,存在时将被忽略 OUT_OF_SERVICE 外部系统可访问,单目前不可用,存在一个即为此状态 默认情况下仅仅返回状态信息
{"status":"UP"}
如果要返回详细信息则需要在配置文件中配置
配置
management: endpoint: health: show-details: always
返回详细信息,可以看到
diskSpace
属性,所有应用程序的都会有此属性它表示磁盘运行状况,如果可用磁盘空间低于阈值,则状态会变为DOWN
{ "status": "UP", "components": { "db": { "status": "UP", "details": { "database": "H2", "validationQuery": "isValid()" } }, "discoveryComposite": { "status": "UP", "components": { "discoveryClient": { "status": "UP", "details": { "services": [] } }, "eureka": { "description": "Eureka discovery client has not yet successfully connected to a Eureka server", "status": "UP", "details": { "applications": {} } } } }, "diskSpace": { "status": "UP", "details": { "total": 499839078400, "free": 450688933888, "threshold": 10485760, "exists": true } }, "hystrix": { "status": "UP" }, "ping": { "status": "UP" }, "refreshScope": { "status": "UP" } } }
/beans 查看系统的bean信息,以下展示部分片段,可以看到
discoveryClientHealthIndicator
和其注入的依赖{ "contexts": { "application-1": { "beans": { ... "discoveryClientHealthIndicator": { "aliases": [], "scope": "singleton", "type": "org.springframework.cloud.client.discovery.health.DiscoveryClientHealthIndicator", "resource": "class path resource [org/springframework/cloud/client/CommonsClientAutoConfiguration$DiscoveryLoadBalancerConfiguration.class]", "dependencies": ["spring.cloud.discovery.client.health-indicator-org.springframework.cloud.client.discovery.health.DiscoveryClientHealthIndicatorProperties"] }, ... }, "parentId": null } } }
/conditions 返回自动配置报告,包含了
positiveMatches
(通过的条件配置),negativeMatches
(失败的条件配置),unconditionalClasses
(无条件类),如以下片段{ "contexts": { "application-1": { "positiveMatches": { ... "MongoDataAutoConfiguration#mongoTemplate": [{ "condition": "OnBeanCondition", "message": "@ConditionalOnMissingBean (types:org.springframework.data.mongodb.core.MongoTemplate;SearchStrategy: all) did not find any beans " }] ... }, "negativeMatches": { ... "DispatcherServletAutoConfiguration": { "notMatched": [{ "condition": "OnClassCondition", "message": "@ConditionalOnClass did not find required class 'org.springframework.web.servlet.DispatcherServlet '" }], "matched": [] } ... }, "unconditionalClasses": [ ... "org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration " ... ] } } }
/env 可以查看环境变量丶JVM系统属性丶application.properties丶application.yml丶甚至
Spring Cloud Config Server
{ "activeProfiles": [ "development" ], "propertySources": [{ "name": "systemEnvironment", "properties": { "PATH": { "value": "/usr/bin:/bin:/usr/sbin:/sbin", "origin": "System Environment Property \"PATH\"" }, "HOME": { "value": "/Users/habuma", "origin": "System Environment Property \"HOME\"" } } }, { "name": "applicationConfig: [classpath:/application.yml]", "properties": { "spring.application.name": { "value": "ingredient-service", "origin": "class path resource [application.yml]:3:11" }, "server.port": { "value": 8081, "origin": "class path resource [application.yml]:9:9" } } } ] }
自定义 Actuator endpoints
1. 概述
- 一些
Actuator endpoints
允许定制,同时也允许创建自定义Actuator endpoints
2. /info
端点
2.1 一般信息添加
*方式一: *可以通过在
application.yml
中配置添加自定义信息info: contact: email: [email protected] phone: 123
*方式二: *实现
InfoContributor
接口,并使用@Component
注解,添加自定义信息@Component public class TacoInfoContributor implements InfoContributor { @Override public void contribute(Info.Builder builder) { builder.withDetail("name","taco"); } }
2.2 build-info信息添加
Spring boot提供了一些内置的
InfoContributor
实现,如BuildInfoContributor
可以将项目构建信息build-info.properties
文件内容添加到/info
端点中(如版本信息丶主机丶用户丶时间戳),需要在pom.xml
的build
标签中配置启用配置启用
build-info
<build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <executions> <execution> <goals> <goal>build-info</goal> <!--启用--> </goals> </execution> </executions> </plugin> </plugins> </build>
使用IDEA自动生成
build-info.properties
文件,打开Maven project
找到spring-boot
->spring-boot:build-info
执行即可自动生成,文件会发布到/META-INF/
目录下build.artifact=taco build.group=cn.com.taco build.name=taco build.time=2020-09-10T05\:49\:52.959Z build.version=0.0.1-SNAPSHOT
2.3 GIT提交信息添加
pom.xml中添加插件
<build> <plugins> ... <plugin> <groupId>pl.project13.maven</groupId> <artifactId>git-commit-id-plugin</artifactId> </plugin> ... </plugins> </build>
使用IDEA自动生成
git.properties
文件,打开Maven project
找到Plugins
->git-commit-id
->git-commit-id:version
执行即可自动生成,文件会发布到/classes/
目录下#Generated by Git-Commit-Id-Plugin #Thu Sep 10 14:10:58 CST 2020 git.branch=cloud git.build.host=DESKTOP-B2MV9TT git.build.time=2020-09-10T14\:10\:58+0800 git.build.user.email=[email protected] git.build.user.name=bryson git.build.version=0.0.1-SNAPSHOT git.closest.tag.commit.count= git.closest.tag.name= git.commit.id=a7baf19e542bbd24c8511cd8c202176db637e693 git.commit.id.abbrev=a7baf19 git.commit.id.describe=a7baf19-dirty git.commit.id.describe-short=a7baf19-dirty git.commit.message.full=work cloud git.commit.message.short=work cloud git.commit.time=2020-09-10T10\:28\:19+0800 git.commit.user.email=[email protected] git.commit.user.name=bryson git.dirty=true git.local.branch.ahead=0 git.local.branch.behind=0 git.remote.origin.url=[email protected]\:BrysonBS/learning.git git.tags= git.total.commit.count=64
使用
http://localhost:8762/actuator/info
可以发现只有简略信息(分支和commit时间戳),如果想要显示详细信息则需要在application.yml
中配置{ "git": { "branch": "cloud", "commit": { "id": "a7baf19", "time": "2020-09-10T02:28:19Z" } }, "build": { "artifact": "taco", "name": "taco", "time": "2020-09-10T05:49:52.959Z", "version": "0.0.1-SNAPSHOT", "group": "cn.com.taco" } }
application.yml
中配置,使其显示相信信息application.yml
配置management: info: git: mode: full
使用
http://localhost:8762/actuator/info
结果{ "git": { "local": { "branch": { "ahead": "0", "behind": "0" } }, "commit": { "id": { "describe-short": "a7baf19-dirty", "abbrev": "a7baf19", "full": "a7baf19e542bbd24c8511cd8c202176db637e693", "describe": "a7baf19-dirty" }, "message": { "short": "work cloud", "full": "work cloud" }, "user": { "name": "bryson", "email": "[email protected]" }, "time": "2020-09-10T02:28:19Z" }, "branch": "cloud", "build": { "time": "2020-09-10T06:10:58Z", "version": "0.0.1-SNAPSHOT", "host": "DESKTOP-B2MV9TT", "user": { "name": "bryson", "email": "[email protected]" } }, "tags": "", "total": { "commit": { "count": "64" } }, "closest": { "tag": { "commit": { "count": "" }, "name": "" } }, "remote": { "origin": { "url": "[email protected]:BrysonBS/learning.git" } }, "dirty": "true" }, "build": { "artifact": "taco", "name": "taco", "time": "2020-09-10T05:49:52.959Z", "version": "0.0.1-SNAPSHOT", "group": "cn.com.taco" } }
3. /health端点
Spring Boot默认提供了多个
health indicators
运行状况指示器,可以用于许多集成的外部系统,但是如果没有的情况,可以通过实现HealthIndicator
接口自定义指示器@Component public class WackoHealthIndicator implements HealthIndicator { @Override public Health health() { int hour = Calendar.getInstance().get(Calendar.HOUR_OF_DAY); if (hour > 12) { return Health .outOfService() .withDetail("reason", "I'm out of service after lunchtime") .withDetail("hour", hour) .build(); } if (Math.random() < 0.1) { return Health .down() .withDetail("reason", "I break 10% of the time") .build(); } return Health .up() .withDetail("reason", "All is good!") .build(); } }
4. 注册自定义metrics
(指标)
可以通过
MeterRegistry
进行度量,在类中注入,并使用@Component public class TacoMetrics { private MeterRegistry meterRegistry; //spring4.X以后构造函数参数可以自动注入,无需@Autowired public TacoMetrics(MeterRegistry meterRegistry) { this.meterRegistry = meterRegistry; meterRegistry.counter("taco","test1","test2").increment();//统计,只有一次 } }
使用
http://localhost:8762/actuator/metrics/taco
查看结果{ "name": "taco", "description": null, "baseUnit": null, "measurements": [{ "statistic": "COUNT", "value": 0.0 }], "availableTags": [{ "tag": "test1", "values": ["test2"] }] }
5. 创建自定义端点(endpoints)
与创建Controller类似,但是因为支持
JMX MBeans
所以有额外的内容使用到的注解
@Endpoint
(类似@Controller
)丶@ReadOperation
丶@WriteOperation
丶@DeleteOperation
,这些注解并不代表通信机制,因为支持HTTP
与JMX
使用
@Endpoint
会公开HTTP端点和JMX,如果只想公开HTTP端点则可以使用@WebEndpoint
,如果只想公开JMX则可以使用@JmxEndpoint
注解实例
@Component @Endpoint(id = "notes",enableByDefault = true) //默认为true,如果为false,则可以通过配置属性management.web.endpoints.web.exposure.include开启 //即使为true,如果已经配置management.web.endpoints.web.exposure.include属性,则需要额外开启才行 public class NotesEndpoints { @ReadOperation //读操作: HTTP中对应GET请求 public String notes(){ return "读: "+ LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd hh:mm:ss")); } @WriteOperation //写操作: HTTP中对应POST请求 public String addNotes(){ return "写: "+ LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd hh:mm:ss")); } @DeleteOperation//删除操作: HTTP中对应DELETE请求 public String deleteNotes(){ return "删除: "+ LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd hh:mm:ss")); } }
Securing Actuator
1. 概述
Actuator
提供的信息可能不希望任何人都能看到,或者有些端点可以修改配置属性(如日志级别),这些更需要控制权限, 因此需要配合Spring Security
使用
2. 使用Spring Security控制权限
*不推荐: *由于所有端点的根路径为
/actuator
,因此可以直接控制根路径权限@Override protected void configure(HttpSecurity http) throws Exception { http .authorizeRequests() //使用/actuator/**根路径的缺点是根路径是可以配置的,如果更改了,安全配置就会失效 .antMatchers("/actuator/**").hasRole("ADMIN") //只有ADMIN角色可以访问 .and() .httpBasic(); }
使用
EndpointRequest
替代硬编码路径@Override protected void configure(HttpSecurity http) throws Exception { http .requestMatcher(EndpointRequest.toAnyEndpoint())//替换掉硬编码路径,即使更改配置根路径也不会受到影响 .authorizeRequests() .anyRequest().hasRole("ADMIN") .and() .httpBasic(); }
排除部分端点不设置身份验证:
@Override protected void configure(HttpSecurity http) throws Exception { http .requestMatcher( EndpointRequest.toAnyEndpoint() .excluding("health", "info")) //排除端点/health和/info不做身份验证 .authorizeRequests() .anyRequest().hasRole("ADMIN") .and() .httpBasic(); }
指定验证的端点
@Override protected void configure(HttpSecurity http) throws Exception { http .requestMatcher(EndpointRequest.to("beans", "threaddump", "loggers")) //指定要验证的端点 .authorizeRequests() .anyRequest().hasRole("ADMIN") .and() .httpBasic(); }