micrometer custom metrics

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.

doc

Keywords: Spring SpringBoot Java curl

Added by Octave91 on Sun, 05 Apr 2020 19:04:01 +0300