一、背景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...
有問題及時聯繫