Below worked fine with Spring Boot 2.7.11 and earlier, stopped working with Spring Boot 3.
http://host:8989/metrics
{"timestamp":"2023-05-15T08:17:22.903+00:00","status":404,"error":"Not Found","path":"/metrics/"}
Tested other health endpoint with root - works as expected.
management.server.port=8989
management.server.base-path=/metrics
management.endpoints.web.base-path=/
management.endpoints.web.path-mapping.prometheus=/
Works for health:
management.server.port=8989
management.server.base-path=/metrics
management.endpoints.web.base-path=/
management.endpoints.web.path-mapping.health=/
Was asked to open in spring boot by micrometer owners: https://github.com/micrometer-metrics/micrometer/issues/3839
Comment From: wilkinsona
Thanks for the report but I cannot reproduce the behavior that you have described.
With 2.7.11, the server does not start when using the configuration properties that you have shared:
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.7.11)
2023-05-15 20:41:31.108 INFO 65152 --- [ main] com.example.demo.Gh35426Application : Starting Gh35426Application using Java 17.0.1 on wilkinsonaGMD6R.vmware.com with PID 65152 (/Users/awilkinson/dev/workspaces/spring-projects/spring-boot/3.0.x/gh-35426/bin/main started by awilkinson in /Users/awilkinson/dev/workspaces/spring-projects/spring-boot/3.0.x/gh-35426)
2023-05-15 20:41:31.110 INFO 65152 --- [ main] com.example.demo.Gh35426Application : No active profile set, falling back to 1 default profile: "default"
2023-05-15 20:41:31.858 INFO 65152 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8080 (http)
2023-05-15 20:41:31.866 INFO 65152 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat]
2023-05-15 20:41:31.867 INFO 65152 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/9.0.74]
2023-05-15 20:41:31.943 INFO 65152 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
2023-05-15 20:41:31.943 INFO 65152 --- [ main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 795 ms
2023-05-15 20:41:32.405 INFO 65152 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path ''
2023-05-15 20:41:32.456 INFO 65152 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8989 (http)
2023-05-15 20:41:32.457 INFO 65152 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat]
2023-05-15 20:41:32.457 INFO 65152 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/9.0.74]
2023-05-15 20:41:32.470 INFO 65152 --- [ main] o.a.c.c.C.[.[localhost].[/metrics] : Initializing Spring embedded WebApplicationContext
2023-05-15 20:41:32.470 INFO 65152 --- [ main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 62 ms
2023-05-15 20:41:32.481 INFO 65152 --- [ main] o.s.b.a.e.web.EndpointLinksResolver : Exposing 1 endpoint(s) beneath base path ''
2023-05-15 20:41:32.484 WARN 65152 --- [ main] ConfigServletWebServerApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'webEndpointServletHandlerMapping' defined in class path resource [org/springframework/boot/actuate/autoconfigure/endpoint/web/servlet/WebMvcEndpointManagementContextConfiguration.class]: Invocation of init method failed; nested exception is java.lang.IllegalStateException: Ambiguous mapping. Cannot map 'Actuator root web endpoint' method
Actuator root web endpoint
to {GET [], produces [application/vnd.spring-boot.actuator.v3+json || application/vnd.spring-boot.actuator.v2+json || application/json]}: There is already 'Actuator web endpoint 'health'' bean method
Actuator web endpoint 'health' mapped.
2023-05-15 20:41:32.487 INFO 65152 --- [ main] o.apache.catalina.core.StandardService : Stopping service [Tomcat]
2023-05-15 20:41:32.491 WARN 65152 --- [ main] ConfigServletWebServerApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.context.ApplicationContextException: Failed to start bean 'webServerStartStop'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'webEndpointServletHandlerMapping' defined in class path resource [org/springframework/boot/actuate/autoconfigure/endpoint/web/servlet/WebMvcEndpointManagementContextConfiguration.class]: Invocation of init method failed; nested exception is java.lang.IllegalStateException: Ambiguous mapping. Cannot map 'Actuator root web endpoint' method
Actuator root web endpoint
to {GET [], produces [application/vnd.spring-boot.actuator.v3+json || application/vnd.spring-boot.actuator.v2+json || application/json]}: There is already 'Actuator web endpoint 'health'' bean method
Actuator web endpoint 'health' mapped.
2023-05-15 20:41:32.502 INFO 65152 --- [ main] o.apache.catalina.core.StandardService : Stopping service [Tomcat]
2023-05-15 20:41:32.514 INFO 65152 --- [ main] ConditionEvaluationReportLoggingListener :
Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
2023-05-15 20:41:32.531 ERROR 65152 --- [ main] o.s.boot.SpringApplication : Application run failed
org.springframework.context.ApplicationContextException: Failed to start bean 'webServerStartStop'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'webEndpointServletHandlerMapping' defined in class path resource [org/springframework/boot/actuate/autoconfigure/endpoint/web/servlet/WebMvcEndpointManagementContextConfiguration.class]: Invocation of init method failed; nested exception is java.lang.IllegalStateException: Ambiguous mapping. Cannot map 'Actuator root web endpoint' method
Actuator root web endpoint
to {GET [], produces [application/vnd.spring-boot.actuator.v3+json || application/vnd.spring-boot.actuator.v2+json || application/json]}: There is already 'Actuator web endpoint 'health'' bean method
Actuator web endpoint 'health' mapped.
at org.springframework.context.support.DefaultLifecycleProcessor.doStart(DefaultLifecycleProcessor.java:181) ~[spring-context-5.3.27.jar:5.3.27]
at org.springframework.context.support.DefaultLifecycleProcessor.access$200(DefaultLifecycleProcessor.java:54) ~[spring-context-5.3.27.jar:5.3.27]
at org.springframework.context.support.DefaultLifecycleProcessor$LifecycleGroup.start(DefaultLifecycleProcessor.java:356) ~[spring-context-5.3.27.jar:5.3.27]
at java.base/java.lang.Iterable.forEach(Iterable.java:75) ~[na:na]
at org.springframework.context.support.DefaultLifecycleProcessor.startBeans(DefaultLifecycleProcessor.java:155) ~[spring-context-5.3.27.jar:5.3.27]
at org.springframework.context.support.DefaultLifecycleProcessor.onRefresh(DefaultLifecycleProcessor.java:123) ~[spring-context-5.3.27.jar:5.3.27]
at org.springframework.context.support.AbstractApplicationContext.finishRefresh(AbstractApplicationContext.java:937) ~[spring-context-5.3.27.jar:5.3.27]
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:586) ~[spring-context-5.3.27.jar:5.3.27]
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:147) ~[spring-boot-2.7.11.jar:2.7.11]
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:731) ~[spring-boot-2.7.11.jar:2.7.11]
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:408) ~[spring-boot-2.7.11.jar:2.7.11]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:307) ~[spring-boot-2.7.11.jar:2.7.11]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1303) ~[spring-boot-2.7.11.jar:2.7.11]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1292) ~[spring-boot-2.7.11.jar:2.7.11]
at com.example.demo.Gh35426Application.main(Gh35426Application.java:10) ~[main/:na]
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'webEndpointServletHandlerMapping' defined in class path resource [org/springframework/boot/actuate/autoconfigure/endpoint/web/servlet/WebMvcEndpointManagementContextConfiguration.class]: Invocation of init method failed; nested exception is java.lang.IllegalStateException: Ambiguous mapping. Cannot map 'Actuator root web endpoint' method
Actuator root web endpoint
to {GET [], produces [application/vnd.spring-boot.actuator.v3+json || application/vnd.spring-boot.actuator.v2+json || application/json]}: There is already 'Actuator web endpoint 'health'' bean method
Actuator web endpoint 'health' mapped.
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1804) ~[spring-beans-5.3.27.jar:5.3.27]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:620) ~[spring-beans-5.3.27.jar:5.3.27]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:542) ~[spring-beans-5.3.27.jar:5.3.27]
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:335) ~[spring-beans-5.3.27.jar:5.3.27]
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[spring-beans-5.3.27.jar:5.3.27]
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:333) ~[spring-beans-5.3.27.jar:5.3.27]
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:208) ~[spring-beans-5.3.27.jar:5.3.27]
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:955) ~[spring-beans-5.3.27.jar:5.3.27]
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:920) ~[spring-context-5.3.27.jar:5.3.27]
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:583) ~[spring-context-5.3.27.jar:5.3.27]
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:147) ~[spring-boot-2.7.11.jar:2.7.11]
at org.springframework.boot.actuate.autoconfigure.web.server.ManagementContextAutoConfiguration$DifferentManagementContextConfiguration.onApplicationEvent(ManagementContextAutoConfiguration.java:149) ~[spring-boot-actuator-autoconfigure-2.7.11.jar:2.7.11]
at org.springframework.boot.actuate.autoconfigure.web.server.ManagementContextAutoConfiguration$DifferentManagementContextConfiguration.onApplicationEvent(ManagementContextAutoConfiguration.java:122) ~[spring-boot-actuator-autoconfigure-2.7.11.jar:2.7.11]
at org.springframework.context.event.SimpleApplicationEventMulticaster.doInvokeListener(SimpleApplicationEventMulticaster.java:176) ~[spring-context-5.3.27.jar:5.3.27]
at org.springframework.context.event.SimpleApplicationEventMulticaster.invokeListener(SimpleApplicationEventMulticaster.java:169) ~[spring-context-5.3.27.jar:5.3.27]
at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:143) ~[spring-context-5.3.27.jar:5.3.27]
at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:421) ~[spring-context-5.3.27.jar:5.3.27]
at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:378) ~[spring-context-5.3.27.jar:5.3.27]
at org.springframework.boot.web.servlet.context.WebServerStartStopLifecycle.start(WebServerStartStopLifecycle.java:46) ~[spring-boot-2.7.11.jar:2.7.11]
at org.springframework.context.support.DefaultLifecycleProcessor.doStart(DefaultLifecycleProcessor.java:178) ~[spring-context-5.3.27.jar:5.3.27]
... 14 common frames omitted
Caused by: java.lang.IllegalStateException: Ambiguous mapping. Cannot map 'Actuator root web endpoint' method
Actuator root web endpoint
to {GET [], produces [application/vnd.spring-boot.actuator.v3+json || application/vnd.spring-boot.actuator.v2+json || application/json]}: There is already 'Actuator web endpoint 'health'' bean method
Actuator web endpoint 'health' mapped.
at org.springframework.web.servlet.handler.AbstractHandlerMethodMapping$MappingRegistry.validateMethodMapping(AbstractHandlerMethodMapping.java:669) ~[spring-webmvc-5.3.27.jar:5.3.27]
at org.springframework.web.servlet.handler.AbstractHandlerMethodMapping$MappingRegistry.register(AbstractHandlerMethodMapping.java:635) ~[spring-webmvc-5.3.27.jar:5.3.27]
at org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.registerMapping(AbstractHandlerMethodMapping.java:189) ~[spring-webmvc-5.3.27.jar:5.3.27]
at org.springframework.boot.actuate.endpoint.web.servlet.AbstractWebMvcEndpointHandlerMapping.registerLinksMapping(AbstractWebMvcEndpointHandlerMapping.java:252) ~[spring-boot-actuator-2.7.11.jar:2.7.11]
at org.springframework.boot.actuate.endpoint.web.servlet.AbstractWebMvcEndpointHandlerMapping.initHandlerMethods(AbstractWebMvcEndpointHandlerMapping.java:182) ~[spring-boot-actuator-2.7.11.jar:2.7.11]
at org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.afterPropertiesSet(AbstractHandlerMethodMapping.java:213) ~[spring-webmvc-5.3.27.jar:5.3.27]
at org.springframework.boot.actuate.endpoint.web.servlet.AbstractWebMvcEndpointHandlerMapping.afterPropertiesSet(AbstractWebMvcEndpointHandlerMapping.java:171) ~[spring-boot-actuator-2.7.11.jar:2.7.11]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1863) ~[spring-beans-5.3.27.jar:5.3.27]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1800) ~[spring-beans-5.3.27.jar:5.3.27]
... 33 common frames omitted
With 3.0.6 the server starts but a request to localhost:8989/metrics/ returns the hypermedia links to all of the actuator's endpoints.
If you would like us to spend some more time investigating, please spend some time providing a complete yet minimal sample that reproduces the behavior that you have described. You can share it with us by pushing it to a separate repository on GitHub or by zipping it up and attaching it to this issue.
Comment From: pavelorehov
Created small demo app where it is reproduces with spring boot 3. Added comments in pom.xml how to downgrade to 2.7.11 where it works perfect. https://github.com/pavelorehov/sb3prometheus/tree/main/demo
Comment From: wilkinsona
Thank you. I've now reproduced the behaviour that you have described. The following properties were missing from those shared above:
management.endpoints.web.discovery.enabled=false
management.endpoints.web.exposure.include=*
I believe the change in behaviour is due to https://github.com/spring-projects/spring-framework/issues/28552. The health endpoint isn't affected as its operations mean that it has a mapping for /**. Most other endpoints have no such mapping and they also cannot be mapped to the root any more.
When the base path and an endpoint's path mapping are set to /, the resulting pattern for the endpoint is an empty string. This then clashes with Tomcat redirecting requests to the context root by appending a /. In the context of this issue, this means that a request to http://localhost:8989/metrics is redirected to http://localhost:8989/metrics/ which doesn't match. As a workaround, the problem does not occur with 3.0.x if server.tomcat.redirect-context-root is set to false.
https://github.com/spring-projects/spring-boot/issues/31563 tracked Boot adapting to the changes in Framework but this looks like a scenario that we didn't consider. We'll have to review the changes that we made and see if we can accommodate this scenario without breaking anything else.
Comment From: pavelorehov
Thank you, can server.tomcat.redirect-context-root=false be temporary WA until you find permanent solution ?
Comment From: wilkinsona
Yes, it's certainly worth trying to see if it meets your needs.
Comment From: wilkinsona
@Eddyzhang7 That seems unrelated to this issue as you don't appear to have mapped anything to /. If you believe you have found a bug, please open a new issue and provide a minimal sample that reproduces the problem.