Spring Boot 2.x 自定义metrics 并导出到influxdb
Step 1.添加依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <dependency> <groupId>io.micrometer</groupId> <artifactId>micrometer-registry-influx</artifactId> </dependency>
Step 2.修改Spring配置文件application.yml
management:
endpoints:
enabled-by-default: true
web:
exposure:
include: '*'
endpoint:
health:
show-details: always
metrics:
export:
influx:
enabled: true
db: spring
uri: http://localhost:8086
step: 10s
user-name: admin
password: admin
Step 3. 实现micrometer的MeterBinder接口
public class DemoMetrics implements MeterBinder { AtomicInteger count = new AtomicInteger(0); @Override public void bindTo(MeterRegistry meterRegistry) { Gauge.builder("demo.count", count, c -> c.incrementAndGet()) .tags("host", "localhost") .description("demo of custom meter binder") .register(meterRegistry); }
这里实现了MeterBinder接口的bindTo方法,将要采集的指标注册到MeterRegistry
Step 4.注册
@Bean public DemoMetrics demoMetrics(){ return new DemoMetrics(); }
在springboot只要标注下bean,注入到spring容器后,springboot会自动注册到registry。springboot已经帮你初始化了包括UptimeMetrics等一系列metrics。
源码解析
MetricsAutoConfiguration
spring-boot-actuator-autoconfigure-2.0.0.RELEASE-sources.jar!/org/springframework/boot/actuate/autoconfigure/metrics/MetricsAutoConfiguration.java
@Configuration @ConditionalOnClass(Timed.class) @EnableConfigurationProperties(MetricsProperties.class) @AutoConfigureBefore(CompositeMeterRegistryAutoConfiguration.class) public class MetricsAutoConfiguration { @Bean @ConditionalOnMissingBean public Clock micrometerClock() { return Clock.SYSTEM; } @Bean public static MeterRegistryPostProcessor meterRegistryPostProcessor( ApplicationContext context) { return new MeterRegistryPostProcessor(context); } @Bean @Order(0) public PropertiesMeterFilter propertiesMeterFilter(MetricsProperties properties) { return new PropertiesMeterFilter(properties); } @Configuration @ConditionalOnProperty(value = "management.metrics.binders.jvm.enabled", matchIfMissing = true) static class JvmMeterBindersConfiguration { @Bean @ConditionalOnMissingBean public JvmGcMetrics jvmGcMetrics() { return new JvmGcMetrics(); } @Bean @ConditionalOnMissingBean public JvmMemoryMetrics jvmMemoryMetrics() { return new JvmMemoryMetrics(); } @Bean @ConditionalOnMissingBean public JvmThreadMetrics jvmThreadMetrics() { return new JvmThreadMetrics(); } @Bean @ConditionalOnMissingBean public ClassLoaderMetrics classLoaderMetrics() { return new ClassLoaderMetrics(); } } @Configuration static class MeterBindersConfiguration { @Bean @ConditionalOnClass(name = { "ch.qos.logback.classic.LoggerContext", "org.slf4j.LoggerFactory" }) @Conditional(LogbackLoggingCondition.class) @ConditionalOnMissingBean(LogbackMetrics.class) @ConditionalOnProperty(value = "management.metrics.binders.logback.enabled", matchIfMissing = true) public LogbackMetrics logbackMetrics() { return new LogbackMetrics(); } @Bean @ConditionalOnProperty(value = "management.metrics.binders.uptime.enabled", matchIfMissing = true) @ConditionalOnMissingBean public UptimeMetrics uptimeMetrics() { return new UptimeMetrics(); } @Bean @ConditionalOnProperty(value = "management.metrics.binders.processor.enabled", matchIfMissing = true) @ConditionalOnMissingBean public ProcessorMetrics processorMetrics() { return new ProcessorMetrics(); } @Bean @ConditionalOnProperty(name = "management.metrics.binders.files.enabled", matchIfMissing = true) @ConditionalOnMissingBean public FileDescriptorMetrics fileDescriptorMetrics() { return new FileDescriptorMetrics(); } } static class LogbackLoggingCondition extends SpringBootCondition { @Override public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) { ILoggerFactory loggerFactory = LoggerFactory.getILoggerFactory(); ConditionMessage.Builder message = ConditionMessage .forCondition("LogbackLoggingCondition"); if (loggerFactory instanceof LoggerContext) { return ConditionOutcome.match( message.because("ILoggerFactory is a Logback LoggerContext")); } return ConditionOutcome .noMatch(message.because("ILoggerFactory is an instance of " + loggerFactory.getClass().getCanonicalName())); } } }
可以看到这里注册了好多metrics,比如UptimeMetrics,JvmGcMetrics,ProcessorMetrics,FileDescriptorMetrics等这里重点看使用@Bean标注了MeterRegistryPostProcessor
MeterRegistryPostProcessor
spring-boot-actuator-autoconfigure-2.0.0.RELEASE-sources.jar!/org/springframework/boot/actuate/autoconfigure/metrics/MeterRegistryPostProcessor.java
class MeterRegistryPostProcessor implements BeanPostProcessor { private final ApplicationContext context; private volatile MeterRegistryConfigurer configurer; MeterRegistryPostProcessor(ApplicationContext context) { this.context = context; } @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { if (bean instanceof MeterRegistry) { getConfigurer().configure((MeterRegistry) bean); } return bean; } @SuppressWarnings("unchecked") private MeterRegistryConfigurer getConfigurer() { if (this.configurer == null) { this.configurer = new MeterRegistryConfigurer(beansOfType(MeterBinder.class), beansOfType(MeterFilter.class), (Collection<MeterRegistryCustomizer<?>>) (Object) beansOfType( MeterRegistryCustomizer.class), this.context.getBean(MetricsProperties.class).isUseGlobalRegistry()); } return this.configurer; } private <T> Collection<T> beansOfType(Class<T> type) { return this.context.getBeansOfType(type).values(); } }
可以看到这里new了一个MeterRegistryConfigurer,重点注意这里使用beansOfType(MeterBinder.class)方法的返回值给其构造器
MeterRegistryConfigurer
spring-boot-actuator-autoconfigure-2.0.0.RELEASE-sources.jar!/org/springframework/boot/actuate/autoconfigure/metrics/MeterRegistryConfigurer.java
class MeterRegistryConfigurer { private final Collection<MeterRegistryCustomizer<?>> customizers; private final Collection<MeterFilter> filters; private final Collection<MeterBinder> binders; private final boolean addToGlobalRegistry; MeterRegistryConfigurer(Collection<MeterBinder> binders, Collection<MeterFilter> filters, Collection<MeterRegistryCustomizer<?>> customizers, boolean addToGlobalRegistry) { this.binders = (binders != null ? binders : Collections.emptyList()); this.filters = (filters != null ? filters : Collections.emptyList()); this.customizers = (customizers != null ? customizers : Collections.emptyList()); this.addToGlobalRegistry = addToGlobalRegistry; } void configure(MeterRegistry registry) { if (registry instanceof CompositeMeterRegistry) { return; } // Customizers must be applied before binders, as they may add custom // tags or alter timer or summary configuration. customize(registry); addFilters(registry); addBinders(registry); if (this.addToGlobalRegistry && registry != Metrics.globalRegistry) { Metrics.addRegistry(registry); } } @SuppressWarnings("unchecked") private void customize(MeterRegistry registry) { LambdaSafe.callbacks(MeterRegistryCustomizer.class, this.customizers, registry) .withLogger(MeterRegistryConfigurer.class) .invoke((customizer) -> customizer.customize(registry)); } private void addFilters(MeterRegistry registry) { this.filters.forEach(registry.config()::meterFilter); } private void addBinders(MeterRegistry registry) { this.binders.forEach((binder) -> binder.bindTo(registry)); } }
可以看到configure方法里头调用了addBinders,也就是把托管给spring容器的MeterBinder实例bindTo到meterRegistry