Deprecates the support for Prometheus simpleclient but ensures that it can work in conjunction with support for the latest Prometheus client auto-configuration. That is, if both are on the classpath, both will be auto-configured, except for the PrometheusScrapeEndpoint where the one that depends on the latest Prometheus client will take precedence over the simpleclient version. If a user wants to override this behavior, they can declare a bean of PrometheusSimpleclientScrapeEndpoint and it will be configured (see PrometheusSimpleclientScrapeEndpointIntegrationTests). They can also have both endpoints but will need to choose a different ID for one of them by creating a custom class annotated with e.g. @WebEndpoint(id = "customprometheus") and extend the corresponding endpoint class. See an example of this in SecondCustomPrometheusScrapeEndpointIntegrationTests.

Pushgateway support is not currently available in the latest Prometheus client, so auto-configuration support for it is only available with simpleclient.

Comment From: mhalbritter

Thanks for the PR Tommy!

If I build this locally and create a boot application using it, using these dependencies:

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-actuator'
    implementation 'org.springframework.boot:spring-boot-starter-web'
    runtimeOnly 'io.micrometer:micrometer-registry-prometheus'
}

it fails on startup with

Exception in thread "main" java.lang.IllegalStateException: java.lang.NoClassDefFoundError: io/micrometer/prometheus/HistogramFlavor
    at org.springframework.boot.SpringApplication.handleRunFailure(SpringApplication.java:825)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:344)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1354)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1343)
    at org.example.sb40023.Sb40023Application.main(Sb40023Application.java:10)
Caused by: java.lang.NoClassDefFoundError: io/micrometer/prometheus/HistogramFlavor
    at java.base/java.lang.Class.getDeclaredFields0(Native Method)
    at java.base/java.lang.Class.privateGetDeclaredFields(Class.java:3297)
    at java.base/java.lang.Class.getDeclaredField(Class.java:2608)
    at org.springframework.boot.context.properties.bind.DefaultBindConstructorProvider$Constructors.isInnerClass(DefaultBindConstructorProvider.java:144)
    at org.springframework.boot.context.properties.bind.DefaultBindConstructorProvider$Constructors.getCandidateConstructors(DefaultBindConstructorProvider.java:134)
    at org.springframework.boot.context.properties.bind.DefaultBindConstructorProvider$Constructors.getConstructors(DefaultBindConstructorProvider.java:103)
    at org.springframework.boot.context.properties.bind.DefaultBindConstructorProvider.getBindConstructor(DefaultBindConstructorProvider.java:55)
    at org.springframework.boot.context.properties.ConfigurationPropertiesBean.deduceBindMethod(ConfigurationPropertiesBean.java:288)
    at org.springframework.boot.context.properties.ConfigurationPropertiesBeanRegistrar.createBeanDefinition(ConfigurationPropertiesBeanRegistrar.java:93)
    at org.springframework.boot.context.properties.ConfigurationPropertiesBeanRegistrar.registerBeanDefinition(ConfigurationPropertiesBeanRegistrar.java:89)
    at org.springframework.boot.context.properties.ConfigurationPropertiesBeanRegistrar.register(ConfigurationPropertiesBeanRegistrar.java:61)
    at org.springframework.boot.context.properties.ConfigurationPropertiesBeanRegistrar.register(ConfigurationPropertiesBeanRegistrar.java:55)
    at java.base/java.lang.Iterable.forEach(Iterable.java:75)
    at org.springframework.boot.context.properties.EnableConfigurationPropertiesRegistrar.registerBeanDefinitions(EnableConfigurationPropertiesRegistrar.java:49)
    at org.springframework.context.annotation.ImportBeanDefinitionRegistrar.registerBeanDefinitions(ImportBeanDefinitionRegistrar.java:86)
    at org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader.lambda$loadBeanDefinitionsFromRegistrars$1(ConfigurationClassBeanDefinitionReader.java:376)
    at java.base/java.util.LinkedHashMap.forEach(LinkedHashMap.java:721)
    at org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader.loadBeanDefinitionsFromRegistrars(ConfigurationClassBeanDefinitionReader.java:375)
    at org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader.loadBeanDefinitionsForConfigurationClass(ConfigurationClassBeanDefinitionReader.java:148)
    at org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader.loadBeanDefinitions(ConfigurationClassBeanDefinitionReader.java:120)
    at org.springframework.context.annotation.ConfigurationClassPostProcessor.processConfigBeanDefinitions(ConfigurationClassPostProcessor.java:428)
    at org.springframework.context.annotation.ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry(ConfigurationClassPostProcessor.java:289)
    at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanDefinitionRegistryPostProcessors(PostProcessorRegistrationDelegate.java:349)
    at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:118)
    at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:788)
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:606)
    at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146)
    at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:754)
    at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:456)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:334)
    ... 3 more
Caused by: java.lang.ClassNotFoundException: io.micrometer.prometheus.HistogramFlavor
    at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:641)
    at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188)
    at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:525)
    ... 33 more

I think this is because of

private io.micrometer.prometheus.HistogramFlavor histogramFlavor = io.micrometer.prometheus.HistogramFlavor.Prometheus;

in PrometheusProperties.

Or is it supposed to only work if I have both io.micrometer:micrometer-registry-prometheus and io.micrometer:micrometer-registry-prometheus-simpleclient in my dependencies?

Comment From: mhalbritter

If I include both dependencies and do a curl http://localhost:8080/actuator/prometheus, I'll get this stacktrace:

2024-03-20T16:33:23.235+01:00 ERROR 219199 --- [sb-40023] [nio-8080-exec-7] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed: java.lang.IllegalArgumentException: 'jvm_info': Illegal metric name. The metric name must not include the '_info' suffix. Call PrometheusNaming.sanitizeMetricName(name) to avoid this error.] with root cause

java.lang.IllegalArgumentException: 'jvm_info': Illegal metric name. The metric name must not include the '_info' suffix. Call PrometheusNaming.sanitizeMetricName(name) to avoid this error.
    at io.prometheus.metrics.model.snapshots.MetricMetadata.validate(MetricMetadata.java:106) ~[prometheus-metrics-model-1.1.0.jar:na]
    at io.prometheus.metrics.model.snapshots.MetricMetadata.<init>(MetricMetadata.java:63) ~[prometheus-metrics-model-1.1.0.jar:na]
    at io.micrometer.prometheusmetrics.PrometheusMeterRegistry.getMetadata(PrometheusMeterRegistry.java:529) ~[micrometer-registry-prometheus-1.13.0-M2.jar:1.13.0-M2]
    at io.micrometer.prometheusmetrics.PrometheusMeterRegistry.getMetadata(PrometheusMeterRegistry.java:522) ~[micrometer-registry-prometheus-1.13.0-M2.jar:1.13.0-M2]
    at io.micrometer.prometheusmetrics.PrometheusMeterRegistry.lambda$newGauge$10(PrometheusMeterRegistry.java:315) ~[micrometer-registry-prometheus-1.13.0-M2.jar:1.13.0-M2]
    at io.micrometer.prometheusmetrics.MicrometerCollector.collect(MicrometerCollector.java:77) ~[micrometer-registry-prometheus-1.13.0-M2.jar:1.13.0-M2]
    at io.prometheus.metrics.model.registry.PrometheusRegistry.scrape(PrometheusRegistry.java:72) ~[prometheus-metrics-model-1.1.0.jar:na]
    at io.prometheus.metrics.model.registry.PrometheusRegistry.scrape(PrometheusRegistry.java:57) ~[prometheus-metrics-model-1.1.0.jar:na]
    at org.springframework.boot.actuate.metrics.export.prometheus.PrometheusScrapeEndpoint.scrape(PrometheusScrapeEndpoint.java:58) ~[spring-boot-actuator-3.3.0-SNAPSHOT.jar:3.3.0-SNAPSHOT]
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) ~[na:na]
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
    at java.base/java.lang.reflect.Method.invoke(Method.java:568) ~[na:na]
    at org.springframework.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:281) ~[spring-core-6.1.5.jar:6.1.5]
    at org.springframework.boot.actuate.endpoint.invoke.reflect.ReflectiveOperationInvoker.invoke(ReflectiveOperationInvoker.java:74) ~[spring-boot-actuator-3.3.0-SNAPSHOT.jar:3.3.0-SNAPSHOT]
    at org.springframework.boot.actuate.endpoint.annotation.AbstractDiscoveredOperation.invoke(AbstractDiscoveredOperation.java:60) ~[spring-boot-actuator-3.3.0-SNAPSHOT.jar:3.3.0-SNAPSHOT]
    at org.springframework.boot.actuate.endpoint.web.servlet.AbstractWebMvcEndpointHandlerMapping$ServletWebOperationAdapter.handle(AbstractWebMvcEndpointHandlerMapping.java:327) ~[spring-boot-actuator-3.3.0-SNAPSHOT.jar:3.3.0-SNAPSHOT]
    at org.springframework.boot.actuate.endpoint.web.servlet.AbstractWebMvcEndpointHandlerMapping$OperationHandler.handle(AbstractWebMvcEndpointHandlerMapping.java:434) ~[spring-boot-actuator-3.3.0-SNAPSHOT.jar:3.3.0-SNAPSHOT]
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) ~[na:na]
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
    at java.base/java.lang.reflect.Method.invoke(Method.java:568) ~[na:na]
    at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:255) ~[spring-web-6.1.5.jar:6.1.5]
    at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:188) ~[spring-web-6.1.5.jar:6.1.5]
    at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:118) ~[spring-webmvc-6.1.5.jar:6.1.5]
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:925) ~[spring-webmvc-6.1.5.jar:6.1.5]
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:830) ~[spring-webmvc-6.1.5.jar:6.1.5]
    at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) ~[spring-webmvc-6.1.5.jar:6.1.5]
    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1089) ~[spring-webmvc-6.1.5.jar:6.1.5]
    at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:979) ~[spring-webmvc-6.1.5.jar:6.1.5]
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1014) ~[spring-webmvc-6.1.5.jar:6.1.5]
    at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:903) ~[spring-webmvc-6.1.5.jar:6.1.5]
    at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:564) ~[tomcat-embed-core-10.1.19.jar:6.0]
    at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:885) ~[spring-webmvc-6.1.5.jar:6.1.5]
    at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:658) ~[tomcat-embed-core-10.1.19.jar:6.0]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:205) ~[tomcat-embed-core-10.1.19.jar:10.1.19]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149) ~[tomcat-embed-core-10.1.19.jar:10.1.19]
    at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:51) ~[tomcat-embed-websocket-10.1.19.jar:10.1.19]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174) ~[tomcat-embed-core-10.1.19.jar:10.1.19]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149) ~[tomcat-embed-core-10.1.19.jar:10.1.19]
    at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) ~[spring-web-6.1.5.jar:6.1.5]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) ~[spring-web-6.1.5.jar:6.1.5]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174) ~[tomcat-embed-core-10.1.19.jar:10.1.19]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149) ~[tomcat-embed-core-10.1.19.jar:10.1.19]
    at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93) ~[spring-web-6.1.5.jar:6.1.5]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) ~[spring-web-6.1.5.jar:6.1.5]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174) ~[tomcat-embed-core-10.1.19.jar:10.1.19]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149) ~[tomcat-embed-core-10.1.19.jar:10.1.19]
    at org.springframework.web.filter.ServerHttpObservationFilter.doFilterInternal(ServerHttpObservationFilter.java:109) ~[spring-web-6.1.5.jar:6.1.5]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) ~[spring-web-6.1.5.jar:6.1.5]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174) ~[tomcat-embed-core-10.1.19.jar:10.1.19]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149) ~[tomcat-embed-core-10.1.19.jar:10.1.19]
    at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) ~[spring-web-6.1.5.jar:6.1.5]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) ~[spring-web-6.1.5.jar:6.1.5]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174) ~[tomcat-embed-core-10.1.19.jar:10.1.19]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149) ~[tomcat-embed-core-10.1.19.jar:10.1.19]
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:167) ~[tomcat-embed-core-10.1.19.jar:10.1.19]
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:90) ~[tomcat-embed-core-10.1.19.jar:10.1.19]
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:482) ~[tomcat-embed-core-10.1.19.jar:10.1.19]
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:115) ~[tomcat-embed-core-10.1.19.jar:10.1.19]
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:93) ~[tomcat-embed-core-10.1.19.jar:10.1.19]
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74) ~[tomcat-embed-core-10.1.19.jar:10.1.19]
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:344) ~[tomcat-embed-core-10.1.19.jar:10.1.19]
    at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:391) ~[tomcat-embed-core-10.1.19.jar:10.1.19]
    at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:63) ~[tomcat-embed-core-10.1.19.jar:10.1.19]
    at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:896) ~[tomcat-embed-core-10.1.19.jar:10.1.19]
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1744) ~[tomcat-embed-core-10.1.19.jar:10.1.19]
    at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:52) ~[tomcat-embed-core-10.1.19.jar:10.1.19]
    at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191) ~[tomcat-embed-core-10.1.19.jar:10.1.19]
    at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659) ~[tomcat-embed-core-10.1.19.jar:10.1.19]
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:63) ~[tomcat-embed-core-10.1.19.jar:10.1.19]
    at java.base/java.lang.Thread.run(Thread.java:840) ~[na:na]

I think this is because of the auto-configured JvmInfoMetrics bean.

Comment From: mhalbritter

It works if i only use io.micrometer:micrometer-registry-prometheus-simpleclient.

Comment From: jonatan-ivanov

java.lang.IllegalArgumentException: 'jvm_info': Illegal metric name. The metric name must not include the '_info' suffix.

This error is because of some new validation logic in the new Prometheus client. :( I'm working on a fix but I'm also planning to talk about this with the maintainer of the client since as far as I know the server does not care.

Comment From: wilkinsona

Or is it supposed to only work if I have both io.micrometer:micrometer-registry-prometheus and io.micrometer:micrometer-registry-prometheus-simpleclient in my dependencies?

I think we need to get it to work with only one or the other but I'm not yet sure how to do that. My initial thoughts are in https://github.com/spring-projects/spring-boot/issues/39904 which we can hopefully close as superseded by this PR.

Comment From: shakuzen

Or is it supposed to only work if I have both io.micrometer:micrometer-registry-prometheus and io.micrometer:micrometer-registry-prometheus-simpleclient in my dependencies?

The intention was for it to work with either alone and with both. However, I apparently did not add a test that catches the issue you found. Is there an easy way to run the WebEndpointTest in PrometheusScrapeEndpointIntegrationTests with a filtered classloader?

Two possible solutions for this:

1) Duplicate the PrometheusProperties like other classes having one for simpleclient and one for the latest Prometheus client. I think I was trying to avoid this to have less public things to deprecate and remove later, and I wasn't sure about two ConfigurationProperties using the same prefix.

2) Replace io.micrometer.prometheus.HistogramFlavor with an equivalent enum type in Boot in PrometheusProperties and convert to the Micrometer type only in the PrometheusSimpleclientPropertiesConfigAdapter. This would be a breaking change for programatic access to PrometheusProperties but for binding via properties/yaml/envars it would be compatible.

What do you think?

As for the jvm_info metric issue, if Boot wants to get this PR into M3, maybe temporarily disabling binding the JvmInfoMetrics is the best solution until RC1 when we should have that fixed on the Micrometer side (see https://github.com/micrometer-metrics/micrometer/pull/4866).

Comment From: wilkinsona

As for the jvm_info metric issue, if Boot wants to get this PR into M3

I think it's too late for that unfortunately. I'd rather give ourselves a bit more time and fix this in RC1.

Comment From: mhalbritter

Hey Tommy, thanks for the update. When using this in a Boot app, it now fails with:

Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'prometheusMeterRegistry' defined in class path resource [org/springframework/boot/actuate/autoconfigure/metrics/export/prometheus/PrometheusMetricsExportAutoConfiguration.class]: Unexpected exception during bean creation
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:535) ~[spring-beans-6.1.5.jar:6.1.5]
    at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:326) ~[spring-beans-6.1.5.jar:6.1.5]
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[spring-beans-6.1.5.jar:6.1.5]
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:324) ~[spring-beans-6.1.5.jar:6.1.5]
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:200) ~[spring-beans-6.1.5.jar:6.1.5]
    at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:254) ~[spring-beans-6.1.5.jar:6.1.5]
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1443) ~[spring-beans-6.1.5.jar:6.1.5]
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1353) ~[spring-beans-6.1.5.jar:6.1.5]
    at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:904) ~[spring-beans-6.1.5.jar:6.1.5]
    at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:782) ~[spring-beans-6.1.5.jar:6.1.5]
    ... 88 common frames omitted
Caused by: java.lang.TypeNotPresentException: Type io.prometheus.metrics.tracer.common.SpanContext not present
    at java.base/sun.reflect.generics.factory.CoreReflectionFactory.makeNamedType(CoreReflectionFactory.java:117) ~[na:na]
    at java.base/sun.reflect.generics.visitor.Reifier.visitClassTypeSignature(Reifier.java:125) ~[na:na]
    at java.base/sun.reflect.generics.tree.ClassTypeSignature.accept(ClassTypeSignature.java:49) ~[na:na]
    at java.base/sun.reflect.generics.visitor.Reifier.reifyTypeArguments(Reifier.java:68) ~[na:na]
    at java.base/sun.reflect.generics.visitor.Reifier.visitClassTypeSignature(Reifier.java:138) ~[na:na]
    at java.base/sun.reflect.generics.tree.ClassTypeSignature.accept(ClassTypeSignature.java:49) ~[na:na]
    at java.base/sun.reflect.generics.repository.ConstructorRepository.computeParameterTypes(ConstructorRepository.java:111) ~[na:na]
    at java.base/sun.reflect.generics.repository.ConstructorRepository.getParameterTypes(ConstructorRepository.java:87) ~[na:na]
    at java.base/java.lang.reflect.Executable.getGenericParameterTypes(Executable.java:298) ~[na:na]
    at java.base/java.lang.reflect.Method.getGenericParameterTypes(Method.java:333) ~[na:na]
    at org.springframework.core.MethodParameter.getGenericParameterType(MethodParameter.java:519) ~[spring-core-6.1.5.jar:6.1.5]
    at org.springframework.core.SerializableTypeWrapper$MethodParameterTypeProvider.getType(SerializableTypeWrapper.java:297) ~[spring-core-6.1.5.jar:6.1.5]
    at org.springframework.core.SerializableTypeWrapper.forTypeProvider(SerializableTypeWrapper.java:106) ~[spring-core-6.1.5.jar:6.1.5]
    at org.springframework.core.ResolvableType.forType(ResolvableType.java:1459) ~[spring-core-6.1.5.jar:6.1.5]
    at org.springframework.core.ResolvableType.forMethodParameter(ResolvableType.java:1380) ~[spring-core-6.1.5.jar:6.1.5]
    at org.springframework.core.ResolvableType.forMethodParameter(ResolvableType.java:1362) ~[spring-core-6.1.5.jar:6.1.5]
    at org.springframework.core.ResolvableType.forMethodParameter(ResolvableType.java:1329) ~[spring-core-6.1.5.jar:6.1.5]
    at org.springframework.beans.factory.config.DependencyDescriptor.getResolvableType(DependencyDescriptor.java:292) ~[spring-beans-6.1.5.jar:6.1.5]
    at org.springframework.beans.factory.support.GenericTypeAwareAutowireCandidateResolver.checkGenericTypeMatch(GenericTypeAwareAutowireCandidateResolver.java:77) ~[spring-beans-6.1.5.jar:6.1.5]
    at org.springframework.beans.factory.support.GenericTypeAwareAutowireCandidateResolver.isAutowireCandidate(GenericTypeAwareAutowireCandidateResolver.java:69) ~[spring-beans-6.1.5.jar:6.1.5]
    at org.springframework.beans.factory.annotation.QualifierAnnotationAutowireCandidateResolver.isAutowireCandidate(QualifierAnnotationAutowireCandidateResolver.java:147) ~[spring-beans-6.1.5.jar:6.1.5]
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.isAutowireCandidate(DefaultListableBeanFactory.java:885) ~[spring-beans-6.1.5.jar:6.1.5]
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.isAutowireCandidate(DefaultListableBeanFactory.java:844) ~[spring-beans-6.1.5.jar:6.1.5]
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.isAutowireCandidate(DefaultListableBeanFactory.java:827) ~[spring-beans-6.1.5.jar:6.1.5]
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.findAutowireCandidates(DefaultListableBeanFactory.java:1652) ~[spring-beans-6.1.5.jar:6.1.5]
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1397) ~[spring-beans-6.1.5.jar:6.1.5]
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1353) ~[spring-beans-6.1.5.jar:6.1.5]
    at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:904) ~[spring-beans-6.1.5.jar:6.1.5]
    at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:782) ~[spring-beans-6.1.5.jar:6.1.5]
    at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:542) ~[spring-beans-6.1.5.jar:6.1.5]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1335) ~[spring-beans-6.1.5.jar:6.1.5]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1165) ~[spring-beans-6.1.5.jar:6.1.5]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:562) ~[spring-beans-6.1.5.jar:6.1.5]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:522) ~[spring-beans-6.1.5.jar:6.1.5]
    ... 97 common frames omitted
Caused by: java.lang.ClassNotFoundException: io.prometheus.metrics.tracer.common.SpanContext
    at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:641) ~[na:na]
    at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188) ~[na:na]
    at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:525) ~[na:na]
    at java.base/java.lang.Class.forName0(Native Method) ~[na:na]
    at java.base/java.lang.Class.forName(Class.java:467) ~[na:na]
    at java.base/sun.reflect.generics.factory.CoreReflectionFactory.makeNamedType(CoreReflectionFactory.java:114) ~[na:na]
    ... 130 common frames omitted

I think the problem is

    @Bean
    @ConditionalOnMissingBean
    public PrometheusMeterRegistry prometheusMeterRegistry(PrometheusConfig prometheusConfig,
            PrometheusRegistry prometheusRegistry, Clock clock, ObjectProvider<SpanContext> spanContext) {

The SpanContext class is only there if prometheus-metrics-tracer-common is on the classpath, and it appears that it's not by default (where is this coming from? Is this transitively pulled in by something tracing related or do users have to add that themselves?).

Comment From: mhalbritter

And we have to do something for the duplicated property classes. Right now, the metadata JSON has this:

    {
      "name": "management.prometheus.metrics.export.enabled",
      "type": "java.lang.Boolean",
      "description": "Whether exporting of metrics to this backend is enabled.",
      "sourceType": "org.springframework.boot.actuate.autoconfigure.metrics.export.prometheus.PrometheusProperties",
      "defaultValue": true
    },

which is the new one.

But it also has this:

    {
      "name": "management.prometheus.metrics.export.enabled",
      "type": "java.lang.Boolean",
      "description": "Whether exporting of metrics to this backend is enabled.",
      "sourceType": "org.springframework.boot.actuate.autoconfigure.metrics.export.prometheus.PrometheusSimpleclientProperties",
      "defaultValue": true,
      "deprecated": true,
      "deprecation": {}
    },

which is the deprecated old one.

In IntelliJ, it looks like this:

SpringBoot Add support for Prometheus Client 1.x and simpleclient

which is weird.

Can we get away with not duplicating the classes, but essentially duplicating the HistogramFlavor enum in Boot's codebase?

Comment From: mhalbritter

Thanks a lot Tommy!