spring-projects / spring-boot

Spring Boot helps you to create Spring-powered, production-grade applications and services with absolute minimum fuss.
https://spring.io/projects/spring-boot
Apache License 2.0
75.34k stars 40.72k forks source link

Auto-configure VirtualThreadMetrics from micrometer-java21 #43122

Open shakuzen opened 1 week ago

shakuzen commented 1 week ago

Micrometer added a new MeterBinder for virtual thread metrics via https://github.com/micrometer-metrics/micrometer/pull/5067 - documentation is at https://docs.micrometer.io/micrometer/reference/reference/jvm.html#_java_21_metrics. It is in a new module micrometer-java21. It would be nice if Spring Boot has auto-configuration for VirtualThreadMetrics.

nosan commented 1 week ago

class file has wrong version 65.0, should be 61.0 Please remove or make sure it appears in the correct subdirectory of the classpath.

micrometer-java21 is built using JDK21, whereas Spring Boot source code is JDK17 source compatible, hence either reflection or multi-release jar should be used to add support for VirtualThreadMetrics.

I've prototyped changes in my branch by using reflection and FactoryBean.

I also checked VirtualThreadMetrics with a native-image, and unfortunately, I got the following exception:

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'virtualThreadMetrics': Instantiation of supplied bean failed
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.obtainFromSupplier(AbstractAutowireCapableBeanFactory.java:1239)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1176)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:563)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523)
        at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:336)
        at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:288)
        at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:334)
        at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199)
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.instantiateSingleton(DefaultListableBeanFactory.java:1122)
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingleton(DefaultListableBeanFactory.java:1093)
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:1030)
        at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:987)
        at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:627)
        at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146)
        at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:752)
        at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:439)
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:318)
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:1361)
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:1350)
        at task.gh43122.Gh43122Application.main(Gh43122Application.java:12)
        at java.base@21.0.5/java.lang.invoke.LambdaForm$DMH/sa346b79c.invokeStaticInit(LambdaForm$DMH)
Caused by: java.lang.UnsatisfiedLinkError: jdk.jfr.internal.JVM.getPid()Ljava/lang/String; [symbol: Java_jdk_jfr_internal_JVM_getPid or Java_jdk_jfr_internal_JVM_getPid__]
        at org.graalvm.nativeimage.builder/com.oracle.svm.core.jni.access.JNINativeLinkage.getOrFindEntryPoint(JNINativeLinkage.java:152)
        at org.graalvm.nativeimage.builder/com.oracle.svm.core.jni.JNIGeneratedMethodSupport.nativeCallAddress(JNIGeneratedMethodSupport.java:54)
        at jdk.jfr@21.0.5/jdk.jfr.internal.JVM.getPid(Native Method)
        at jdk.jfr@21.0.5/jdk.jfr.internal.Repository.createRepository(Repository.java:110)
        at jdk.jfr@21.0.5/jdk.jfr.internal.Repository.setBasePath(Repository.java:65)
        at jdk.jfr@21.0.5/jdk.jfr.internal.Repository.ensureRepository(Repository.java:79)
        at jdk.jfr@21.0.5/jdk.jfr.internal.PlatformRecorder.<init>(PlatformRecorder.java:81)
        at jdk.jfr@21.0.5/jdk.jfr.FlightRecorder.getFlightRecorder(FlightRecorder.java:171)
        at jdk.jfr@21.0.5/jdk.jfr.Recording.<init>(Recording.java:107)
        at jdk.jfr@21.0.5/jdk.jfr.Recording.<init>(Recording.java:131)
        at jdk.jfr@21.0.5/jdk.jfr.consumer.RecordingStream.<init>(RecordingStream.java:108)
        at jdk.jfr@21.0.5/jdk.jfr.consumer.RecordingStream.<init>(RecordingStream.java:101)
        at io.micrometer.java21.instrument.binder.jdk.VirtualThreadMetrics.createRecordingStream(VirtualThreadMetrics.java:78)
        at io.micrometer.java21.instrument.binder.jdk.VirtualThreadMetrics.<init>(VirtualThreadMetrics.java:57)
        at io.micrometer.java21.instrument.binder.jdk.VirtualThreadMetrics.<init>(VirtualThreadMetrics.java:49)
        at task.gh43122.Gh43122Application.virtualThreadMetrics(Gh43122Application.java:17)
        at task.gh43122.Gh43122Application$$SpringCGLIB$$0.CGLIB$virtualThreadMetrics$0(<generated>)
        at task.gh43122.Gh43122Application$$SpringCGLIB$$FastClass$$1.invoke(<generated>)
        at org.springframework.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:258)
        at org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.intercept(ConfigurationClassEnhancer.java:348)
        at task.gh43122.Gh43122Application$$SpringCGLIB$$0.virtualThreadMetrics(<generated>)
        at task.gh43122.Gh43122Application__BeanDefinitions.lambda$getVirtualThreadMetricsInstanceSupplier$0(Gh43122Application__BeanDefinitions.java:32)
        at org.springframework.util.function.ThrowingFunction.apply(ThrowingFunction.java:63)
        at org.springframework.util.function.ThrowingFunction.apply(ThrowingFunction.java:51)
        at org.springframework.beans.factory.aot.BeanInstanceSupplier.lambda$get$1(BeanInstanceSupplier.java:219)
        at org.springframework.util.function.ThrowingSupplier.get(ThrowingSupplier.java:58)
        at org.springframework.util.function.ThrowingSupplier.get(ThrowingSupplier.java:46)
        at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiateWithFactoryMethod(SimpleInstantiationStrategy.java:88)
        at org.springframework.beans.factory.aot.BeanInstanceSupplier.invokeBeanSupplier(BeanInstanceSupplier.java:256)
        at org.springframework.beans.factory.aot.BeanInstanceSupplier.get(BeanInstanceSupplier.java:219)
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.obtainInstanceFromSupplier(DefaultListableBeanFactory.java:979)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.obtainFromSupplier(AbstractAutowireCapableBeanFactory.java:1233)
        ... 20 more

Sample to reproduce: gh-43122.zip

mvn clean spring-boot:build-image -Pnative
docker run -p 8080:8080 docker.io/library/gh-43122:0.0.1-SNAPSHOT
oleksiizaitsev7 commented 3 days ago

Hi it can be done with some conditions as we have here. But I'm not sure if that is the right way or preferable strategy for the java 21 support