order
This paper mainly studies how to use metrics of custom micrometer
Example
DemoMetrics
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); } }
Here, the bindTo method of the MeterBinder interface is implemented, and the indicators to be collected are registered in the MeterRegistry
register
- Primitive mode
new DemoMetrics().bindTo(registry);
- springboot autoconfigure
@Bean public DemoMetrics demoMetrics(){ return new DemoMetrics(); }
After the bean is labeled in springboot and injected into the spring container, springboot will automatically register in the registry. Springboot has helped you initialize a series of metrics, including UptimeMetrics. See the source code analysis section for details.
Verification
curl -i http://localhost:8080/actuator/metrics/demo.count
Return instance
{ "name": "demo.count", "measurements": [ { "statistic": "VALUE", "value": 6 } ], "availableTags": [ { "tag": "host", "values": [ "localhost" ] } ] }
Source code analysis
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())); } } }
You can see that there are many metrics registered here, such as UptimeMetrics, JvmGcMetrics, ProcessorMetrics, FileDescriptorMetrics, etc
Here we focus on using @ Bean to annotate 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(); } }
As you can see here, a MeterRegistryConfigurer is in new. Pay attention to using the return value of the beansOfType(MeterBinder.class) method to its constructor
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)); } }
You can see that addBinders is called in the configure method, that is, the MeterBinder instance bindTo managed to the spring container is transferred to the meterRegistry
Summary
For the micrometer introduced by springboot2, to customize metrics, you only need to implement the MeterBinder interface, and then host it to spring. The autoconfigure of springboot helps you automatically register to the meterRegistry.