一、背景java
使用prometheus作监控系统时(java),通常的作法就是系统暴露端点URL给 prometheus,诸如 /metrics ,而后prometheus拉取这个url中的指标数据,
主要用到的东西有(spring-boot-starter-actuator, micrometer-registry-prometheus)
可是 问题是,默认暴露的端点是 /prometheus,全路径为 /actuator/prometheus,只有一个URL,那么若是有这样的场景该如何:
那么就须要添加多个URL,该如何作呢?git
二、实践github
查看micrometer源码(主要是PrometheusMetricsExportAutoConfiguration
此类),能够知道默认端点prometheus
的初始化流程,
也没查到其余公开的API能够方便的添加暴露多个端点URL,那么就能够仿照他的流程本身再写一套这个配置,经实践本身写的配置不会太多,
例如 我想暴露一个 apple
的端点,即 /actuator/apple
的URL,那么代码以下:web
首先是定义端点spring
@WebEndpoint(id = "apple") public class AppleScrapeEndPoint { private final CollectorRegistry collectorRegistry; public AppleScrapeEndPoint(CollectorRegistry collectorRegistry) { this.collectorRegistry = collectorRegistry; } @ReadOperation(produces = TextFormat.CONTENT_TYPE_004) public String scrape() { try { Writer writer = new StringWriter(); TextFormat.write004(writer, this.collectorRegistry.metricFamilySamples()); return writer.toString(); } catch (IOException ex) { // This actually never happens since StringWriter::write() doesn't throw any // IOException throw new RuntimeException("Writing metrics failed", ex); } } }
而后是定义ApplePropertiesConfigAdapter
app
public class ApplePropertiesConfigAdapter extends PropertiesConfigAdapter<PrometheusProperties> implements PrometheusConfig { ApplePropertiesConfigAdapter(PrometheusProperties properties) { super(properties); } @Override public String get(String key) { return null; } @Override public boolean descriptions() { return get(PrometheusProperties::isDescriptions, PrometheusConfig.super::descriptions); } @Override public Duration step() { return get(PrometheusProperties::getStep, PrometheusConfig.super::step); } }
最后是配置初始化ide
@Configuration @AutoConfigureAfter(value = {PrometheusMetricsExportAutoConfiguration.class}) @ConditionalOnClass(value = {PrometheusMeterRegistry.class}) @ConditionalOnProperty(prefix = "management.metrics.export.apple", name = "enabled", havingValue = "true", matchIfMissing = true) public class ApplePrometheusAutoConfiguration { @Bean(name = "applePrometheusProperties") @ConfigurationProperties(prefix = "management.metrics.export.apple") public PrometheusProperties applePrometheusProperties() { return new PrometheusProperties(); } @Bean(name = "applePrometheusConfig") public PrometheusConfig applePrometheusConfig() { return new ApplePropertiesConfigAdapter(applePrometheusProperties()); } @Bean(name = "appleMeterRegistry") public PrometheusMeterRegistry appleMeterRegistry(Clock clock) { return new PrometheusMeterRegistry(applePrometheusConfig(), appleCollectorRegistry(), clock); } @Bean(name = "appleCollectorRegistry") public CollectorRegistry appleCollectorRegistry() { System.out.println("=======appleCollectorRegistry"); return new CollectorRegistry(true); } @Configuration @ConditionalOnEnabledEndpoint(endpoint = AppleScrapeEndPoint.class) public static class TicketScrapeEndpointConfiguration { @Resource private CollectorRegistry appleCollectorRegistry; @Bean(name = "appleEndpoint") @ConditionalOnMissingBean public AppleScrapeEndPoint appleEndpoint() { return new AppleScrapeEndPoint(appleCollectorRegistry); } } }
而后再配置文件中配置新添加的端点spring-boot
management: endpoint: prometheus: # 关闭默认的prometheus端点,新建本身的 enabled: false health: show-details: always endpoints: web: exposure: include: ['health', 'apple']
这样就完成了,就能够在 /actuator
这个URL中看到本身新添加的URL。
若是想添加多个,那么照着上述copy一份代码,改更名称啥的就能够了,别忘了在配置文件中include
里添加。post
这样基本能解决问题了,可是看着不太舒服,我有多个URL就须要COPY多份这样的代码,并且基本还差很少同样,因此咱们能够考虑 主动配置,
减小重复代码的建立,具体以下:ui
例如我想再添加一个a
的端点,即 /actuator/a
首先是定义端点:
@Component @DatagridEndpoint @WebEndpoint(id = "a") public class AEndpoint { private CollectorRegistry collectorRegistry; public AEndpoint(){ } @ReadOperation(produces = TextFormat.CONTENT_TYPE_004) public String scrape() { try { Writer writer = new StringWriter(); TextFormat.write004(writer, this.collectorRegistry.metricFamilySamples()); return writer.toString(); } catch (IOException ex) { // This actually never happens since StringWriter::write() doesn't throw any // IOException throw new RuntimeException("Writing metrics failed", ex); } } }
而后是配置流程
@Slf4j @Component @AutoConfigureAfter(value = {PrometheusMetricsExportAutoConfiguration.class}) @ConditionalOnClass(value = {PrometheusMeterRegistry.class}) public class MetricsExportAutoConfiguration implements BeanDefinitionRegistryPostProcessor, ApplicationContextAware { private ApplicationContext applicationContext; @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; } public class AutoPropertiesConfigAdapter extends PropertiesConfigAdapter<PrometheusProperties> implements io.micrometer.prometheus.PrometheusConfig { AutoPropertiesConfigAdapter(PrometheusProperties properties) { super(properties); } @Override public String get(String key) { return null; } @Override public boolean descriptions() { return get(PrometheusProperties::isDescriptions, io.micrometer.prometheus.PrometheusConfig.super::descriptions); } @Override public Duration step() { return get(PrometheusProperties::getStep, PrometheusConfig.super::step); } } @Override public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException { } @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory factory) throws BeansException { Map<String, Object> beansMap = applicationContext.getBeansWithAnnotation(DatagridEndpoint.class); if (CollectionUtils.isEmpty(beansMap)) { return; } Clock clock = applicationContext.getBean(Clock.class); Preconditions.checkNotNull(clock); for (Map.Entry<String, Object> entry : beansMap.entrySet()) { Object bean = entry.getValue(); WebEndpoint webEndpoint = bean.getClass().getAnnotation(WebEndpoint.class); if (null == webEndpoint) { continue; } String endPointName = webEndpoint.id(); if (Strings.isNullOrEmpty(endPointName)) { continue; } // prometheus properties bean BeanDefinitionBuilder prometheusPropertiesBeanDefinitionBuilder = BeanDefinitionBuilder .genericBeanDefinition(PrometheusProperties.class); BeanDefinition prometheusPropertiesBeanDefinition = prometheusPropertiesBeanDefinitionBuilder.getRawBeanDefinition(); ((DefaultListableBeanFactory) factory).registerBeanDefinition(endPointName + "PrometheusProperties", prometheusPropertiesBeanDefinition); PrometheusProperties prometheusProperties = applicationContext.getBean(endPointName + "PrometheusProperties", PrometheusProperties.class); // prometheus config bean BeanDefinitionBuilder prometheusConfigBeanDefinitionBuilder = BeanDefinitionBuilder .genericBeanDefinition(AutoPropertiesConfigAdapter.class, () -> new AutoPropertiesConfigAdapter(prometheusProperties)); BeanDefinition prometheusConfigBeanDefinition = prometheusConfigBeanDefinitionBuilder.getRawBeanDefinition(); ((DefaultListableBeanFactory) factory).registerBeanDefinition(endPointName + "PrometheusConfig", prometheusConfigBeanDefinition); // collector registry bean BeanDefinitionBuilder collectorRegistryBeanDefinitionBuilder = BeanDefinitionBuilder .genericBeanDefinition(CollectorRegistry.class, () -> new CollectorRegistry(true)); BeanDefinition collectorRegistryBeanDefinition = collectorRegistryBeanDefinitionBuilder.getRawBeanDefinition(); ((DefaultListableBeanFactory) factory).registerBeanDefinition(endPointName + "CollectorRegistry", collectorRegistryBeanDefinition); PrometheusConfig prometheusConfig = applicationContext.getBean(endPointName + "PrometheusConfig", AutoPropertiesConfigAdapter.class); CollectorRegistry collectorRegistry = applicationContext.getBean(endPointName + "CollectorRegistry", CollectorRegistry.class); // prometheus meter registry bean BeanDefinitionBuilder meterRegistryBeanDefinitionBuilder = BeanDefinitionBuilder .genericBeanDefinition(PrometheusMeterRegistry.class, () -> new PrometheusMeterRegistry(prometheusConfig, collectorRegistry, clock)); BeanDefinition meterRegistryBeanDefinition = meterRegistryBeanDefinitionBuilder.getRawBeanDefinition(); ((DefaultListableBeanFactory) factory).registerBeanDefinition(endPointName + "MeterRegistry", meterRegistryBeanDefinition); Reflect.on(bean).set("collectorRegistry", collectorRegistry); } } }
最后也是在配置文件里include
添加暴露的端点
management: endpoint: prometheus: # 关闭默认的prometheus端点,新建本身的 enabled: false health: show-details: always endpoints: web: exposure: include: ['health', 'apple', 'a']
ok,下次若是想添加额外的,那么只须要建立和端点a
同样的类,改下id
值,而后再配置文件里include
里暴露下就能够了,MetricsExportAutoConfiguration
这个类会自动建立其余的配置,就不须要重复代码了
哦,对,关于DatagridEndpoint
这个注解,就只是个简单的注解而已,以下
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface DatagridEndpoint { }
这样就完成了。
固然,上述只是很潦草的代码,各位能够看着本身改改,更适合本身的项目!
源码见:https://github.com/kute/prome...
有问题及时联系