前言
配置监控时,我们首要面对的是监控数据如果采集的问题。一般我们可以把监控指标分为两类:基础监控和业务监控。监控的目的无非是为了解服务运行状况、发现服务故障和帮助定位故障原因。
基础监控
包括 CPU、内存、磁盘、端口和进程等机器、网络的操作系统级别的信息。通常情况下,成熟的监控系统(例如开源的 Prometheus、Zabbix 等)均会提供基础监控项的采集能力,这里不做过多介绍。但需要注意的一点,机器级别的基础监控指标一般并不能代表服务的真实运行状况,例如单台实例的故障对一个设计合理的分布式系统来说并不会带来严重后果。所以只有结合业务相关监控指标,基础监控指标才有意义。
业务监控
业务监控指标由业务系统内部的服务产生,一般能够真实反应业务运行状态。设计合理的系统一般都会提供相关监控指标供监控系统采集。监控数据的采集方法一般可以分为以下几大类。
日志:日志可以包含服务运行的方方面面,是重要的监控数据来源。例如,通过 Nginx access 日志可以统计出错误(5xx)、延迟(响应时间)和流量,结合已知的容量上限就可以计算出饱和度。一般除监控系统提供的日志采集插件外,如 Rsyslog、Logstash、Filebeat、Flume 等都是比较优秀的日志采集软件。
JMX:多数 Java 开发的服务均可由 JMX 接口输出监控指标。不少监控系统也有集成 JMX 采集插件,除此之外我们也可通过 jmxtrans、jmxcmd 工具进行采集。
REST:提供 REST API 来进行监控数据的采集,如 Hadoop、ElasticSearch。
OpenMetrics:得益于 Prometheus 的流行,作为 Prometheus 的监控数据采集方案,OpenMetrics 可能很快会成为未来监控的业界标准。目前绝大部分热门开源服务均有官方或非官方的 exporter 可供使用。
命令行:一些服务提供本地的命令来输出监控指标。
主动上报:对于采用 PUSH 模型的监控系统来说,服务可以采取主动上报的方式把监控指标 push 到监控系统,如 Java 服务可使用 Metrics 接口自定义 sink 输出。另外,运维也可以使用自定义的监控插件来完成监控的采集。
黄金指标
搭建环境
1 2 3 4 5 6 7 8 brew install prometheus brew install grafana brew info prometheus
springboot配置
maven依赖
1 2 3 4 5 6 7 8 <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-actuator</artifactId > </dependency > <dependency > <groupId > io.micrometer</groupId > <artifactId > micrometer-registry-prometheus</artifactId > </dependency >
application.yml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 management: endpoints: web: exposure: include: prometheus,health,info,metric metrics: export: prometheus: enabled: true tags: application: ${spring.application.name} endpoint: health: show-details: always info: app: name: ${spring.application.name}
启动类注入bean
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 @Slf4j @EnableDiscoveryClient @SpringBootApplication public class NlpKbProxyApplication { private static final String line = "\n----------------------------------------------------------" ; public static void main (String[] args) throws UnknownHostException { ConfigurableApplicationContext context = SpringApplication.run(NlpKbProxyApplication.class, args); String swaggerEnabled = context.getEnvironment().getProperty("swagger.enabled" ); afterRun(context, Boolean.parseBoolean(swaggerEnabled)); } private static void afterRun (ConfigurableApplicationContext applicationContext, boolean swaggerEnabled) throws UnknownHostException { if (swaggerEnabled) { AbstractServletWebServerFactory webServerFactory = applicationContext.getBean(AbstractServletWebServerFactory.class); int port = webServerFactory.getPort(); String contextPath = webServerFactory.getContextPath(); log.info("\n访问swaggger接口调试 URLs:" + line + "\n" + "Local: \t\thttp://{}:{}{}/swagger-ui/index.html" + line , InetAddress.getLocalHost().getHostAddress(), port, contextPath); log.info("\n健康检查 URLs:" + line + "\n" + "Local: \t\thttp://{}:{}{}/actuator" + line , InetAddress.getLocalHost().getHostAddress(), port, contextPath); } } @Bean MeterRegistryCustomizer<MeterRegistry> configurer ( @Value("${spring.application.name}") String applicationName) { return (registry) -> registry.config().commonTags("application" , applicationName); } }
metrix指标
访问 http://localhost:8080/actuator , 可以看到
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 { "_links" : { "self" : { "href" : "http://localhost:8080/actuator" , "templated" : false }, "health-path" : { "href" : "http://localhost:8080/actuator/health/{*path}" , "templated" : true }, "health" : { "href" : "http://localhost:8080/actuator/health" , "templated" : false }, "info" : { "href" : "http://localhost:8080/actuator/info" , "templated" : false }, "prometheus" : { "href" : "http://localhost:8080/actuator/prometheus" , "templated" : false } } }
系统指标
如果要监控系统基础指标, 此时并没有metrix, 需要在 prometheus.yml
中监听9100
端口采集的metrix。
下载系统 node_exporter
, 访问 https://prometheus.io/download/ , 如图
启动后访问 http://localhost:9100/ , 可以看到系统metrix。启动日志如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 ➜ node_exporter-1.1.1.darwin-amd64 ./node_exporter level=info ts=2021-02-26T16:35:39.925Z caller=node_exporter.go:178 msg="Starting node_exporter" version="(version=1.1.1, branch=HEAD, revision=4e837d4da79cc59ee3ed1471ba9a0d9547e95540)" level=info ts=2021-02-26T16:35:39.925Z caller=node_exporter.go:179 msg="Build context" build_context="(go=go1.15.8, user=root@f9e5f7d18c30, date=20210213-13:38:51)" level=info ts=2021-02-26T16:35:39.925Z caller=filesystem_common.go:74 collector=filesystem msg="Parsed flag --collector.filesystem.ignored-mount-points" flag=^/(dev)($|/) level=info ts=2021-02-26T16:35:39.925Z caller=filesystem_common.go:76 collector=filesystem msg="Parsed flag --collector.filesystem.ignored-fs-types" flag=^devfs$ level=info ts=2021-02-26T16:35:39.925Z caller=node_exporter.go:106 msg="Enabled collectors" level=info ts=2021-02-26T16:35:39.925Z caller=node_exporter.go:113 collector=boottime level=info ts=2021-02-26T16:35:39.925Z caller=node_exporter.go:113 collector=cpu level=info ts=2021-02-26T16:35:39.925Z caller=node_exporter.go:113 collector=diskstats level=info ts=2021-02-26T16:35:39.925Z caller=node_exporter.go:113 collector=filesystem level=info ts=2021-02-26T16:35:39.925Z caller=node_exporter.go:113 collector=loadavg level=info ts=2021-02-26T16:35:39.925Z caller=node_exporter.go:113 collector=meminfo level=info ts=2021-02-26T16:35:39.925Z caller=node_exporter.go:113 collector=netdev level=info ts=2021-02-26T16:35:39.925Z caller=node_exporter.go:113 collector=textfile level=info ts=2021-02-26T16:35:39.925Z caller=node_exporter.go:113 collector=time level=info ts=2021-02-26T16:35:39.925Z caller=node_exporter.go:113 collector=uname level=info ts=2021-02-26T16:35:39.926Z caller=node_exporter.go:195 msg="Listening on" address=:9100 level=info ts=2021-02-26T16:35:39.926Z caller=tls_config.go:191 msg="TLS is disabled." http2=false
更多exporters: https://prometheus.io/docs/instrumenting/exporters/
配置prometheus
prometheus会主动从应用拉取数据, 所以我们需要配置prometheus。
vim /usr/local/etc/prometheus.yml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 global: # 默认每隔15秒执行一次表达式判断告警规则 scrape_interval: 15s # 默认每隔15秒向各个监控端点拉取一次指标数据 evaluation_interval: 15s # scrape_timeout is set to the global default (10s). # Attach these labels to any time series or alerts when communicating with # external systems (federation, remote storage, Alertmanager). external_labels: monitor: 'codelab-monitor' rule_files: # 表达式计算和记录,告警规则等自定义的配置文件 # - "first.rules" # - "second.rules" # A scrape configuration containing exactly one endpoint to scrape: # Here it's Prometheus itself. scrape_configs: - job_name: "prometheus" static_configs: # 两个监控的端点,9090是Prometheus自己,9100是机器的监控组件Node Exporter - targets: ['127.0.0.1:9090', '127.0.0.1:9100'] - job_name: 'demo-java' # job name metrics_path: '/actuator/prometheus' # 指标获取路径, 默认 '/metrics' scrape_interval: 5s # pull模式, 抓取间隔 # basic_auth: # Spring Security basic auth # username: 'actuator' # password: 'actuator' static_configs: - targets: ['127.0.0.1:8080'] # 实例的地址,默认的协议是http labels: instance: demo-java
访问 http://localhost:9090/targets 查看实例
配置grafana
grafana默认帐号密码 admin/admin
, 访问 http://localhost:3000/ 登录
配置数据源
配置数据源
这里我们选择 prometheus
填写prometheus地址 http://localhost:9090
主题配置
导入dashboard
访问 https://grafana.com/grafana/dashboards 选择 dashboard
主题.
8919主题
1860主题
4701主题
10280主题
12856主题
Docker环境搭建
我们以 windows
环境为例
prometheus
1 docker run -d --name=prometheus -p 9090:9090 -v E:/docker/prometheus/prometheus.yml:/etc/prometheus/prometheus.yml prom/prometheus
下载(https://github.com/prometheus/node_exporter )并运行node_exporter。metrix地址 http://192.168.141.1:9182/metrics
prometheus.yml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 global: scrape_interval: 15s evaluation_interval: 15s external_labels: monitor: 'codelab-monitor' rule_files: scrape_configs: - job_name: 'prometheus' static_configs: - targets: ['host.docker.internal:9090' ] - job_name: 'eachbot-nlp-kb-proxy' metrics_path: '/kbp/actuator/prometheus' static_configs: - targets: ['192.168.141.1:7103' ] labels: instance: eachbot-nlp-kb-proxy - job_name: windows static_configs: - targets: ['192.168.141.1:9182' ] labels: instance: localhost
grafana
1 docker run -d --name=grafana --restart=always -p 3000:3000 grafana/grafana
选择windows下dashboard主题10467
业务指标
Metrics 种类
Prometheus 客户端库主要提供四种主要的 Metrics 类型:
Counter(计数器)
一种累加的 Metrics,始终增加。典型的应用如:http 请求的个数,结束的任务数,出现的错误数等等。一般在定义Counter类型指标的名称时推荐使用_total作为后缀。
Gauge(测量仪)
一种常规的 Metrics,仪表盘,当前值的一次快照(snapshot)测量,可增可减。典型的应用如:当前同时在线用户数、温度、内存使用率,运行的 goroutines 的个数,可以任意加减。
Histogram(直方图)
可以理解为柱状图,通过分桶(bucket)方式统计样本分布。典型的应用如:请求持续时间,响应大小,可以对观察结果采样,分组及统计。
类型为Histogram的监控指标prometheus_tsdb_compaction_chunk_range_bucket。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 # HELP prometheus_tsdb_compaction_chunk_range Final time range of chunks on their first compaction # TYPE prometheus_tsdb_compaction_chunk_range histogram prometheus_tsdb_compaction_chunk_range_bucket{le="100"} 0 prometheus_tsdb_compaction_chunk_range_bucket{le="400"} 0 prometheus_tsdb_compaction_chunk_range_bucket{le="1600"} 0 prometheus_tsdb_compaction_chunk_range_bucket{le="6400"} 0 prometheus_tsdb_compaction_chunk_range_bucket{le="25600"} 0 prometheus_tsdb_compaction_chunk_range_bucket{le="102400"} 0 prometheus_tsdb_compaction_chunk_range_bucket{le="409600"} 0 prometheus_tsdb_compaction_chunk_range_bucket{le="1.6384e+06"} 260 prometheus_tsdb_compaction_chunk_range_bucket{le="6.5536e+06"} 780 prometheus_tsdb_compaction_chunk_range_bucket{le="2.62144e+07"} 780 prometheus_tsdb_compaction_chunk_range_bucket{le="+Inf"} 780 prometheus_tsdb_compaction_chunk_range_sum 1.1540798e+09 prometheus_tsdb_compaction_chunk_range_count 780
从样本可以看出, 小于1.6384e+06的样本数为260, 小于 6.5536e+06的样本数为780, 总样本为780.
Summary(汇总)
根据样本统计出百分位 类似于Histogram。典型的应用如:请求持续时间,响应大小,提供观测值的 count 和 sum 功能,提供百分位的功能,即可以按百分比划分跟踪结果。
例如,指标prometheus_tsdb_wal_fsync_duration_seconds的指标类型为Summary。 它记录了Prometheus Server中wal_fsync处理的处理时间,通过访问Prometheus Server的/metrics地址,可以获取到以下监控样本数据:
1 2 3 4 5 6 7 # HELP prometheus_tsdb_wal_fsync_duration_seconds Duration of WAL fsync. # TYPE prometheus_tsdb_wal_fsync_duration_seconds summary prometheus_tsdb_wal_fsync_duration_seconds{quantile="0.5"} 0.012352463 prometheus_tsdb_wal_fsync_duration_seconds{quantile="0.9"} 0.014458005 prometheus_tsdb_wal_fsync_duration_seconds{quantile="0.99"} 0.017316173 prometheus_tsdb_wal_fsync_duration_seconds_sum 2.888716127000002 prometheus_tsdb_wal_fsync_duration_seconds_count 216
从上面的样本中可以得知当前Prometheus Server进行wal_fsync操作的总次数为216次,耗时2.888716127000002s。其中中位数(quantile=0.5)的耗时为0.012352463,9分位数(quantile=0.9)的耗时为0.014458005s。
表达式
rate[1m]:表示 1 分钟的增长速率,只能用于 counter 类型的数据。
sum:表示 value 求和。
by type:表示将求和后的数据按 metric 原始值中的 type 进行分组。
Legend format:表示指标名称的格式。
Resolution:默认打点步长是 15s,Resolution 表示是否将多个样本数据合并成一个点。
性能指标-接口平均响应时间
5分钟内请求总时间除以总次数得到api响应时间均值
1 rate(api_request_duration_milliseconds_sum{route="g/search", method="POST"}[5m]) / rate(api_request_duration_milliseconds_count{route="g/search", method="POST"}[5m])
发现慢接口
1 2 3 4 5 6 7 topk(20, sum by (route) ( rate(api_request_duration_milliseconds_sum{status_code!="500", method!="OPTIONS"}[2m]) / rate(api_request_duration_milliseconds_count{status_code!="500", method!="OPTIONS"}[2m]) ) )
sum byroute 的响应时间 取Top20,得到响应时间慢的API
1 topk(10, sum by (route) (rate(api_request_duration_milliseconds_count{status_code!="500"}[5m])))
稍微调整下可以拿到当前时间请求频繁的接口
失败接口
基于方法和错误码统计Center的整体 rpc 请求错误频率:
1 sum by (method, code) (rate(rpc_invoke_cnt_c{job="Center",code!="0"}[1m]))
QPS
QPS 的话,可以使用自带的函数 rate,rate(http_requests_total[10s]) 表示以 10s 作为时间单元来统计 QPS。
具体请参考: https://yunlzheng.gitbook.io/prometheus-book/parti-prometheus-ji-chu/promql/prometheus-promql-functions
AOP采集metrix指标
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 <dependencies > <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter</artifactId > <version > ${springboot.version}</version > </dependency > <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-configuration-processor</artifactId > <version > ${springboot.version}</version > </dependency > <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-aop</artifactId > <version > ${springboot.version}</version > </dependency > <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-actuator</artifactId > <version > ${springboot.version}</version > </dependency > <dependency > <groupId > io.micrometer</groupId > <artifactId > micrometer-registry-prometheus</artifactId > <version > 1.6.4</version > </dependency > <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-web</artifactId > <version > ${springboot.version}</version > </dependency > </dependencies >
1 2 3 4 5 6 @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE}) @Documented @Import({PrometheusMetricsRegistrar.class}) public @interface EnablePrometheus {}
1 2 3 4 5 6 7 8 public class PrometheusMetricsRegistrar implements ImportBeanDefinitionRegistrar { @Override public void registerBeanDefinitions (AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { BeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition(PrometheusMetricsAspect.class).getBeanDefinition(); registry.registerBeanDefinition("prometheusMetricsAspect" , beanDefinition); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 @Component @Aspect public class PrometheusMetricsAspect { private final Log logger = LogFactory.getLog(getClass()); @Autowired private CollectorRegistry collectorRegistry; private Counter requestTotal; private Counter requestError; private Histogram apiTimeConsuming; @PostConstruct private void init () { requestTotal = Counter.build().name("easyliao_http_requests_total" ).labelNames("uri" , "method" , "status" ) .help("http请求总数" ).register(collectorRegistry); requestError = Counter.build().name("easyliao_http_requests_error" ).labelNames("uri" , "method" , "status" ) .help("http请求错误数" ).register(collectorRegistry); apiTimeConsuming = Histogram.build().name("easyliao_api_time_consuming" ).labelNames("uri" , "method" , "status" ) .help("接口耗时" ).buckets(0.01 , 0.02 , 0.05 , 0.1 , 0.2 , 0.5 ).register(collectorRegistry); } @Pointcut("@annotation(com.easyliao.monitor.annotation.PrometheusMetrics)") public void api () {} @Around(value = "api()") public Object metricsCollector (ProceedingJoinPoint point) throws Throwable { MethodSignature methodSignature = (MethodSignature) point.getSignature(); PrometheusMetrics prometheusMetrics = methodSignature.getMethod().getAnnotation(PrometheusMetrics.class); if (prometheusMetrics != null ) { String uri; HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()) .getRequest(); HttpServletResponse response = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()) .getResponse(); if (StringUtils.isNotEmpty(prometheusMetrics.name())) { uri = prometheusMetrics.name(); } else { uri = request.getRequestURI(); } String method = request.getMethod(); int status = response.getStatus(); requestTotal.labels(uri, method, String.valueOf(status)).inc(); Histogram.Timer requestTimer = apiTimeConsuming.labels(uri, method, String.valueOf(status)).startTimer(); Object object; try { object = point.proceed(); } catch (Exception e) { requestError.labels(uri, method, String.valueOf(status)).inc(); throw e; } finally { requestTimer.observeDuration(); } return object; } else { return point.proceed(); } } }
1 2 3 4 5 6 7 8 9 10 11 12 @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface PrometheusMetrics { String name () default "" ; }
拦截器采集metrix指标
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 @Component public class CounterMetrics { @Autowired private CollectorRegistry collectorRegistry; @Bean public Counter httpRequestsTotalCounterCollector () { return Counter.build().name("http_requests_total" ).labelNames("path" , "method" , "code" ).help("http请求总计数" ) .register(collectorRegistry); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 @Component public class GuageMetrics { @Autowired private CollectorRegistry collectorRegistry; @Bean public Gauge httpInprogressRequestsGuageCollector () { return Gauge.build() .name("http_inprogress_requests" ).labelNames("path" , "method" , "code" ) .help("http当前正在处理的请求数" ).register(collectorRegistry); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 @Component public class HistogramMetrics { @Autowired private CollectorRegistry collectorRegistry; @Bean public Histogram httpRequestsBytesHistogramCollector () { return Histogram.build().labelNames("path" , "method" , "code" ) .name("http_requests_bytes_histogram" ).help("http bucket 请求大小区间分布图" ) .register(collectorRegistry); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 @Component public class SummaryMetrics { @Autowired private CollectorRegistry collectorRegistry; @Bean public Summary httpRequestsBytesSummaryCollector () { return Summary.build() .name("http_requests_bytes_summary" ) .labelNames("path" , "method" , "code" ) .help("Request bytes " ).register(collectorRegistry); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 @Slf4j public class PrometheusInterceptor implements HandlerInterceptor { @Autowired @Qualifier("httpRequestsTotalCounterCollector") private Counter httpRequestsTotalCounterCollector; @Autowired @Qualifier("httpInprogressRequestsGuageCollector") private Gauge httpInprogressRequestsGuageCollector; @Autowired @Qualifier("httpRequestsBytesHistogramCollector") private Histogram httpRequestsBytesHistogramCollector; @Autowired @Qualifier("httpRequestsBytesSummaryCollector") private Summary httpRequestsBytesSummaryCollector; @Override public boolean preHandle (HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { log.info("Prometheus intercrptor preHandle ..." ); String requestURI = request.getRequestURI(); String method = request.getMethod(); int status = response.getStatus(); httpRequestsTotalCounterCollector.labels(requestURI, method, String.valueOf(status)).inc(); httpInprogressRequestsGuageCollector.labels(requestURI, method, String.valueOf(status)).inc(); httpRequestsBytesHistogramCollector.labels(requestURI, method, String.valueOf(status)).observe(request.getContentLength()); httpRequestsBytesSummaryCollector.labels(requestURI, method, String.valueOf(status)).observe(new Random().nextInt(100 )); return HandlerInterceptor.super .preHandle(request, response, handler); } @Override public void postHandle (HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { HandlerInterceptor.super .postHandle(request, response, handler, modelAndView); } @Override public void afterCompletion (HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { String requestURI = request.getRequestURI(); String method = request.getMethod(); int status = response.getStatus(); httpInprogressRequestsGuageCollector.labels(requestURI, method, String.valueOf(status)).dec(); HandlerInterceptor.super .afterCompletion(request, response, handler, ex); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 @Configuration public class InterceptorConfiguration implements WebMvcConfigurer { @Bean public HandlerInterceptor prometheusInterceptor () { return new PrometheusInterceptor(); } @Override public void addInterceptors (InterceptorRegistry registry) { registry.addInterceptor(prometheusInterceptor()).addPathPatterns("/**" ); } }
最后
本文到此结束,感谢阅读。如果您觉得不错,请关注公众号【当我遇上你】,您的支持是我写作的最大动力。
参考