本文主要研究下如何使用自定义micrometer的metrics

实例

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);
    }
}
这里实现了MeterBinder接口的bindTo方法,将要采集的指标注册到MeterRegistry

注册

  • 原始方式
new DemoMetrics().bindTo(registry);
  • springboot autoconfigure
@Bean
public DemoMetrics demoMetrics(){
    return new DemoMetrics();
}
在springboot只要标注下bean,注入到spring容器后,springboot会自动注册到registry。springboot已经帮你初始化了包括UptimeMetrics等一系列metrics。详见源码解析部分。

验证

curl -i http://localhost:8080/actuator/metrics/demo.count

返回实例

{
  "name": "demo.count",
  "measurements": [
    {
      "statistic": "VALUE",
      "value": 6
    }
  ],
  "availableTags": [
    {
      "tag": "host",
      "values": [
        "localhost"
      ]
    }
  ]
}

源码解析

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

小结

springboot2引入的micrometer,自定义metrics只需要实现MeterBinder接口,然后托管给spring即可,springboot的autoconfigure帮你自动注册到meterRegistry。

doc


codecraft
11.9k 声望2k 粉丝

当一个代码的工匠回首往事时,不因虚度年华而悔恨,也不因碌碌无为而羞愧,这样,当他老的时候,可以很自豪告诉世人,我曾经将代码注入生命去打造互联网的浪潮之巅,那是个很疯狂的时代,我在一波波的浪潮上留下...


引用和评论

0 条评论